From 15e24ec54f0268ed3729c7296556c20cb6fc5f42 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Mon, 31 Mar 2014 17:21:12 +0300 Subject: Fix too wide Table columns while sorting from init (#13517) Change-Id: I2c356c539541381e744c8960d00aa3e018beb67d --- client/src/com/vaadin/client/ui/VScrollTable.java | 9 ++++----- client/src/com/vaadin/client/ui/table/TableConnector.java | 10 +++++++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index 7c22fa2199..261c50b73b 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -1130,6 +1130,9 @@ public class VScrollTable extends FlowPanel implements HasWidgets, // cell to accomodate for the size of the sort arrow. HeaderCell sortedHeader = tHead.getHeaderCell(sortColumn); if (sortedHeader != null) { + // Mark header as sorted now. Any earlier marking would lead to + // columns with wrong sizes + sortedHeader.setSorted(true); tHead.resizeCaptionContainer(sortedHeader); } // Also recalculate the width of the captionContainer element in the @@ -3329,11 +3332,7 @@ public class VScrollTable extends FlowPanel implements HasWidgets, if (col.hasAttribute("sortable")) { c.setSortable(true); - if (cid.equals(sortColumn)) { - c.setSorted(true); - } else { - c.setSorted(false); - } + c.setSorted(false); } else { c.setSortable(false); } diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java index e8b14955ed..4d7d3c6277 100644 --- a/client/src/com/vaadin/client/ui/table/TableConnector.java +++ b/client/src/com/vaadin/client/ui/table/TableConnector.java @@ -303,9 +303,13 @@ public class TableConnector extends AbstractHasComponentsConnector implements getWidget().tabIndex = getState().tabIndex; getWidget().setProperTabIndex(); - if (getWidget().initializedAndAttached) { - getWidget().resizeSortedColumnForSortIndicator(); - } + Scheduler.get().scheduleFinally(new ScheduledCommand() { + + @Override + public void execute() { + getWidget().resizeSortedColumnForSortIndicator(); + } + }); // Remember this to detect situations where overflow hack might be // needed during scrolling -- cgit v1.2.3 From a86798ca4d5bae6e07e61a79665db40e9364dff3 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Fri, 4 Apr 2014 13:02:49 +0300 Subject: Fix ScreenshotTB3Test removing reference images Change-Id: Iaeb6943b534e3df4dad2f8689837425cdb6a65bc --- uitest/src/com/vaadin/tests/tb3/ScreenshotTB3Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uitest/src/com/vaadin/tests/tb3/ScreenshotTB3Test.java b/uitest/src/com/vaadin/tests/tb3/ScreenshotTB3Test.java index 9f966d7973..c423eaff11 100644 --- a/uitest/src/com/vaadin/tests/tb3/ScreenshotTB3Test.java +++ b/uitest/src/com/vaadin/tests/tb3/ScreenshotTB3Test.java @@ -107,7 +107,7 @@ public abstract class ScreenshotTB3Test extends AbstractTB3Test { for (File referenceFile : referenceFiles) { if (testBench(driver).compareScreen(referenceFile)) { // There might be failure files because of retries in TestBench. - deleteFailureFiles(referenceFile); + deleteFailureFiles(getErrorFileFromReference(referenceFile)); break; } else { failedReferenceFiles.add(referenceFile); -- cgit v1.2.3 From 591996b94e7b7c432708611819087b8e03d21ea5 Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Mon, 7 Apr 2014 13:50:18 +0300 Subject: Updated javadoc copyright years. Change-Id: I56c8d6d34af90d0dc8ca605b62ebfe5ad95a4e5e --- build.properties | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.properties b/build.properties index 548169c457..6a296d315a 100644 --- a/build.properties +++ b/build.properties @@ -1,8 +1,7 @@ javadoc.doctitle=

Vaadin

-javadoc.bottom=Copyright © 2000-2011 Vaadin Ltd. All Rights Reserved. +javadoc.bottom=Copyright © 2000-2014 Vaadin Ltd. All Rights Reserved. ivy.organisation=com.vaadin vaadin.vendor=Vaadin Ltd vaadin.url=http://vaadin.com vaadin.java.version=1.6 vaadin.version=0.0.0.unversioned-development-build - -- cgit v1.2.3 From 08b0589d05e69da58b58c42b96a69c9331fdce25 Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Fri, 4 Apr 2014 10:27:46 +0300 Subject: Refactored TimeoutRedirectResetsOnActivity tests. -Added RetryOnFail test rule. Change-Id: I0b2cd5af0a144c29855c3ba1e158ba54900a9523 --- .../ui/TimeoutRedirectResetsOnActivity.java | 40 +++++++++-- .../ui/TimeoutRedirectResetsOnActivityTest.java | 81 ++++++++++------------ uitest/src/com/vaadin/tests/tb3/RetryOnFail.java | 65 +++++++++++++++++ 3 files changed, 135 insertions(+), 51 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/tb3/RetryOnFail.java diff --git a/uitest/src/com/vaadin/tests/components/ui/TimeoutRedirectResetsOnActivity.java b/uitest/src/com/vaadin/tests/components/ui/TimeoutRedirectResetsOnActivity.java index 2c649c9ca8..bf58846060 100644 --- a/uitest/src/com/vaadin/tests/components/ui/TimeoutRedirectResetsOnActivity.java +++ b/uitest/src/com/vaadin/tests/components/ui/TimeoutRedirectResetsOnActivity.java @@ -23,22 +23,50 @@ import com.vaadin.server.VaadinRequest; import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Label; public class TimeoutRedirectResetsOnActivity extends AbstractTestUI { + private int maxActiveInterval = 15; + private int timeoutOverhead = 15; + @Override protected void setup(VaadinRequest request) { - setupTimout(request); + setupTimeout(request); + + Label startedLabel = new Label(); + startedLabel.setValue(String.valueOf(System.currentTimeMillis())); + startedLabel.setId("startedTime"); + + Label originalLabel = new Label(); + originalLabel.setId("originalExpireTime"); + originalLabel.setValue(String.valueOf(getExpireTime())); - addComponent(new Button("clicky", new Button.ClickListener() { + final Label expiresLabel = new Label(); + expiresLabel.setId("actualExpireTime"); + + Button button = new Button("Reset", new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { - // NOOP + expiresLabel.setValue(String.valueOf(getExpireTime())); } - })); + + }); + button.setId("reset"); + + addComponent(button); + + addComponent(startedLabel); + addComponent(originalLabel); + addComponent(expiresLabel); + } + + private long getExpireTime() { + return System.currentTimeMillis() + + (maxActiveInterval + timeoutOverhead) * 1000; } - private void setupTimout(VaadinRequest request) { + private void setupTimeout(VaadinRequest request) { request.getService().setSystemMessagesProvider( new SystemMessagesProvider() { @Override @@ -57,7 +85,7 @@ public class TimeoutRedirectResetsOnActivity extends AbstractTestUI { * implementation details in * com.vaadin.server.communication.MetadataWriter */ - getSession().getSession().setMaxInactiveInterval(10); + getSession().getSession().setMaxInactiveInterval(maxActiveInterval); } @Override diff --git a/uitest/src/com/vaadin/tests/components/ui/TimeoutRedirectResetsOnActivityTest.java b/uitest/src/com/vaadin/tests/components/ui/TimeoutRedirectResetsOnActivityTest.java index 272bacb8d5..f4a7a68983 100644 --- a/uitest/src/com/vaadin/tests/components/ui/TimeoutRedirectResetsOnActivityTest.java +++ b/uitest/src/com/vaadin/tests/components/ui/TimeoutRedirectResetsOnActivityTest.java @@ -15,71 +15,62 @@ */ package com.vaadin.tests.components.ui; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; +import org.junit.Rule; import org.junit.Test; -import org.openqa.selenium.By; -import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; import com.vaadin.tests.tb3.MultiBrowserTest; +import com.vaadin.tests.tb3.RetryOnFail; public class TimeoutRedirectResetsOnActivityTest extends MultiBrowserTest { + + @Rule + // Timing issues are really hard to resolve in a way that this test would be + // 100% reliable on all browsers. Hence we shall allow one retry. + public RetryOnFail retry = new RetryOnFail(); + + private int waitBeforeActivity = 4000; + private int communicationOverhead = 2000; + + private static int i = 0; + @Test public void verifyRedirectWorks() throws Exception { setDebug(true); openTestURL(); - long startTime = System.currentTimeMillis(); - while (System.currentTimeMillis() - startTime < 30000) { - clickTheButton(); - Thread.sleep(1000); - } + long startedTime = getTime("startedTime"); + long originalExpireTime = getTime("originalExpireTime"); - assertTrue("button disappeared before timeout", buttonIsStillThere()); + Thread.sleep(waitBeforeActivity); + hitButton("reset"); - Thread.sleep(30000); - assertTrue("no redirection occurred within 30 seconds", - !buttonIsStillThere()); - } + Thread.sleep(200); - private boolean buttonIsStillThere() { - try { - return getButton() != null; - } catch (NoSuchElementException e) { - return false; - } - } + long actualExpireTime = getTime("actualExpireTime"); + + Thread.sleep(originalExpireTime - startedTime - waitBeforeActivity); + + assertThat(driver.getCurrentUrl(), is(getTestUrl())); + + testBench().disableWaitForVaadin(); + Thread.sleep(actualExpireTime - originalExpireTime + + communicationOverhead); - private void clickTheButton() { - getButton().click(); + assertThat(driver.getCurrentUrl(), is(not(getTestUrl()))); } - private WebElement getButton() { - /* - * For some reason, the vaadinElement() method doesn't work when tests - * are run outside of "/run/" and "/run-push/" contexts. The given error - * message says that the generated Vaadin path doesn't match any - * elements, but when that selector is put into the recorder, the - * recorder finds it. - * - * XPath works fine. - */ - /*- - return vaadinElement("/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]"); - */ - return getDriver().findElement( - By.xpath("//div[contains(@class,'v-button')]")); + private long getTime(String id) { + WebElement element = vaadinElementById(id); + return Long.parseLong(element.getText()); } @Override - protected String getDeploymentPath() { - /* - * AbstractTB3Test assumes only /run/ and /run-push/ contexts, so this - * method needs some overriding. - */ - return "/12446/" - + TimeoutRedirectResetsOnActivity.class.getCanonicalName() - + "?restartApplication&debug"; + protected String getTestUrl() { + return super.getTestUrl() + "?restartApplication"; } } diff --git a/uitest/src/com/vaadin/tests/tb3/RetryOnFail.java b/uitest/src/com/vaadin/tests/tb3/RetryOnFail.java new file mode 100644 index 0000000000..b8a3c0e5f3 --- /dev/null +++ b/uitest/src/com/vaadin/tests/tb3/RetryOnFail.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.tests.tb3; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * ALWAYS declare the reason for using this test rule in a + * test. + * + *

+ * Violators and abusers of this rule will be punished. + *

+ * + * @since 7.1.14 + * @author Vaadin Ltd + */ +public class RetryOnFail implements TestRule { + private int retryCount = 1; + + @Override + public Statement apply(Statement base, Description description) { + return statement(base, description); + } + + private Statement statement(final Statement base, + final Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + Throwable caughtThrowable = null; + + for (int i = 0; i <= retryCount; i++) { + try { + base.evaluate(); + return; + } catch (Throwable t) { + caughtThrowable = t; + System.err.println(String.format( + "%s: run %s/%s failed.", + description.getDisplayName(), i + 1, + retryCount + 1)); + System.err.println(t.getMessage()); + } + } + throw caughtThrowable; + } + }; + } +} -- cgit v1.2.3 From 5057c59a02b6570f6a314dee3379688f0a48981b Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Wed, 2 Apr 2014 08:51:27 +0300 Subject: Added integration test for WebSphere Portal. (#13309) Change-Id: I576fb145cd34f98ffa90657e20f5a7c1854f470c --- uitest/integration_tests.xml | 30 +++- uitest/ivy.xml | 4 +- .../WebSpherePortalIntegrationTest.java | 163 +++++++++++++++++++++ 3 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 uitest/src/com/vaadin/tests/integration/WebSpherePortalIntegrationTest.java diff --git a/uitest/integration_tests.xml b/uitest/integration_tests.xml index 77c5a94e26..c0d73580bb 100644 --- a/uitest/integration_tests.xml +++ b/uitest/integration_tests.xml @@ -114,7 +114,7 @@ - + @@ -123,6 +123,7 @@ + @@ -343,6 +344,20 @@ + + ##teamcity[testStarted name='websphereportal8' flowId='websphereportal8'] + + + + + + + + + + ##teamcity[testFinished name='websphereportal8' flowId='websphereportal8']" + + @@ -375,7 +390,7 @@ - @@ -387,7 +402,7 @@ - @@ -439,6 +454,7 @@ + @@ -468,11 +484,11 @@ - - @@ -515,7 +531,7 @@ - @@ -565,7 +581,7 @@ - diff --git a/uitest/ivy.xml b/uitest/ivy.xml index 3ed5609319..add29985f1 100644 --- a/uitest/ivy.xml +++ b/uitest/ivy.xml @@ -57,7 +57,9 @@ + rev="${vaadin.version}" conf="build-provided-> build"> + + Date: Sat, 5 Apr 2014 15:14:59 +0300 Subject: Drag image for text-area should contain text of text-area (#13557). Change-Id: Ief653c3f15d18fdd076f0fb80f8a91ae429a54d3 --- client/src/com/vaadin/client/ui/VTextArea.java | 15 +++++- .../com/vaadin/client/ui/dd/VDragCloneAware.java | 40 ++++++++++++++ client/src/com/vaadin/client/ui/dd/VDragEvent.java | 31 +++++++++++ .../draganddropwrapper/DragAndDropTextArea.java | 56 ++++++++++++++++++++ .../DragAndDropTextAreaTest.java | 61 ++++++++++++++++++++++ 5 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 client/src/com/vaadin/client/ui/dd/VDragCloneAware.java create mode 100644 uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextArea.java create mode 100644 uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextAreaTest.java diff --git a/client/src/com/vaadin/client/ui/VTextArea.java b/client/src/com/vaadin/client/ui/VTextArea.java index 2a697969df..627d14a058 100644 --- a/client/src/com/vaadin/client/ui/VTextArea.java +++ b/client/src/com/vaadin/client/ui/VTextArea.java @@ -17,6 +17,7 @@ package com.vaadin.client.ui; import com.google.gwt.core.client.Scheduler; +import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Overflow; import com.google.gwt.dom.client.Style.WhiteSpace; import com.google.gwt.dom.client.TextAreaElement; @@ -30,6 +31,7 @@ import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.vaadin.client.BrowserInfo; import com.vaadin.client.Util; +import com.vaadin.client.ui.dd.VDragCloneAware; /** * This class represents a multiline textfield (textarea). @@ -40,7 +42,7 @@ import com.vaadin.client.Util; * @author Vaadin Ltd. * */ -public class VTextArea extends VTextField { +public class VTextArea extends VTextField implements VDragCloneAware { public static final String CLASSNAME = "v-textarea"; private boolean wordwrap = true; private MaxLengthHandler maxLengthHandler = new MaxLengthHandler(); @@ -294,4 +296,15 @@ public class VTextArea extends VTextField { // class instead of directly each other. } + @Override + public void initDragImageCopy(Element element) { + // Fix for #13557 - drag image doesn't show original text area text. + // It happens because "value" property is not copied into the cloned + // element + String value = getElement().getPropertyString("value"); + if (value != null) { + element.setPropertyString("value", value); + } + } + } diff --git a/client/src/com/vaadin/client/ui/dd/VDragCloneAware.java b/client/src/com/vaadin/client/ui/dd/VDragCloneAware.java new file mode 100644 index 0000000000..546ca88a9f --- /dev/null +++ b/client/src/com/vaadin/client/ui/dd/VDragCloneAware.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.client.ui.dd; + +import com.google.gwt.dom.client.Element; + +/** + * Widget could implement this interface if drag image requires additional + * initialization/configuration. Method {@link #initDragImageCopy(Element)} + * allows to change/correct drag image element when element is dragged via DnD. + * + * @since 7.1 + * @author Vaadin Ltd + */ +public interface VDragCloneAware { + + /** + * This method is called for cloned element which corresponds + * to the widget element. One could modify/correct this element + * for drag image. + * + * @since 7.1 + * @param element + * cloned element of drag image + */ + void initDragImageCopy(Element element); +} diff --git a/client/src/com/vaadin/client/ui/dd/VDragEvent.java b/client/src/com/vaadin/client/ui/dd/VDragEvent.java index a4667e57f3..0f0e1ecdad 100644 --- a/client/src/com/vaadin/client/ui/dd/VDragEvent.java +++ b/client/src/com/vaadin/client/ui/dd/VDragEvent.java @@ -20,11 +20,14 @@ import java.util.Map; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.dom.client.TableElement; import com.google.gwt.dom.client.TableSectionElement; import com.google.gwt.event.dom.client.MouseOverEvent; import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.EventListener; import com.vaadin.client.BrowserInfo; import com.vaadin.client.Util; @@ -184,6 +187,7 @@ public class VDragEvent { cloneNode = table.cast(); } } + syncContent(element, cloneNode); if (alignImageToEvent) { int absoluteTop = element.getAbsoluteTop(); int absoluteLeft = element.getAbsoluteLeft(); @@ -198,4 +202,31 @@ public class VDragEvent { } + /** + * Do additional content sync between original element and its + * copy if needed. + * + * @since 7.1 + * @param original + * original element + * @param copy + * copy of original element + */ + protected void syncContent(Element original, Element copy) { + for (int i = 0; i < original.getChildCount(); i++) { + Node child = original.getChild(i); + if (child instanceof Element) { + syncContent((Element) child, (Element) copy.getChild(i)); + } + } + doSyncContent(original, copy); + } + + private void doSyncContent(Element original, Element copy) { + EventListener eventListener = Event.getEventListener(original); + if (eventListener instanceof VDragCloneAware) { + ((VDragCloneAware) eventListener).initDragImageCopy(copy); + } + } + } diff --git a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextArea.java b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextArea.java new file mode 100644 index 0000000000..410eb21170 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextArea.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.tests.components.draganddropwrapper; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.DragAndDropWrapper; +import com.vaadin.ui.DragAndDropWrapper.DragStartMode; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.VerticalLayout; + +/** + * Test UI for text area: drag image should contain text-area text. + * + * @since 7.1 + * @author Vaadin Ltd + */ +public class DragAndDropTextArea extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + VerticalLayout dndLayout = new VerticalLayout(); + + TextArea area = new TextArea(); + area.setValue("text"); + + dndLayout.addComponent(area); + DragAndDropWrapper wrapper = new DragAndDropWrapper(dndLayout); + wrapper.setDragStartMode(DragStartMode.WRAPPER); + addComponent(wrapper); + } + + @Override + protected String getTestDescription() { + return "Drag image for textarea should contain text-area text"; + } + + @Override + protected Integer getTicketNumber() { + return 13557; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextAreaTest.java b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextAreaTest.java new file mode 100644 index 0000000000..2abf3599ac --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextAreaTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.tests.components.draganddropwrapper; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test for drag image of text area which should contain text-area text. + * + * @since 7.1 + * @author Vaadin Ltd + */ +public class DragAndDropTextAreaTest extends MultiBrowserTest { + + @Test + public void testTextAreaDndImage() { + openTestURL(); + + WebElement wrapper = driver.findElement(By + .className("v-verticallayout")); + Actions actions = new Actions(driver); + actions.clickAndHold(wrapper); + actions.moveByOffset(50, 50); + actions.perform(); + + WebElement dragElement = driver.findElement(By + .className("v-drag-element")); + List children = dragElement.findElements(By.xpath(".//*")); + boolean found = false; + for (WebElement child : children) { + if ("text".equals(child.getAttribute("value"))) { + found = true; + } + } + + Assert.assertTrue( + "Text value is not found in the DnD image of text area", found); + } + +} -- cgit v1.2.3 From ee4ae9fac8342f51ff5152cf878df125a743aef3 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Fri, 11 Apr 2014 08:41:06 +0300 Subject: Merge from 7.1 to 7.2 Change-Id: I3db340a7c971be6abf18514f044114802edfc4de --- scripts/automerge7.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/automerge7.sh b/scripts/automerge7.sh index 079dec7eea..3349d8d622 100755 --- a/scripts/automerge7.sh +++ b/scripts/automerge7.sh @@ -2,7 +2,7 @@ IGNORE=7.0 FROM=7.1 -TO=master +TO=7.2 IGNORE_HEAD=origin/$IGNORE FROM_HEAD=origin/$FROM -- cgit v1.2.3 From 51c2e93172b0132ead54573c74117efac29c1cec Mon Sep 17 00:00:00 2001 From: Jonatan Kronqvist Date: Fri, 11 Apr 2014 14:15:52 +0300 Subject: Revert "Added ItemSetAddEvent and ItemSetRemoveEvent (#2794)" This reverts commit 85251833de3bd101d388b20fdb9b02c532a9f1c9. Conflicts: server/src/com/vaadin/data/util/AbstractInMemoryContainer.java server/tests/src/com/vaadin/data/util/TestIndexedContainer.java Change-Id: I49dfdc58ae5841892c6d8e205c520437da9a05e5 --- server/src/com/vaadin/data/Container.java | 54 ------ .../vaadin/data/util/AbstractBeanContainer.java | 20 +-- .../data/util/AbstractInMemoryContainer.java | 149 ++--------------- .../src/com/vaadin/data/util/IndexedContainer.java | 7 +- .../vaadin/data/util/BeanItemContainerTest.java | 185 --------------------- .../com/vaadin/data/util/TestIndexedContainer.java | 113 ------------- 6 files changed, 18 insertions(+), 510 deletions(-) diff --git a/server/src/com/vaadin/data/Container.java b/server/src/com/vaadin/data/Container.java index 1e053d1091..ef507c5f31 100644 --- a/server/src/com/vaadin/data/Container.java +++ b/server/src/com/vaadin/data/Container.java @@ -582,60 +582,6 @@ public interface Container extends Serializable { public Item addItemAt(int index, Object newItemId) throws UnsupportedOperationException; - /** - * An Event object specifying information about the added - * items. - */ - public interface ItemAddEvent extends ItemSetChangeEvent { - - /** - * Gets the item id of the first added item. - * - * @return item id of the first added item - */ - public Object getFirstItemId(); - - /** - * Gets the index of the first added item. - * - * @return index of the first added item - */ - public int getFirstIndex(); - - /** - * Gets the number of the added items. - * - * @return the number of added items. - */ - public int getAddedItemsCount(); - } - - /** - * An Event object specifying information about the removed - * items. - */ - public interface ItemRemoveEvent extends ItemSetChangeEvent { - /** - * Gets the item id of the first removed item. - * - * @return item id of the first removed item - */ - public Object getFirstItemId(); - - /** - * Gets the index of the first removed item. - * - * @return index of the first removed item - */ - public int getFirstIndex(); - - /** - * Gets the number of the removed items. - * - * @return the number of removed items - */ - public int getRemovedItemsCount(); - } } /** diff --git a/server/src/com/vaadin/data/util/AbstractBeanContainer.java b/server/src/com/vaadin/data/util/AbstractBeanContainer.java index d94588bdc9..b19cdd980c 100644 --- a/server/src/com/vaadin/data/util/AbstractBeanContainer.java +++ b/server/src/com/vaadin/data/util/AbstractBeanContainer.java @@ -222,7 +222,6 @@ public abstract class AbstractBeanContainer extends @Override public boolean removeAllItems() { int origSize = size(); - IDTYPE firstItem = getFirstVisibleItem(); internalRemoveAllItems(); @@ -235,7 +234,7 @@ public abstract class AbstractBeanContainer extends // fire event only if the visible view changed, regardless of whether // filtered out items were removed or not if (origSize != 0) { - fireItemsRemoved(0, firstItem, origSize); + fireItemSetChange(); } return true; @@ -680,8 +679,6 @@ public abstract class AbstractBeanContainer extends protected void addAll(Collection collection) throws IllegalStateException, IllegalArgumentException { boolean modified = false; - int origSize = size(); - for (BEANTYPE bean : collection) { // TODO skipping invalid beans - should not allow them in javadoc? if (bean == null @@ -702,22 +699,13 @@ public abstract class AbstractBeanContainer extends if (modified) { // Filter the contents when all items have been added if (isFiltered()) { - doFilterContainer(!getFilters().isEmpty()); - } - if (visibleNewItemsWasAdded(origSize)) { - // fire event about added items - int firstPosition = origSize; - IDTYPE firstItemId = getVisibleItemIds().get(firstPosition); - int affectedItems = size() - origSize; - fireItemsAdded(firstPosition, firstItemId, affectedItems); + filterAll(); + } else { + fireItemSetChange(); } } } - private boolean visibleNewItemsWasAdded(int origSize) { - return size() > origSize; - } - /** * Use the bean resolver to get the identifier for a bean. * diff --git a/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java b/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java index cae9f30fc9..84304431bc 100644 --- a/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java +++ b/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java @@ -15,10 +15,8 @@ */ package com.vaadin.data.util; -import java.io.Serializable; import java.util.Collection; import java.util.Collections; -import java.util.EventObject; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; @@ -148,83 +146,6 @@ public abstract class AbstractInMemoryContainerEvent object specifying information about the added - * items. - * - *

- * This class provides information about the first added item and the number - * of added items. - *

- */ - protected static class BaseItemAddEvent extends BaseItemAddOrRemoveEvent - implements Container.Indexed.ItemAddEvent { - - public BaseItemAddEvent(Container source, Object itemId, int index, - int count) { - super(source, itemId, index, count); - } - - @Override - public int getAddedItemsCount() { - return getAffectedItemsCount(); - } - } - - /** - * An Event object specifying information about the removed - * items. - * - *

- * This class provides information about the first removed item and the - * number of removed items. - *

- */ - protected static class BaseItemRemoveEvent extends BaseItemAddOrRemoveEvent - implements Container.Indexed.ItemRemoveEvent { - - public BaseItemRemoveEvent(Container source, Object itemId, int index, - int count) { - super(source, itemId, index, count); - } - - @Override - public int getRemovedItemsCount() { - return getAffectedItemsCount(); - } - } - /** * Get an item even if filtered out. * @@ -977,69 +898,36 @@ public abstract class AbstractInMemoryContainer= 0) { - super.fireItemAdded(position, itemId, item); + fireItemSetChange(new IndexedContainer.ItemSetChangeEvent(this, + position)); } } @@ -1211,5 +1211,4 @@ public class IndexedContainer extends public Collection getContainerFilters() { return super.getContainerFilters(); } - } diff --git a/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java b/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java index b9633753b4..c7d992dbeb 100644 --- a/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java +++ b/server/tests/src/com/vaadin/data/util/BeanItemContainerTest.java @@ -10,15 +10,8 @@ import java.util.Map; import junit.framework.Assert; -import org.easymock.Capture; -import org.easymock.EasyMock; - import com.vaadin.data.Container; -import com.vaadin.data.Container.Indexed.ItemAddEvent; -import com.vaadin.data.Container.Indexed.ItemRemoveEvent; -import com.vaadin.data.Container.ItemSetChangeListener; import com.vaadin.data.Item; -import com.vaadin.data.util.filter.Compare; /** * Test basic functionality of BeanItemContainer. @@ -734,182 +727,4 @@ public class BeanItemContainerTest extends AbstractBeanContainerTest { assertNull(container.getContainerProperty(john, "address.street") .getValue()); } - - public void testItemAddedEvent() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - ItemSetChangeListener addListener = createListenerMockFor(container); - addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class)); - EasyMock.replay(addListener); - - container.addItem(bean); - - EasyMock.verify(addListener); - } - - public void testItemAddedEvent_AddedItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - container.addItem(bean); - - assertEquals(bean, capturedEvent.getValue().getFirstItemId()); - } - - public void testItemAddedEvent_addItemAt_IndexOfAddedItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - container.addItem(bean); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - container.addItemAt(1, new Person("")); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - public void testItemAddedEvent_addItemAfter_IndexOfAddedItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - container.addItem(bean); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - container.addItemAfter(bean, new Person("")); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - public void testItemAddedEvent_amountOfAddedItems() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - List beans = Arrays.asList(new Person("Jack"), new Person( - "John")); - - container.addAll(beans); - - assertEquals(2, capturedEvent.getValue().getAddedItemsCount()); - } - - public void testItemAddedEvent_someItemsAreFiltered_amountOfAddedItemsIsReducedByAmountOfFilteredItems() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - List beans = Arrays.asList(new Person("Jack"), new Person( - "John")); - container.addFilter(new Compare.Equal("name", "John")); - - container.addAll(beans); - - assertEquals(1, capturedEvent.getValue().getAddedItemsCount()); - } - - public void testItemAddedEvent_someItemsAreFiltered_addedItemIsTheFirstVisibleItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - List beans = Arrays.asList(new Person("Jack"), bean); - container.addFilter(new Compare.Equal("name", "John")); - - container.addAll(beans); - - assertEquals(bean, capturedEvent.getValue().getFirstItemId()); - } - - public void testItemRemovedEvent() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - container.addItem(bean); - ItemSetChangeListener removeListener = createListenerMockFor(container); - removeListener.containerItemSetChange(EasyMock - .isA(ItemRemoveEvent.class)); - EasyMock.replay(removeListener); - - container.removeItem(bean); - - EasyMock.verify(removeListener); - } - - public void testItemRemovedEvent_RemovedItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - Person bean = new Person("John"); - container.addItem(bean); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent(removeListener); - EasyMock.replay(removeListener); - - container.removeItem(bean); - - assertEquals(bean, capturedEvent.getValue().getFirstItemId()); - } - - public void testItemRemovedEvent_indexOfRemovedItem() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - container.addItem(new Person("Jack")); - Person secondBean = new Person("John"); - container.addItem(secondBean); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent(removeListener); - EasyMock.replay(removeListener); - - container.removeItem(secondBean); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - public void testItemRemovedEvent_amountOfRemovedItems() { - BeanItemContainer container = new BeanItemContainer( - Person.class); - container.addItem(new Person("Jack")); - container.addItem(new Person("John")); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent(removeListener); - EasyMock.replay(removeListener); - - container.removeAllItems(); - - assertEquals(2, capturedEvent.getValue().getRemovedItemsCount()); - } - - private Capture captureAddEvent( - ItemSetChangeListener addListener) { - Capture capturedEvent = new Capture(); - addListener.containerItemSetChange(EasyMock.capture(capturedEvent)); - return capturedEvent; - } - - private Capture captureRemoveEvent( - ItemSetChangeListener removeListener) { - Capture capturedEvent = new Capture(); - removeListener.containerItemSetChange(EasyMock.capture(capturedEvent)); - return capturedEvent; - } - - private ItemSetChangeListener createListenerMockFor( - BeanItemContainer container) { - ItemSetChangeListener listener = EasyMock - .createNiceMock(ItemSetChangeListener.class); - container.addItemSetChangeListener(listener); - return listener; - } } diff --git a/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java b/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java index 5da0bdc8a2..09e5a26c15 100644 --- a/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java +++ b/server/tests/src/com/vaadin/data/util/TestIndexedContainer.java @@ -4,12 +4,6 @@ import java.util.List; import junit.framework.Assert; -import org.easymock.Capture; -import org.easymock.EasyMock; - -import com.vaadin.data.Container.Indexed.ItemAddEvent; -import com.vaadin.data.Container.Indexed.ItemRemoveEvent; -import com.vaadin.data.Container.ItemSetChangeListener; import com.vaadin.data.Item; public class TestIndexedContainer extends AbstractInMemoryContainerTest { @@ -277,113 +271,6 @@ public class TestIndexedContainer extends AbstractInMemoryContainerTest { counter.assertNone(); } - public void testItemAddedEvent() { - IndexedContainer container = new IndexedContainer(); - ItemSetChangeListener addListener = createListenerMockFor(container); - addListener.containerItemSetChange(EasyMock.isA(ItemAddEvent.class)); - EasyMock.replay(addListener); - - container.addItem(); - - EasyMock.verify(addListener); - } - - public void testItemAddedEvent_AddedItem() { - IndexedContainer container = new IndexedContainer(); - ItemSetChangeListener addListener = createListenerMockFor(container); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - Object itemId = container.addItem(); - - assertEquals(itemId, capturedEvent.getValue().getFirstItemId()); - } - - public void testItemAddedEvent_IndexOfAddedItem() { - IndexedContainer container = new IndexedContainer(); - ItemSetChangeListener addListener = createListenerMockFor(container); - container.addItem(); - Capture capturedEvent = captureAddEvent(addListener); - EasyMock.replay(addListener); - - container.addItemAt(1); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - public void testItemRemovedEvent() { - IndexedContainer container = new IndexedContainer(); - Object itemId = container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - removeListener.containerItemSetChange(EasyMock - .isA(ItemRemoveEvent.class)); - EasyMock.replay(removeListener); - - container.removeItem(itemId); - - EasyMock.verify(removeListener); - } - - public void testItemRemovedEvent_RemovedItem() { - IndexedContainer container = new IndexedContainer(); - Object itemId = container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent(removeListener); - EasyMock.replay(removeListener); - - container.removeItem(itemId); - - assertEquals(itemId, capturedEvent.getValue().getFirstItemId()); - } - - public void testItemRemovedEvent_indexOfRemovedItem() { - IndexedContainer container = new IndexedContainer(); - container.addItem(); - Object secondItemId = container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent(removeListener); - EasyMock.replay(removeListener); - - container.removeItem(secondItemId); - - assertEquals(1, capturedEvent.getValue().getFirstIndex()); - } - - public void testItemRemovedEvent_amountOfRemovedItems() { - IndexedContainer container = new IndexedContainer(); - container.addItem(); - container.addItem(); - ItemSetChangeListener removeListener = createListenerMockFor(container); - Capture capturedEvent = captureRemoveEvent(removeListener); - EasyMock.replay(removeListener); - - container.removeAllItems(); - - assertEquals(2, capturedEvent.getValue().getRemovedItemsCount()); - } - - private Capture captureAddEvent( - ItemSetChangeListener addListener) { - Capture capturedEvent = new Capture(); - addListener.containerItemSetChange(EasyMock.capture(capturedEvent)); - return capturedEvent; - } - - private Capture captureRemoveEvent( - ItemSetChangeListener removeListener) { - Capture capturedEvent = new Capture(); - removeListener.containerItemSetChange(EasyMock.capture(capturedEvent)); - return capturedEvent; - } - - private ItemSetChangeListener createListenerMockFor( - IndexedContainer container) { - ItemSetChangeListener listener = EasyMock - .createNiceMock(ItemSetChangeListener.class); - container.addItemSetChangeListener(listener); - return listener; - } - // Ticket 8028 public void testGetItemIdsRangeIndexOutOfBounds() { IndexedContainer ic = new IndexedContainer(); -- cgit v1.2.3 From 7e5d44d19bd02ecc7b900ba4926380d2fa7e7668 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Mon, 7 Apr 2014 17:32:29 +0300 Subject: Introduce a drag threshold for Drag and Drop (#13381) Based on partial fix by Fabian Lange. Change-Id: I1a18c6ea105d87496b196b93e701aaccb987b3e7 --- .../vaadin/client/ui/dd/VDragAndDropManager.java | 74 +++++++++++----------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java b/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java index b911c28a07..47fd3c88a2 100644 --- a/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java +++ b/client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java @@ -31,7 +31,6 @@ import com.google.gwt.user.client.Command; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; -import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; @@ -345,10 +344,7 @@ public class VDragAndDropManager { .addNativePreviewHandler(defaultDragAndDropEventHandler); if (dragElement != null && dragElement.getParentElement() == null) { - // deferred attaching drag image is on going, we can - // hurry with it now - lazyAttachDragElement.cancel(); - lazyAttachDragElement.run(); + attachDragElement(); } } // just capture something to prevent text selection in IE @@ -370,6 +366,13 @@ public class VDragAndDropManager { deferredStartRegistration = Event .addNativePreviewHandler(new NativePreviewHandler() { + private int startX = Util + .getTouchOrMouseClientX(currentDrag + .getCurrentGwtEvent()); + private int startY = Util + .getTouchOrMouseClientY(currentDrag + .getCurrentGwtEvent()); + @Override public void onPreviewNativeEvent( NativePreviewEvent event) { @@ -422,13 +425,23 @@ public class VDragAndDropManager { } case Event.ONMOUSEMOVE: case Event.ONTOUCHMOVE: - if (deferredStartRegistration != null) { - deferredStartRegistration.removeHandler(); - deferredStartRegistration = null; + int currentX = Util + .getTouchOrMouseClientX(event + .getNativeEvent()); + int currentY = Util + .getTouchOrMouseClientY(event + .getNativeEvent()); + if (Math.abs(startX - currentX) > 3 + || Math.abs(startY - currentY) > 3) { + if (deferredStartRegistration != null) { + deferredStartRegistration + .removeHandler(); + deferredStartRegistration = null; + } + currentDrag.setCurrentGwtEvent(event + .getNativeEvent()); + startDrag.execute(); } - currentDrag.setCurrentGwtEvent(event - .getNativeEvent()); - startDrag.execute(); break; default: // on any other events, clean up the @@ -718,16 +731,7 @@ public class VDragAndDropManager { updateDragImagePosition(); if (isStarted) { - lazyAttachDragElement.run(); - } else { - /* - * To make our default dnd handler as compatible as possible, we - * need to defer the appearance of dragElement. Otherwise events - * that are derived from sequences of other events might not - * fire as domchanged will fire between them or mouse up might - * happen on dragElement. - */ - lazyAttachDragElement.schedule(300); + attachDragElement(); } } } @@ -736,24 +740,20 @@ public class VDragAndDropManager { return dragElement; } - private final Timer lazyAttachDragElement = new Timer() { - - @Override - public void run() { - if (dragElement != null && dragElement.getParentElement() == null) { - ApplicationConnection connection = getCurrentDragApplicationConnection(); - Element dragImageParent; - if (connection == null) { - VConsole.error("Could not determine ApplicationConnection for current drag operation. The drag image will likely look broken"); - dragImageParent = RootPanel.getBodyElement(); - } else { - dragImageParent = VOverlay.getOverlayContainer(connection); - } - dragImageParent.appendChild(dragElement); + public void attachDragElement() { + if (dragElement != null && dragElement.getParentElement() == null) { + ApplicationConnection connection = getCurrentDragApplicationConnection(); + Element dragImageParent; + if (connection == null) { + VConsole.error("Could not determine ApplicationConnection for current drag operation. The drag image will likely look broken"); + dragImageParent = RootPanel.getBodyElement(); + } else { + dragImageParent = VOverlay.getOverlayContainer(connection); } - + dragImageParent.appendChild(dragElement); } - }; + + } private Command deferredCommand; -- cgit v1.2.3 From cd94b21f563a7d4c92a6e7390269d501d480cc6e Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Thu, 3 Apr 2014 17:19:46 +0300 Subject: Discourage use of setNeedsLayout while a layout is running (#13542) Change-Id: Ia4831db39528061f4ac4b98b861b8030a261cf9a --- client/src/com/vaadin/client/LayoutManager.java | 37 +++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/client/src/com/vaadin/client/LayoutManager.java b/client/src/com/vaadin/client/LayoutManager.java index fbf540273f..69f3f08144 100644 --- a/client/src/com/vaadin/client/LayoutManager.java +++ b/client/src/com/vaadin/client/LayoutManager.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.logging.Logger; import com.google.gwt.core.client.Duration; import com.google.gwt.core.client.JsArrayString; @@ -827,6 +828,12 @@ public class LayoutManager { /** * Marks that a ManagedLayout should be layouted in the next layout phase * even if none of the elements managed by the layout have been resized. + *

+ * This method should not be invoked during a layout phase since it only + * controls what will happen in the beginning of the next phase. If you want + * to explicitly cause some layout to be considered in an ongoing layout + * phase, you should use {@link #setNeedsMeasure(ComponentConnector)} + * instead. * * @param layout * the managed layout that should be layouted @@ -840,14 +847,25 @@ public class LayoutManager { * Marks that a ManagedLayout should be layouted horizontally in the next * layout phase even if none of the elements managed by the layout have been * resized horizontally. - * + *

* For SimpleManagedLayout which is always layouted in both directions, this * has the same effect as {@link #setNeedsLayout(ManagedLayout)}. + *

+ * This method should not be invoked during a layout phase since it only + * controls what will happen in the beginning of the next phase. If you want + * to explicitly cause some layout to be considered in an ongoing layout + * phase, you should use {@link #setNeedsMeasure(ComponentConnector)} + * instead. * * @param layout * the managed layout that should be layouted */ public final void setNeedsHorizontalLayout(ManagedLayout layout) { + if (isLayoutRunning()) { + getLogger() + .warning( + "setNeedsHorizontalLayout should not be run while a layout phase is in progress."); + } needsHorizontalLayout.add(layout.getConnectorId()); } @@ -855,14 +873,25 @@ public class LayoutManager { * Marks that a ManagedLayout should be layouted vertically in the next * layout phase even if none of the elements managed by the layout have been * resized vertically. - * + *

* For SimpleManagedLayout which is always layouted in both directions, this * has the same effect as {@link #setNeedsLayout(ManagedLayout)}. + *

+ * This method should not be invoked during a layout phase since it only + * controls what will happen in the beginning of the next phase. If you want + * to explicitly cause some layout to be considered in an ongoing layout + * phase, you should use {@link #setNeedsMeasure(ComponentConnector)} + * instead. * * @param layout * the managed layout that should be layouted */ public final void setNeedsVerticalLayout(ManagedLayout layout) { + if (isLayoutRunning()) { + getLogger() + .warning( + "setNeedsVerticalLayout should not be run while a layout phase is in progress."); + } needsVerticalLayout.add(layout.getConnectorId()); } @@ -1609,4 +1638,8 @@ public class LayoutManager { protected void cleanMeasuredSizes() { } + private static Logger getLogger() { + return Logger.getLogger(LayoutManager.class.getName()); + } + } -- cgit v1.2.3 From 02998d815a989ec13e1a49372c2c010233f6bfb9 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Fri, 11 Apr 2014 15:28:24 +0300 Subject: Updated Window API based on 7.2 API review Change-Id: Ie6fa8b51c2fd1d1fe7def9ca7c11b4023eae52b7 --- server/src/com/vaadin/ui/Window.java | 23 ++++++---- .../tests/server/component/window/WindowTest.java | 53 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 server/tests/src/com/vaadin/tests/server/component/window/WindowTest.java diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java index d3afdaacf1..aaba601309 100644 --- a/server/src/com/vaadin/ui/Window.java +++ b/server/src/com/vaadin/ui/Window.java @@ -18,9 +18,6 @@ package com.vaadin.ui; import java.io.Serializable; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; import java.util.Map; import com.vaadin.event.FieldEvents.BlurEvent; @@ -1020,12 +1017,12 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * @param connectors * with the components to use as description */ - public void setAssistiveDescription(Connector... connectors) { - if (connectors == null) { + public void setAssistiveDescription(Component... components) { + if (components == null) { throw new IllegalArgumentException( "Parameter connectors must be non-null"); } else { - getState().contentDescription = connectors; + getState().contentDescription = components; } } @@ -1036,9 +1033,17 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * * @return list of previously set components */ - public List getAssistiveDescription() { - return Collections.unmodifiableList(Arrays - .asList(getState().contentDescription)); + public Component[] getAssistiveDescription() { + Connector[] contentDescription = getState().contentDescription; + if (contentDescription == null) { + return null; + } + + Component[] target = new Component[contentDescription.length]; + System.arraycopy(contentDescription, 0, target, 0, + contentDescription.length); + + return target; } /** diff --git a/server/tests/src/com/vaadin/tests/server/component/window/WindowTest.java b/server/tests/src/com/vaadin/tests/server/component/window/WindowTest.java new file mode 100644 index 0000000000..2cd19ee153 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/window/WindowTest.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.tests.server.component.window; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.ui.Button; +import com.vaadin.ui.Label; +import com.vaadin.ui.Window; + +public class WindowTest { + + public Window window; + + @Before + public void setup() { + window = new Window(); + } + + @Test + public void testAssistiveDescription() { + Label l1 = new Label("label 1"); + Button b2 = new Button("button 2"); + window.setAssistiveDescription(l1, b2); + + Assert.assertEquals(2, window.getAssistiveDescription().length); + Assert.assertEquals(l1, window.getAssistiveDescription()[0]); + Assert.assertEquals(b2, window.getAssistiveDescription()[1]); + + // Modifying return value must not change actual value + window.getAssistiveDescription()[0] = null; + + Assert.assertEquals(2, window.getAssistiveDescription().length); + Assert.assertEquals(l1, window.getAssistiveDescription()[0]); + Assert.assertEquals(b2, window.getAssistiveDescription()[1]); + + } +} -- cgit v1.2.3 From ee203f581aead7b234a4b64db9a6006e2887dc47 Mon Sep 17 00:00:00 2001 From: Denis Anisimov Date: Sun, 13 Apr 2014 22:08:22 +0300 Subject: Apply abstract ordered layout settings for replaced component (#13568). Change-Id: If6863d518d902ee48bb73fbb0c9b3725cb7c8707 --- .../src/com/vaadin/ui/AbstractOrderedLayout.java | 10 +++ .../LayoutSettingsOnReplace.java | 85 ++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 server/tests/src/com/vaadin/tests/server/component/abstractorderedlayout/LayoutSettingsOnReplace.java diff --git a/server/src/com/vaadin/ui/AbstractOrderedLayout.java b/server/src/com/vaadin/ui/AbstractOrderedLayout.java index c9eb756daa..f5fd4d7bfc 100644 --- a/server/src/com/vaadin/ui/AbstractOrderedLayout.java +++ b/server/src/com/vaadin/ui/AbstractOrderedLayout.java @@ -213,8 +213,12 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements if (oldLocation == -1) { addComponent(newComponent); } else if (newLocation == -1) { + Alignment alignment = getComponentAlignment(oldComponent); + float expandRatio = getExpandRatio(oldComponent); + removeComponent(oldComponent); addComponent(newComponent, oldLocation); + applyLayoutSettings(newComponent, alignment, expandRatio); } else { // Both old and new are in the layout if (oldLocation > newLocation) { @@ -444,4 +448,10 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements defaultComponentAlignment = defaultAlignment; } + private void applyLayoutSettings(Component target, Alignment alignment, + float expandRatio) { + setComponentAlignment(target, alignment); + setExpandRatio(target, expandRatio); + } + } diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractorderedlayout/LayoutSettingsOnReplace.java b/server/tests/src/com/vaadin/tests/server/component/abstractorderedlayout/LayoutSettingsOnReplace.java new file mode 100644 index 0000000000..0af21d8cb8 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/abstractorderedlayout/LayoutSettingsOnReplace.java @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.tests.server.component.abstractorderedlayout; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.AbstractOrderedLayout; +import com.vaadin.ui.Alignment; + +/** + * Tests for abstract layout settings which should be preserved on replace + * component + * + * @since 7.2 + * @author Vaadin Ltd + */ +public class LayoutSettingsOnReplace { + + @Test + public void testExpandRatio() { + AbstractOrderedLayout layout = new AbstractOrderedLayout() { + }; + + AbstractComponent first = new AbstractComponent() { + }; + AbstractComponent second = new AbstractComponent() { + }; + + layout.addComponent(first); + layout.addComponent(second); + + int ratio = 2; + layout.setExpandRatio(first, ratio); + layout.setExpandRatio(second, 1); + + AbstractComponent replace = new AbstractComponent() { + }; + layout.replaceComponent(first, replace); + + Assert.assertEquals("Expand ratio for replaced component is not " + + "the same as for previous one", ratio, + layout.getExpandRatio(replace), 0.0001); + } + + @Test + public void testAlignment() { + AbstractOrderedLayout layout = new AbstractOrderedLayout() { + }; + + AbstractComponent first = new AbstractComponent() { + }; + AbstractComponent second = new AbstractComponent() { + }; + + layout.addComponent(first); + layout.addComponent(second); + + Alignment alignment = Alignment.BOTTOM_RIGHT; + layout.setComponentAlignment(first, alignment); + layout.setComponentAlignment(second, Alignment.MIDDLE_CENTER); + + AbstractComponent replace = new AbstractComponent() { + }; + layout.replaceComponent(first, replace); + + Assert.assertEquals("Alignment for replaced component is not " + + "the same as for previous one", alignment, + layout.getComponentAlignment(replace)); + } +} -- cgit v1.2.3 From 72d0aa0b82078ec2cda9c2f36a699d3854217db4 Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Mon, 14 Apr 2014 10:51:02 +0300 Subject: Update Window Javadoc based on 7.2 API review changes Change-Id: Ifdc1379892d8f798f2ceabcf83d772fa6a76dd4b --- server/src/com/vaadin/ui/Window.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java index aaba601309..7268aed101 100644 --- a/server/src/com/vaadin/ui/Window.java +++ b/server/src/com/vaadin/ui/Window.java @@ -1014,8 +1014,8 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * window. Text contained in these components will be read by assistive * devices when it is opened. * - * @param connectors - * with the components to use as description + * @param components + * the components to use as description */ public void setAssistiveDescription(Component... components) { if (components == null) { @@ -1031,7 +1031,7 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, * contained in these components will be read by assistive devices when the * window is opened. * - * @return list of previously set components + * @return array of previously set components */ public Component[] getAssistiveDescription() { Connector[] contentDescription = getState().contentDescription; -- cgit v1.2.3 From d2e24feb09ccba7f3a2f253687488774af2bc340 Mon Sep 17 00:00:00 2001 From: Jonatan Kronqvist Date: Tue, 8 Apr 2014 15:02:58 +0300 Subject: Update some APIs based on the 7.2 API review comments * NotificationConfiguration "helpers" removed from Notification * NotificationConfiguration methods accept Type instead of style (String) * Tab.setIconAltText -> Tab.setIconAlternateText * Remove the two new TabSheet.addTab() methods * UI.reinit() -> UI.refresh() Change-Id: I97488e7c6de8cfacc591450d69c821b2973b8707 --- client/src/com/vaadin/client/ui/VNotification.java | 45 ++-- client/src/com/vaadin/client/ui/VWindow.java | 2 +- .../com/vaadin/annotations/PreserveOnRefresh.java | 2 +- server/src/com/vaadin/server/Page.java | 2 +- server/src/com/vaadin/server/UIProvider.java | 2 +- .../vaadin/server/communication/UIInitHandler.java | 2 +- server/src/com/vaadin/ui/Notification.java | 167 ++------------ .../com/vaadin/ui/NotificationConfiguration.java | 240 +++++++-------------- server/src/com/vaadin/ui/TabSheet.java | 85 ++------ server/src/com/vaadin/ui/UI.java | 10 +- server/src/com/vaadin/ui/Window.java | 2 +- .../tests/src/com/vaadin/ui/UIInitRefreshTest.java | 116 ++++++++++ .../tests/src/com/vaadin/ui/UIInitReinitTest.java | 116 ---------- .../ui/ui/NotificationConfigurationBean.java | 137 ------------ .../com/vaadin/shared/ui/ui/NotificationRole.java | 25 +++ shared/src/com/vaadin/shared/ui/ui/UIState.java | 83 ++++--- .../com/vaadin/shared/ui/window/WindowRole.java | 25 +++ .../com/vaadin/shared/ui/window/WindowState.java | 7 - .../notification/NotificationsWaiAria.java | 19 +- .../tests/components/tabsheet/TabSheetIcons.java | 2 +- .../com/vaadin/tests/components/ui/UIRefresh.java | 49 +++++ .../vaadin/tests/components/ui/UIRefreshTest.java | 39 ++++ .../com/vaadin/tests/components/ui/UIReinit.java | 49 ----- .../vaadin/tests/components/ui/UIReinitTest.java | 39 ---- .../components/window/ExtraWindowShownWaiAria.java | 2 +- 25 files changed, 469 insertions(+), 798 deletions(-) create mode 100644 server/tests/src/com/vaadin/ui/UIInitRefreshTest.java delete mode 100644 server/tests/src/com/vaadin/ui/UIInitReinitTest.java delete mode 100644 shared/src/com/vaadin/shared/ui/ui/NotificationConfigurationBean.java create mode 100644 shared/src/com/vaadin/shared/ui/ui/NotificationRole.java create mode 100644 shared/src/com/vaadin/shared/ui/window/WindowRole.java create mode 100644 uitest/src/com/vaadin/tests/components/ui/UIRefresh.java create mode 100644 uitest/src/com/vaadin/tests/components/ui/UIRefreshTest.java delete mode 100644 uitest/src/com/vaadin/tests/components/ui/UIReinit.java delete mode 100644 uitest/src/com/vaadin/tests/components/ui/UIReinitTest.java diff --git a/client/src/com/vaadin/client/ui/VNotification.java b/client/src/com/vaadin/client/ui/VNotification.java index 3aa3fa847d..a43f508f6e 100644 --- a/client/src/com/vaadin/client/ui/VNotification.java +++ b/client/src/com/vaadin/client/ui/VNotification.java @@ -38,9 +38,9 @@ import com.vaadin.client.UIDL; import com.vaadin.client.Util; import com.vaadin.client.ui.aria.AriaHelper; import com.vaadin.shared.Position; -import com.vaadin.shared.ui.ui.NotificationConfigurationBean; -import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role; import com.vaadin.shared.ui.ui.UIConstants; +import com.vaadin.shared.ui.ui.UIState.NotificationTypeConfiguration; +import com.vaadin.shared.ui.ui.NotificationRole; public class VNotification extends VOverlay { @@ -161,20 +161,20 @@ public class VNotification extends VOverlay { } public void show(Widget widget, Position position, String style) { - NotificationConfigurationBean styleSetup = getUiState(style); + NotificationTypeConfiguration styleSetup = getUiState(style); setWaiAriaRole(styleSetup); FlowPanel panel = new FlowPanel(); - if (styleSetup.hasAssistivePrefix()) { - panel.add(new Label(styleSetup.getAssistivePrefix())); + if (hasPrefix(styleSetup)) { + panel.add(new Label(styleSetup.prefix)); AriaHelper.setVisibleForAssistiveDevicesOnly(panel.getElement(), true); } panel.add(widget); - if (styleSetup.hasAssistivePostfix()) { - panel.add(new Label(styleSetup.getAssistivePostfix())); + if (hasPostfix(styleSetup)) { + panel.add(new Label(styleSetup.postfix)); AriaHelper.setVisibleForAssistiveDevicesOnly(panel.getElement(), true); } @@ -182,8 +182,16 @@ public class VNotification extends VOverlay { show(position, style); } + private boolean hasPostfix(NotificationTypeConfiguration styleSetup) { + return styleSetup != null && styleSetup.postfix != null && !styleSetup.postfix.isEmpty(); + } + + private boolean hasPrefix(NotificationTypeConfiguration styleSetup) { + return styleSetup != null && styleSetup.prefix != null && !styleSetup.prefix.isEmpty(); + } + public void show(String html, Position position, String style) { - NotificationConfigurationBean styleSetup = getUiState(style); + NotificationTypeConfiguration styleSetup = getUiState(style); String assistiveDeviceOnlyStyle = AriaHelper.ASSISTIVE_DEVICE_ONLY_STYLE; setWaiAriaRole(styleSetup); @@ -191,32 +199,31 @@ public class VNotification extends VOverlay { String type = ""; String usage = ""; - if (styleSetup != null && styleSetup.hasAssistivePrefix()) { + if (hasPrefix(styleSetup)) { type = "" - + styleSetup.getAssistivePrefix() + ""; + + styleSetup.prefix + ""; } - if (styleSetup != null && styleSetup.hasAssistivePostfix()) { + if (hasPostfix(styleSetup)) { usage = "" - + styleSetup.getAssistivePostfix() + ""; + + styleSetup.postfix + ""; } setWidget(new HTML(type + html + usage)); show(position, style); } - private NotificationConfigurationBean getUiState(String style) { - NotificationConfigurationBean styleSetup = getApplicationConnection() - .getUIConnector().getState().notificationConfiguration.setup + private NotificationTypeConfiguration getUiState(String style) { + return getApplicationConnection() + .getUIConnector().getState().notificationConfigurations .get(style); - return styleSetup; } - private void setWaiAriaRole(NotificationConfigurationBean styleSetup) { + private void setWaiAriaRole(NotificationTypeConfiguration styleSetup) { Roles.getAlertRole().set(getElement()); - if (styleSetup != null && styleSetup.getAssistiveRole() != null) { - if (Role.STATUS == styleSetup.getAssistiveRole()) { + if (styleSetup != null && styleSetup.notificationRole != null) { + if (NotificationRole.STATUS == styleSetup.notificationRole) { Roles.getStatusRole().set(getElement()); } } diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java index 396fc76eb0..5b6595ea43 100644 --- a/client/src/com/vaadin/client/ui/VWindow.java +++ b/client/src/com/vaadin/client/ui/VWindow.java @@ -67,7 +67,7 @@ import com.vaadin.client.ui.window.WindowMoveHandler; import com.vaadin.shared.Connector; import com.vaadin.shared.EventId; import com.vaadin.shared.ui.window.WindowMode; -import com.vaadin.shared.ui.window.WindowState.WindowRole; +import com.vaadin.shared.ui.window.WindowRole; /** * "Sub window" component. diff --git a/server/src/com/vaadin/annotations/PreserveOnRefresh.java b/server/src/com/vaadin/annotations/PreserveOnRefresh.java index 801c1e78f2..48ea74c217 100644 --- a/server/src/com/vaadin/annotations/PreserveOnRefresh.java +++ b/server/src/com/vaadin/annotations/PreserveOnRefresh.java @@ -32,7 +32,7 @@ import com.vaadin.ui.UI; * current UI instance when a reload is detected. *

* Whenever a request is received that reloads a preserved UI, the UI's - * {@link UI#reinit(com.vaadin.server.VaadinRequest) reinit} method is invoked + * {@link UI#refresh(com.vaadin.server.VaadinRequest) refresh} method is invoked * by the framework. *

* By using diff --git a/server/src/com/vaadin/server/Page.java b/server/src/com/vaadin/server/Page.java index 70b8306bc3..64c658cd3e 100644 --- a/server/src/com/vaadin/server/Page.java +++ b/server/src/com/vaadin/server/Page.java @@ -636,7 +636,7 @@ public class Page implements Serializable { } public void init(VaadinRequest request) { - // NOTE: UI.reinit makes assumptions about the semantics of this method. + // NOTE: UI.refresh makes assumptions about the semantics of this method. // It should be kept in sync if this method is changed. // Extract special parameter sent by vaadinBootstrap.js diff --git a/server/src/com/vaadin/server/UIProvider.java b/server/src/com/vaadin/server/UIProvider.java index a76f396767..46439650c0 100644 --- a/server/src/com/vaadin/server/UIProvider.java +++ b/server/src/com/vaadin/server/UIProvider.java @@ -131,7 +131,7 @@ public abstract class UIProvider implements Serializable { * the value of window.name in the browser. *

* Whenever a preserved UI is reused, its - * {@link UI#reinit(com.vaadin.server.VaadinRequest) reinit} method is + * {@link UI#refresh(com.vaadin.server.VaadinRequest) refresh} method is * invoked by the framework first. * * diff --git a/server/src/com/vaadin/server/communication/UIInitHandler.java b/server/src/com/vaadin/server/communication/UIInitHandler.java index 5dc44208d1..76460e153a 100644 --- a/server/src/com/vaadin/server/communication/UIInitHandler.java +++ b/server/src/com/vaadin/server/communication/UIInitHandler.java @@ -266,7 +266,7 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler { */ private void reinitUI(UI ui, VaadinRequest request) { UI.setCurrent(ui); - ui.doReinit(request); + ui.doRefresh(request); } /** diff --git a/server/src/com/vaadin/ui/Notification.java b/server/src/com/vaadin/ui/Notification.java index 31fa265b02..7064bfcf39 100644 --- a/server/src/com/vaadin/ui/Notification.java +++ b/server/src/com/vaadin/ui/Notification.java @@ -21,7 +21,6 @@ import java.io.Serializable; import com.vaadin.server.Page; import com.vaadin.server.Resource; import com.vaadin.shared.Position; -import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role; /** * A notification message, used to display temporary messages to the user - for @@ -64,7 +63,27 @@ import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role; */ public class Notification implements Serializable { public enum Type { - HUMANIZED_MESSAGE, WARNING_MESSAGE, ERROR_MESSAGE, TRAY_NOTIFICATION, ASSISTIVE_NOTIFICATION; + HUMANIZED_MESSAGE("humanized"), WARNING_MESSAGE("warning"), ERROR_MESSAGE( + "error"), TRAY_NOTIFICATION("tray"), + /** + * @since 7.2 + */ + ASSISTIVE_NOTIFICATION("assistive"); + + private String style; + + Type(String style) { + this.style = style; + } + + /** + * @since 7.2 + * + * @return the style name for this notification type. + */ + public String getStyle() { + return style; + } } @Deprecated @@ -187,44 +206,28 @@ public class Notification implements Serializable { } private void setType(Type type) { + styleName = type.getStyle(); switch (type) { case WARNING_MESSAGE: delayMsec = 1500; - styleName = "warning"; - setNavigationConfiguration("Warning: ", "", Role.ALERT); break; case ERROR_MESSAGE: delayMsec = -1; - styleName = "error"; - setNavigationConfiguration("Error: ", " - close with ESC", - Role.ALERT); break; case TRAY_NOTIFICATION: delayMsec = 3000; position = Position.BOTTOM_RIGHT; - styleName = "tray"; - setNavigationConfiguration("Info: ", "", Role.STATUS); break; case ASSISTIVE_NOTIFICATION: delayMsec = 3000; position = Position.ASSISTIVE; - styleName = "assistive"; - setNavigationConfiguration("Note: ", "", Role.ALERT); break; case HUMANIZED_MESSAGE: default: - styleName = "humanized"; - setNavigationConfiguration("Info: ", "", Role.ALERT); break; } } - private void setNavigationConfiguration(String prefix, String postfix, - Role ariaRole) { - UI.getCurrent().getNotificationConfiguration() - .setStyleConfiguration(styleName, prefix, postfix, ariaRole); - } - /** * Gets the caption part of the notification message. * @@ -339,132 +342,6 @@ public class Notification implements Serializable { return styleName; } - /** - * Sets the accessibility prefix for a notification type. - * - * This prefix is read to assistive device users before the content of the - * notification, but not visible on the page. - * - * @param type - * Type of the notification - * @param prefix - * String that is placed before the notification content - */ - public void setAssistivePrefixForType(Type type, String prefix) { - UI.getCurrent().getNotificationConfiguration() - .setAssistivePrefixForStyle(getStyle(type), prefix); - } - - /** - * Gets the accessibility prefix for a notification type. - * - * This prefix is read to assistive device users before the content of the - * notification, but not visible on the page. - * - * @param type - * Type of the notification - * @return The accessibility prefix for the provided notification type - */ - public String getAssistivePrefixForType(Type type) { - return UI.getCurrent().getNotificationConfiguration() - .getAssistivePrefixForStyle(getStyle(type)); - } - - /** - * Sets the accessibility postfix for a notification type. - * - * This postfix is read to assistive device users after the content of the - * notification, but not visible on the page. - * - * @param type - * Type of the notification - * @param postfix - * String that is placed after the notification content - */ - public void setAssistivePostfixForType(Type type, String postfix) { - UI.getCurrent().getNotificationConfiguration() - .setAssistivePostfixForStyle(getStyle(type), postfix); - } - - /** - * Gets the accessibility postfix for a notification type. - * - * This postfix is read to assistive device users after the content of the - * notification, but not visible on the page. - * - * @param type - * Type of the notification - * @return The accessibility postfix for the provided notification type - */ - public String getAssistivePostfixForType(Type type) { - return UI.getCurrent().getNotificationConfiguration() - .getAssistivePostfixForStyle(getStyle(type)); - } - - /** - * Sets the WAI-ARIA role for a notification type. - * - * This role defines how an assistive device handles a notification. - * Available roles are alert and status (@see Roles - * Model). - * - * The default role is alert. - * - * @param type - * Type of the notification - * @param role - * Role to set for the notification type - */ - public void setAssistiveRoleForType(Type type, Role role) { - UI.getCurrent().getNotificationConfiguration() - .setAssistiveRoleForStyle(getStyle(type), role); - } - - /** - * Gets the WAI-ARIA role for a notification type. - * - * This role defines how an assistive device handles a notification. - * Available roles are alert and status (@see Roles - * Model) - * - * The default role is alert. - * - * @param type - * Type of the notification - * @return Role to set for the notification type - */ - public Role getAssistiveRoleForType(Type type) { - return UI.getCurrent().getNotificationConfiguration() - .getAssistiveRoleForStyle(getStyle(type)); - } - - private String getStyle(Type type) { - String style = ""; - - switch (type) { - case WARNING_MESSAGE: - style = "warning"; - break; - case ERROR_MESSAGE: - style = "error"; - break; - case TRAY_NOTIFICATION: - style = "tray"; - break; - case ASSISTIVE_NOTIFICATION: - style = "assistive"; - break; - case HUMANIZED_MESSAGE: - default: - style = "humanized"; - break; - } - - return style; - } - /** * Sets whether html is allowed in the caption and description. If set to * true, the texts are passed to the browser as html and the developer is diff --git a/server/src/com/vaadin/ui/NotificationConfiguration.java b/server/src/com/vaadin/ui/NotificationConfiguration.java index 52d3e76d63..e6d19f84f7 100644 --- a/server/src/com/vaadin/ui/NotificationConfiguration.java +++ b/server/src/com/vaadin/ui/NotificationConfiguration.java @@ -21,112 +21,99 @@ package com.vaadin.ui; import java.io.Serializable; -import com.vaadin.shared.ui.ui.NotificationConfigurationBean; -import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role; -import com.vaadin.shared.ui.ui.UIState.NotificationConfigurationState; +import com.vaadin.shared.ui.ui.NotificationRole; +import com.vaadin.shared.ui.ui.UIState.NotificationTypeConfiguration; +import com.vaadin.ui.Notification.Type; /** * Provides methods for configuring the notification. - * + * * @author Vaadin Ltd - * @since 7.1 + * @since 7.2 */ public interface NotificationConfiguration extends Serializable { - public void setStyleConfiguration(String style, String prefix, - String postfix, Role ariaRole); - /** - * Returns the complete configuration object for the given notification - * style. - * - * @param style - * String of the notification style to return - * @return The notification configuration object - */ - public NotificationConfigurationBean getStyleConfiguration(String style); - - /** - * Sets the accessibility prefix for the given notification style. - * - * This prefix is read to assistive device users in front of the content of - * the notification, but not visible on the page. - * - * @param style - * String of the notification style + * Sets the accessibility prefix for a notification type. + *

+ * This prefix is read to assistive device users before the content of the + * notification, but not visible on the page. + * + * @param type + * type of the notification * @param prefix - * String that is placed before the notification content + * string that is placed before the notification content */ - public void setAssistivePrefixForStyle(String style, String prefix); + public void setAssistivePrefix(Type type, String prefix); /** - * Returns the accessibility prefix for the given notification style. - * - * This prefix is read to assistive device users in front of the content of - * the notification, but not visible on the page. - * - * @param style - * String of the notification style - * @return The prefix of the provided notification style + * Gets the accessibility prefix for a notification type. + *

+ * This prefix is read to assistive device users before the content of the + * notification, but not visible on the page. + * + * @param type + * type of the notification + * @return The accessibility prefix for the provided notification type */ - public String getAssistivePrefixForStyle(String style); + public String getAssistivePrefix(Type type); /** - * Sets the accessibility postfix for the given notification style. - * + * Sets the accessibility postfix for a notification type. + *

* This postfix is read to assistive device users after the content of the * notification, but not visible on the page. - * - * @param style - * String of the notification style + * + * @param type + * type of the notification * @param postfix - * String that is placed after the notification content + * string that is placed after the notification content */ - public void setAssistivePostfixForStyle(String style, String postfix); + public void setAssistivePostfix(Type type, String postfix); /** - * Returns the accessibility postfix for the given notification style. - * + * Gets the accessibility postfix for a notification type. + *

* This postfix is read to assistive device users after the content of the * notification, but not visible on the page. - * - * @param style - * String of the notification style - * @return The postfix of the provided notification style + * + * @param type + * type of the notification + * @return The accessibility postfix for the provided notification type */ - public String getAssistivePostfixForStyle(String style); + public String getAssistivePostfix(Type type); /** - * Sets the WAI-ARIA role for a notification style. - * + * Sets the WAI-ARIA role for a notification type. + *

* This role defines how an assistive device handles a notification. - * Available roles are alert, alertdialog and status (@see Roles - * Model) - * + * Model). + * * The default role is alert. - * - * @param style - * String of the notification style + * + * @param type + * type of the notification * @param role - * Role to set for the notification type + * role to set for the notification type */ - public void setAssistiveRoleForStyle(String style, Role role); + public void setAssistiveRole(Type type, NotificationRole role); /** - * Returns the WAI-ARIA role for a notification style. - * + * Gets the WAI-ARIA role for a notification type. + *

* This role defines how an assistive device handles a notification. - * Available roles are alert, alertdialog and status (@see Roles - * Model ) - * + * Model) + *

* The default role is alert. - * - * @param style - * String of the notification style - * @return The current Role for the notification type + * + * @param type + * type of the notification + * @return role to set for the notification type */ - public Role getAssistiveRoleForStyle(String style); + public NotificationRole getAssistiveRole(Type type); } class NotificationConfigurationImpl implements NotificationConfiguration { @@ -137,133 +124,62 @@ class NotificationConfigurationImpl implements NotificationConfiguration { this.ui = ui; } - /* - * (non-Javadoc) - * - * @see - * com.vaadin.ui.NotificationConfiguration#setStyleConfiguration(java.lang - * .String, java.lang.String, java.lang.String, - * com.vaadin.ui.NotificationConfiguration.Role) - */ - @Override - public void setStyleConfiguration(String style, String prefix, - String postfix, Role ariaRole) { - getState().setup.put(style, new NotificationConfigurationBean(prefix, - postfix, ariaRole)); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.ui.NotificationConfiguration#getStyleConfiguration(java.lang - * .String) - */ @Override - public NotificationConfigurationBean getStyleConfiguration(String style) { - return getState(false).setup.get(style); + public void setAssistivePrefix(Type type, String prefix) { + getConfigurationBean(type).prefix = prefix; } - /* - * (non-Javadoc) - * - * @see - * com.vaadin.ui.NotificationConfiguration#setStylePrefix(java.lang.String, - * java.lang.String) - */ @Override - public void setAssistivePrefixForStyle(String style, String prefix) { - getConfigurationBean(style).setAssistivePrefix(prefix); - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.ui.NotificationConfiguration#getStylePrefix(java.lang.String) - */ - @Override - public String getAssistivePrefixForStyle(String style) { - NotificationConfigurationBean styleSetup = getState().setup.get(style); + public String getAssistivePrefix(Type type) { + NotificationTypeConfiguration styleSetup = getTypeConf(type); if (styleSetup != null) { - return styleSetup.getAssistivePrefix(); + return styleSetup.prefix; } return null; } - /* - * (non-Javadoc) - * - * @see - * com.vaadin.ui.NotificationConfiguration#setStylePostfix(com.vaadin.ui - * .Notification.Type, java.lang.String) - */ @Override - public void setAssistivePostfixForStyle(String style, String postfix) { - getConfigurationBean(style).setAssistivePostfix(postfix); + public void setAssistivePostfix(Type type, String postfix) { + getConfigurationBean(type).postfix = postfix; } - /* - * (non-Javadoc) - * - * @see - * com.vaadin.ui.NotificationConfiguration#getStylePostfix(com.vaadin.ui - * .Notification.Type) - */ @Override - public String getAssistivePostfixForStyle(String style) { - NotificationConfigurationBean styleSetup = getState().setup.get(style); + public String getAssistivePostfix(Type type) { + NotificationTypeConfiguration styleSetup = getTypeConf(type); if (styleSetup != null) { - return styleSetup.getAssistivePostfix(); + return styleSetup.postfix; } return null; } - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.NotificationConfiguration#setStyleRole(com.vaadin.ui. - * Notification.Type, com.vaadin.ui.NotificationConfiguration.Role) - */ @Override - public void setAssistiveRoleForStyle(String style, Role role) { - getConfigurationBean(style).setAssistiveRole(role); + public void setAssistiveRole(Type type, NotificationRole role) { + getConfigurationBean(type).notificationRole = role; } - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.NotificationConfiguration#getStyleRole(com.vaadin.ui. - * Notification.Type) - */ @Override - public Role getAssistiveRoleForStyle(String style) { - NotificationConfigurationBean styleSetup = getState().setup.get(style); + public NotificationRole getAssistiveRole(Type type) { + NotificationTypeConfiguration styleSetup = getTypeConf(type); if (styleSetup != null) { - return styleSetup.getAssistiveRole(); + return styleSetup.notificationRole; } return null; } - private NotificationConfigurationBean getConfigurationBean(String style) { - NotificationConfigurationBean styleSetup = getState().setup.get(style); + private NotificationTypeConfiguration getConfigurationBean(Type type) { + NotificationTypeConfiguration styleSetup = getTypeConf(type); if (styleSetup == null) { - styleSetup = new NotificationConfigurationBean(); - getState().setup.put(style, styleSetup); + styleSetup = new NotificationTypeConfiguration(); + ui.getState().notificationConfigurations.put(type.getStyle(), styleSetup); } return styleSetup; } - private NotificationConfigurationState getState() { - return ui.getState().notificationConfiguration; + private NotificationTypeConfiguration getTypeConf(Type type) { + return ui.getState().notificationConfigurations.get(type.getStyle()); } - - private NotificationConfigurationState getState(boolean markAsDirty) { - return ui.getState(markAsDirty).notificationConfiguration; - } - } diff --git a/server/src/com/vaadin/ui/TabSheet.java b/server/src/com/vaadin/ui/TabSheet.java index 8e2c40fc0f..17ea352d49 100644 --- a/server/src/com/vaadin/ui/TabSheet.java +++ b/server/src/com/vaadin/ui/TabSheet.java @@ -292,34 +292,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * @return the created {@link Tab} */ public Tab addTab(Component c, String caption, Resource icon) { - return addTab(c, caption, icon, "", components.size()); - } - - /** - * Adds a new tab into TabSheet. - * - * The first tab added to a tab sheet is automatically selected and a tab - * selection event is fired. - * - * If the component is already present in the tab sheet, changes its caption - * and icon and icon alternate text and returns the corresponding (old) tab, - * preserving other tab metadata. - * - * @param c - * the component to be added onto tab - should not be null. - * @param caption - * the caption to be set for the component and used rendered in - * tab bar - * @param icon - * the icon to be set for the component and used rendered in tab - * bar - * @param iconAltText - * the alternate text for the icon - * @return the created {@link Tab} - */ - public Tab addTab(Component c, String caption, Resource icon, - String iconAltText) { - return addTab(c, caption, icon, iconAltText, components.size()); + return addTab(c, caption, icon, components.size()); } /** @@ -344,43 +317,13 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, * the position at where the the tab should be added. * @return the created {@link Tab} */ - public Tab addTab(Component c, String caption, Resource icon, int position) { - return addTab(c, caption, icon, "", position); - } - - /** - * Adds a new tab into TabSheet. - * - * The first tab added to a tab sheet is automatically selected and a tab - * selection event is fired. - * - * If the component is already present in the tab sheet, changes its caption - * and icon and icon alternate text and returns the corresponding (old) tab, - * preserving other tab metadata like the position. - * - * @param tabComponent - * the component to be added onto tab - should not be null. - * @param caption - * the caption to be set for the component and used rendered in - * tab bar - * @param icon - * the icon to be set for the component and used rendered in tab - * bar - * @param iconAltText - * the alternate text for the icon - * @param position - * the position at where the the tab should be added. - * @return the created {@link Tab} - */ - public Tab addTab(Component tabComponent, String caption, Resource icon, - String iconAltText, int position) { - + public Tab addTab(Component tabComponent, String caption, Resource icon, int position) { if (tabComponent == null) { return null; } else if (tabs.containsKey(tabComponent)) { Tab tab = tabs.get(tabComponent); tab.setCaption(caption); - tab.setIcon(icon, iconAltText); + tab.setIcon(icon); return tab; } else { components.add(position, tabComponent); @@ -461,11 +404,11 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, Tab tab = ((TabSheet) source).getTab(c); caption = tab.getCaption(); icon = tab.getIcon(); - iconAltText = tab.getIconAltText(); + iconAltText = tab.getIconAlternateText(); } source.removeComponent(c); - addTab(c, caption, icon, iconAltText); - + Tab tab = addTab(c, caption, icon); + tab.setIconAlternateText(iconAltText); } } @@ -968,16 +911,20 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, /** * Gets the icon alt text for the tab. + * + * @since 7.2 */ - public String getIconAltText(); + public String getIconAlternateText(); /** * Sets the icon alt text for the tab. - * + * + * @since 7.2 + * * @param iconAltText * the icon to set */ - public void setIconAltText(String iconAltText); + public void setIconAlternateText(String iconAltText); /** * Gets the description for the tab. The description can be used to @@ -1135,12 +1082,12 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, } @Override - public String getIconAltText() { + public String getIconAlternateText() { return tabState.iconAltText; } @Override - public void setIconAltText(String iconAltText) { + public void setIconAlternateText(String iconAltText) { tabState.iconAltText = iconAltText; markAsDirty(); } @@ -1428,7 +1375,7 @@ public class TabSheet extends AbstractComponentContainer implements Focusable, */ private static void copyTabMetadata(Tab from, Tab to) { to.setCaption(from.getCaption()); - to.setIcon(from.getIcon(), from.getIconAltText()); + to.setIcon(from.getIcon(), from.getIconAlternateText()); to.setDescription(from.getDescription()); to.setVisible(from.isVisible()); to.setEnabled(from.isEnabled()); diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index 2b2e773601..5fbd654dcf 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -668,10 +668,10 @@ public abstract class UI extends AbstractSingleComponentContainer implements * @param request * the request that caused this UI to be reloaded */ - public void doReinit(VaadinRequest request) { + public void doRefresh(VaadinRequest request) { // This is a horrible hack. We want to have the most recent location and - // browser window size available in reinit(), but we want to call - // listeners, if any, only after reinit(). So we momentarily assign the + // browser window size available in refresh(), but we want to call + // listeners, if any, only after refresh(). So we momentarily assign the // old values back before setting the new values again to ensure the // events are properly fired. @@ -683,7 +683,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements page.init(request); - reinit(request); + refresh(request); URI newLocation = page.getLocation(); int newWidth = page.getBrowserWindowWidth(); @@ -709,7 +709,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements * @param request * the request that caused this UI to be reloaded */ - protected void reinit(VaadinRequest request) { + protected void refresh(VaadinRequest request) { } /** diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java index d3afdaacf1..022adc6373 100644 --- a/server/src/com/vaadin/ui/Window.java +++ b/server/src/com/vaadin/ui/Window.java @@ -41,7 +41,7 @@ import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.ui.window.WindowMode; import com.vaadin.shared.ui.window.WindowServerRpc; import com.vaadin.shared.ui.window.WindowState; -import com.vaadin.shared.ui.window.WindowState.WindowRole; +import com.vaadin.shared.ui.window.WindowRole; import com.vaadin.util.ReflectTools; /** diff --git a/server/tests/src/com/vaadin/ui/UIInitRefreshTest.java b/server/tests/src/com/vaadin/ui/UIInitRefreshTest.java new file mode 100644 index 0000000000..a807e4b656 --- /dev/null +++ b/server/tests/src/com/vaadin/ui/UIInitRefreshTest.java @@ -0,0 +1,116 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.ui; + +import org.easymock.EasyMock; +import org.easymock.IMocksControl; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.server.Page.BrowserWindowResizeEvent; +import com.vaadin.server.Page.BrowserWindowResizeListener; +import com.vaadin.server.Page.UriFragmentChangedEvent; +import com.vaadin.server.Page.UriFragmentChangedListener; +import com.vaadin.server.VaadinRequest; + +public class UIInitRefreshTest { + + private boolean initCalled; + private boolean refreshCalled; + private boolean fragmentChangeCalled; + private boolean browserWindowResizeCalled; + + private class TestUI extends UI implements UriFragmentChangedListener, + BrowserWindowResizeListener { + @Override + protected void init(VaadinRequest request) { + getPage().addBrowserWindowResizeListener(this); + getPage().addUriFragmentChangedListener(this); + + initCalled = true; + + Assert.assertEquals("foo", getPage().getUriFragment()); + Assert.assertEquals(100, getPage().getBrowserWindowWidth()); + Assert.assertEquals(100, getPage().getBrowserWindowHeight()); + + Assert.assertFalse(fragmentChangeCalled); + Assert.assertFalse(browserWindowResizeCalled); + } + + @Override + protected void refresh(VaadinRequest request) { + refreshCalled = true; + + Assert.assertEquals("bar", getPage().getUriFragment()); + Assert.assertEquals(200, getPage().getBrowserWindowWidth()); + Assert.assertEquals(200, getPage().getBrowserWindowHeight()); + + Assert.assertFalse(fragmentChangeCalled); + Assert.assertFalse(browserWindowResizeCalled); + } + + @Override + public void browserWindowResized(BrowserWindowResizeEvent event) { + Assert.assertEquals(200, event.getWidth()); + Assert.assertEquals(200, event.getHeight()); + browserWindowResizeCalled = true; + } + + @Override + public void uriFragmentChanged(UriFragmentChangedEvent event) { + Assert.assertEquals("bar", event.getUriFragment()); + fragmentChangeCalled = true; + } + }; + + @Before + public void setUp() { + initCalled = refreshCalled = fragmentChangeCalled = browserWindowResizeCalled = false; + } + + @Test + public void testListenersCalled() { + IMocksControl control = EasyMock.createNiceControl(); + + VaadinRequest initRequest = control.createMock(VaadinRequest.class); + EasyMock.expect(initRequest.getParameter("v-loc")).andReturn( + "http://example.com/#foo"); + EasyMock.expect(initRequest.getParameter("v-cw")).andReturn("100"); + EasyMock.expect(initRequest.getParameter("v-ch")).andReturn("100"); + + VaadinRequest reinitRequest = control.createMock(VaadinRequest.class); + EasyMock.expect(reinitRequest.getParameter("v-loc")).andReturn( + "http://example.com/#bar"); + EasyMock.expect(reinitRequest.getParameter("v-cw")).andReturn("200"); + EasyMock.expect(reinitRequest.getParameter("v-ch")).andReturn("200"); + + control.replay(); + + UI ui = new TestUI(); + ui.doInit(initRequest, 0, ""); + + Assert.assertTrue(initCalled); + Assert.assertFalse(fragmentChangeCalled); + Assert.assertFalse(browserWindowResizeCalled); + + ui.doRefresh(reinitRequest); + + Assert.assertTrue(refreshCalled); + Assert.assertTrue(fragmentChangeCalled); + Assert.assertTrue(browserWindowResizeCalled); + } +} diff --git a/server/tests/src/com/vaadin/ui/UIInitReinitTest.java b/server/tests/src/com/vaadin/ui/UIInitReinitTest.java deleted file mode 100644 index f8ec0e68c2..0000000000 --- a/server/tests/src/com/vaadin/ui/UIInitReinitTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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.vaadin.ui; - -import org.easymock.EasyMock; -import org.easymock.IMocksControl; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import com.vaadin.server.Page.BrowserWindowResizeEvent; -import com.vaadin.server.Page.BrowserWindowResizeListener; -import com.vaadin.server.Page.UriFragmentChangedEvent; -import com.vaadin.server.Page.UriFragmentChangedListener; -import com.vaadin.server.VaadinRequest; - -public class UIInitReinitTest { - - private boolean initCalled; - private boolean reinitCalled; - private boolean fragmentChangeCalled; - private boolean browserWindowResizeCalled; - - private class TestUI extends UI implements UriFragmentChangedListener, - BrowserWindowResizeListener { - @Override - protected void init(VaadinRequest request) { - getPage().addBrowserWindowResizeListener(this); - getPage().addUriFragmentChangedListener(this); - - initCalled = true; - - Assert.assertEquals("foo", getPage().getUriFragment()); - Assert.assertEquals(100, getPage().getBrowserWindowWidth()); - Assert.assertEquals(100, getPage().getBrowserWindowHeight()); - - Assert.assertFalse(fragmentChangeCalled); - Assert.assertFalse(browserWindowResizeCalled); - } - - @Override - protected void reinit(VaadinRequest request) { - reinitCalled = true; - - Assert.assertEquals("bar", getPage().getUriFragment()); - Assert.assertEquals(200, getPage().getBrowserWindowWidth()); - Assert.assertEquals(200, getPage().getBrowserWindowHeight()); - - Assert.assertFalse(fragmentChangeCalled); - Assert.assertFalse(browserWindowResizeCalled); - } - - @Override - public void browserWindowResized(BrowserWindowResizeEvent event) { - Assert.assertEquals(200, event.getWidth()); - Assert.assertEquals(200, event.getHeight()); - browserWindowResizeCalled = true; - } - - @Override - public void uriFragmentChanged(UriFragmentChangedEvent event) { - Assert.assertEquals("bar", event.getUriFragment()); - fragmentChangeCalled = true; - } - }; - - @Before - public void setUp() { - initCalled = reinitCalled = fragmentChangeCalled = browserWindowResizeCalled = false; - } - - @Test - public void testListenersCalled() { - IMocksControl control = EasyMock.createNiceControl(); - - VaadinRequest initRequest = control.createMock(VaadinRequest.class); - EasyMock.expect(initRequest.getParameter("v-loc")).andReturn( - "http://example.com/#foo"); - EasyMock.expect(initRequest.getParameter("v-cw")).andReturn("100"); - EasyMock.expect(initRequest.getParameter("v-ch")).andReturn("100"); - - VaadinRequest reinitRequest = control.createMock(VaadinRequest.class); - EasyMock.expect(reinitRequest.getParameter("v-loc")).andReturn( - "http://example.com/#bar"); - EasyMock.expect(reinitRequest.getParameter("v-cw")).andReturn("200"); - EasyMock.expect(reinitRequest.getParameter("v-ch")).andReturn("200"); - - control.replay(); - - UI ui = new TestUI(); - ui.doInit(initRequest, 0, ""); - - Assert.assertTrue(initCalled); - Assert.assertFalse(fragmentChangeCalled); - Assert.assertFalse(browserWindowResizeCalled); - - ui.doReinit(reinitRequest); - - Assert.assertTrue(reinitCalled); - Assert.assertTrue(fragmentChangeCalled); - Assert.assertTrue(browserWindowResizeCalled); - } -} diff --git a/shared/src/com/vaadin/shared/ui/ui/NotificationConfigurationBean.java b/shared/src/com/vaadin/shared/ui/ui/NotificationConfigurationBean.java deleted file mode 100644 index 6c8c743d57..0000000000 --- a/shared/src/com/vaadin/shared/ui/ui/NotificationConfigurationBean.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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.vaadin.shared.ui.ui; - -import java.io.Serializable; - -/** - * Holds configuration information for a notification type. - * - * @author Vaadin Ltd - */ -public class NotificationConfigurationBean implements Serializable { - /** - * Available WAI-ARIA roles for a notification. - */ - public enum Role { - ALERT, STATUS - } - - private String prefix; - private String postfix; - private Role role = Role.ALERT; - - public NotificationConfigurationBean() { - } - - public NotificationConfigurationBean(String prefix, String postfix, - Role role) { - this.prefix = prefix; - this.postfix = postfix; - this.role = role; - } - - /** - * Returns the accessibility prefix, which is placed before the notification - * content. - * - * @return the prefix - */ - public String getAssistivePrefix() { - return prefix; - } - - /** - * Sets the accessibility prefix, which is placed before the notification - * content. - * - * @param pefix - * the prefix to set - */ - public void setAssistivePrefix(String prefix) { - this.prefix = prefix; - } - - /** - * Checks if an accessibility prefix is set. - * - * @return true when assistivePrefix is not null and has a length > 0, false - * otherwise - */ - public boolean hasAssistivePrefix() { - return prefix != null && !prefix.isEmpty(); - } - - /** - * Returns the accessibility postfix, which is placed after the notification - * content. - * - * @return the postfix - */ - public String getAssistivePostfix() { - return postfix; - } - - /** - * Sets the accessibility postfix, which is placed after the notification - * content. - * - * @param postfix - * the postfix to set - */ - public void setAssistivePostfix(String postfix) { - this.postfix = postfix; - } - - /** - * Checks if an accessibility postfix is set. - * - * @return true when postfix is not null and has a length > 0, false - * otherwise - */ - public boolean hasAssistivePostfix() { - return postfix != null && !postfix.isEmpty(); - } - - /** - * Returns the WAI-ARIA role that defines how an assistive device will - * inform the user about a notification. - * - * @return the role - */ - public Role getAssistiveRole() { - return role; - } - - /** - * Sets the WAI-ARIA role that defines how an assistive device will inform - * the user about a notification. - * - * Available roles are alert, alertdialog and status (@see Roles - * Model). - * - * @param role - * the role to set - */ - public void setAssistiveRole(Role role) { - this.role = role; - } -} diff --git a/shared/src/com/vaadin/shared/ui/ui/NotificationRole.java b/shared/src/com/vaadin/shared/ui/ui/NotificationRole.java new file mode 100644 index 0000000000..218e5e59e6 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/ui/NotificationRole.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.shared.ui.ui; + +/** + * Available WAI-ARIA roles for a notification. + * + * @since 7.2 + */ +public enum NotificationRole { + ALERT, STATUS +} diff --git a/shared/src/com/vaadin/shared/ui/ui/UIState.java b/shared/src/com/vaadin/shared/ui/ui/UIState.java index d2e6f037e9..08fe3c7d64 100644 --- a/shared/src/com/vaadin/shared/ui/ui/UIState.java +++ b/shared/src/com/vaadin/shared/ui/ui/UIState.java @@ -23,17 +23,50 @@ import java.util.Map; import com.vaadin.shared.communication.PushMode; import com.vaadin.shared.ui.TabIndexState; -import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role; public class UIState extends TabIndexState { public TooltipConfigurationState tooltipConfiguration = new TooltipConfigurationState(); public LoadingIndicatorConfigurationState loadingIndicatorConfiguration = new LoadingIndicatorConfigurationState(); - public NotificationConfigurationState notificationConfiguration = new NotificationConfigurationState(); public int pollInterval = -1; // Informing users of assistive devices, that the content of this container // is announced automatically and does not need to be navigated into public String overlayContainerLabel = "This content is announced automatically and does not need to be navigated into."; + public Map notificationConfigurations = new HashMap(); + { + notificationConfigurations.put("error", + new NotificationTypeConfiguration("Error: ", + " - close with ESC-key", NotificationRole.ALERT)); + notificationConfigurations.put("warning", + new NotificationTypeConfiguration("Warning: ", null, + NotificationRole.ALERT)); + notificationConfigurations.put("humanized", + new NotificationTypeConfiguration("Info: ", null, + NotificationRole.ALERT)); + notificationConfigurations.put("tray", + new NotificationTypeConfiguration("Status: ", null, + NotificationRole.STATUS)); + notificationConfigurations.put("assistive", + new NotificationTypeConfiguration("Note: ", null, + NotificationRole.STATUS)); + } + /** + * State related to the Page class. + */ + public PageState pageState = new PageState(); + /** + * State related to the LocaleService class. + */ + public LocaleServiceState localeServiceState = new LocaleServiceState(); + /** + * Configuration for the push channel + */ + public PushConfigurationState pushConfiguration = new PushConfigurationState(); + { + primaryStyleName = "v-ui"; + // Default is 1 for legacy reasons + tabIndex = 1; + } public static class LoadingIndicatorConfigurationState implements Serializable { @@ -50,19 +83,19 @@ public class UIState extends TabIndexState { public int maxWidth = 500; } - public static class NotificationConfigurationState implements Serializable { - public Map setup = new HashMap(); - { - setup.put("error", new NotificationConfigurationBean("Error: ", - " - close with ESC-key", Role.ALERT)); - setup.put("warning", new NotificationConfigurationBean("Warning: ", - null, Role.ALERT)); - setup.put("humanized", new NotificationConfigurationBean("Info: ", - null, Role.ALERT)); - setup.put("tray", new NotificationConfigurationBean("Status: ", - null, Role.STATUS)); - setup.put("assistive", new NotificationConfigurationBean("Note: ", - null, Role.STATUS)); + public static class NotificationTypeConfiguration implements Serializable { + public String prefix; + public String postfix; + public NotificationRole notificationRole = NotificationRole.ALERT; + + public NotificationTypeConfiguration() { + } + + public NotificationTypeConfiguration(String prefix, String postfix, + NotificationRole role) { + this.prefix = prefix; + this.postfix = postfix; + this.notificationRole = role; } } @@ -80,26 +113,6 @@ public class UIState extends TabIndexState { } } - /** - * State related to the Page class. - */ - public PageState pageState = new PageState(); - /** - * State related to the LocaleService class. - */ - public LocaleServiceState localeServiceState = new LocaleServiceState(); - - /** - * Configuration for the push channel - */ - public PushConfigurationState pushConfiguration = new PushConfigurationState(); - - { - primaryStyleName = "v-ui"; - // Default is 1 for legacy reasons - tabIndex = 1; - } - public static class LocaleServiceState implements Serializable { public List localeData = new ArrayList(); } diff --git a/shared/src/com/vaadin/shared/ui/window/WindowRole.java b/shared/src/com/vaadin/shared/ui/window/WindowRole.java new file mode 100644 index 0000000000..fc6b099620 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/window/WindowRole.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.shared.ui.window; + +/** + * Available WAI-ARIA roles for a window. + * + * @since 7.2 + */ +public enum WindowRole { + ALERTDIALOG, DIALOG +} diff --git a/shared/src/com/vaadin/shared/ui/window/WindowState.java b/shared/src/com/vaadin/shared/ui/window/WindowState.java index e27bf87e88..ebb1995287 100644 --- a/shared/src/com/vaadin/shared/ui/window/WindowState.java +++ b/shared/src/com/vaadin/shared/ui/window/WindowState.java @@ -23,13 +23,6 @@ public class WindowState extends PanelState { primaryStyleName = "v-window"; } - /** - * Available WAI-ARIA roles for a window. - */ - public enum WindowRole { - ALERTDIALOG, DIALOG - } - public boolean modal = false; public boolean resizable = true; public boolean resizeLazy = false; diff --git a/uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.java b/uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.java index 27af49a397..ecf704835f 100644 --- a/uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.java +++ b/uitest/src/com/vaadin/tests/components/notification/NotificationsWaiAria.java @@ -2,7 +2,7 @@ package com.vaadin.tests.components.notification; import com.vaadin.data.Item; import com.vaadin.server.Page; -import com.vaadin.shared.ui.ui.NotificationConfigurationBean.Role; +import com.vaadin.shared.ui.ui.NotificationRole; import com.vaadin.tests.components.TestBase; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; @@ -11,8 +11,10 @@ import com.vaadin.ui.ComboBox; import com.vaadin.ui.NativeSelect; import com.vaadin.ui.Notification; import com.vaadin.ui.Notification.Type; +import com.vaadin.ui.NotificationConfiguration; import com.vaadin.ui.TextArea; import com.vaadin.ui.TextField; +import com.vaadin.ui.UI; public class NotificationsWaiAria extends TestBase { @@ -36,9 +38,9 @@ public class NotificationsWaiAria extends TestBase { " - closes automatically after 10 seconds"); addComponent(postfix); - role = new NativeSelect("Role"); - role.addItem(Role.ALERT); - role.addItem(Role.STATUS); + role = new NativeSelect("NotificationRole"); + role.addItem(NotificationRole.ALERT); + role.addItem(NotificationRole.STATUS); role.setValue(role.getItemIds().iterator().next()); addComponent(role); @@ -96,9 +98,12 @@ public class NotificationsWaiAria extends TestBase { Notification n = new Notification(tf.getValue(), typeValue); n.setHtmlContentAllowed(true); - n.setAssistivePrefixForType(typeValue, prefix.getValue()); - n.setAssistivePostfixForType(typeValue, postfix.getValue()); - n.setAssistiveRoleForType(typeValue, (Role) role.getValue()); + NotificationConfiguration notificationConf = UI.getCurrent() + .getNotificationConfiguration(); + notificationConf.setAssistivePrefix(typeValue, prefix.getValue()); + notificationConf.setAssistivePostfix(typeValue, postfix.getValue()); + notificationConf + .setAssistiveRole(typeValue, (NotificationRole) role.getValue()); n.show(Page.getCurrent()); } diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.java b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.java index ccdc4ecb38..c5e01969a1 100644 --- a/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.java +++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabSheetIcons.java @@ -46,7 +46,7 @@ public class TabSheetIcons extends TestBase { for (Component c : tab) { tabsheet.addTab(c); - tabsheet.getTab(c).setIconAltText( + tabsheet.getTab(c).setIconAlternateText( "iconalt" + tabsheet.getComponentCount()); } diff --git a/uitest/src/com/vaadin/tests/components/ui/UIRefresh.java b/uitest/src/com/vaadin/tests/components/ui/UIRefresh.java new file mode 100644 index 0000000000..b61e32c984 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/UIRefresh.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.tests.components.ui; + +import com.vaadin.annotations.PreserveOnRefresh; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Label; + +@PreserveOnRefresh +public class UIRefresh extends AbstractTestUI { + + public static final String REINIT_ID = "reinit"; + + @Override + protected void setup(VaadinRequest request) { + } + + @Override + protected void refresh(VaadinRequest request) { + Label l = new Label("Reinit!"); + l.setId(REINIT_ID); + addComponent(l); + } + + @Override + public String getTestDescription() { + return "UI reinit after refresh"; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(12191); + } +} diff --git a/uitest/src/com/vaadin/tests/components/ui/UIRefreshTest.java b/uitest/src/com/vaadin/tests/components/ui/UIRefreshTest.java new file mode 100644 index 0000000000..974c4bfe5a --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/UIRefreshTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.tests.components.ui; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class UIRefreshTest extends MultiBrowserTest { + + @Test + public void testUIRefresh() { + openTestURL(); + Assert.assertFalse(reinitLabelExists()); + // Reload the page; UI.refresh should be invoked + openTestURL(); + Assert.assertTrue(reinitLabelExists()); + } + + private boolean reinitLabelExists() { + return !getDriver().findElements(By.id(UIRefresh.REINIT_ID)).isEmpty(); + } +} diff --git a/uitest/src/com/vaadin/tests/components/ui/UIReinit.java b/uitest/src/com/vaadin/tests/components/ui/UIReinit.java deleted file mode 100644 index bef7a5a6d1..0000000000 --- a/uitest/src/com/vaadin/tests/components/ui/UIReinit.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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.vaadin.tests.components.ui; - -import com.vaadin.annotations.PreserveOnRefresh; -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractTestUI; -import com.vaadin.ui.Label; - -@PreserveOnRefresh -public class UIReinit extends AbstractTestUI { - - public static final String REINIT_ID = "reinit"; - - @Override - protected void setup(VaadinRequest request) { - } - - @Override - protected void reinit(VaadinRequest request) { - Label l = new Label("Reinit!"); - l.setId(REINIT_ID); - addComponent(l); - } - - @Override - public String getTestDescription() { - return "UI reinit after refresh"; - } - - @Override - protected Integer getTicketNumber() { - return Integer.valueOf(12191); - } -} diff --git a/uitest/src/com/vaadin/tests/components/ui/UIReinitTest.java b/uitest/src/com/vaadin/tests/components/ui/UIReinitTest.java deleted file mode 100644 index 82132d3c65..0000000000 --- a/uitest/src/com/vaadin/tests/components/ui/UIReinitTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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.vaadin.tests.components.ui; - -import org.junit.Assert; -import org.junit.Test; - -import com.vaadin.testbench.By; -import com.vaadin.tests.tb3.MultiBrowserTest; - -public class UIReinitTest extends MultiBrowserTest { - - @Test - public void testUIReinit() { - openTestURL(); - Assert.assertFalse(reinitLabelExists()); - // Reload the page; UI.reinit should be invoked - openTestURL(); - Assert.assertTrue(reinitLabelExists()); - } - - private boolean reinitLabelExists() { - return !getDriver().findElements(By.id(UIReinit.REINIT_ID)).isEmpty(); - } -} diff --git a/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.java b/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.java index 39989926e7..c7379f666b 100644 --- a/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.java +++ b/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAria.java @@ -2,7 +2,7 @@ package com.vaadin.tests.components.window; import com.vaadin.server.ThemeResource; import com.vaadin.server.VaadinRequest; -import com.vaadin.shared.ui.window.WindowState.WindowRole; +import com.vaadin.shared.ui.window.WindowRole; import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; -- cgit v1.2.3 From 758f26b7d767f536a09ae38c28dfbf859377e66a Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Mon, 14 Apr 2014 11:02:49 +0300 Subject: Fix NPE when removing tabsheet (#13402) Change-Id: Idc78fc1bc6380d7bfada99f1efe2484b04ce3c74 --- client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java b/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java index b472300c21..564e5847d9 100644 --- a/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java +++ b/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java @@ -176,7 +176,11 @@ public class TabsheetConnector extends TabsheetBaseConnector implements * (Re-)render the content of the active tab. */ protected void renderContent() { - ComponentConnector contentConnector = getChildComponents().get(0); + ComponentConnector contentConnector = null; + if (!getChildComponents().isEmpty()) { + contentConnector = getChildComponents().get(0); + } + if (null != contentConnector) { getWidget().renderContent(contentConnector.getWidget()); } else { -- cgit v1.2.3 From 35e2a34c9c4dfcf9117db31f6403401129c9befc Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Mon, 14 Apr 2014 10:19:17 +0300 Subject: Fix FieldGroup and TransactionalPropertyWrapper memory leaks (#13438) Change-Id: Ifafb6d87b4280f8bd9e631235fff62f42de4b4c8 --- .../src/com/vaadin/data/fieldgroup/FieldGroup.java | 8 ++++++- .../data/util/TransactionalPropertyWrapper.java | 26 +++++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java index 23f2da53ce..c5f9907610 100644 --- a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java +++ b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java @@ -316,12 +316,18 @@ public class FieldGroup implements Serializable { "The given field is not part of this FieldBinder"); } + TransactionalPropertyWrapper wrapper = null; Property fieldDataSource = field.getPropertyDataSource(); if (fieldDataSource instanceof TransactionalPropertyWrapper) { - fieldDataSource = ((TransactionalPropertyWrapper) fieldDataSource) + wrapper = (TransactionalPropertyWrapper) fieldDataSource; + fieldDataSource = ((TransactionalPropertyWrapper) fieldDataSource) .getWrappedProperty(); + } if (fieldDataSource == getItemProperty(propertyId)) { + if (null != wrapper) { + wrapper.detachFromProperty(); + } field.setPropertyDataSource(null); } fieldToPropertyId.remove(field); diff --git a/server/src/com/vaadin/data/util/TransactionalPropertyWrapper.java b/server/src/com/vaadin/data/util/TransactionalPropertyWrapper.java index 6b0119c503..3bac5bdfb8 100644 --- a/server/src/com/vaadin/data/util/TransactionalPropertyWrapper.java +++ b/server/src/com/vaadin/data/util/TransactionalPropertyWrapper.java @@ -48,18 +48,32 @@ public class TransactionalPropertyWrapper extends AbstractProperty private boolean inTransaction = false; private boolean valueChangePending; private T valueBeforeTransaction; + private final ValueChangeListener listener = new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + fireValueChange(); + } + }; public TransactionalPropertyWrapper(Property wrappedProperty) { this.wrappedProperty = wrappedProperty; if (wrappedProperty instanceof ValueChangeNotifier) { ((ValueChangeNotifier) wrappedProperty) - .addListener(new ValueChangeListener() { + .addValueChangeListener(listener); + } + } - @Override - public void valueChange(ValueChangeEvent event) { - fireValueChange(); - } - }); + /** + * Removes the ValueChangeListener from wrapped Property that was added by + * TransactionalPropertyWrapper. + * + * @since 7.1.14 + */ + public void detachFromProperty() { + if (wrappedProperty instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) wrappedProperty) + .removeValueChangeListener(listener); } } -- cgit v1.2.3 From c544c6cbfac98fb6c2e22465d89802caa6fd0ee6 Mon Sep 17 00:00:00 2001 From: Leif Åstrand Date: Mon, 14 Apr 2014 11:11:07 +0300 Subject: Fix copyright headers not passing the validation Also let through some other formatting changes that Eclipse insisted on when saving the touched files. Change-Id: I319de35c4862555b010d6da6d4a5e096619e2c34 --- .../com/vaadin/client/ApplicationConnection.java | 3 +-- .../client/extensions/ResponsiveConnector.java | 26 +++++++++++----------- server/src/com/vaadin/server/FontAwesome.java | 3 ++- server/src/com/vaadin/server/FontIcon.java | 3 ++- server/src/com/vaadin/server/Responsive.java | 8 +++---- 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 5d614439bb..229460ed20 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -1,4 +1,4 @@ -/* +/* * Copyright 2000-2013 Vaadin Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not @@ -65,7 +65,6 @@ import com.google.gwt.user.client.Window.ClosingHandler; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConfiguration.ErrorMessage; -import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent; import com.vaadin.client.ResourceLoader.ResourceLoadEvent; import com.vaadin.client.ResourceLoader.ResourceLoadListener; import com.vaadin.client.communication.HasJavaScriptConnectorHelper; diff --git a/client/src/com/vaadin/client/extensions/ResponsiveConnector.java b/client/src/com/vaadin/client/extensions/ResponsiveConnector.java index 500e4a0916..20a5278e1a 100644 --- a/client/src/com/vaadin/client/extensions/ResponsiveConnector.java +++ b/client/src/com/vaadin/client/extensions/ResponsiveConnector.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2013 Vaadin Ltd. - * + * * 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 @@ -100,13 +100,13 @@ public class ResponsiveConnector extends AbstractExtensionConnector implements * @return The selectors in a comma delimited string. */ protected String constructSelectorsForTarget() { - String primaryStyle = this.target.getState().primaryStyleName; + String primaryStyle = target.getState().primaryStyleName; StringBuilder selectors = new StringBuilder(); selectors.append(".").append(primaryStyle); - if (this.target.getState().styles != null - && this.target.getState().styles.size() > 0) { - for (String style : this.target.getState().styles) { + if (target.getState().styles != null + && target.getState().styles.size() > 0) { + for (String style : target.getState().styles) { selectors.append(",.").append(style); selectors.append(",.").append(primaryStyle).append(".") .append(style); @@ -118,8 +118,8 @@ public class ResponsiveConnector extends AbstractExtensionConnector implements } // Allow the ID to be used as the selector as well for ranges - if (this.target.getState().id != null) { - selectors.append(",#").append(this.target.getState().id); + if (target.getState().id != null) { + selectors.append(",#").append(target.getState().id); } return selectors.toString(); } @@ -128,7 +128,7 @@ public class ResponsiveConnector extends AbstractExtensionConnector implements public void onUnregister() { super.onUnregister(); LayoutManager.get(getConnection()).removeElementResizeListener( - this.target.getWidget().getElement(), this); + target.getWidget().getElement(), this); } /** @@ -314,7 +314,7 @@ public class ResponsiveConnector extends AbstractExtensionConnector implements int height = event.getLayoutManager() .getOuterHeight(event.getElement()); - com.google.gwt.user.client.Element element = this.target.getWidget() + com.google.gwt.user.client.Element element = target.getWidget() .getElement(); boolean forceRedraw = false; @@ -323,7 +323,7 @@ public class ResponsiveConnector extends AbstractExtensionConnector implements event.getElement()); if (!"".equals(currentWidthRanges)) { - this.target.getWidget().getElement() + target.getWidget().getElement() .setAttribute("width-range", currentWidthRanges); forceRedraw = true; } else { @@ -335,7 +335,7 @@ public class ResponsiveConnector extends AbstractExtensionConnector implements event.getElement()); if (!"".equals(currentHeightRanges)) { - this.target.getWidget().getElement() + target.getWidget().getElement() .setAttribute("height-range", currentHeightRanges); forceRedraw = true; } else { diff --git a/server/src/com/vaadin/server/FontAwesome.java b/server/src/com/vaadin/server/FontAwesome.java index a7f4c7b342..d4ad612d45 100644 --- a/server/src/com/vaadin/server/FontAwesome.java +++ b/server/src/com/vaadin/server/FontAwesome.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 Vaadin Ltd. + * Copyright 2000-2013 Vaadin Ltd. * * 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 @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.vaadin.server; /** diff --git a/server/src/com/vaadin/server/FontIcon.java b/server/src/com/vaadin/server/FontIcon.java index 45279f2c44..6b9a55cf20 100644 --- a/server/src/com/vaadin/server/FontIcon.java +++ b/server/src/com/vaadin/server/FontIcon.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 Vaadin Ltd. + * Copyright 2000-2013 Vaadin Ltd. * * 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 @@ -13,6 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ + package com.vaadin.server; import com.vaadin.shared.ui.label.ContentMode; diff --git a/server/src/com/vaadin/server/Responsive.java b/server/src/com/vaadin/server/Responsive.java index d69c204c94..b92870b621 100644 --- a/server/src/com/vaadin/server/Responsive.java +++ b/server/src/com/vaadin/server/Responsive.java @@ -1,12 +1,12 @@ /* * Copyright 2000-2013 Vaadin Ltd. - * + * * 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 @@ -49,7 +49,7 @@ import com.vaadin.ui.Component; * *

  * CssLayout layout = new CssLayout();
- * layout.setStyleName("responsive");
+ * layout.setStyleName("responsive");
  * layout.setSizeFull();
  * Responsive.makeResponsive(layout);
  * 
-- cgit v1.2.3 From 487963914e6ff7ab8def7af809ef2db4b79faa55 Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Wed, 9 Apr 2014 10:58:53 +0300 Subject: Redo Push reconnect tests. Change-Id: I75330f67f6a57658e95b9510502bf6a1e38924ad --- .../src/com/vaadin/tests/push/BasicPushTest.java | 71 +++++----- .../tests/push/ReconnectLongPollingTest.java | 1 + .../vaadin/tests/push/ReconnectStreamingTest.java | 1 + .../src/com/vaadin/tests/push/ReconnectTest.java | 152 ++++++++++----------- .../tests/tb3/MultiBrowserTestWithProxy.java | 6 +- 5 files changed, 113 insertions(+), 118 deletions(-) diff --git a/uitest/src/com/vaadin/tests/push/BasicPushTest.java b/uitest/src/com/vaadin/tests/push/BasicPushTest.java index 670876e0f4..7f4492aeba 100644 --- a/uitest/src/com/vaadin/tests/push/BasicPushTest.java +++ b/uitest/src/com/vaadin/tests/push/BasicPushTest.java @@ -15,9 +15,10 @@ */ package com.vaadin.tests.push; -import org.junit.Assert; import org.junit.Test; +import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedCondition; import com.vaadin.tests.annotations.TestCategory; import com.vaadin.tests.tb3.AbstractTB3Test; @@ -26,49 +27,28 @@ import com.vaadin.tests.tb3.MultiBrowserTest; @TestCategory("push") public abstract class BasicPushTest extends MultiBrowserTest { + @Override + public void setup() throws Exception { + super.setup(); + } + @Test public void testPush() throws InterruptedException { openTestURL(); - // Test client initiated push - Assert.assertEquals(0, getClientCounter()); getIncrementButton().click(); - Assert.assertEquals("Client counter not incremented by button click", - 1, getClientCounter()); + testBench().disableWaitForVaadin(); + + waitUntilClientCounterChanges(1); + getIncrementButton().click(); getIncrementButton().click(); getIncrementButton().click(); - Assert.assertEquals("Four clicks should have incremented counter to 4", - 4, getClientCounter()); + waitUntilClientCounterChanges(4); // Test server initiated push getServerCounterStartButton().click(); - try { - Assert.assertEquals(0, getServerCounter()); - sleep(3000); - int serverCounter = getServerCounter(); - if (serverCounter < 1) { - // No push has happened - Assert.fail("No push has occured within 3s"); - } - sleep(3000); - if (getServerCounter() <= serverCounter) { - // No push has happened - Assert.fail("Only one push took place within 6s"); - - } - } finally { - // Avoid triggering push assertions - getServerCounterStopButton().click(); - } - } - - private int getServerCounter() { - return getServerCounter(this); - } - - private int getClientCounter() { - return getClientCounter(this); + waitUntilServerCounterChanges(); } public static int getClientCounter(AbstractTB3Test t) { @@ -81,10 +61,6 @@ public abstract class BasicPushTest extends MultiBrowserTest { return getIncrementButton(this); } - private WebElement getServerCounterStopButton() { - return getServerCounterStopButton(this); - } - private WebElement getServerCounterStartButton() { return getServerCounterStartButton(this); } @@ -107,4 +83,25 @@ public abstract class BasicPushTest extends MultiBrowserTest { return t.vaadinElementById(BasicPush.INCREMENT_BUTTON_ID); } + private void waitUntilClientCounterChanges(final int expectedValue) { + waitUntil(new ExpectedCondition() { + + @Override + public Boolean apply(WebDriver input) { + return BasicPushTest.getClientCounter(BasicPushTest.this) == expectedValue; + } + }, 10); + } + + private void waitUntilServerCounterChanges() { + final int counter = BasicPushTest.getServerCounter(this); + waitUntil(new ExpectedCondition() { + + @Override + public Boolean apply(WebDriver input) { + return BasicPushTest.getServerCounter(BasicPushTest.this) > counter; + } + }, 10); + } + } \ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/push/ReconnectLongPollingTest.java b/uitest/src/com/vaadin/tests/push/ReconnectLongPollingTest.java index 6d0c0c53b0..4ea0c7daf4 100644 --- a/uitest/src/com/vaadin/tests/push/ReconnectLongPollingTest.java +++ b/uitest/src/com/vaadin/tests/push/ReconnectLongPollingTest.java @@ -15,6 +15,7 @@ */ package com.vaadin.tests.push; + public class ReconnectLongPollingTest extends ReconnectTest { @Override diff --git a/uitest/src/com/vaadin/tests/push/ReconnectStreamingTest.java b/uitest/src/com/vaadin/tests/push/ReconnectStreamingTest.java index ebacf5be37..4e2143f9e0 100755 --- a/uitest/src/com/vaadin/tests/push/ReconnectStreamingTest.java +++ b/uitest/src/com/vaadin/tests/push/ReconnectStreamingTest.java @@ -15,6 +15,7 @@ */ package com.vaadin.tests.push; + public class ReconnectStreamingTest extends ReconnectTest { @Override diff --git a/uitest/src/com/vaadin/tests/push/ReconnectTest.java b/uitest/src/com/vaadin/tests/push/ReconnectTest.java index 5938ab1ff5..b4159ff4c2 100644 --- a/uitest/src/com/vaadin/tests/push/ReconnectTest.java +++ b/uitest/src/com/vaadin/tests/push/ReconnectTest.java @@ -15,115 +15,101 @@ */ package com.vaadin.tests.push; -import org.junit.Assert; import org.junit.Test; +import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedCondition; +import com.jcraft.jsch.JSchException; import com.vaadin.tests.tb3.MultiBrowserTestWithProxy; public abstract class ReconnectTest extends MultiBrowserTestWithProxy { - @Test - public void testShortDisconnect() throws Exception { + @Override + public void setup() throws Exception { + super.setup(); + setDebug(true); openTestURL(); startTimer(); waitUntilServerCounterChanges(); - disconnectProxy(); - Thread.sleep(1000); - connectProxy(); - waitUntilServerCounterChanges(); + + testBench().disableWaitForVaadin(); } @Test - public void testUserActionWhileDisconnectedWithDelay() throws Exception { - setDebug(true); - openTestURL(); - startTimer(); - waitUntilServerCounterChanges(); + public void messageIsQueuedOnDisconnect() throws JSchException { disconnectProxy(); - Assert.assertEquals(0, getClientCounter()); - getIncrementClientCounterButton().click(); - // No change while disconnected - Assert.assertEquals(0, getClientCounter()); - // Firefox sends extra onopen calls after a while, which breaks - // everything - Thread.sleep(10000); - connectProxy(); - waitUntilServerCounterChanges(); - // The change should have appeared when reconnected - Assert.assertEquals(1, getClientCounter()); + + clickButtonAndWaitForTwoReconnectAttempts(); + + connectAndVerifyConnectionEstablished(); + waitUntilClientCounterChanges(1); } @Test - public void testUserActionWhileDisconnected() throws Exception { - setDebug(true); - openTestURL(); - startTimer(); - waitUntilServerCounterChanges(); + public void messageIsNotSentBeforeConnectionIsEstablished() + throws JSchException, InterruptedException { disconnectProxy(); - Assert.assertEquals(0, getClientCounter()); - getIncrementClientCounterButton().click(); - // No change while disconnected - Assert.assertEquals(0, getClientCounter()); - Thread.sleep(1000); - connectProxy(); - waitUntilServerCounterChanges(); - // The change should have appeared when reconnected - Assert.assertEquals(1, getClientCounter()); - // IE has problems with another reconnect - disconnectProxy(); + waitForNextReconnectionAttempt(); + clickButtonAndWaitForTwoReconnectAttempts(); + + connectAndVerifyConnectionEstablished(); + waitUntilClientCounterChanges(1); + } + + private void clickButtonAndWaitForTwoReconnectAttempts() { + clickClientButton(); + + // Reconnection attempt is where pending messages can + // falsely be sent to server. + waitForNextReconnectionAttempt(); + + // Waiting for the second reconnection attempt makes sure that the + // first attempt has been completed or aborted. + waitForNextReconnectionAttempt(); + } + + private void clickClientButton() { getIncrementClientCounterButton().click(); - Assert.assertEquals(1, getClientCounter()); - Thread.sleep(1000); - connectProxy(); - waitUntilServerCounterChanges(); - Assert.assertEquals(2, getClientCounter()); } - @Test - public void testLongDisconnect() throws Exception { - setDebug(true); - openTestURL(); - startTimer(); - waitUntilServerCounterChanges(); - disconnectProxy(); - Thread.sleep(12000); - connectProxy(); - waitUntilServerCounterChanges(); + private void waitForNextReconnectionAttempt() { + clearDebugMessages(); + waitForDebugMessage("Reopening push connection"); } - @Test - public void testReallyLongDisconnect() throws Exception { - setDebug(true); - openTestURL(); - startTimer(); - waitUntilServerCounterChanges(); - disconnectProxy(); - Thread.sleep(120000); - connectProxy(); - waitUntilServerCounterChanges(); + private void clearDebugMessages() { + driver.findElement( + By.xpath("//button[@class='v-debugwindow-button' and @title='Clear log']")) + .click(); } - @Test - public void testMultipleDisconnects() throws Exception { - setDebug(true); - openTestURL(); - startTimer(); - waitUntilServerCounterChanges(); - for (int i = 0; i < 5; i++) { - disconnectProxy(); - Thread.sleep(1000); - connectProxy(); - waitUntilServerCounterChanges(); - } + private boolean hasDebugMessage(String message) { + return getDebugMessage(message) != null; + } + + private WebElement getDebugMessage(String message) { + return driver.findElement(By.xpath(String.format( + "//span[@class='v-debugwindow-message' and text()='%s']", + message))); + } + + private void waitForDebugMessage(final String expectedMessage) { + waitUntil(new ExpectedCondition() { + + @Override + public Boolean apply(WebDriver input) { + return hasDebugMessage(expectedMessage); + } + }, 30); } - private int getClientCounter() { - return BasicPushTest.getClientCounter(this); + private void connectAndVerifyConnectionEstablished() throws JSchException { + connectProxy(); + waitUntilServerCounterChanges(); } private WebElement getIncrementClientCounterButton() { @@ -141,6 +127,16 @@ public abstract class ReconnectTest extends MultiBrowserTestWithProxy { }, 30); } + private void waitUntilClientCounterChanges(final int expectedValue) { + waitUntil(new ExpectedCondition() { + + @Override + public Boolean apply(WebDriver input) { + return BasicPushTest.getClientCounter(ReconnectTest.this) == expectedValue; + } + }, 5); + } + private void startTimer() { BasicPushTest.getServerCounterStartButton(this).click(); } diff --git a/uitest/src/com/vaadin/tests/tb3/MultiBrowserTestWithProxy.java b/uitest/src/com/vaadin/tests/tb3/MultiBrowserTestWithProxy.java index d600b5fef2..d3e9ed6ef8 100755 --- a/uitest/src/com/vaadin/tests/tb3/MultiBrowserTestWithProxy.java +++ b/uitest/src/com/vaadin/tests/tb3/MultiBrowserTestWithProxy.java @@ -19,7 +19,6 @@ import java.io.File; import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; -import org.junit.Before; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; @@ -38,8 +37,9 @@ public abstract class MultiBrowserTestWithProxy extends MultiBrowserTest { System.getProperty("sshkey.file"), sshDir + "id_rsa", sshDir + "id_dsa", sshDir + "id_rsa2" }; - @Before - public void setupInitialProxy() throws JSchException { + @Override + public void setup() throws Exception { + super.setup(); connectProxy(); } -- cgit v1.2.3 From 168de1fefa0f09b07d438968575033e6d234cfb2 Mon Sep 17 00:00:00 2001 From: Teemu Suo-Anttila Date: Mon, 14 Apr 2014 11:14:32 +0000 Subject: Revert "Drag image for text-area should contain text of text-area (#13557)" This reverts commit f227f0c1068f17e5491bd399d9f5bde16a0c8272. Synchronising content after IE wraps the given element into a table with a body causes a NullPointerException on client side. This patch also contains new API and should be targeted to master branch. Change-Id: Ia19acd9fa31c7b67507bf797a2bab7c28ea37b4b --- client/src/com/vaadin/client/ui/VTextArea.java | 15 +----- .../com/vaadin/client/ui/dd/VDragCloneAware.java | 40 -------------- client/src/com/vaadin/client/ui/dd/VDragEvent.java | 31 ----------- .../draganddropwrapper/DragAndDropTextArea.java | 56 -------------------- .../DragAndDropTextAreaTest.java | 61 ---------------------- 5 files changed, 1 insertion(+), 202 deletions(-) delete mode 100644 client/src/com/vaadin/client/ui/dd/VDragCloneAware.java delete mode 100644 uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextArea.java delete mode 100644 uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextAreaTest.java diff --git a/client/src/com/vaadin/client/ui/VTextArea.java b/client/src/com/vaadin/client/ui/VTextArea.java index 627d14a058..2a697969df 100644 --- a/client/src/com/vaadin/client/ui/VTextArea.java +++ b/client/src/com/vaadin/client/ui/VTextArea.java @@ -17,7 +17,6 @@ package com.vaadin.client.ui; import com.google.gwt.core.client.Scheduler; -import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Overflow; import com.google.gwt.dom.client.Style.WhiteSpace; import com.google.gwt.dom.client.TextAreaElement; @@ -31,7 +30,6 @@ import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.vaadin.client.BrowserInfo; import com.vaadin.client.Util; -import com.vaadin.client.ui.dd.VDragCloneAware; /** * This class represents a multiline textfield (textarea). @@ -42,7 +40,7 @@ import com.vaadin.client.ui.dd.VDragCloneAware; * @author Vaadin Ltd. * */ -public class VTextArea extends VTextField implements VDragCloneAware { +public class VTextArea extends VTextField { public static final String CLASSNAME = "v-textarea"; private boolean wordwrap = true; private MaxLengthHandler maxLengthHandler = new MaxLengthHandler(); @@ -296,15 +294,4 @@ public class VTextArea extends VTextField implements VDragCloneAware { // class instead of directly each other. } - @Override - public void initDragImageCopy(Element element) { - // Fix for #13557 - drag image doesn't show original text area text. - // It happens because "value" property is not copied into the cloned - // element - String value = getElement().getPropertyString("value"); - if (value != null) { - element.setPropertyString("value", value); - } - } - } diff --git a/client/src/com/vaadin/client/ui/dd/VDragCloneAware.java b/client/src/com/vaadin/client/ui/dd/VDragCloneAware.java deleted file mode 100644 index 546ca88a9f..0000000000 --- a/client/src/com/vaadin/client/ui/dd/VDragCloneAware.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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.vaadin.client.ui.dd; - -import com.google.gwt.dom.client.Element; - -/** - * Widget could implement this interface if drag image requires additional - * initialization/configuration. Method {@link #initDragImageCopy(Element)} - * allows to change/correct drag image element when element is dragged via DnD. - * - * @since 7.1 - * @author Vaadin Ltd - */ -public interface VDragCloneAware { - - /** - * This method is called for cloned element which corresponds - * to the widget element. One could modify/correct this element - * for drag image. - * - * @since 7.1 - * @param element - * cloned element of drag image - */ - void initDragImageCopy(Element element); -} diff --git a/client/src/com/vaadin/client/ui/dd/VDragEvent.java b/client/src/com/vaadin/client/ui/dd/VDragEvent.java index 0f0e1ecdad..a4667e57f3 100644 --- a/client/src/com/vaadin/client/ui/dd/VDragEvent.java +++ b/client/src/com/vaadin/client/ui/dd/VDragEvent.java @@ -20,14 +20,11 @@ import java.util.Map; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.NativeEvent; -import com.google.gwt.dom.client.Node; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.dom.client.TableElement; import com.google.gwt.dom.client.TableSectionElement; import com.google.gwt.event.dom.client.MouseOverEvent; import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.EventListener; import com.vaadin.client.BrowserInfo; import com.vaadin.client.Util; @@ -187,7 +184,6 @@ public class VDragEvent { cloneNode = table.cast(); } } - syncContent(element, cloneNode); if (alignImageToEvent) { int absoluteTop = element.getAbsoluteTop(); int absoluteLeft = element.getAbsoluteLeft(); @@ -202,31 +198,4 @@ public class VDragEvent { } - /** - * Do additional content sync between original element and its - * copy if needed. - * - * @since 7.1 - * @param original - * original element - * @param copy - * copy of original element - */ - protected void syncContent(Element original, Element copy) { - for (int i = 0; i < original.getChildCount(); i++) { - Node child = original.getChild(i); - if (child instanceof Element) { - syncContent((Element) child, (Element) copy.getChild(i)); - } - } - doSyncContent(original, copy); - } - - private void doSyncContent(Element original, Element copy) { - EventListener eventListener = Event.getEventListener(original); - if (eventListener instanceof VDragCloneAware) { - ((VDragCloneAware) eventListener).initDragImageCopy(copy); - } - } - } diff --git a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextArea.java b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextArea.java deleted file mode 100644 index 410eb21170..0000000000 --- a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextArea.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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.vaadin.tests.components.draganddropwrapper; - -import com.vaadin.server.VaadinRequest; -import com.vaadin.tests.components.AbstractTestUI; -import com.vaadin.ui.DragAndDropWrapper; -import com.vaadin.ui.DragAndDropWrapper.DragStartMode; -import com.vaadin.ui.TextArea; -import com.vaadin.ui.VerticalLayout; - -/** - * Test UI for text area: drag image should contain text-area text. - * - * @since 7.1 - * @author Vaadin Ltd - */ -public class DragAndDropTextArea extends AbstractTestUI { - - @Override - protected void setup(VaadinRequest request) { - VerticalLayout dndLayout = new VerticalLayout(); - - TextArea area = new TextArea(); - area.setValue("text"); - - dndLayout.addComponent(area); - DragAndDropWrapper wrapper = new DragAndDropWrapper(dndLayout); - wrapper.setDragStartMode(DragStartMode.WRAPPER); - addComponent(wrapper); - } - - @Override - protected String getTestDescription() { - return "Drag image for textarea should contain text-area text"; - } - - @Override - protected Integer getTicketNumber() { - return 13557; - } - -} diff --git a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextAreaTest.java b/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextAreaTest.java deleted file mode 100644 index 2abf3599ac..0000000000 --- a/uitest/src/com/vaadin/tests/components/draganddropwrapper/DragAndDropTextAreaTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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.vaadin.tests.components.draganddropwrapper; - -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.interactions.Actions; - -import com.vaadin.testbench.By; -import com.vaadin.tests.tb3.MultiBrowserTest; - -/** - * Test for drag image of text area which should contain text-area text. - * - * @since 7.1 - * @author Vaadin Ltd - */ -public class DragAndDropTextAreaTest extends MultiBrowserTest { - - @Test - public void testTextAreaDndImage() { - openTestURL(); - - WebElement wrapper = driver.findElement(By - .className("v-verticallayout")); - Actions actions = new Actions(driver); - actions.clickAndHold(wrapper); - actions.moveByOffset(50, 50); - actions.perform(); - - WebElement dragElement = driver.findElement(By - .className("v-drag-element")); - List children = dragElement.findElements(By.xpath(".//*")); - boolean found = false; - for (WebElement child : children) { - if ("text".equals(child.getAttribute("value"))) { - found = true; - } - } - - Assert.assertTrue( - "Text value is not found in the DnD image of text area", found); - } - -} -- cgit v1.2.3 From 8314e3c82cd35d13a0eeb381b681b4b03ba8a0eb Mon Sep 17 00:00:00 2001 From: Sauli Tähkäpää Date: Mon, 14 Apr 2014 11:54:22 +0300 Subject: Downgrade Atmosphere client jQuery 2.0.3 -> 1.11.0. (#13374) Change-Id: I5103be114ed5c45a533b89b4c973bb5c91d47a8b --- push/build.xml | 5 +- push/lib/jquery/jquery-1.11.0.js | 10337 +++++++++++++++++++++++++++++++++++++ 2 files changed, 10339 insertions(+), 3 deletions(-) create mode 100644 push/lib/jquery/jquery-1.11.0.js diff --git a/push/build.xml b/push/build.xml index c307ee621d..dfc8d03156 100644 --- a/push/build.xml +++ b/push/build.xml @@ -17,7 +17,7 @@ - + @@ -42,11 +42,10 @@ - - + diff --git a/push/lib/jquery/jquery-1.11.0.js b/push/lib/jquery/jquery-1.11.0.js new file mode 100644 index 0000000000..3c88fa8b7f --- /dev/null +++ b/push/lib/jquery/jquery-1.11.0.js @@ -0,0 +1,10337 @@ +/*! + * jQuery JavaScript Library v1.11.0 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-01-23T21:02Z + */ + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper window is present, + // execute the factory and get jQuery + // For environments that do not inherently posses a window with a document + // (such as Node.js), expose a jQuery-making factory as module.exports + // This accentuates the need for the creation of a real window + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +// + +var deletedIds = []; + +var slice = deletedIds.slice; + +var concat = deletedIds.concat; + +var push = deletedIds.push; + +var indexOf = deletedIds.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var trim = "".trim; + +var support = {}; + + + +var + version = "1.11.0", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return a 'clean' array + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return just the object + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: deletedIds.sort, + splice: deletedIds.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + return obj - parseFloat( obj ) >= 0; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( support.ownLast ) { + for ( key in obj ) { + return hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: trim && !trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( indexOf ) { + return indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + while ( j < len ) { + first[ i++ ] = second[ j++ ]; + } + + // Support: IE<9 + // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) + if ( len !== len ) { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: function() { + return +( new Date() ); + }, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v1.10.16 + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-01-13 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + compile, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== strundefined && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, + doc = node ? node.ownerDocument || node : preferredDoc, + parent = doc.defaultView; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", function() { + setDocument(); + }, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", function() { + setDocument(); + }); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { + div.innerHTML = "
"; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8, Opera 10-12 + // Nothing should be selected when empty strings follow ^= or $= or *= + if ( div.querySelectorAll("[t^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [elem] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[5] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] && match[4] !== undefined ) { + match[2] = match[4]; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (oldCache = outerCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context !== document && context; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + match = tokenize( selector ); + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + } + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +} + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome<14 +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; + }); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +}); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.extend({ + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +jQuery.fn.extend({ + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.unique( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.unique( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +}); +var rnotwhite = (/\S+/g); + + + +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + + } else if ( !(--remaining) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend({ + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + } +}); + +/** + * Clean-up method for dom ready events + */ +function detach() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } +} + +/** + * The ready event handler and self cleanup method + */ +function completed() { + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + + +var strundefined = typeof undefined; + + + +// Support: IE<9 +// Iteration over object's inherited properties before its own +var i; +for ( i in jQuery( support ) ) { + break; +} +support.ownLast = i !== "0"; + +// Note: most support tests are defined in their respective modules. +// false until the test is run +support.inlineBlockNeedsLayout = false; + +jQuery(function() { + // We need to execute this one support test ASAP because we need to know + // if body.style.zoom needs to be set. + + var container, div, + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + // Setup + container = document.createElement( "div" ); + container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + + div = document.createElement( "div" ); + body.appendChild( container ).appendChild( div ); + + if ( typeof div.style.zoom !== strundefined ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.style.cssText = "border:0;margin:0;width:1px;padding:1px;display:inline;zoom:1"; + + if ( (support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 )) ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); + + // Null elements to avoid leaks in IE + container = div = null; +}); + + + + +(function() { + var div = document.createElement( "div" ); + + // Execute the test only if not already executed in another module. + if (support.deleteExpando == null) { + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +})(); + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( elem ) { + var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], + nodeType = +elem.nodeType || 1; + + // Do not set data on non-element DOM nodes because it will not be cleared (#8335). + return nodeType !== 1 && nodeType !== 9 ? + false : + + // Nodes accept data unless otherwise specified; rejection can be conditional + !noData || noData !== true && elem.getAttribute("classid") === noData; +}; + + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + +function internalData( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // The following elements (space-suffixed to avoid Object.prototype collisions) + // throw uncatchable exceptions if you attempt to set expando properties + noData: { + "applet ": true, + "embed ": true, + // ...but Flash objects (which have this classid) *can* handle expandos + "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var i, name, data, + elem = this[0], + attrs = elem && elem.attributes; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + name = attrs[i].name; + + if ( name.indexOf("data-") === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return arguments.length > 1 ? + + // Sets one value + this.each(function() { + jQuery.data( this, key, value ); + }) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + + +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + }; + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; +}; +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + var fragment = document.createDocumentFragment(), + div = document.createElement("div"), + input = document.createElement("input"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
a"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName( "tbody" ).length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = + document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + input.type = "checkbox"; + input.checked = true; + fragment.appendChild( input ); + support.appendChecked = input.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE6-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // #11217 - WebKit loses check when the name is after the checked attribute + fragment.appendChild( div ); + div.innerHTML = ""; + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + support.noCloneEvent = true; + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Execute the test only if not already executed in another module. + if (support.deleteExpando == null) { + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + } + + // Null elements to avoid leaks in IE. + fragment = div = input = null; +})(); + + +(function() { + var i, eventName, + div = document.createElement( "div" ); + + // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event) + for ( i in { submit: true, change: true, focusin: true }) { + eventName = "on" + i; + + if ( !(support[ i + "Bubbles" ] = eventName in window) ) { + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + div.setAttribute( eventName, "t" ); + support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +})(); + + +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && jQuery.acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Even when returnValue equals to undefined Firefox will still show alert + if ( event.result !== undefined ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && ( + // Support: IE < 9 + src.returnValue === false || + // Support: Android < 4.0 + src.getPreventDefault && src.getPreventDefault() ) ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + jQuery._removeData( doc, fix ); + } else { + jQuery._data( doc, fix, attaches ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!support.noCloneEvent || !support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[1] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + deletedIds.push( id ); + } + } + } + } + } +}); + +jQuery.fn.extend({ + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map(function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[i], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle ? + + // Use of this method is a temporary fix (more like optmization) until something better comes along, + // since it was removed from specification and supported only in FF + window.getDefaultComputedStyle( elem[ 0 ] ).display : jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = (iframe || jQuery( "