From f34c3027a06a8a46f17a7c0bd813afcbc9f11a10 Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Fri, 10 Oct 2014 12:17:17 +0200 Subject: [PATCH 01/20] replace the BrowserFrame with NavigateableSinglePage (it's just an iframe yet) --- .../annis/gui/components/IframeState.java | 38 +++++++++ .../components/NavigateableSinglePage.java | 65 +++++++++++++++ .../annis/gui/tutorial/TutorialPanel.java | 8 +- .../gui/components/navigateablesinglepage.js | 50 ++++++++++++ .../VAADIN/tutorial/index_singlepage.html | 79 +++++++++++++++++++ 5 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 annis-gui/src/main/java/annis/gui/components/IframeState.java create mode 100644 annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java create mode 100644 annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js create mode 100644 annis-gui/src/main/webapp/VAADIN/tutorial/index_singlepage.html diff --git a/annis-gui/src/main/java/annis/gui/components/IframeState.java b/annis-gui/src/main/java/annis/gui/components/IframeState.java new file mode 100644 index 0000000000..24e39e779c --- /dev/null +++ b/annis-gui/src/main/java/annis/gui/components/IframeState.java @@ -0,0 +1,38 @@ +/* + * Copyright 2014 Corpuslinguistic working group Humboldt University Berlin. + * + * 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 annis.gui.components; + +import com.vaadin.shared.ui.JavaScriptComponentState; + +/** + * + * @author Thomas Krause + */ +public class IframeState extends JavaScriptComponentState +{ + private String source; + + public String getSource() + { + return source; + } + + public void setSource(String source) + { + this.source = source; + } + +} diff --git a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java new file mode 100644 index 0000000000..debf8ee168 --- /dev/null +++ b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java @@ -0,0 +1,65 @@ +/* + * Copyright 2014 Corpuslinguistic working group Humboldt University Berlin. + * + * 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 annis.gui.components; + +import com.vaadin.annotations.JavaScript; +import com.vaadin.ui.AbstractJavaScriptComponent; +import com.vaadin.ui.JavaScriptFunction; +import org.json.JSONArray; +import org.json.JSONException; + +/** + * Embedds a single HTML page and adds navigation to it's + * headers (if they have an id). + * + * This is e.g. usefull for documentation. + * @author Thomas Krause + */ +@JavaScript( +{ + "vaadin://jquery.js", "navigateablesinglepage.js" +}) +public class NavigateableSinglePage extends AbstractJavaScriptComponent +{ + public NavigateableSinglePage() + { + addFunction("scrolled", new JavaScriptFunction() + { + + @Override + public void call(JSONArray arguments) throws JSONException + { + onScroll(arguments.getString(0)); + } + }); + } + + private void onScroll(String headerID) + { + + } + + @Override + public final IframeState getState() + { + return (IframeState) super.getState(); + } + + public void setSource(String source) + { + getState().setSource(source); + } +} diff --git a/annis-gui/src/main/java/annis/gui/tutorial/TutorialPanel.java b/annis-gui/src/main/java/annis/gui/tutorial/TutorialPanel.java index 9ff2bff345..110da2f4ec 100644 --- a/annis-gui/src/main/java/annis/gui/tutorial/TutorialPanel.java +++ b/annis-gui/src/main/java/annis/gui/tutorial/TutorialPanel.java @@ -15,6 +15,7 @@ */ package annis.gui.tutorial; +import annis.gui.components.NavigateableSinglePage; import com.vaadin.server.ExternalResource; import com.vaadin.server.VaadinService; import com.vaadin.ui.BrowserFrame; @@ -26,17 +27,16 @@ */ public class TutorialPanel extends VerticalLayout { - private BrowserFrame embedded; + private NavigateableSinglePage embedded; public TutorialPanel() { setSizeFull(); - embedded = new BrowserFrame(); + embedded = new NavigateableSinglePage(); embedded.setSizeFull(); addComponent(embedded); String contextPath = VaadinService.getCurrentRequest().getContextPath(); - embedded.setSource(new ExternalResource(contextPath + "/VAADIN/tutorial/index.html")); - + embedded.setSource(contextPath + "/VAADIN/tutorial/index_singlepage.html"); } } diff --git a/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js b/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js new file mode 100644 index 0000000000..41caf11862 --- /dev/null +++ b/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js @@ -0,0 +1,50 @@ +/* + * Copyright 2013 Corpuslinguistic working group Humboldt University Berlin. + * + * 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. + */ + +window.annis_gui_components_NavigateableSinglePage = function () { + + var connector = this; + var rootDiv = $(this.getElement(this.getConnectorId())); + + function initElement() { + var iframeElement = $(document.createElement("iframe")); + rootDiv.append(iframeElement); + iframeElement.attr("frameborder", 0); + iframeElement.attr("width", "100%"); + iframeElement.attr("height", "100%"); + iframeElement.attr("allowtransparency", "true"); + iframeElement.attr("src", connector.getState().source); + + var iframeContent = $(iframeElement.get(0).contentDocument); + + $(iframeContent).bind('scroll',function(){ + connector.scrolled("test"); + }); + } + + this.onStateChange = function () { + var iframe = rootDiv.find("iframe"); + if (iframe.length === 0) + { + initElement(); + } + else + { + iframe.attr("src", connector.getState().source); + } + }; + +}; diff --git a/annis-gui/src/main/webapp/VAADIN/tutorial/index_singlepage.html b/annis-gui/src/main/webapp/VAADIN/tutorial/index_singlepage.html new file mode 100644 index 0000000000..7e75ee27f8 --- /dev/null +++ b/annis-gui/src/main/webapp/VAADIN/tutorial/index_singlepage.html @@ -0,0 +1,79 @@ + + + + + ANNIS tutorial + + + + + +
+ +

Using the ANNIS Interface

+The ANNIS interface is comprised of several areas, the most important of which are the search form and the results tab. +

The Search Form

+ANNIS search form +

The Search Form can be found on the left of the interface window. It's bottom part shows the list of currently available corpora. +By clicking on the line with the corpus name, it is possible to select which corpora should be searched in (hold down 'ctrl' to select multiple corpora simultaneously). +You may also configure groups of corpora or type in the filter box to view subsets of the corpora (see the ANNIS User Guide for details). +

+

+The "AQL" field at the top of the form is used for inputting queries manually (see the tutorials on the ANNIS Query Language from the ANNIS website). +As soon as one or several corpora are selected and a query is entered or modified, the query will be validated automatically and possible errors in the query syntax will be commented on in the "Status" box below. +

+

+Once a valid query has been entered, pressing the "Search" button will retrieve the number of matching positions and documents in the selected corpora in the Status box and open the Result tab to display the first set of matches.

+ANNIS search options +

+In order to get more control over the search you can click on "Search +Options" to unfold more detailed search options. The context +surrounding the matching expressions in the result list ist determined +by the "Left Context" and "Right Context" options and can be changed on each side (by default up to 20 tokens). Some corpora allow longer spans, depending on their configuration. +Entire texts can also be viewed using special document visualizations or by clicking on the document icon next to each corpus name.

+

The Result Window

+ANNIS result window toolbar +

The result window shows search results in pages of 10 hits each by +default (this can be changed in the Search Form). The toolbar at the +top of the window allows you to navigate between these pages. The +"Token Annotations" button on the toolbar allows you to toggle the +token based annotations, such as lemmas and parts-of-speech, on or off +for you convenience. You can use hyperlinks to your queries by copying +the current browser URL for e-mail or citation purposes, allowing others to reproduce your +query.

+

If your corpus contains multiple alternative text layers or segmentations (e.g. normalized and un-normalized text), +you may switch between these on the fly by using the "base text" menu.

+KWIC concordance in ANNIS +

+The result list itself initially shows a KWIC (key word in context) +concordance of matching positions in the selected corpora, with the +matching region marked red and the context in black on either side. Context can be adjusted for +individual search results up to the maximum allowed for the current corpus. +Token annotations are displayed in gray under each token, and hovering +over them with the mouse will show the annotation name and +namespace. More complex annotation levels can be expanded, if +available, by clicking on the plus icon next to the level's name, +e.g. dependencies and information structure for the annotations in the dependency tree and grid +views in the picture below.

+visualization of different annotation levels +
+ +

Using the ANNIS Query Builder

+

To open the graphical query builder, click on the Query Builder button. On the left-hand side of the toolbar at the top of the query builder canvans, you will see the Add Node button. +Use this button to define nodes to be searched for (tokens, non-terminal nodes or annotations). Creating nodes and modifying them on the canvas will immediately update the AQL field in the Search Form with your query, though updating the query on the Search Form will not create a new graph in the Query Builder.

+The Query Builder tab and the Create Node button +

In each node you create you may click on The Add Node button to specify an annotation value. The annotation name can be typed in or selected from a drop down list. The operator field in the middle allows you to choose between an exact match +(the '=' symbol) or wildcard search using Regular Expressions (the '~' symbol). The annotation value is given on the right, and should NOT be surrounded by quotations (see the example below). +It is also possible to specify multiple annotations applying to the same position by clicking on The Add Node button multiple times. Clicking on The Clear all nodes button will delete the values in the node. +To search for word forms, simply choose "tok" as the field name on the left. A node with no data entered will match any node, that is an underspecified token or non-terminal node or annotation.

+Query Builder node +

+To specify the relationship between nodes, first click on the "Add Edge" link at the top left of one node, and then click the "Dock" link which becomes available on the other nodes. +An edge will connect the nodes with an extra box from which operators may be selected (see below). For operators allowing additional labels (e.g. the dominance operator > allows edge labels to be specified), +you may type directly into the edge's operator box, as in the example with a "func" label in the image below. Note that the node clicked on first (where the "Edge" button was clicked) will be the first node in the resulting +quey, i.e. if this is the first node it will dominate the second node (#1 > #2) and not the other way around, as also represented by the arrows along the edge.

+Connecting nodes with an edge + + + + From 6b7a3f58d095abaf08f391f82b87d076fcba954b Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Fri, 10 Oct 2014 13:57:28 +0200 Subject: [PATCH 02/20] detect scroll events --- .../java/annis/gui/components/NavigateableSinglePage.java | 1 - .../annis/gui/components/navigateablesinglepage.js | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java index debf8ee168..bc57a40634 100644 --- a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java +++ b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java @@ -49,7 +49,6 @@ public void call(JSONArray arguments) throws JSONException private void onScroll(String headerID) { - } @Override diff --git a/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js b/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js index 41caf11862..26f23740e2 100644 --- a/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js +++ b/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js @@ -28,11 +28,11 @@ window.annis_gui_components_NavigateableSinglePage = function () { iframeElement.attr("allowtransparency", "true"); iframeElement.attr("src", connector.getState().source); - var iframeContent = $(iframeElement.get(0).contentDocument); - - $(iframeContent).bind('scroll',function(){ + var iframeWindow = $(iframeElement.get(0).contentWindow); + iframeWindow.on('scroll',function(){ connector.scrolled("test"); }); + } this.onStateChange = function () { From 0e139fe2d071a9f5a71ec545909f2a6382396cd8 Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Fri, 10 Oct 2014 15:26:32 +0200 Subject: [PATCH 03/20] try to find the current active header --- .../components/NavigateableSinglePage.java | 76 +++++++++++++------ .../gui/components/navigateablesinglepage.js | 36 +++++++-- 2 files changed, 82 insertions(+), 30 deletions(-) diff --git a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java index bc57a40634..2c225eb7ba 100644 --- a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java +++ b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java @@ -18,47 +18,73 @@ import com.vaadin.annotations.JavaScript; import com.vaadin.ui.AbstractJavaScriptComponent; import com.vaadin.ui.JavaScriptFunction; +import com.vaadin.ui.Label; +import com.vaadin.ui.VerticalLayout; import org.json.JSONArray; import org.json.JSONException; /** - * Embedds a single HTML page and adds navigation to it's - * headers (if they have an id). - * + * Embedds a single HTML page and adds navigation to it's headers (if they have + * an id). + * * This is e.g. usefull for documentation. + * * @author Thomas Krause */ -@JavaScript( -{ - "vaadin://jquery.js", "navigateablesinglepage.js" -}) -public class NavigateableSinglePage extends AbstractJavaScriptComponent +public class NavigateableSinglePage extends VerticalLayout { + + private final IFrameComponent iframe = new IFrameComponent(); + private final Label lblHeaderID = new Label(); + public NavigateableSinglePage() { - addFunction("scrolled", new JavaScriptFunction() - { - - @Override - public void call(JSONArray arguments) throws JSONException - { - onScroll(arguments.getString(0)); - } - }); + lblHeaderID.setCaption("Selected header ID: "); + + lblHeaderID.setWidth("100%"); + lblHeaderID.setHeight("-1px"); + iframe.setSizeFull(); + + addComponent(lblHeaderID); + addComponent(iframe); + + setExpandRatio(iframe, 1.0f); } - + private void onScroll(String headerID) { + lblHeaderID.setValue(headerID); } - - @Override - public final IframeState getState() + + public void setSource(String source) { - return (IframeState) super.getState(); + iframe.getState().setSource(source); } - - public void setSource(String source) + + @JavaScript( + { + "vaadin://jquery.js", "navigateablesinglepage.js" + }) + private class IFrameComponent extends AbstractJavaScriptComponent { - getState().setSource(source); + + public IFrameComponent() + { + addFunction("scrolled", new JavaScriptFunction() + { + + @Override + public void call(JSONArray arguments) throws JSONException + { + onScroll(arguments.getString(0)); + } + }); + } + + @Override + public final IframeState getState() + { + return (IframeState) super.getState(); + } } } diff --git a/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js b/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js index 26f23740e2..9933dbec5d 100644 --- a/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js +++ b/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js @@ -14,7 +14,7 @@ * limitations under the License. */ -window.annis_gui_components_NavigateableSinglePage = function () { +window.annis_gui_components_NavigateableSinglePage_IFrameComponent = function () { var connector = this; var rootDiv = $(this.getElement(this.getConnectorId())); @@ -27,12 +27,38 @@ window.annis_gui_components_NavigateableSinglePage = function () { iframeElement.attr("height", "100%"); iframeElement.attr("allowtransparency", "true"); iframeElement.attr("src", connector.getState().source); - + var iframeWindow = $(iframeElement.get(0).contentWindow); - iframeWindow.on('scroll',function(){ - connector.scrolled("test"); - }); + iframeWindow.on('scroll', function () { + + // find ID of the first header which is inside the visible range + var headersWithID = $(iframeWindow.get(0).document).find("h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]"); + + if (headersWithID.length > 0) + { + var top = iframeWindow.scrollTop(); + var windowHeight = iframeWindow.height(); + var visibleBorder = top + (windowHeight / 3); + + var lastInvisibleID = headersWithID.attr('id') + // find the last header which is (even slightly) invisible + $.each(headersWithID, function (key) { + + var offset = $(this).offset().top; + + // is invisible? + if (offset < visibleBorder) { + lastInvisibleID = $(this).attr('id'); + } else { + return false; + } + }); + connector.scrolled(lastInvisibleID); + } + + + }); } this.onStateChange = function () { From efe97b1c407d97ba86668d7bb83cdaf009d8fead Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Fri, 10 Oct 2014 15:36:22 +0200 Subject: [PATCH 04/20] use an imaginary line at the first quarter of the iframe for determining the active header --- .../resources/annis/gui/components/navigateablesinglepage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js b/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js index 9933dbec5d..dc68a74106 100644 --- a/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js +++ b/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js @@ -38,7 +38,7 @@ window.annis_gui_components_NavigateableSinglePage_IFrameComponent = function () { var top = iframeWindow.scrollTop(); var windowHeight = iframeWindow.height(); - var visibleBorder = top + (windowHeight / 3); + var visibleBorder = top + (windowHeight / 4); var lastInvisibleID = headersWithID.attr('id') From 4754897957cc84be0d26231a1a3335fb274df87d Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Fri, 10 Oct 2014 16:25:54 +0200 Subject: [PATCH 05/20] first steps to parse the navigation for the tutorial from the headers --- .../components/NavigateableSinglePage.java | 66 +++++++++++++++++++ .../annis/gui/tutorial/TutorialPanel.java | 46 +++++++++++-- 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java index 2c225eb7ba..ee7dec9d76 100644 --- a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java +++ b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java @@ -15,13 +15,24 @@ */ package annis.gui.components; +import com.google.common.base.Joiner; import com.vaadin.annotations.JavaScript; import com.vaadin.ui.AbstractJavaScriptComponent; import com.vaadin.ui.JavaScriptFunction; import com.vaadin.ui.Label; import com.vaadin.ui.VerticalLayout; +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.json.JSONArray; import org.json.JSONException; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Embedds a single HTML page and adds navigation to it's headers (if they have @@ -33,10 +44,14 @@ */ public class NavigateableSinglePage extends VerticalLayout { + + private final static Logger log = LoggerFactory.getLogger(NavigateableSinglePage.class); private final IFrameComponent iframe = new IFrameComponent(); private final Label lblHeaderID = new Label(); + private final static Pattern regexHeader = Pattern.compile("h([1-6])"); + public NavigateableSinglePage() { lblHeaderID.setCaption("Selected header ID: "); @@ -49,6 +64,7 @@ public NavigateableSinglePage() addComponent(iframe); setExpandRatio(iframe, 1.0f); + } private void onScroll(String headerID) @@ -59,6 +75,56 @@ private void onScroll(String headerID) public void setSource(String source) { iframe.getState().setSource(source); + parseHTML(source); + } + + private LinkedHashMap parseHTML(String source) + { + + LinkedHashMap result = new LinkedHashMap<>(); + try + { + Document doc = Jsoup.connect(source).get(); + + ArrayList currentPath = new ArrayList<>(); + // find all headers that have an ID +// for(Element e : doc.getElementsByAttribute("id")) + for(Element e : doc.getAllElements()) + { + Matcher m = regexHeader.matcher(e.tagName()); + if(m.matches()) + { + int level = Integer.parseInt(m.group(1))-1; + + // decide wether to expand the path (one level deeper) or to truncate + if(level == 0) + { + currentPath.clear(); } + else if(currentPath.size() >= level) + { + // truncate + currentPath = new ArrayList<>(currentPath.subList(0, level)); + } + + + if(currentPath.isEmpty() && level > 0) + { + // fill the path with empty elements + for(int i=0; i < level; i++) + { + currentPath.add(""); + } + } + currentPath.add(e.text()); + log.info("current path: {}", Joiner.on(" | ").join(currentPath)); + } + } + } + catch (IOException ex) + { + log.error("Could not parse iframe source", ex); + } + return result; } @JavaScript( diff --git a/annis-gui/src/main/java/annis/gui/tutorial/TutorialPanel.java b/annis-gui/src/main/java/annis/gui/tutorial/TutorialPanel.java index 110da2f4ec..a8bbff426b 100644 --- a/annis-gui/src/main/java/annis/gui/tutorial/TutorialPanel.java +++ b/annis-gui/src/main/java/annis/gui/tutorial/TutorialPanel.java @@ -16,10 +16,15 @@ package annis.gui.tutorial; import annis.gui.components.NavigateableSinglePage; -import com.vaadin.server.ExternalResource; +import com.google.common.base.Strings; import com.vaadin.server.VaadinService; -import com.vaadin.ui.BrowserFrame; +import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; +import java.net.URI; +import java.net.URISyntaxException; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -27,16 +32,47 @@ */ public class TutorialPanel extends VerticalLayout { + + private static final Logger log = LoggerFactory.getLogger(TutorialPanel.class); + private NavigateableSinglePage embedded; + public TutorialPanel() { - setSizeFull(); - + setSizeFull(); + embedded = new NavigateableSinglePage(); embedded.setSizeFull(); addComponent(embedded); String contextPath = VaadinService.getCurrentRequest().getContextPath(); - embedded.setSource(contextPath + "/VAADIN/tutorial/index_singlepage.html"); + + URI appURI = UI.getCurrent().getPage().getLocation(); + URI tutorialURI; + try + { + String oldPath = appURI.getPath(); + if(oldPath == null) + { + oldPath = ""; + } + if(oldPath.endsWith("/")) + { + oldPath = oldPath.substring(0, oldPath.length()-1); + } + tutorialURI = new URI(appURI.getScheme(), + appURI.getUserInfo(), + appURI.getHost(), + appURI.getPort(), + oldPath + "/VAADIN/tutorial/index_singlepage.html", + null, + null); + embedded.setSource(tutorialURI.toASCIIString()); + } + catch (URISyntaxException ex) + { + log.error("Invalid tutorial URI", ex); + } + } } From 5065c23e591e09f74ef0a7d6f8902f490118d496 Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Fri, 10 Oct 2014 16:47:01 +0200 Subject: [PATCH 06/20] adding all other chapters to the single page tutorial --- .../webapp/VAADIN/tutorial/ANNIS_tutorial.css | 2 - .../VAADIN/tutorial/index_singlepage.html | 776 ++++++++++++++++-- 2 files changed, 700 insertions(+), 78 deletions(-) diff --git a/annis-gui/src/main/webapp/VAADIN/tutorial/ANNIS_tutorial.css b/annis-gui/src/main/webapp/VAADIN/tutorial/ANNIS_tutorial.css index 8b4c4d5754..8c99f59e96 100644 --- a/annis-gui/src/main/webapp/VAADIN/tutorial/ANNIS_tutorial.css +++ b/annis-gui/src/main/webapp/VAADIN/tutorial/ANNIS_tutorial.css @@ -9,8 +9,6 @@ .code { font-family:monospace;} .comment {color:#999999;} - pre {text-indent: 30px} - img { padding:1px; border:1px solid #021a40; background-color:#ffffff;} diff --git a/annis-gui/src/main/webapp/VAADIN/tutorial/index_singlepage.html b/annis-gui/src/main/webapp/VAADIN/tutorial/index_singlepage.html index 7e75ee27f8..85dc36382d 100644 --- a/annis-gui/src/main/webapp/VAADIN/tutorial/index_singlepage.html +++ b/annis-gui/src/main/webapp/VAADIN/tutorial/index_singlepage.html @@ -1,79 +1,703 @@ - - - ANNIS tutorial - - - - - -
- -

Using the ANNIS Interface

-The ANNIS interface is comprised of several areas, the most important of which are the search form and the results tab. -

The Search Form

-ANNIS search form -

The Search Form can be found on the left of the interface window. It's bottom part shows the list of currently available corpora. -By clicking on the line with the corpus name, it is possible to select which corpora should be searched in (hold down 'ctrl' to select multiple corpora simultaneously). -You may also configure groups of corpora or type in the filter box to view subsets of the corpora (see the ANNIS User Guide for details). -

-

-The "AQL" field at the top of the form is used for inputting queries manually (see the tutorials on the ANNIS Query Language from the ANNIS website). -As soon as one or several corpora are selected and a query is entered or modified, the query will be validated automatically and possible errors in the query syntax will be commented on in the "Status" box below. -

-

-Once a valid query has been entered, pressing the "Search" button will retrieve the number of matching positions and documents in the selected corpora in the Status box and open the Result tab to display the first set of matches.

-ANNIS search options -

-In order to get more control over the search you can click on "Search -Options" to unfold more detailed search options. The context -surrounding the matching expressions in the result list ist determined -by the "Left Context" and "Right Context" options and can be changed on each side (by default up to 20 tokens). Some corpora allow longer spans, depending on their configuration. -Entire texts can also be viewed using special document visualizations or by clicking on the document icon next to each corpus name.

-

The Result Window

-ANNIS result window toolbar -

The result window shows search results in pages of 10 hits each by -default (this can be changed in the Search Form). The toolbar at the -top of the window allows you to navigate between these pages. The -"Token Annotations" button on the toolbar allows you to toggle the -token based annotations, such as lemmas and parts-of-speech, on or off -for you convenience. You can use hyperlinks to your queries by copying -the current browser URL for e-mail or citation purposes, allowing others to reproduce your -query.

-

If your corpus contains multiple alternative text layers or segmentations (e.g. normalized and un-normalized text), -you may switch between these on the fly by using the "base text" menu.

-KWIC concordance in ANNIS -

-The result list itself initially shows a KWIC (key word in context) -concordance of matching positions in the selected corpora, with the -matching region marked red and the context in black on either side. Context can be adjusted for -individual search results up to the maximum allowed for the current corpus. -Token annotations are displayed in gray under each token, and hovering -over them with the mouse will show the annotation name and -namespace. More complex annotation levels can be expanded, if -available, by clicking on the plus icon next to the level's name, -e.g. dependencies and information structure for the annotations in the dependency tree and grid -views in the picture below.

-visualization of different annotation levels -
- -

Using the ANNIS Query Builder

-

To open the graphical query builder, click on the Query Builder button. On the left-hand side of the toolbar at the top of the query builder canvans, you will see the Add Node button. -Use this button to define nodes to be searched for (tokens, non-terminal nodes or annotations). Creating nodes and modifying them on the canvas will immediately update the AQL field in the Search Form with your query, though updating the query on the Search Form will not create a new graph in the Query Builder.

-The Query Builder tab and the Create Node button -

In each node you create you may click on The Add Node button to specify an annotation value. The annotation name can be typed in or selected from a drop down list. The operator field in the middle allows you to choose between an exact match -(the '=' symbol) or wildcard search using Regular Expressions (the '~' symbol). The annotation value is given on the right, and should NOT be surrounded by quotations (see the example below). -It is also possible to specify multiple annotations applying to the same position by clicking on The Add Node button multiple times. Clicking on The Clear all nodes button will delete the values in the node. -To search for word forms, simply choose "tok" as the field name on the left. A node with no data entered will match any node, that is an underspecified token or non-terminal node or annotation.

-Query Builder node -

-To specify the relationship between nodes, first click on the "Add Edge" link at the top left of one node, and then click the "Dock" link which becomes available on the other nodes. -An edge will connect the nodes with an extra box from which operators may be selected (see below). For operators allowing additional labels (e.g. the dominance operator > allows edge labels to be specified), -you may type directly into the edge's operator box, as in the example with a "func" label in the image below. Note that the node clicked on first (where the "Edge" button was clicked) will be the first node in the resulting -quey, i.e. if this is the first node it will dominate the second node (#1 > #2) and not the other way around, as also represented by the arrows along the edge.

-Connecting nodes with an edge - - - + + + ANNIS tutorial + + + + + +
+ +

ANNIS interface

+ +

Using the ANNIS Interface

+ The ANNIS interface is comprised of several areas, the most important of which are the search form and the results tab. +

The Search Form

+ ANNIS search form +

The Search Form can be found on the left of the interface window. It's bottom part shows the list of currently available corpora. + By clicking on the line with the corpus name, it is possible to select which corpora should be searched in (hold down 'ctrl' to select multiple corpora simultaneously). + You may also configure groups of corpora or type in the filter box to view subsets of the corpora (see the ANNIS User Guide for details). +

+

+ The "AQL" field at the top of the form is used for inputting queries manually (see the tutorials on the ANNIS Query Language from the ANNIS website). + As soon as one or several corpora are selected and a query is entered or modified, the query will be validated automatically and possible errors in the query syntax will be commented on in the "Status" box below. +

+

+ Once a valid query has been entered, pressing the "Search" button will retrieve the number of matching positions and documents in the selected corpora in the Status box and open the Result tab to display the first set of matches.

+ ANNIS search options +

+ In order to get more control over the search you can click on "Search + Options" to unfold more detailed search options. The context + surrounding the matching expressions in the result list ist determined + by the "Left Context" and "Right Context" options and can be changed on each side (by default up to 20 tokens). Some corpora allow longer spans, depending on their configuration. + Entire texts can also be viewed using special document visualizations or by clicking on the document icon next to each corpus name.

+

The Result Window

+ ANNIS result window toolbar +

The result window shows search results in pages of 10 hits each by + default (this can be changed in the Search Form). The toolbar at the + top of the window allows you to navigate between these pages. The + "Token Annotations" button on the toolbar allows you to toggle the + token based annotations, such as lemmas and parts-of-speech, on or off + for you convenience. You can use hyperlinks to your queries by copying + the current browser URL for e-mail or citation purposes, allowing others to reproduce your + query.

+

If your corpus contains multiple alternative text layers or segmentations (e.g. normalized and un-normalized text), + you may switch between these on the fly by using the "base text" menu.

+ KWIC concordance in ANNIS +

+ The result list itself initially shows a KWIC (key word in context) + concordance of matching positions in the selected corpora, with the + matching region marked red and the context in black on either side. Context can be adjusted for + individual search results up to the maximum allowed for the current corpus. + Token annotations are displayed in gray under each token, and hovering + over them with the mouse will show the annotation name and + namespace. More complex annotation levels can be expanded, if + available, by clicking on the plus icon next to the level's name, + e.g. dependencies and information structure for the annotations in the dependency tree and grid + views in the picture below.

+ visualization of different annotation levels + +

Using the ANNIS Query Builder

+

To open the graphical query builder, click on the Query Builder button. On the left-hand side of the toolbar at the top of the query builder canvans, you will see the Add Node button. + Use this button to define nodes to be searched for (tokens, non-terminal nodes or annotations). Creating nodes and modifying them on the canvas will immediately update the AQL field in the Search Form with your query, though updating the query on the Search Form will not create a new graph in the Query Builder.

+ The Query Builder tab and the Create Node button +

In each node you create you may click on The Add Node button to specify an annotation value. The annotation name can be typed in or selected from a drop down list. The operator field in the middle allows you to choose between an exact match + (the '=' symbol) or wildcard search using Regular Expressions (the '~' symbol). The annotation value is given on the right, and should NOT be surrounded by quotations (see the example below). + It is also possible to specify multiple annotations applying to the same position by clicking on The Add Node button multiple times. Clicking on The Clear all nodes button will delete the values in the node. + To search for word forms, simply choose "tok" as the field name on the left. A node with no data entered will match any node, that is an underspecified token or non-terminal node or annotation.

+ Query Builder node +

+ To specify the relationship between nodes, first click on the "Add Edge" link at the top left of one node, and then click the "Dock" link which becomes available on the other nodes. + An edge will connect the nodes with an extra box from which operators may be selected (see below). For operators allowing additional labels (e.g. the dominance operator > allows edge labels to be specified), + you may type directly into the edge's operator box, as in the example with a "func" label in the image below. Note that the node clicked on first (where the "Edge" button was clicked) will be the first node in the resulting + quey, i.e. if this is the first node it will dominate the second node (#1 > #2) and not the other way around, as also represented by the arrows along the edge.

+ Connecting nodes with an edge + +

ANNIS Query language

+ +

Searching for Word Forms

+

+ To search for word forms in ANNIS, simply select a corpus (in this + example the small PCC2 demo corpus) and enter a search string between + double quotation marks, e.g.:

+
+ "statisch" +
+

Note that the search is case + sensitive, so it will not + find cases of capitalized 'Statisch', for example at the beginning of a + sentence. In order to find both options, you can either look for one + form OR the other using the pipe sign ( | + ): +

+ +
+ "statisch" | "Statisch" +
+ + +

or else you can use regular + expressions, which must be + surrounded by slashes ( / + ) instead of quotation marks:

+ +
+ /[Ss]tatisch/ +
+ +

To look for a sequence of + multiple word forms, enter your search terms separated by & + and then specify that the relation between the elements is one of precedence, + as signified by the period ( . + ) operator:

+ +
+ "so" & "statisch" + & #1 . #2 +
+ +

The expression #1 + . #2 signifies that the first + element ("so") precedes the second element ("statisch"). Alternatively, you can also place the operator directly between the search elements as a shortcut. The following shortcut query is equivalent: +

+ +
+ "so" . "statisch" +
+ +

+ For indirect + precedence (where other + tokens may stand between the search terms), use the .* + operator:

+ +
+ /[Ss]o/ & "statisch" + & "wie" & #1 . #2 & #2 .* #3 +

OR using shortcuts:

+ /[Ss]o/ . "statisch" .* "wie" +
+ + +

The queries above find sequences + beginning with either "So" or "so", followed directly by "statisch", + which must be followed either directly or indirectly (.*) by "wie". A + range of allowed distances can also be specified numerically as follows:

+ +
+ /[Ss]tatisch/ & "wie" + & #1 .1,5 #2 +

OR:

+ /[Ss]tatisch/ .1,5 "wie" +
+ +

Meaning the two words may + appear at a distance of 1 to 5 tokens. The operator .* + allows a distance of up to 50 tokens by default, so searching with .1,50 + is the same as using .* + instead. Greater distances (e.g. .1,100 + for 'within 100 tokens') should always be specified explicitly.

+

+ Finally, we can add metadata restrictions to the query, which filter + out documents not matching our definitions. Metadata attributes must be + preceded by the prefix meta:: and may not be bound (i.e. they are not + referred to as #1 etc. and the numbering of other elements ignores + their existence): +

+ +
+ + /[Ss]tatisch/ & "wie" & #1 .1,5 #2 & meta::Genre="Sport" + +
+ +

To view metadata for a search result or for a corpus, press the "i" + icon next to it in the result window or in the search form + respectively. +

+ +

Searching for Annotations

+

Annotations may be searched for using an annotation name and value. The names of the annotations vary from corpus to corpus, + though many corpora contain part-of-speech and lemma annotations with the names pos and lemma respectively (annotation names are case sensitive). + For example, to search for all forms of the German verb sein 'to be' in a corpus with lemma annotation such as PCC2, simply select the PCC2 corpus and enter:

+
+ lemma="sein" +
+

+ Negative searches are also possible using != instead of =. For negated tokens (word forms) use the reserved attribute tok. For example: +

+
+ + lemma!="sein" + +

+ or: +

+ + tok!="ist" + +
+

+ Metadata can also be negated similarly: +

+
+ + lemma="sein" & meta::Genre!="Sport" + +
+ +

To only find finite forms of this verb in PCC2, use the part-of-speech (pos) annotation concurrently, and specify that both the lemma and pos should apply to the same element:

+
+ lemma="sein" & pos="VAFIN" & #1 _=_ #2 +

OR (using a shortcut):

+ lemma="sein" _=_ pos="VAFIN" +
+ +

The expression #1 _=_ #2 uses the span identity operator to specify that the first annotation and the second annotation apply to exactly the same position in the corpus. + Annotations can also apply to longer spans than a single token: for example, in PCC2, the annotation Inf-Stat signifies the information structure status of a discourse referent. + This annotation can also apply to phrases longer than one token. The following query finds spans containing new discourse referents, not previously mentioned in the text:

+ +
+ exmaralda:Inf-Stat="new" +
+ +

If the corpus contains no more than one annotation type named Inf-Stat, the optional namespace (in this case exmaralda:) may be dropped; if there are multiple annotations with the same name but different namespaces, + dropping the namespace will find all of those annotations. In order to view the span of tokens to which this annotation applies, enter the and click on "Search", then open the exmaralda annotation level to view the grid containing the span. + Further operators can test the relationships between potentially overlapping annotations in spans. + For example, the operator _i_ examines whether one annotation fully contains the span of another annotation (the i stands for 'includes'):

+
+ Topic="ab" & Inf-Stat="new" & #1 _i_ #2 +

OR (using a shortcut):

+ Topic="ab" _i_ Inf-Stat="new" +
+ +

This query finds aboutness topics (Topic="ab") containing information structurally new discourse referents.

+ +

Searching using Regular Expressions

+

+ When searching for word forms and annotation values, it is possible to employ wildcards as placeholders for a variety of characters, using Regular Expression syntax (see here for detailed information). To search for wildcards use slashes instead of quotation marks to surround your search term. For example, you can use the period (.) to replace any single character: +

+
+ tok=/de./ +
+

+ This finds word forms such as "der", "dem", "den" etc. It is also possible to make characters optional by following them with a question mark (?). The following example finds cases of "das" and "dass", since the second "s" is optional: +

+
+ tok=/dass?/ +
+

+ It is also possible to specify an arbitrary number of repetitions, with an asterisk (*) signifying zero or more occurrences and a plus (+) signifying at least one occurrence. For example, the first query below finds "da", "das", and "dass" (since the asterisk means zero or more times the preceding "s"), while the second finds "das" and "dass", since at least one "s" must be found: +

+
+ tok=/das*/

+ tok=/das+/ +
+

+ It is possible to combine these operators with the period operator to mean any number of occurrences of an arbitrary character. For example, the query below searches for pos (part-of-speech) annotations that begin with "VA", corresponding to all forms of auxiliary verbs. The string "VA" means that the result must begin with "VA", the period stands for any character, and the asterisk means that 'any character' can be repeated zero or more time, as above. +

+
+ pos=/VA.*/ +
+

+ This finds both finite verbs ("VAFIN") and non-finite ones ("VAINF"). It is also possible to search for explicit alternatives by either specifying characters in square brackets or longer strings in round brackets separated by pipe signs. The first example below finds either "dem" or "der" (i.e. "de" followed by either "m" or "r") while the second example finds lemma annotations that are either "sein" or "werden". +

+
+ tok=/de[mr]/

+ lemma=/(sein|werden)/ +
+

+ Finally, negative searches can be used as usual with the exclamation point, and regular expressions can generally be used also in edge annotations. For example, if we search for trees (see also Searching for Trees) where a node dominates another node with edges not containing an object, we can use a wildcard to rule out all edges labels beginning with "O" for object: +

+
+ cat="VP" & cat & #1 >[func!=/O.*/] #2 +

OR (using a shortcut):

+ cat="VP" & cat & #1 >[func!=/O.*/] #2 +
+ +

Searching for Trees

+

In corpora containing hierarchical structures, annotations such as syntax trees can be searched for by defining terminal or none-terminal node annotations and their values. + A simple search for prepostional phrases in the small PCC2 demo corpus looks like this:

+
+ tiger:cat="PP" +
+

If the corpus contains no more than one annotation called cat, the optional namespace, in this case tiger:, may be dropped. This finds all PP nodes in the corpus. To find all PP nodes directly dominating a proper name, a second element can be specified with the appropriate part-of-speech (pos) value:

+
+ cat="PP" & pos="NE" & #1 > #2 +

OR (using a shortcut):

+ cat="PP" > pos="NE" +
+

The operator > signifies direct dominance, which must hold between the first and the second element. + Once the Result Tab is shown you may open the "tiger" annotation level to see the corresponding tree. +

+ +

+ Note that since the context is set to a number of tokens left and right of the search term, the tree for the whole sentence may not be retrieved. To do this, you may want to specifically search for the + sentence dominating the PP. To do so, specify the sentence in another element and use the indirect dominance ( >* ) operator:

+
+ cat="S" & cat="PP" & pos="NE" & #1 >* #2 & #2 > #3 +

OR (using a shortcut):

+ cat="S" >* cat="PP" > pos="NE" +
+ +

If the annotations in the corpus support it, you may also look for edge labels. Using the following query will find all adjunct modifiers of a VP, dominated by the VP node through an edge labeled MO. + Since we do not know anything about the modifying node, whether it is a non-terminal node or a token, we simply use the node element as a place holder. + This element can match any node or annotation in the graph:

+ +
+ cat="VP" & node & #1 >[tiger:func="MO"] #2 +

OR (using a shortcut):

+ cat="VP" >[tiger:func="MO"] node +
+

+ It is also possible to negate the label of the dominance edge as in the following query: +

+
+ + cat="VP" & node & #1 >[tiger:func!="MO"] #2 + +

OR (using a shortcut):

+ + cat="VP" >[tiger:func!="MO"] node + +
+

+ which finds all VPs dominating a node with a label other than MO. +

+ +

Searching for Pointing Relations

+

Pointing relations are used to express an arbitrary directed + relationship between two elements (terminals or non-terminals) without + implying dominance or coverage inheritance. For instance, in the pcc2 + demo corpus, elements in the mmax: namespace may point to + each other to express coreference or anaphoric relations. The + following query searches for two np_form annotations, which + specify for example whether a nominal phrase is pronominal, definite + or indefinite. +

+
+ mmax:np_form="pper" &
mmax:np_form="defnp" &
#1 + ->anaphor_antecedent #2
+
+

+ Using the pointing relation operator -> with the + type anaphor_antecedent, the first np_form, which + should be a personal pronoun (pper), is said to be the + anaphor to its antecedent, the second np_form, which is definite + (defnp). To see a visualization of the coreference + relations, open the mmax annotation level in the example corpus. In + the image below, one of the matches for the above query is highlighted + in red (die Seeburger und einige Groß-Glienicker ... sie 'the + Seeburgers and some Groß-Glienickers... they'). Other discourse + referents in the text (marked with an underline) may be clicked on, + causing coreferential chains containing them to be highlighted as + well. Note that discourse referents may overlap, leading to multiple + underlines: Die Seeburger 'the Seeburgers' is a shorter discourse + referent overlapping with the larger one ('the Seeburgers and some + Groß-Glienickers'), and each referent has its own + underline. Annotations of the coreference edges of each relation can + be viewed by hovering of the appropriate underline. +

+ + + +

+ Another way to use pointing relations is found in syntactic dependency trees. The queries in this case can use both pointing relation types and annotations, as in the following query: +

+ +
+ pos="VVFIN" & tok & #1 ->dep[func="obja"] #2 +

OR (using a shortcut):

+ pos="VVFIN" ->dep[func="obja"] tok +
+ +

+ This query searches for a finite verb (with the part-of-speech VVFIN) + and a token, with a pointing relation of the type 'dep' (for + dependency) between the two, annotated with 'func="obja"' (the + function Object, Accusative). The result can be viewed with the + dependency arch visualizer, which shows the verb gibt 'gives' and its + object Wunder 'miracles'. +

+ + + +

Exporting Results

+

+ To export search results, open the menu "More" between the Search and History buttons and select "Export":

+ +

Enter the query whose results you want to export as usual in the AQL box. Note that you do not need to carry out the query first. You can enter the query and export without pressing Search before. Several exporter modules can be selected from the Export tab shown below.

+ + +

+ The SimpleTextExporter simply gives the text for all tokens in each search result, including context, in a one-row-per-hit format. The tokens covered by the match area are marked with square brackets and the results are numbered, as in the following example:

+
+          1. Tor zum 1:0 für die [Ukraine] stürzte der 1,62 Meter große 
+ 2. der 1,62 Meter große Gennadi [Subow] die deutsche Nationalelf vorübergehend in
+ 3. und Reputation kämpfenden Mannschaft von [Rudi] Völler der Weg zur Weltmeisterschaft
+ 4. Reputation kämpfenden Mannschaft von Rudi [Völler] der Weg zur Weltmeisterschaft endgültig
+ 5. die deutschen Nationalkicker einen " [Rudi] Riese " auf der Bank
+
+

+ The TextExporter adds all annotations of each token separated by slashes (e.g. dogs/NN/dog for the token dogs annotated with a part-of-speech NN and a lemma dog). +

+

+ The GridExporter adds all annotations available for the span of retrieved tokens, with each annotation layer in a separate line. Annotations are separated by spaces and the hierarchical order of annotations is lost, though the span of tokens covered by each annotation may optionally be given in square brackets (to turn this off use the optional parameter numbers=false in the ‘Parameters’ box). The user can specify annotation layers to be exported in the additional ‘Annotation Keys’ box, and annotation names should be separated by comas, as in the image above. Metadata annotations can also be exported by entering “metakeys=” and a list of comma separated metadata names in the Parameters box. If nothing is specified, all available annotations and no metadata will be exported. Multiple options are separated by a semicolon, e.g. the Parameters metakeys=Genre,Titel;numbers=false. An example output with token numbers looks as follows.

+
1.	tok	 ein Dialog angestoßen . 
+	cat	 VP[1-6] NP[1-5] 
+	pos	 ART[1-1] NN[2-2] VVPP[6-6] $.[7-7] 
+

+ Meaning that the annotation cat="NP" applies to tokens 1-5 in the search result, and so on. Note that when specifying annotation layers, if the reserved name 'tok' is not specified, the tokens themselves will not be exported (annotations only). +

+

The WekaExporter outputs the + format used by the WEKA machine learning + tool (http://www.cs.waikato.ac.nz/ml/weka/). Only the attributes + of the search elements (#1, #2 etc. in AQL) are outputted, and are + separated by commas. The order and name of the attributes is declared + in the beginning of the export text, as in this example: +

+
@relation name
+
+@attribute #1_id string
+@attribute #1_span string
+@attribute #1_anno_tiger:cat string
+@attribute #2_id string
+@attribute #2_span string
+@attribute #2_anno_tiger:lemma string
+@attribute #2_anno_tiger:morph string
+@attribute #2_anno_tiger:pos string
+
+@data
+
+'58762617','die beiden geladenen Jugendlichen','NP','58762624','geladenen','geladen','Pos.Nom.Pl.*','ADJA'
+'58762652','keine böse Absicht','NP','58762656','böse','böse','Pos.Nom.Sg.Fem','ADJA'
+'58762886','eine große Chance','NP','58762889','große','groß','Pos.Nom.Sg.Fem','ADJA'
+'58763054','die Dallgower Gemeindevertreter','NP','58763062','Dallgower','Dallgower','Pos.*.*.*','ADJA'
+'58763072','wie die deutsche Abwehrreihe der Fußballkicker','NP','58763078','deutsche','deutsch','Pos.Nom.Sg.Fem','ADJA'
+'58763386','Döberitzer Heide','NP','58763389','Döberitzer','Döberitzer','Pos.*.*.*','ADJA'
+

The export shows the properties + of an NP node dominating a token with + the part-of-speech ADJA. Since the token also has other attributes, + such as the lemma, the token text and morphology, these are also + retrieved.

+ +

It is also possible to output metadata annotations per hit using the WekaExporter. To do so, use the parameter metakeys=meta1,meta2 etc. For example, if your documents have a metadata annotation called 'genre', you may export it for each search result as a further column using metakeys=Genre in the parameters box. +

+

+ The CSVExporter behaves much like the WekaExporter, except that the Weka header specifying the content of the columns is not used (useful for importing into spreadsheet programs such as Excel or Calc). +

+ +

Note that exporting may be slow in both exporters if the + result set is very large. +

+ +

Frequency Analysis

+

+ To perform a frequency analysis, enter the query whose results you want to analyze as usual in the AQL box. Note that you do not need to carry out the query first. Next, open the menu “More” between the Search and History buttons and select “Frequency Analysis”: + +

+ +

The interface will open the frequency analysis tab shown below. Initially, rows will be generated for the nodes present in the query. For example, two rows are automatically generated for the following query, which finds any pair of consecutive tokens:

+
+ tok . tok

+
+ + +

+ Clicking on “Perform frequency analysis” will produce a breakdown of all consecutive token bigrams in the corpus. The frequency graph will only show the first 500 elements, but the table below it will give the entire list of values, which can also be exported as a CSV file. +

+ +

+ To edit the analysis or analyze a new query, click the New Analysis button. It is also possible to add annotations to the analysis that were not in the original query, provided that these are expected to belong to some other node in the query. For example, the tokens in the pcc2 corpus also have part-of-speech, lemma and morphological information. We can replace the lines in the analysis specifying that tok values should be counted with pos values, which gives us part-of-speech bigrams. We can also add a lemma annotation belonging to the first search element, by clicking the Add button and entering the node definition number and annotation name we are interested in: +

+ +

+ As a result, we will get a count for each combination of values grouped by the first and second tokens parts-of-speech, as well as the first token’s lemma. +

+ +

Complete List of Operators

+ The ANNIS Query Language (AQL) currently includes the following operators:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorDescriptionIllustrationNotes
.direct precedence
+ A B +
For non-terminal nodes, precedence is determined by the right most and left most terminal children
.*indirect precedence
A x y z B +
For specific sizes of precedence spans, .n,m can be used, e.g. .3,4 - between 3 and 4 token distance
>direct dominance
+ A
+ |
+ B +
A specific edge type may be specifed, e.g.: >secedge to find secondary edges. Edges labels are specified in brackets, e.g. >[func="OA"] for an edge with the function 'object, accusative'
>*indirect dominance
+ A
+ |
+ ...
+ |
+ B +
For specific distance of dominance, >n,m can be used, e.g. >3,4 - dominates with 3 to 4 edges distance
_=_identical coverage
A
B
Applies when two annotation cover the exact same span of tokens
_i_inclusion
+ AAA
+ B
+
Applies when one annotation covers a span identical to or larger than another
_o_overlap
+ AAA   
+    BBB
+
For overlap only on the left or right side, use _ol_ and _or_ respectively
_l_left aligned 
AAA
+ BB 
+
Both elements span an area beginning with the same token
_r_right aligned
AA
+ BBB +
+
 Both elements span an area ending with the same token
^Directly near
AB or
BA
+
Elements stand next to each other in some order. Use ^n,m or ^* to specify possible distance in tokens as shown below.
^*Indirectly near
A x y z B or
B x y z A
+
Elements appear near each other in some order within 50 tokens (by default). Use ^n,m to specify exact distance range.
==Value identity
+ A = B +
The value of the annotation or token A is identical to that of B (this operator does not bind, i.e. the nodes must be connected by some other criteria too)
!=Value difference
+ A ≠ B +
The value of the annotation or token A is different from B (this operator does not bind, i.e. the nodes must be connected by some other criteria too)
->LABELlabeled pointing relation
+ + + + + +
      LABEL      
                
V  
A B
+
A labeled, directed relationship between two elements. Annotations can be specified with + ->LABEL[annotation="VALUE"] +
->LABEL *indirect pointing relation
+ + + + + +
LABEL   ...   LABEL
                        
   V  
A B
+ +
An indirect labeled relationship between two elements. The length of the chain may be specified with + ->LABEL n,m for relation chains of length n to m + +
>@lleft-most child
+ A
+ / | \
+ B x y +
 
>@rright-most child
+ A
+
/ | \ +
+ x y B +
 
$Common parent node
+ x
+ / \
+ A B +
 
$*Common ancestor node
+ x
+ |
+ ...
+ / \
+ A B +
 
+ #x:arity=n + + Arity + + x
+  / | \
+  1 … n +
Specifies the amount of directly dominated children that the searched node has
+ #x:tokenarity=n + + Tokenarity + + x
...
/ \
+ 1 ... n +
Specifies the length of the span of tokens covered by the node
+ #x:root + + Root + + ___
x
... +
Specifies that the node is not dominated by any other node within its namespace
+ +
+ + From d8b6d7e955614c5a1e6ed692b1b2a90e615892c4 Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Fri, 10 Oct 2014 17:22:35 +0200 Subject: [PATCH 07/20] add structure to a menu bar --- .../components/NavigateableSinglePage.java | 59 ++++++++++++++----- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java index ee7dec9d76..6ac18c6b58 100644 --- a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java +++ b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java @@ -20,10 +20,13 @@ import com.vaadin.ui.AbstractJavaScriptComponent; import com.vaadin.ui.JavaScriptFunction; import com.vaadin.ui.Label; +import com.vaadin.ui.MenuBar; +import com.vaadin.ui.MenuBar.MenuItem; import com.vaadin.ui.VerticalLayout; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashMap; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.JSONArray; @@ -50,6 +53,8 @@ public class NavigateableSinglePage extends VerticalLayout private final IFrameComponent iframe = new IFrameComponent(); private final Label lblHeaderID = new Label(); + private MenuBar navigation; + private final static Pattern regexHeader = Pattern.compile("h([1-6])"); public NavigateableSinglePage() @@ -75,21 +80,30 @@ private void onScroll(String headerID) public void setSource(String source) { iframe.getState().setSource(source); - parseHTML(source); + if(navigation != null) + { + removeComponent(navigation); + } + navigation = createMenubarFromHTML(source); + addComponent(navigation, 0); } - private LinkedHashMap parseHTML(String source) + private MenuBar createMenubarFromHTML(String source) { + MenuBar mbNavigation = new MenuBar(); + MenuItem navRoot = mbNavigation.addItem("Choose topic", null); + + LinkedHashMap result = new LinkedHashMap<>(); try { Document doc = Jsoup.connect(source).get(); - ArrayList currentPath = new ArrayList<>(); + ArrayList itemPath = new ArrayList<>(); // find all headers that have an ID -// for(Element e : doc.getElementsByAttribute("id")) - for(Element e : doc.getAllElements()) + for(Element e : doc.getElementsByAttribute("id")) +// for(Element e : doc.getAllElements()) { Matcher m = regexHeader.matcher(e.tagName()); if(m.matches()) @@ -99,24 +113,24 @@ private LinkedHashMap parseHTML(String source) // decide wether to expand the path (one level deeper) or to truncate if(level == 0) { - currentPath.clear(); } - else if(currentPath.size() >= level) + itemPath.clear(); + } + else if(itemPath.size() >= level) { // truncate - currentPath = new ArrayList<>(currentPath.subList(0, level)); + itemPath = new ArrayList<>(itemPath.subList(0, level)); } - - if(currentPath.isEmpty() && level > 0) + if(itemPath.isEmpty() && level > 0) { // fill the path with empty elements for(int i=0; i < level; i++) { - currentPath.add(""); + itemPath.add(createItem(navRoot, itemPath, "")); } } - currentPath.add(e.text()); - log.info("current path: {}", Joiner.on(" | ").join(currentPath)); + itemPath.add(createItem(navRoot, itemPath, e.text())); + log.info("current path: {}", Joiner.on(" | ").join(itemPath)); } } } @@ -124,7 +138,24 @@ else if(currentPath.size() >= level) { log.error("Could not parse iframe source", ex); } - return result; + return mbNavigation; + } + + private MenuItem createItem(MenuItem rootItem, List path, String caption) + { + MenuItem parent; + + if(path.isEmpty()) + { + parent = rootItem; + } + else + { + parent = path.get(path.size()-1); + + } + MenuItem child = parent.addItem(caption, null); + return child; } @JavaScript( From 81c93e6fb9fb8bbf38ac40858c7b2cb4da51f965 Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Fri, 10 Oct 2014 18:37:32 +0200 Subject: [PATCH 08/20] use a huger menu bar for the tutorial --- .../components/NavigateableSinglePage.java | 46 +++++++++++------ .../annis/gui/resultview/ResultViewPanel.java | 1 + .../webapp/VAADIN/themes/annis/annis.scss | 49 +++++++++++++------ .../webapp/VAADIN/themes/annis/styles.css | 17 +++++++ 4 files changed, 83 insertions(+), 30 deletions(-) diff --git a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java index 6ac18c6b58..c03a31f341 100644 --- a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java +++ b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java @@ -23,10 +23,13 @@ import com.vaadin.ui.MenuBar; import com.vaadin.ui.MenuBar.MenuItem; import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.ValoTheme; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.JSONArray; @@ -51,21 +54,16 @@ public class NavigateableSinglePage extends VerticalLayout private final static Logger log = LoggerFactory.getLogger(NavigateableSinglePage.class); private final IFrameComponent iframe = new IFrameComponent(); - private final Label lblHeaderID = new Label(); private MenuBar navigation; - + private Map menuItemRegistry = new HashMap<>(); + private MenuItem lastCheckedItem; private final static Pattern regexHeader = Pattern.compile("h([1-6])"); public NavigateableSinglePage() { - lblHeaderID.setCaption("Selected header ID: "); - - lblHeaderID.setWidth("100%"); - lblHeaderID.setHeight("-1px"); iframe.setSizeFull(); - - addComponent(lblHeaderID); + addComponent(iframe); setExpandRatio(iframe, 1.0f); @@ -74,7 +72,21 @@ public NavigateableSinglePage() private void onScroll(String headerID) { - lblHeaderID.setValue(headerID); + if(navigation != null) + { + MenuItem navRoot = navigation.getItems().get(0); + MenuItem toSelect = menuItemRegistry.get(headerID); + if(toSelect != null) + { + navRoot.setText(toSelect.getText()); + toSelect.setStyleName("huge-selected"); + if(lastCheckedItem != null && lastCheckedItem != toSelect) + { + lastCheckedItem.setStyleName("huge"); + } + lastCheckedItem = toSelect; + } + } } public void setSource(String source) @@ -84,18 +96,19 @@ public void setSource(String source) { removeComponent(navigation); } - navigation = createMenubarFromHTML(source); + menuItemRegistry.clear(); + navigation = createMenubarFromHTML(source, menuItemRegistry); addComponent(navigation, 0); } - private MenuBar createMenubarFromHTML(String source) + private MenuBar createMenubarFromHTML(String source, Map idToMenuItem) { MenuBar mbNavigation = new MenuBar(); + mbNavigation.setStyleName("huge"); MenuItem navRoot = mbNavigation.addItem("Choose topic", null); + navRoot.setStyleName("huge"); - - LinkedHashMap result = new LinkedHashMap<>(); try { Document doc = Jsoup.connect(source).get(); @@ -103,7 +116,6 @@ private MenuBar createMenubarFromHTML(String source) ArrayList itemPath = new ArrayList<>(); // find all headers that have an ID for(Element e : doc.getElementsByAttribute("id")) -// for(Element e : doc.getAllElements()) { Matcher m = regexHeader.matcher(e.tagName()); if(m.matches()) @@ -129,8 +141,9 @@ else if(itemPath.size() >= level) itemPath.add(createItem(navRoot, itemPath, "")); } } - itemPath.add(createItem(navRoot, itemPath, e.text())); - log.info("current path: {}", Joiner.on(" | ").join(itemPath)); + MenuItem item = createItem(navRoot, itemPath, e.text()); + itemPath.add(item); + idToMenuItem.put(e.id(), item); } } } @@ -155,6 +168,7 @@ private MenuItem createItem(MenuItem rootItem, List path, String capti } MenuItem child = parent.addItem(caption, null); + child.setStyleName("huge"); return child; } diff --git a/annis-gui/src/main/java/annis/gui/resultview/ResultViewPanel.java b/annis-gui/src/main/java/annis/gui/resultview/ResultViewPanel.java index 72c94064ec..12b96fbd8e 100644 --- a/annis-gui/src/main/java/annis/gui/resultview/ResultViewPanel.java +++ b/annis-gui/src/main/java/annis/gui/resultview/ResultViewPanel.java @@ -43,6 +43,7 @@ import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.themes.ChameleonTheme; +import com.vaadin.ui.themes.ValoTheme; import de.hu_berlin.german.korpling.saltnpepper.salt.saltCommon.SaltProject; import de.hu_berlin.german.korpling.saltnpepper.salt.saltCommon.sCorpusStructure.SCorpusGraph; import de.hu_berlin.german.korpling.saltnpepper.salt.saltCommon.sCorpusStructure.SDocument; diff --git a/annis-gui/src/main/webapp/VAADIN/themes/annis/annis.scss b/annis-gui/src/main/webapp/VAADIN/themes/annis/annis.scss index 5b949503a9..36fa8da5a2 100644 --- a/annis-gui/src/main/webapp/VAADIN/themes/annis/annis.scss +++ b/annis-gui/src/main/webapp/VAADIN/themes/annis/annis.scss @@ -44,25 +44,25 @@ $font-size: 10pt; @mixin annis { @include valo; -/* @include annis-base; */ + /* @include annis-base; */ @include partitur; .hidden { display: none; } - + .result-view-css { width: 100%; } - + .result-view-no-content { text-align: center; } - + .v-table-cell-content-kwic-anno { color: grey; } - + .kwic { td { color: grey; @@ -80,7 +80,7 @@ $font-size: 10pt; background-color: white; border-style:none; } - + } .drag-source-enabled { @@ -121,20 +121,20 @@ $font-size: 10pt; .tigerquery-builder-overlay { z-index: 10; } - + textarea.border-layout { border-width: 1px; border-style: solid; border-color: #b3b3b3; } - + .border-layout { border-width: 1px; border-style: solid; border-color: #b3b3b3; } - - + + /* Always synchronize with the MatchedNodeColors class! */ @include match-color(1, rgb(255,0,0)); // Red @include match-color(2, rgb(199,21,133)); // MediumVioletRed @@ -177,7 +177,7 @@ $font-size: 10pt; padding: 4px 13px; text-overflow: ellipsis; } - + .exception-message-caption { font-size: 18pt; } @@ -187,7 +187,7 @@ $font-size: 10pt; border-style: dashed; border-width: 1px; } - + .help-tab { .v-accordion-item-caption { background-color: #ecffe7; @@ -201,11 +201,11 @@ $font-size: 10pt; color: black; } } - + .grey-selection tr.v-selected { background-color: #808080; } - + .info-bar { background-color: $annis-color; border-style: solid; @@ -213,4 +213,25 @@ $font-size: 10pt; border-width: 1px 0px 0px 0px; } + $huge-menubar-height: 40px; + + .v-menubar-popup { + + .v-menubar-menuitem-huge, .v-menubar-menuitem-huge-selected { + line-height: $huge-menubar-height; + .v-menubar-submenu-indicator + .v-menubar-menuitem-caption:after { + line-height: $huge-menubar-height; + } + } + + .v-menubar-menuitem-huge-selected { + text-decoration: underline; + } + } + + .v-menubar-huge { + line-height: $huge-menubar-height; + height: $huge-menubar-height + 2px; + } + } \ No newline at end of file diff --git a/annis-gui/src/main/webapp/VAADIN/themes/annis/styles.css b/annis-gui/src/main/webapp/VAADIN/themes/annis/styles.css index 475ebc883b..a69fa195fd 100644 --- a/annis-gui/src/main/webapp/VAADIN/themes/annis/styles.css +++ b/annis-gui/src/main/webapp/VAADIN/themes/annis/styles.css @@ -11631,6 +11631,23 @@ div.v-layout.v-horizontal.v-widget { border-width: 1px 0px 0px 0px; } +.annis .v-menubar-popup .v-menubar-menuitem-huge, .annis .v-menubar-popup .v-menubar-menuitem-huge-selected { + line-height: 40px; +} + +.annis .v-menubar-popup .v-menubar-menuitem-huge .v-menubar-submenu-indicator + .v-menubar-menuitem-caption:after, .annis .v-menubar-popup .v-menubar-menuitem-huge-selected .v-menubar-submenu-indicator + .v-menubar-menuitem-caption:after { + line-height: 40px; +} + +.annis .v-menubar-popup .v-menubar-menuitem-huge-selected { + text-decoration: underline; +} + +.annis .v-menubar-huge { + line-height: 40px; + height: 42px; +} + .annis .v-app-loading:before { background-image: url(images/annisloading.gif); margin-top: -100px; From 3500fa277957097069febf5aef9ac9c288aa4e7d Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Fri, 10 Oct 2014 18:45:38 +0200 Subject: [PATCH 09/20] allow selection of a chapter in tutorial --- .../components/NavigateableSinglePage.java | 38 +++++++++++++++++-- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java index c03a31f341..2825b7f0ce 100644 --- a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java +++ b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java @@ -138,10 +138,10 @@ else if(itemPath.size() >= level) // fill the path with empty elements for(int i=0; i < level; i++) { - itemPath.add(createItem(navRoot, itemPath, "")); + itemPath.add(createItem(navRoot, itemPath, "", null)); } } - MenuItem item = createItem(navRoot, itemPath, e.text()); + MenuItem item = createItem(navRoot, itemPath, e.text(), e.id()); itemPath.add(item); idToMenuItem.put(e.id(), item); } @@ -154,7 +154,7 @@ else if(itemPath.size() >= level) return mbNavigation; } - private MenuItem createItem(MenuItem rootItem, List path, String caption) + private MenuItem createItem(MenuItem rootItem, List path, String caption, String id) { MenuItem parent; @@ -167,10 +167,40 @@ private MenuItem createItem(MenuItem rootItem, List path, String capti parent = path.get(path.size()-1); } - MenuItem child = parent.addItem(caption, null); + MenuItem child = parent.addItem(caption, new IDSelectionCommand(id)); child.setStyleName("huge"); return child; } + + private class IDSelectionCommand implements MenuBar.Command + { + private final String id; + + public IDSelectionCommand(String id) + { + this.id = id; + } + + @Override + public void menuSelected(MenuItem selectedItem) + { + if(id != null) + { + String lastSource = iframe.getState().getSource(); + // remove the fragment part + if(lastSource != null) + { + int hashPos = lastSource.lastIndexOf('#'); + if(hashPos >= 0) + { + lastSource = lastSource.substring(0, hashPos); + } + iframe.getState().setSource(lastSource + "#" + id); + } + } + } + + } @JavaScript( { From fd35b82edd9aaa2777877a2e99eac25843152999 Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Fri, 10 Oct 2014 19:10:55 +0200 Subject: [PATCH 10/20] ignore scrolling the first time when the anchor was manually set --- .../components/NavigateableSinglePage.java | 110 ++++++++++-------- .../gui/components/navigateablesinglepage.js | 71 ++++++----- 2 files changed, 103 insertions(+), 78 deletions(-) diff --git a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java index 2825b7f0ce..a5ef342644 100644 --- a/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java +++ b/annis-gui/src/main/java/annis/gui/components/NavigateableSinglePage.java @@ -50,41 +50,46 @@ */ public class NavigateableSinglePage extends VerticalLayout { - - private final static Logger log = LoggerFactory.getLogger(NavigateableSinglePage.class); + + private final static Logger log = LoggerFactory.getLogger( + NavigateableSinglePage.class); private final IFrameComponent iframe = new IFrameComponent(); - + private MenuBar navigation; - private Map menuItemRegistry = new HashMap<>(); - private MenuItem lastCheckedItem; + + private final Map menuItemRegistry = new HashMap<>(); + + private MenuItem lastSelectedItem; + private final static Pattern regexHeader = Pattern.compile("h([1-6])"); - + public NavigateableSinglePage() { iframe.setSizeFull(); - + + setSpacing(true); addComponent(iframe); - + setExpandRatio(iframe, 1.0f); - + } private void onScroll(String headerID) { - if(navigation != null) + if (navigation != null) { - MenuItem navRoot = navigation.getItems().get(0); + MenuItem navRoot = navigation.getItems().get(0); MenuItem toSelect = menuItemRegistry.get(headerID); - if(toSelect != null) + if (toSelect != null) { navRoot.setText(toSelect.getText()); toSelect.setStyleName("huge-selected"); - if(lastCheckedItem != null && lastCheckedItem != toSelect) + if (lastSelectedItem != null && lastSelectedItem != toSelect) { - lastCheckedItem.setStyleName("huge"); - } - lastCheckedItem = toSelect; + lastSelectedItem.setStyleName("huge"); + } + lastSelectedItem = toSelect; } } } @@ -92,7 +97,7 @@ private void onScroll(String headerID) public void setSource(String source) { iframe.getState().setSource(source); - if(navigation != null) + if (navigation != null) { removeComponent(navigation); } @@ -100,43 +105,44 @@ public void setSource(String source) navigation = createMenubarFromHTML(source, menuItemRegistry); addComponent(navigation, 0); } - - private MenuBar createMenubarFromHTML(String source, Map idToMenuItem) + + private MenuBar createMenubarFromHTML(String source, + Map idToMenuItem) { - + MenuBar mbNavigation = new MenuBar(); mbNavigation.setStyleName("huge"); MenuItem navRoot = mbNavigation.addItem("Choose topic", null); navRoot.setStyleName("huge"); - + try { Document doc = Jsoup.connect(source).get(); - + ArrayList itemPath = new ArrayList<>(); // find all headers that have an ID - for(Element e : doc.getElementsByAttribute("id")) + for (Element e : doc.getElementsByAttribute("id")) { Matcher m = regexHeader.matcher(e.tagName()); - if(m.matches()) + if (m.matches()) { - int level = Integer.parseInt(m.group(1))-1; - + int level = Integer.parseInt(m.group(1)) - 1; + // decide wether to expand the path (one level deeper) or to truncate - if(level == 0) + if (level == 0) { itemPath.clear(); } - else if(itemPath.size() >= level) + else if (itemPath.size() >= level) { // truncate itemPath = new ArrayList<>(itemPath.subList(0, level)); } - - if(itemPath.isEmpty() && level > 0) + + if (itemPath.isEmpty() && level > 0) { // fill the path with empty elements - for(int i=0; i < level; i++) + for (int i = 0; i < level; i++) { itemPath.add(createItem(navRoot, itemPath, "", null)); } @@ -153,59 +159,69 @@ else if(itemPath.size() >= level) } return mbNavigation; } - - private MenuItem createItem(MenuItem rootItem, List path, String caption, String id) + + private MenuItem createItem(MenuItem rootItem, List path, + String caption, String id) { MenuItem parent; - - if(path.isEmpty()) + + if (path.isEmpty()) { parent = rootItem; } else { - parent = path.get(path.size()-1); - + parent = path.get(path.size() - 1); + } MenuItem child = parent.addItem(caption, new IDSelectionCommand(id)); child.setStyleName("huge"); return child; } - + private class IDSelectionCommand implements MenuBar.Command { + private final String id; - + public IDSelectionCommand(String id) { this.id = id; } - + @Override public void menuSelected(MenuItem selectedItem) { - if(id != null) + if (id != null) { + MenuItem navRoot = navigation.getItems().get(0); String lastSource = iframe.getState().getSource(); // remove the fragment part - if(lastSource != null) + if (lastSource != null) { int hashPos = lastSource.lastIndexOf('#'); - if(hashPos >= 0) + if (hashPos >= 0) { lastSource = lastSource.substring(0, hashPos); } + selectedItem.setStyleName("huge-selected"); + navRoot.setText(selectedItem.getText()); + if (lastSelectedItem != null && lastSelectedItem != selectedItem) + { + lastSelectedItem.setStyleName("huge"); + } + lastSelectedItem = selectedItem; iframe.getState().setSource(lastSource + "#" + id); } } } - + } @JavaScript( - { - "vaadin://jquery.js", "navigateablesinglepage.js" - }) + { + "vaadin://jquery.js", "navigateablesinglepage.js" + }) private class IFrameComponent extends AbstractJavaScriptComponent { diff --git a/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js b/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js index dc68a74106..dbcbfaef35 100644 --- a/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js +++ b/annis-gui/src/main/resources/annis/gui/components/navigateablesinglepage.js @@ -19,48 +19,56 @@ window.annis_gui_components_NavigateableSinglePage_IFrameComponent = function () var connector = this; var rootDiv = $(this.getElement(this.getConnectorId())); - function initElement() { - var iframeElement = $(document.createElement("iframe")); - rootDiv.append(iframeElement); - iframeElement.attr("frameborder", 0); - iframeElement.attr("width", "100%"); - iframeElement.attr("height", "100%"); - iframeElement.attr("allowtransparency", "true"); - iframeElement.attr("src", connector.getState().source); + var ignoreScroll = false; - var iframeWindow = $(iframeElement.get(0).contentWindow); - iframeWindow.on('scroll', function () { + function addScrollListener(iframe) { - // find ID of the first header which is inside the visible range - var headersWithID = $(iframeWindow.get(0).document).find("h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]"); + var iframeWindow = $(iframe.get(0).contentWindow); + iframeWindow.on('scroll', function () { - if (headersWithID.length > 0) + if(!ignoreScroll) { - var top = iframeWindow.scrollTop(); - var windowHeight = iframeWindow.height(); - var visibleBorder = top + (windowHeight / 4); - + // find ID of the first header which is inside the visible range + var headersWithID = $(iframeWindow.get(0).document).find("h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]"); - var lastInvisibleID = headersWithID.attr('id') - // find the last header which is (even slightly) invisible - $.each(headersWithID, function (key) { + if (headersWithID.length > 0) + { + var top = iframeWindow.scrollTop(); + var windowHeight = iframeWindow.height(); + var visibleBorder = top + (windowHeight / 4); - var offset = $(this).offset().top; - // is invisible? - if (offset < visibleBorder) { - lastInvisibleID = $(this).attr('id'); - } else { - return false; - } - }); - connector.scrolled(lastInvisibleID); - } + var lastInvisibleID = headersWithID.attr('id') + // find the last header which is (even slightly) invisible + $.each(headersWithID, function (key) { + var offset = $(this).offset().top; + // is invisible? + if (offset < visibleBorder) { + lastInvisibleID = $(this).attr('id'); + } else { + return false; + } + }); + connector.scrolled(lastInvisibleID); + } + } // end if scroll should be ignored once + ignoreScroll = false; }); } + function initElement() { + var iframeElement = $(document.createElement("iframe")); + rootDiv.append(iframeElement); + iframeElement.attr("frameborder", 0); + iframeElement.attr("width", "100%"); + iframeElement.attr("height", "100%"); + iframeElement.attr("allowtransparency", "true"); + iframeElement.attr("src", connector.getState().source); + addScrollListener(iframeElement); + } + this.onStateChange = function () { var iframe = rootDiv.find("iframe"); if (iframe.length === 0) @@ -69,7 +77,8 @@ window.annis_gui_components_NavigateableSinglePage_IFrameComponent = function () } else { - iframe.attr("src", connector.getState().source); + ignoreScroll = true; + iframe.attr("src", connector.getState().source); } }; From 2fe98cb7713c0381e1f0f3c873a1e9ed85612937 Mon Sep 17 00:00:00 2001 From: Thomas Krause Date: Sun, 12 Oct 2014 13:14:04 +0200 Subject: [PATCH 11/20] updating JQuery --- .../src/main/resources/VAADIN/jquery.js | 10489 ++++++++-------- 1 file changed, 5425 insertions(+), 5064 deletions(-) diff --git a/annis-widgets/src/main/resources/VAADIN/jquery.js b/annis-widgets/src/main/resources/VAADIN/jquery.js index ebc6c187d7..9f7b3d3895 100644 --- a/annis-widgets/src/main/resources/VAADIN/jquery.js +++ b/annis-widgets/src/main/resources/VAADIN/jquery.js @@ -1,81 +1,84 @@ /*! - * jQuery JavaScript Library v2.0.3 + * jQuery JavaScript Library v2.1.1 * http://jquery.com/ * * Includes Sizzle.js * http://sizzlejs.com/ * - * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2013-07-03T13:30Z + * Date: 2014-05-01T17:11Z */ -(function( window, undefined ) { + +(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+ -//"use strict"; -var - // A central reference to the root jQuery(document) - rootjQuery, +// - // The deferred used on DOM ready - readyList, +var arr = []; - // Support: IE9 - // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined` - core_strundefined = typeof undefined, +var slice = arr.slice; - // Use the correct document accordingly with window argument (sandbox) - location = window.location, - document = window.document, - docElem = document.documentElement, +var concat = arr.concat; - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, +var push = arr.push; - // Map over the $ in case of overwrite - _$ = window.$, +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; - // [[Class]] -> type pairs - class2type = {}, - // List of deleted data cache ids, so we can reuse them - core_deletedIds = [], - core_version = "2.0.3", +var + // Use the correct document accordingly with window argument (sandbox) + document = window.document, - // Save a reference to some core methods - core_concat = core_deletedIds.concat, - core_push = core_deletedIds.push, - core_slice = core_deletedIds.slice, - core_indexOf = core_deletedIds.indexOf, - core_toString = class2type.toString, - core_hasOwn = class2type.hasOwnProperty, - core_trim = core_version.trim, + version = "2.1.1", // Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); }, - // Used for matching numbers - core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, - - // Used for splitting on whitespace - core_rnotwhite = /\S+/g, - - // 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-]*))$/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + // Support: Android<4.1 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // Matches dashed string for camelizing rmsPrefix = /^-ms-/, @@ -84,114 +87,13 @@ var // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { return letter.toUpperCase(); - }, - - // The ready event handler and self cleanup method - completed = function() { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - jQuery.ready(); }; jQuery.fn = jQuery.prototype = { // The current version of jQuery being used - jquery: core_version, + jquery: version, constructor: jQuery, - init: function( selector, context, rootjQuery ) { - 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 - 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 ) { - // 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 rootjQuery.ready( selector ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, // Start with an empty selector selector: "", @@ -200,19 +102,19 @@ jQuery.fn = jQuery.prototype = { length: 0, toArray: function() { - return core_slice.call( this ); + 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 num != null ? - // Return a 'clean' array - this.toArray() : + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); + // Return all the elements in a clean array + slice.call( this ); }, // Take an array of elements and push it onto the stack @@ -237,15 +139,14 @@ jQuery.fn = jQuery.prototype = { return jQuery.each( this, callback, args ); }, - ready: function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); }, slice: function() { - return this.pushStack( core_slice.apply( this, arguments ) ); + return this.pushStack( slice.apply( this, arguments ) ); }, first: function() { @@ -262,26 +163,17 @@ jQuery.fn = jQuery.prototype = { return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); }, - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - end: function() { return this.prevObject || this.constructor(null); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. - push: core_push, - sort: [].sort, - splice: [].splice + push: push, + sort: arr.sort, + splice: arr.splice }; -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, @@ -292,9 +184,10 @@ jQuery.extend = jQuery.fn.extend = function() { // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; - target = arguments[1] || {}; + // skip the boolean and the target - i = 2; + target = arguments[ i ] || {}; + i++; } // Handle case when target is a string or something (possible in deep copy) @@ -303,9 +196,9 @@ jQuery.extend = jQuery.fn.extend = function() { } // extend jQuery itself if only one argument is passed - if ( length === i ) { + if ( i === length ) { target = this; - --i; + i--; } for ( ; i < length; i++ ) { @@ -348,60 +241,16 @@ jQuery.extend = jQuery.fn.extend = function() { jQuery.extend({ // Unique for each copy of jQuery on the page - expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), - - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, + // Assume jQuery is ready without the ready module + isReady: true, - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } + error: function( msg ) { + throw new Error( msg ); }, - // 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; - } - - // 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"); - } - }, + noop: function() {}, // See test/unit/core.js for details concerning isFunction. // Since version 1.3, DOM methods and functions like alert @@ -417,17 +266,10 @@ jQuery.extend({ }, isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, - - type: function( obj ) { - if ( obj == null ) { - return String( obj ); - } - // Support: Safari <= 5.1 (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ core_toString.call(obj) ] || "object" : - typeof 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 !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; }, isPlainObject: function( obj ) { @@ -439,16 +281,8 @@ jQuery.extend({ return false; } - // Support: Firefox <20 - // The try/catch suppresses exceptions thrown when attempting to access - // the "constructor" property of certain host objects, ie. |window.location| - // https://bugzilla.mozilla.org/show_bug.cgi?id=814622 - try { - if ( obj.constructor && - !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { - return false; - } - } catch ( e ) { + if ( obj.constructor && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { return false; } @@ -465,69 +299,20 @@ jQuery.extend({ return true; }, - error: function( msg ) { - throw new Error( msg ); - }, - - // data: string of html - // context (optional): If specified, the fragment will be created in this context, defaults to document - // keepScripts (optional): If true, will include scripts passed in the html string - parseHTML: function( data, context, keepScripts ) { - if ( !data || typeof data !== "string" ) { - return null; - } - if ( typeof context === "boolean" ) { - keepScripts = context; - context = false; - } - context = context || document; - - var parsed = rsingleTag.exec( data ), - scripts = !keepScripts && []; - - // Single tag - if ( parsed ) { - return [ context.createElement( parsed[1] ) ]; - } - - parsed = jQuery.buildFragment( [ data ], context, scripts ); - - if ( scripts ) { - jQuery( scripts ).remove(); - } - - return jQuery.merge( [], parsed.childNodes ); - }, - - parseJSON: JSON.parse, - - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE9 - try { - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } catch ( e ) { - xml = undefined; - } - - if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); + type: function( obj ) { + if ( obj == null ) { + return obj + ""; } - return xml; + // Support: Android < 4.0, iOS < 6 (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; }, - noop: function() {}, - // Evaluates a script in a global context globalEval: function( code ) { var script, - indirect = eval; + indirect = eval; code = jQuery.trim( code ); @@ -607,8 +392,11 @@ jQuery.extend({ return obj; }, + // Support: Android<4.1 trim: function( text ) { - return text == null ? "" : core_trim.call( text ); + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); }, // results is for internal usage only @@ -622,7 +410,7 @@ jQuery.extend({ [ arr ] : arr ); } else { - core_push.call( ret, arr ); + push.call( ret, arr ); } } @@ -630,22 +418,16 @@ jQuery.extend({ }, inArray: function( elem, arr, i ) { - return arr == null ? -1 : core_indexOf.call( arr, elem, i ); + return arr == null ? -1 : indexOf.call( arr, elem, i ); }, merge: function( first, second ) { - var l = second.length, - i = first.length, - j = 0; + var len = +second.length, + j = 0, + i = first.length; - if ( typeof l === "number" ) { - for ( ; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; } first.length = i; @@ -653,23 +435,23 @@ jQuery.extend({ return first; }, - grep: function( elems, callback, inv ) { - var retVal, - ret = [], + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], i = 0, - length = elems.length; - inv = !!inv; + length = elems.length, + callbackExpect = !invert; // Go through the array, only saving the items // that pass the validator function for ( ; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); } } - return ret; + return matches; }, // arg is for internal usage only @@ -680,13 +462,13 @@ jQuery.extend({ isArray = isArraylike( elems ), ret = []; - // Go through the array, translating each of the items to their + // 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[ ret.length ] = value; + ret.push( value ); } } @@ -696,13 +478,13 @@ jQuery.extend({ value = callback( elems[ i ], i, arg ); if ( value != null ) { - ret[ ret.length ] = value; + ret.push( value ); } } } // Flatten any nested arrays - return core_concat.apply( [], ret ); + return concat.apply( [], ret ); }, // A global GUID counter for objects @@ -726,9 +508,9 @@ jQuery.extend({ } // Simulated bind - args = core_slice.call( arguments, 2 ); + args = slice.call( arguments, 2 ); proxy = function() { - return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + 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 @@ -737,154 +519,57 @@ jQuery.extend({ return proxy; }, - // Multifunctional method to get and set values of a collection - // The value/s can optionally be executed if it's a function - 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; - } + now: Date.now, - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); - if ( fn ) { - for ( ; i < length; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); - } - } - } +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); - return chainable ? - elems : + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; - }, + if ( obj.nodeType === 1 && length ) { + return true; + } - now: Date.now, - - // A method for quickly swapping in/out CSS properties to get correct calculations. - // Note: this method belongs to the css module but it's needed here for the support module. - // If support gets modularized, this method should be moved back to the css module. - swap: function( elem, options, callback, args ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.apply( elem, args || [] ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; - } -}); - -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 ); - - } else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); - } - } - return readyList.promise( obj ); -}; - -// 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 ( jQuery.isWindow( obj ) ) { - return false; - } - - if ( obj.nodeType === 1 && length ) { - return true; - } - - return type === "array" || type !== "function" && - ( length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj ); + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; } - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); +var Sizzle = /*! - * Sizzle CSS Selector Engine v1.9.4-pre + * Sizzle CSS Selector Engine v1.10.19 * http://sizzlejs.com/ * * Copyright 2013 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2013-06-03 + * Date: 2014-04-18 */ -(function( window, undefined ) { +(function( window ) { var i, support, - cachedruns, Expr, getText, isXML, + tokenize, compile, + select, outermostContext, sortInput, + hasDuplicate, // Local document vars setDocument, @@ -904,11 +589,9 @@ var i, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), - hasDuplicate = false, sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; - return 0; } return 0; }, @@ -950,17 +633,23 @@ var i, // 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 ) + ")*)|.*)\\)|)", + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), @@ -968,8 +657,7 @@ var i, rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - rsibling = new RegExp( whitespace + "*[+~]" ), - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), @@ -990,14 +678,15 @@ var i, 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-]+))$/, - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - + rsibling = /[+~]/, rescape = /'|\\/g, // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters @@ -1005,12 +694,12 @@ var i, funescape = function( _, escaped, escapedWhitespace ) { var high = "0x" + escaped - 0x10000; // NaN means non-codepoint - // Support: Firefox + // Support: Firefox<24 // Workaround erroneous numeric interpretation of +"0x" return high !== high || escapedWhitespace ? escaped : - // BMP codepoint high < 0 ? + // BMP codepoint String.fromCharCode( high + 0x10000 ) : // Supplemental Plane codepoint (surrogate pair) String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); @@ -1074,7 +763,7 @@ function Sizzle( selector, context, results, seed ) { if ( nodeType === 9 ) { elem = context.getElementById( m ); // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 + // 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 @@ -1130,7 +819,7 @@ function Sizzle( selector, context, results, seed ) { while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } - newContext = rsibling.test( selector ) && context.parentNode || context; + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; newSelector = groups.join(","); } @@ -1165,11 +854,11 @@ function createCache() { function cache( key, value ) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key += " " ) > Expr.cacheLength ) { + if ( keys.push( key + " " ) > Expr.cacheLength ) { // Only keep the most recent entries delete cache[ keys.shift() ]; } - return (cache[ key ] = value); + return (cache[ key + " " ] = value); } return cache; } @@ -1292,8 +981,21 @@ function createPositionalPseudo( fn ) { } /** - * Detect xml + * 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 @@ -1302,16 +1004,14 @@ isXML = Sizzle.isXML = function( elem ) { return documentElement ? documentElement.nodeName !== "HTML" : false; }; -// Expose support vars for convenience -support = Sizzle.support = {}; - /** * 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 doc = node ? node.ownerDocument || node : preferredDoc, + var hasCompare, + doc = node ? node.ownerDocument || node : preferredDoc, parent = doc.defaultView; // If no document and documentElement is available, return @@ -1330,10 +1030,17 @@ setDocument = Sizzle.setDocument = function( node ) { // 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.attachEvent && parent !== parent.top ) { - parent.attachEvent( "onbeforeunload", function() { - setDocument(); - }); + 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 @@ -1356,7 +1063,7 @@ setDocument = Sizzle.setDocument = function( node ) { }); // Check if getElementsByClassName can be trusted - support.getElementsByClassName = assert(function( div ) { + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { div.innerHTML = "
"; // Support: Safari<4 @@ -1383,7 +1090,7 @@ setDocument = Sizzle.setDocument = function( node ) { 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] : []; + return m && m.parentNode ? [ m ] : []; } }; Expr.filter["ID"] = function( id ) { @@ -1463,7 +1170,15 @@ setDocument = Sizzle.setDocument = function( node ) { // setting a boolean content attribute, // since its presence should be enough // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; + div.innerHTML = ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowclip^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } // Support: IE8 // Boolean attributes and "value" are not treated correctly @@ -1480,18 +1195,16 @@ setDocument = Sizzle.setDocument = function( node ) { }); assert(function( div ) { - - // Support: Opera 10-12/IE8 - // ^= $= *= and empty values - // Should not select anything // Support: Windows 8 Native Apps - // The type attribute is restricted during .innerHTML assignment + // The type and name attributes are restricted during .innerHTML assignment var input = doc.createElement("input"); input.setAttribute( "type", "hidden" ); - div.appendChild( input ).setAttribute( "t", "" ); + div.appendChild( input ).setAttribute( "name", "D" ); - if ( div.querySelectorAll("[t^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + // 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) @@ -1506,7 +1219,8 @@ setDocument = Sizzle.setDocument = function( node ) { }); } - if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { @@ -1528,11 +1242,12 @@ setDocument = Sizzle.setDocument = function( node ) { /* Contains ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); // Element contains another // Purposefully does not implement inclusive descendent // As in, an element does not contain itself - contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ? + contains = hasCompare || rnative.test( docElem.contains ) ? function( a, b ) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; @@ -1557,7 +1272,7 @@ setDocument = Sizzle.setDocument = function( node ) { ---------------------------------------------------------------------- */ // Document order sorting - sortOrder = docElem.compareDocumentPosition ? + sortOrder = hasCompare ? function( a, b ) { // Flag for duplicate removal @@ -1566,34 +1281,46 @@ setDocument = Sizzle.setDocument = function( node ) { return 0; } - var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); - + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; if ( compare ) { - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + return compare; + } - // Choose the first element that is related to our preferred document - if ( a === doc || contains(preferredDoc, a) ) { - return -1; - } - if ( b === doc || contains(preferredDoc, b) ) { - return 1; - } + // 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; - // Maintain original order - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; + // 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; } - return compare & 4 ? -1 : 1; + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; } - // Not directly comparable, sort on existence of method - return a.compareDocumentPosition ? -1 : 1; + 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, @@ -1601,13 +1328,8 @@ setDocument = Sizzle.setDocument = function( node ) { ap = [ a ], bp = [ b ]; - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - // Parentless nodes are either documents or disconnected - } else if ( !aup || !bup ) { + if ( !aup || !bup ) { return a === doc ? -1 : b === doc ? 1 : aup ? -1 : @@ -1679,7 +1401,7 @@ Sizzle.matchesSelector = function( elem, expr ) { } catch(e) {} } - return Sizzle( expr, document, null, [elem] ).length > 0; + return Sizzle( expr, document, null, [ elem ] ).length > 0; }; Sizzle.contains = function( context, elem ) { @@ -1702,13 +1424,13 @@ Sizzle.attr = function( elem, name ) { fn( elem, name, !documentIsHTML ) : undefined; - return val === undefined ? + return val !== undefined ? + val : support.attributes || !documentIsHTML ? elem.getAttribute( name ) : (val = elem.getAttributeNode(name)) && val.specified ? val.value : - null : - val; + null; }; Sizzle.error = function( msg ) { @@ -1741,6 +1463,10 @@ Sizzle.uniqueSort = function( results ) { } } + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + return results; }; @@ -1756,13 +1482,13 @@ getText = Sizzle.getText = function( elem ) { if ( !nodeType ) { // If no nodeType, this is expected to be an array - for ( ; (node = elem[i]); i++ ) { + 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 (see #11153) + // innerText usage removed for consistency of new lines (jQuery #11153) if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { @@ -1804,7 +1530,7 @@ Expr = Sizzle.selectors = { 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 ); + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); if ( match[2] === "~=" ) { match[3] = " " + match[3] + " "; @@ -1847,15 +1573,15 @@ Expr = Sizzle.selectors = { "PSEUDO": function( match ) { var excess, - unquoted = !match[5] && match[2]; + unquoted = !match[6] && 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]; + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; // Strip excess characters from unquoted arguments } else if ( unquoted && rpseudo.test( unquoted ) && @@ -2159,12 +1885,11 @@ Expr = Sizzle.selectors = { // Contents "empty": function( elem ) { // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), - // not comment, processing instructions, or others - // Thanks to Diego Perini for the nodeName shortcut - // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + // :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.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + if ( elem.nodeType < 6 ) { return false; } } @@ -2191,11 +1916,12 @@ Expr = Sizzle.selectors = { "text": function( elem ) { var attr; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); + + // 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 @@ -2260,7 +1986,7 @@ function setFilters() {} setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); -function tokenize( selector, parseOnly ) { +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; @@ -2281,7 +2007,7 @@ function tokenize( selector, parseOnly ) { // Don't consume trailing commas as valid soFar = soFar.slice( match[0].length ) || soFar; } - groups.push( tokens = [] ); + groups.push( (tokens = []) ); } matched = false; @@ -2325,7 +2051,7 @@ function tokenize( selector, parseOnly ) { Sizzle.error( selector ) : // Cache the tokens tokenCache( selector, groups ).slice( 0 ); -} +}; function toSelector( tokens ) { var i = 0, @@ -2354,8 +2080,8 @@ function addCombinator( matcher, combinator, base ) { // Check against all ancestor/preceding elements function( elem, context, xml ) { - var data, cache, outerCache, - dirkey = dirruns + " " + doneName; + 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 ) { @@ -2370,14 +2096,17 @@ function addCombinator( matcher, combinator, base ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { - if ( (data = cache[1]) === true || data === cachedruns ) { - return data === true; - } + 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 { - cache = outerCache[ dir ] = [ dirkey ]; - cache[1] = matcher( elem, context, xml ) || cachedruns; - if ( cache[1] === true ) { + // 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; } } @@ -2401,6 +2130,15 @@ function elementMatcher( matchers ) { matchers[0]; } +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + function condense( unmatched, map, filter, context, xml ) { var elem, newUnmatched = [], @@ -2571,31 +2309,30 @@ function matcherFromTokens( tokens ) { } function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - // A counter to specify which element is currently being matched - var matcherCachedRuns = 0, - bySet = setMatchers.length > 0, + var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, expandContext ) { + superMatcher = function( seed, context, xml, results, outermost ) { var elem, j, matcher, - setMatched = [], matchedCount = 0, i = "0", unmatched = seed && [], - outermost = expandContext != null, + setMatched = [], contextBackup = outermostContext, - // We must always have either seed elements or context - elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // 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); + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; if ( outermost ) { outermostContext = context !== document && context; - cachedruns = matcherCachedRuns; } // Add elements passing elementMatchers directly to results // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - for ( ; (elem = elems[i]) != null; i++ ) { + // 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++]) ) { @@ -2606,7 +2343,6 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { } if ( outermost ) { dirruns = dirrunsUnique; - cachedruns = ++matcherCachedRuns; } } @@ -2671,7 +2407,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { superMatcher; } -compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], @@ -2679,12 +2415,12 @@ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { if ( !cached ) { // Generate a function of recursive functions that can be used to check each element - if ( !group ) { - group = tokenize( selector ); + if ( !match ) { + match = tokenize( selector ); } - i = group.length; + i = match.length; while ( i-- ) { - cached = matcherFromTokens( group[i] ); + cached = matcherFromTokens( match[i] ); if ( cached[ expando ] ) { setMatchers.push( cached ); } else { @@ -2694,82 +2430,91 @@ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { // Cache the compiled function cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; } 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 ) { +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { var i, tokens, token, type, find, - match = tokenize( selector ); + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); - if ( !seed ) { - // Try to minimize operations if there is only one group - if ( match.length === 1 ) { + results = results || []; - // 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 ] ) { + // Try to minimize operations if there is no seed and only one group + if ( match.length === 1 ) { - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - } - selector = selector.slice( tokens.shift().value.length ); + // 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; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; } - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; + selector = selector.slice( tokens.shift().value.length ); + } - // 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 ) && 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; - } + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; - break; + // 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 + // Compile and execute a filtering function if one is not provided // Provide `match` to avoid retokenization if we modified the selector above - compile( selector, match )( + ( compiled || compile( selector, match ) )( seed, context, !documentIsHTML, results, - rsibling.test( selector ) + rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; -} +}; // One-time assignments @@ -2778,7 +2523,7 @@ 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; +support.detectDuplicates = !!hasDuplicate; // Initialize against the default document setDocument(); @@ -2826,13 +2571,20 @@ if ( !assert(function( div ) { addHandle( booleans, function( elem, name, isXML ) { var val; if ( !isXML ) { - return (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - elem[ name ] === true ? name.toLowerCase() : null; + 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; @@ -2842,219 +2594,629 @@ jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; -})( window ); -// 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( core_rnotwhite ) || [], function( _, flag ) { - object[ flag ] = true; +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 ( indexOf.call( qualifier, elem ) >= 0 ) !== not; }); - 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 ) { +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; - // 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 ); + if ( not ) { + expr = ":not(" + expr + ")"; + } - var // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // 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() ); + 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, + len = this.length, + ret = [], + self = this; + + 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; } - } else if ( memory ) { - list = []; - } else { - self.disable(); } + }) ); + } + + 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, + + // 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[0] === "<" && selector[ 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 ); } - }, - // 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--; - } + + // 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; - }, - // 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; + + // 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 ) { + // Inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; } - 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; + + // 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 ); } - }; - return self; -}; -jQuery.extend({ + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; - 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() { + // 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 = [], + truncate = until !== undefined; + + while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; + }, + + sibling: function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; + } +}); + +jQuery.fn.extend({ + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter(function() { + var i = 0; + for ( ; i < l; 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 indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + 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 ) { + while ( (cur = cur[dir]) && 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 elem.contentDocument || jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.unique( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +}); +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 // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // 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; }, @@ -3062,8 +3224,7 @@ jQuery.extend({ var fns = arguments; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { - var action = tuple[ 0 ], - fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + 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 ); @@ -3073,7 +3234,7 @@ jQuery.extend({ .fail( newDefer.reject ) .progress( newDefer.notify ); } else { - newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); @@ -3085,3638 +3246,4210 @@ jQuery.extend({ promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } - }, - deferred = {}; + }, + 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; + } + + // 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.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); + +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + 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 ); + + } else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// 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, + len = 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 < len; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[0], key ) : emptyGet; +}; + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( owner ) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + +function Data() { + // Support: Android < 4, + // Old WebKit does not have Object.preventExtensions/freeze method, + // return new empty object instead with no [[set]] accessor + Object.defineProperty( this.cache = {}, 0, { + get: function() { + return {}; + } + }); + + this.expando = jQuery.expando + Math.random(); +} + +Data.uid = 1; +Data.accepts = jQuery.acceptData; + +Data.prototype = { + key: function( owner ) { + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return the key for a frozen object. + if ( !Data.accepts( owner ) ) { + return 0; + } + + var descriptor = {}, + // Check if the owner object already has a cache key + unlock = owner[ this.expando ]; + + // If not, create one + if ( !unlock ) { + unlock = Data.uid++; + + // Secure it in a non-enumerable, non-writable property + try { + descriptor[ this.expando ] = { value: unlock }; + Object.defineProperties( owner, descriptor ); + + // Support: Android < 4 + // Fallback to a less secure definition + } catch ( e ) { + descriptor[ this.expando ] = unlock; + jQuery.extend( owner, descriptor ); + } + } + + // Ensure the cache object + if ( !this.cache[ unlock ] ) { + this.cache[ unlock ] = {}; + } + + return unlock; + }, + set: function( owner, data, value ) { + var prop, + // There may be an unlock assigned to this node, + // if there is no entry for this "owner", create one inline + // and set the unlock as though an owner entry had always existed + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + // Handle: [ owner, key, value ] args + if ( typeof data === "string" ) { + cache[ data ] = value; + + // Handle: [ owner, { properties } ] args + } else { + // Fresh assignments by object are shallow copied + if ( jQuery.isEmptyObject( cache ) ) { + jQuery.extend( this.cache[ unlock ], data ); + // Otherwise, copy the properties one-by-one to the cache object + } else { + for ( prop in data ) { + cache[ prop ] = data[ prop ]; + } + } + } + return cache; + }, + get: function( owner, key ) { + // Either a valid cache is found, or will be created. + // New caches will be created and the unlock returned, + // allowing direct access to the newly created + // empty data object. A valid owner object must be provided. + var cache = this.cache[ this.key( owner ) ]; + + return key === undefined ? + cache : cache[ key ]; + }, + access: function( owner, key, value ) { + var stored; + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ((key && typeof key === "string") && value === undefined) ) { + + stored = this.get( owner, key ); + + return stored !== undefined ? + stored : this.get( owner, jQuery.camelCase(key) ); + } + + // [*]When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, name, camel, + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + if ( key === undefined ) { + this.cache[ unlock ] = {}; + + } else { + // Support array or space separated string of keys + if ( jQuery.isArray( key ) ) { + // 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 = key.concat( key.map( jQuery.camelCase ) ); + } else { + camel = jQuery.camelCase( key ); + // Try the string as a key before any manipulation + if ( key in cache ) { + name = [ key, camel ]; + } else { + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + name = camel; + name = name in cache ? + [ name ] : ( name.match( rnotwhite ) || [] ); + } + } + + i = name.length; + while ( i-- ) { + delete cache[ name[ i ] ]; + } + } + }, + hasData: function( owner ) { + return !jQuery.isEmptyObject( + this.cache[ owner[ this.expando ] ] || {} + ); + }, + discard: function( owner ) { + if ( owner[ this.expando ] ) { + delete this.cache[ owner[ this.expando ] ]; + } + } +}; +var data_priv = new Data(); + +var data_user = new Data(); + + + +/* + Implementation Summary + + 1. Enforce API surface and semantic compatibility with 1.9.x branch + 2. Improve the module's maintainability by reducing the storage + paths to a single mechanism. + 3. Use the same single mechanism to support "private" and "user" data. + 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) + 5. Avoid exposing implementation details on user objects (eg. expando properties) + 6. Provide a clear path for implementation upgrade to WeakMap in 2014 +*/ +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + 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 + data_user.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend({ + hasData: function( elem ) { + return data_user.hasData( elem ) || data_priv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return data_user.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + data_user.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to data_priv methods, these can be deprecated. + _data: function( elem, name, data ) { + return data_priv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + data_priv.remove( elem, name ); + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = data_user.get( elem ); + + if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + data_priv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + data_user.set( this, key ); + }); + } + + return access( this, function( value ) { + var data, + camelKey = jQuery.camelCase( key ); + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + // Attempt to get data from the cache + // with the key as-is + data = data_user.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to get data from the cache + // with the key camelized + data = data_user.get( elem, camelKey ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, camelKey, undefined ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each(function() { + // First, attempt to store a copy or reference of any + // data that might've been store with a camelCased key. + var data = data_user.get( this, camelKey ); + + // For HTML5 data-* attribute interop, we have to + // store property names with dashes in a camelCase form. + // This might not apply to all properties...* + data_user.set( this, camelKey, value ); + + // *... In the case of properties that might _actually_ + // have dashes, we need to also store a copy of that + // unchanged property. + if ( key.indexOf("-") !== -1 && data !== undefined ) { + data_user.set( this, key, value ); + } + }); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each(function() { + data_user.remove( this, key ); + }); + } +}); + + +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = data_priv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = data_priv.access( 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 data_priv.get( elem, key ) || data_priv.access( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + data_priv.remove( elem, [ type + "queue", 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 = data_priv.get( 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 ); + }; + +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // #11217 - WebKit loses check when the name is after the checked attribute + // Support: Windows Web Apps (WWA) + // `name` and `type` need .setAttribute for WWA + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // 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; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE9-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +})(); +var strundefined = typeof undefined; + + + +support.focusinBubbles = "onfocusin" in window; + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|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 handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.get( 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 && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } - // Keep pipe for back-compat - promise.pipe = promise.then; + // 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(); - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + // 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 if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + } + } } - // 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; - }); + if ( special.add ) { + special.add.call( elem, handleObj ); - // Make the deferred a promise - promise.promise( deferred ); + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); + // 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; } - // All done! - return deferred; }, - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = core_slice.call( arguments ), - length = resolveValues.length, - - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.hasData( elem ) && data_priv.get( elem ); - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; - if( values === progressValues ) { - deferred.notifyWith( contexts, values ); - } else if ( !( --remaining ) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, + if ( !elemData || !(events = elemData.events) ) { + return; + } - progressValues, progressContexts, resolveContexts; + // 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(); - // 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; + // 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; } - } - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); - return deferred.promise(); - } -}); -jQuery.support = (function( support ) { - var input = document.createElement("input"), - fragment = document.createDocumentFragment(), - div = document.createElement("div"), - select = document.createElement("select"), - opt = select.appendChild( document.createElement("option") ); + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; - // Finish early in limited environments - if ( !input.type ) { - return support; - } + 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 ); - input.type = "checkbox"; + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere) - support.checkOn = input.value !== ""; + // 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 ); + } - // Must access the parent to make an option select properly - // Support: IE9, IE10 - support.optSelected = opt.selected; + delete events[ type ]; + } + } - // Will be defined later - support.reliableMarginRight = true; - support.boxSizingReliable = true; - support.pixelPosition = false; + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + data_priv.remove( elem, "events" ); + } + }, - // Make sure checked status is properly cloned - // Support: IE9, IE10 - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; + trigger: function( event, data, elem, onlyHandlers ) { - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; + var i, cur, tmp, bubbleType, ontype, handle, special, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - // Check if an input maintains its value after becoming a radio - // Support: IE9, IE10 - input = document.createElement("input"); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; + cur = tmp = elem = elem || document; - // #11217 - WebKit loses check when the name is after the checked attribute - input.setAttribute( "checked", "t" ); - input.setAttribute( "name", "t" ); + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } - fragment.appendChild( input ); + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } - // Support: Safari 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + 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; - // Support: Firefox, Chrome, Safari - // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) - support.focusinBubbles = "onfocusin" in window; + // 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 ); - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; + // 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; + } - // Run tests that need a body at doc ready - jQuery(function() { - var container, marginDiv, - // Support: Firefox, Android 2.3 (Prefixed box-sizing versions). - divReset = "padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box", - body = document.getElementsByTagName("body")[ 0 ]; + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); - if ( !body ) { - // Return for frameset docs that don't have a body + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } - container = document.createElement("div"); - container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + // 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 ) ) { - // Check box-sizing and margin behavior. - body.appendChild( container ).appendChild( div ); - div.innerHTML = ""; - // Support: Firefox, Android 2.3 (Prefixed box-sizing versions). - div.style.cssText = "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%"; + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } - // Workaround failing boxSizing test due to offsetWidth returning wrong value - // with some non-1 values of body zoom, ticket #13543 - jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { - support.boxSizing = div.offsetWidth === 4; - }); + // 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 ); + } + } - // Use window.getComputedStyle because jsdom on node.js will break without it. - if ( window.getComputedStyle ) { - support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; - support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { - // Support: Android 2.3 - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. (#3333) - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - marginDiv = div.appendChild( document.createElement("div") ); - marginDiv.style.cssText = div.style.cssText = divReset; - marginDiv.style.marginRight = marginDiv.style.width = "0"; - div.style.width = "1px"; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } - support.reliableMarginRight = - !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + // 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; - body.removeChild( container ); - }); + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { - return support; -})( {} ); + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { -/* - Implementation Summary + // Call a native DOM method on the target with the same name name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { - 1. Enforce API surface and semantic compatibility with 1.9.x branch - 2. Improve the module's maintainability by reducing the storage - paths to a single mechanism. - 3. Use the same single mechanism to support "private" and "user" data. - 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) - 5. Avoid exposing implementation details on user objects (eg. expando properties) - 6. Provide a clear path for implementation upgrade to WeakMap in 2014 -*/ -var data_user, data_priv, - rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, - rmultiDash = /([A-Z])/g; + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; -function Data() { - // Support: Android < 4, - // Old WebKit does not have Object.preventExtensions/freeze method, - // return new empty object instead with no [[set]] accessor - Object.defineProperty( this.cache = {}, 0, { - get: function() { - return {}; - } - }); + if ( tmp ) { + elem[ ontype ] = null; + } - this.expando = jQuery.expando + Math.random(); -} + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; -Data.uid = 1; + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } -Data.accepts = function( owner ) { - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - return owner.nodeType ? - owner.nodeType === 1 || owner.nodeType === 9 : true; -}; + return event.result; + }, -Data.prototype = { - key: function( owner ) { - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return the key for a frozen object. - if ( !Data.accepts( owner ) ) { - return 0; - } + dispatch: function( event ) { - var descriptor = {}, - // Check if the owner object already has a cache key - unlock = owner[ this.expando ]; + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); - // If not, create one - if ( !unlock ) { - unlock = Data.uid++; + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; - // Secure it in a non-enumerable, non-writable property - try { - descriptor[ this.expando ] = { value: unlock }; - Object.defineProperties( owner, descriptor ); + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; - // Support: Android < 4 - // Fallback to a less secure definition - } catch ( e ) { - descriptor[ this.expando ] = unlock; - jQuery.extend( owner, descriptor ); - } + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; } - // Ensure the cache object - if ( !this.cache[ unlock ] ) { - this.cache[ unlock ] = {}; - } + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - return unlock; - }, - set: function( owner, data, value ) { - var prop, - // There may be an unlock assigned to this node, - // if there is no entry for this "owner", create one inline - // and set the unlock as though an owner entry had always existed - unlock = this.key( owner ), - cache = this.cache[ unlock ]; + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; - // Handle: [ owner, key, value ] args - if ( typeof data === "string" ) { - cache[ data ] = value; + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { - // Handle: [ owner, { properties } ] args - } else { - // Fresh assignments by object are shallow copied - if ( jQuery.isEmptyObject( cache ) ) { - jQuery.extend( this.cache[ unlock ], data ); - // Otherwise, copy the properties one-by-one to the cache object - } else { - for ( prop in data ) { - cache[ prop ] = data[ prop ]; - } - } - } - return cache; - }, - get: function( owner, key ) { - // Either a valid cache is found, or will be created. - // New caches will be created and the unlock returned, - // allowing direct access to the newly created - // empty data object. A valid owner object must be provided. - var cache = this.cache[ this.key( owner ) ]; + // 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 ) ) { - return key === undefined ? - cache : cache[ key ]; - }, - access: function( owner, key, value ) { - var stored; - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ((key && typeof key === "string") && value === undefined) ) { + event.handleObj = handleObj; + event.data = handleObj.data; - stored = this.get( owner, key ); + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); - return stored !== undefined ? - stored : this.get( owner, jQuery.camelCase(key) ); + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } } - // [*]When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; + return event.result; }, - remove: function( owner, key ) { - var i, name, camel, - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - if ( key === undefined ) { - this.cache[ unlock ] = {}; + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; - } else { - // Support array or space separated string of keys - if ( jQuery.isArray( key ) ) { - // 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 = key.concat( key.map( jQuery.camelCase ) ); - } else { - camel = jQuery.camelCase( key ); - // Try the string as a key before any manipulation - if ( key in cache ) { - name = [ key, camel ]; - } else { - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - name = camel; - name = name in cache ? - [ name ] : ( name.match( core_rnotwhite ) || [] ); + // 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") ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( 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 }); + } } } + } - i = name.length; - while ( i-- ) { - delete cache[ name[ i ] ]; - } + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); } + + return handlerQueue; }, - hasData: function( owner ) { - return !jQuery.isEmptyObject( - this.cache[ owner[ this.expando ] ] || {} - ); - }, - discard: function( owner ) { - if ( owner[ this.expando ] ) { - delete this.cache[ owner[ this.expando ] ]; - } - } -}; -// These may be used throughout the jQuery core codebase -data_user = new Data(); -data_priv = new Data(); + // 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: {}, -jQuery.extend({ - acceptData: Data.accepts, + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { - hasData: function( elem ) { - return data_user.hasData( elem ) || data_priv.hasData( elem ); - }, + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } - data: function( elem, name, data ) { - return data_user.access( elem, name, data ); + return event; + } }, - removeData: function( elem, name ) { - data_user.remove( elem, name ); - }, + mouseHooks: { + props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button; - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to data_priv methods, these can be deprecated. - _data: function( elem, name, data ) { - return data_priv.access( elem, name, data ); + // 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 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; + } }, - _removeData: function( elem, name ) { - data_priv.remove( elem, name ); - } -}); + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } -jQuery.fn.extend({ - data: function( key, value ) { - var attrs, name, - elem = this[ 0 ], - i = 0, - data = null; + // 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 ]; - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = data_user.get( elem ); + 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; - if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { - attrs = elem.attributes; - for ( ; i < attrs.length; i++ ) { - name = attrs[ i ].name; + event = new jQuery.Event( originalEvent ); - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice(5) ); - dataAttr( elem, name, data[ name ] ); - } - } - data_priv.set( elem, "hasDataAttrs", true ); - } - } + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } - return data; + // Support: Cordova 2.5 (WebKit) (#13255) + // All events should have a target; Cordova deviceready doesn't + if ( !event.target ) { + event.target = document; } - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - data_user.set( this, key ); - }); + // Support: Safari 6.0+, Chrome < 28 + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; } - return jQuery.access( this, function( value ) { - var data, - camelKey = jQuery.camelCase( key ); + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - // Attempt to get data from the cache - // with the key as-is - data = data_user.get( elem, key ); - if ( data !== undefined ) { - return data; + 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 ) { + this.focus(); + return false; } - - // Attempt to get data from the cache - // with the key camelized - data = data_user.get( elem, camelKey ); - if ( data !== undefined ) { - return data; + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, camelKey, undefined ); - if ( data !== undefined ) { - return data; + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { + this.click(); + return false; } + }, - // We tried really hard, but the data doesn't exist. - return; + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); } + }, - // Set the data... - this.each(function() { - // First, attempt to store a copy or reference of any - // data that might've been store with a camelCased key. - var data = data_user.get( this, camelKey ); - - // For HTML5 data-* attribute interop, we have to - // store property names with dashes in a camelCase form. - // This might not apply to all properties...* - data_user.set( this, camelKey, value ); + beforeunload: { + postDispatch: function( event ) { - // *... In the case of properties that might _actually_ - // have dashes, we need to also store a copy of that - // unchanged property. - if ( key.indexOf("-") !== -1 && data !== undefined ) { - data_user.set( this, key, value ); + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; } - }); - }, null, value, arguments.length > 1, null, true ); + } + } }, - removeData: function( key ) { - return this.each(function() { - data_user.remove( this, key ); - }); + 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 = function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); } -}); +}; -function dataAttr( elem, key, data ) { - var name; +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - data = elem.getAttribute( name ); + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; - 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 ) ? JSON.parse( data ) : - data; - } catch( e ) {} + // 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: Android < 4.0 + src.returnValue === false ? + returnTrue : + returnFalse; - // Make sure we set the data so it isn't changed later - data_user.set( elem, key, data ); - } else { - data = undefined; - } + // Event type + } else { + this.type = src; } - return data; -} -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = data_priv.get( elem, type ); - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray( data ) ) { - queue = data_priv.access( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } - dequeue: function( elem, type ) { - type = type || "fx"; + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; + // Mark it as fixed + this[ jQuery.expando ] = true; +}; - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } +// 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, - if ( fn ) { + preventDefault: function() { + var e = this.originalEvent; - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } + this.isDefaultPrevented = returnTrue; - // clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); + if ( e && e.preventDefault ) { + e.preventDefault(); } + }, + stopPropagation: function() { + var e = this.originalEvent; - if ( !startLength && hooks ) { - hooks.empty.fire(); + this.isPropagationStopped = returnTrue; + + if ( e && e.stopPropagation ) { + e.stopPropagation(); } }, + stopImmediatePropagation: function() { + var e = this.originalEvent; - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return data_priv.get( elem, key ) || data_priv.access( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - data_priv.remove( elem, [ type + "queue", key ] ); - }) - }); + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); } -}); +}; -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; +// Create mouseenter/leave events using mouseover/out and event-time checks +// Support: Chrome 15+ +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); + // 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; } + }; +}); - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); +// Create "bubbling" focus and blur events +// Support: Firefox, Chrome, Safari +if ( !support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - // ensure a hooks for this queue - jQuery._queueHooks( this, type ); + // 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 ); + }; - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; + data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ) - 1; - return this.queue( type, function( next, hooks ) { - var timeout = setTimeout( next, time ); - hooks.stop = function() { - clearTimeout( timeout ); - }; - }); - }, - 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 ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + data_priv.remove( doc, fix ); + + } else { + data_priv.access( doc, fix, attaches ); } - }; + } + }; + }); +} - if ( typeof type !== "string" ) { - obj = type; - type = undefined; +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // 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; } - type = type || "fx"; - while( i-- ) { - tmp = data_priv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); + 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; } } - resolve(); - return defer.promise( obj ); - } -}); -var nodeHook, boolHook, - rclass = /[\t\r\n\f]/g, - rreturn = /\r/g, - rfocusable = /^(?:input|select|textarea|button)$/i; + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + 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 ); + }); }, - - removeAttr: function( name ) { + 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.removeAttr( this, name ); + jQuery.event.remove( this, types, fn, selector ); }); }, - prop: function( name, value ) { - return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { + trigger: function( type, data ) { return this.each(function() { - delete this[ jQuery.propFix[ name ] || name ]; + jQuery.event.trigger( type, data, this ); }); }, - - addClass: function( value ) { - var classes, elem, cur, clazz, j, - i = 0, - len = this.length, - proceed = typeof value === "string" && value; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).addClass( value.call( this, j, this.className ) ); - }); - } - - if ( proceed ) { - // The disjunction here is for better compressibility (see removeClass) - classes = ( value || "" ).match( core_rnotwhite ) || []; - - for ( ; i < len; i++ ) { - elem = this[ i ]; - cur = elem.nodeType === 1 && ( elem.className ? - ( " " + elem.className + " " ).replace( rclass, " " ) : - " " - ); - - if ( cur ) { - j = 0; - while ( (clazz = classes[j++]) ) { - if ( cur.indexOf( " " + clazz + " " ) < 0 ) { - cur += clazz + " "; - } - } - elem.className = jQuery.trim( cur ); - - } - } + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); } + } +}); - return this; - }, - removeClass: function( value ) { - var classes, elem, cur, clazz, j, - i = 0, - len = this.length, - proceed = arguments.length === 0 || typeof value === "string" && value; +var + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style|link)/i, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /^$|\/(?:java|ecma)script/i, + rscriptTypeMasked = /^true\/(.*)/, + rcleanScript = /^\s*\s*$/g, - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).removeClass( value.call( this, j, this.className ) ); - }); - } - if ( proceed ) { - classes = ( value || "" ).match( core_rnotwhite ) || []; + // We have to close these tags to support XHTML (#13200) + wrapMap = { - for ( ; i < len; i++ ) { - elem = this[ i ]; - // This expression is here for better compressibility (see addClass) - cur = elem.nodeType === 1 && ( elem.className ? - ( " " + elem.className + " " ).replace( rclass, " " ) : - "" - ); + // Support: IE 9 + option: [ 1, "" ], - if ( cur ) { - j = 0; - while ( (clazz = classes[j++]) ) { - // Remove *all* instances - while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { - cur = cur.replace( " " + clazz + " ", " " ); - } - } - elem.className = value ? jQuery.trim( cur ) : ""; - } - } - } + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], - return this; - }, + _default: [ 0, "", "" ] + }; - toggleClass: function( value, stateVal ) { - var type = typeof value; +// Support: IE 9 +wrapMap.optgroup = wrapMap.option; - if ( typeof stateVal === "boolean" && type === "string" ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; - if ( jQuery.isFunction( value ) ) { - return this.each(function( i ) { - jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); - }); - } +// Support: 1.x compatibility +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = jQuery( this ), - classNames = value.match( core_rnotwhite ) || []; + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} - while ( (className = classNames[ i++ ]) ) { - // check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); - // Toggle whole class name - } else if ( type === core_strundefined || type === "boolean" ) { - if ( this.className ) { - // store className if set - data_priv.set( this, "__className__", this.className ); - } + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute("type"); + } - // If the element has a class name or if we're passed "false", - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || ""; - } - }); - }, + return elem; +} - hasClass: function( selector ) { - var className = " " + selector + " ", - i = 0, - l = this.length; - for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { - return true; - } - } +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; - return false; - }, + for ( ; i < l; i++ ) { + data_priv.set( + elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) + ); + } +} - val: function( value ) { - var hooks, ret, isFunction, - elem = this[0]; +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + if ( dest.nodeType !== 1 ) { + return; + } - if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { - return ret; - } + // 1. Copy private data: events, handlers, etc. + if ( data_priv.hasData( src ) ) { + pdataOld = data_priv.access( src ); + pdataCur = data_priv.set( dest, pdataOld ); + events = pdataOld.events; - ret = elem.value; + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } } - - return; } + } - isFunction = jQuery.isFunction( value ); + // 2. Copy user data + if ( data_user.hasData( src ) ) { + udataOld = data_user.access( src ); + udataCur = jQuery.extend( {}, udataOld ); - return this.each(function( i ) { - var val; + data_user.set( dest, udataCur ); + } +} - if ( this.nodeType !== 1 ) { - return; - } +function getAll( context, tag ) { + var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : + context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : + []; - if ( isFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( jQuery.isArray( val ) ) { - val = jQuery.map(val, function ( value ) { - return value == null ? "" : value + ""; - }); - } +// Support: IE >= 9 +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; - // If set returns undefined, fall back to normal setting - if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - }); + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; } -}); +} jQuery.extend({ - valHooks: { - option: { - get: function( elem ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, option, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one" || index < 0, - values = one ? null : [], - max = one ? index + 1 : options.length, - i = index < 0 ? - max : - one ? index : 0; - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // IE6-9 doesn't update selected after form reset (#2551) - if ( ( option.selected || i === index ) && - // Don't return options that are disabled or in a disabled optgroup - ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && - ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); - // Multi-Selects return an array - values.push( value ); - } - } + // Support: IE >= 9 + // Fix Cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { - return values; - }, + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } - while ( i-- ) { - option = options[ i ]; - if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { - optionSet = true; - } - } + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); - // force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); } - return values; + } else { + cloneCopyEvent( elem, clone ); } } - }, - - attr: function( elem, name, value ) { - var hooks, ret, - nType = elem.nodeType; - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === core_strundefined ) { - return jQuery.prop( elem, name, value ); - } + // Return the cloned set + return clone; + }, - // All attributes are lowercase - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - name = name.toLowerCase(); - hooks = jQuery.attrHooks[ name ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); - } + buildFragment: function( elems, context, scripts, selection ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; - if ( value !== undefined ) { + for ( ; i < l; i++ ) { + elem = elems[ i ]; - if ( value === null ) { - jQuery.removeAttr( elem, name ); + if ( elem || elem === 0 ) { - } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - } else { - elem.setAttribute( name, value + "" ); - return value; - } + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); - } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement("div") ); - } else { - ret = jQuery.find.attr( elem, name ); + // 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 ]; - // Non-existent attributes return null, we normalize to undefined - return ret == null ? - undefined : - ret; - } - }, + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } - removeAttr: function( elem, value ) { - var name, propName, - i = 0, - attrNames = value && value.match( core_rnotwhite ); + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( nodes, tmp.childNodes ); - if ( attrNames && elem.nodeType === 1 ) { - while ( (name = attrNames[i++]) ) { - propName = jQuery.propFix[ name ] || name; + // Remember the top-level container + tmp = fragment.firstChild; - // Boolean attributes get special treatment (#10870) - if ( jQuery.expr.match.bool.test( name ) ) { - // Set corresponding property to false - elem[ propName ] = false; + // Fixes #12346 + // Support: Webkit, IE + tmp.textContent = ""; } - - elem.removeAttribute( name ); } } - }, - attrHooks: { - type: { - set: function( elem, value ) { - if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to default in case type is set after value during creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; + // Remove wrapper from fragment + fragment.textContent = ""; + + 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( fragment.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 ); } - return value; } } } - }, - propFix: { - "for": "htmlFor", - "class": "className" + return fragment; }, - prop: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } + cleanData: function( elems ) { + var data, elem, type, key, + special = jQuery.event.special, + i = 0; - if ( value !== undefined ) { - return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? - ret : - ( elem[ name ] = value ); + for ( ; (elem = elems[ i ]) !== undefined; i++ ) { + if ( jQuery.acceptData( elem ) ) { + key = elem[ data_priv.expando ]; - } else { - return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? - ret : - elem[ name ]; - } - }, + if ( key && (data = data_priv.cache[ key ]) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); - propHooks: { - tabIndex: { - get: function( elem ) { - return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ? - elem.tabIndex : - -1; + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + if ( data_priv.cache[ key ] ) { + // Discard any remaining `private` data + delete data_priv.cache[ key ]; + } + } } + // Discard any remaining `user` data + delete data_user.cache[ elem[ data_user.expando ] ]; } } }); -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { - var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; - - jQuery.expr.attrHandle[ name ] = function( elem, name, isXML ) { - var fn = jQuery.expr.attrHandle[ name ], - ret = isXML ? - undefined : - /* jshint eqeqeq: false */ - // Temporarily disable this handler to check existence - (jQuery.expr.attrHandle[ name ] = undefined) != - getter( elem, name, isXML ) ? - - name.toLowerCase() : - null; - - // Restore handler - jQuery.expr.attrHandle[ name ] = fn; - - return ret; - }; -}); +jQuery.fn.extend({ + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each(function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + }); + }, null, value, arguments.length ); + }, -// Support: IE9+ -// Selectedness for an option in an optgroup can be inaccurate -if ( !jQuery.support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; + 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 ); } - return null; - } - }; -} + }); + }, -jQuery.each([ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -}); + 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 ); + } + }); + }, -// Radios and checkboxes getter/setter -jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( jQuery.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); } - } - }; - if ( !jQuery.support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - // Support: Webkit - // "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - }; - } -}); -var rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + }); + }, -function returnTrue() { - return true; -} + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, -function returnFalse() { - return false; -} + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} + for ( ; (elem = elems[i]) != null; i++ ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } - global: {}, + return this; + }, - add: function( elem, types, handler, data, selector ) { + empty: function() { + var elem, + i = 0; - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.get( elem ); + for ( ; (elem = this[i]) != null; i++ ) { + if ( elem.nodeType === 1 ) { - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); - // 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; + // Remove any remaining nodes + elem.textContent = ""; + } } - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } + return this; + }, - // 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 !== core_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; - } + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - // Handle multiple events separated by a space - types = ( types || "" ).match( core_rnotwhite ) || [""]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); + return this.map(function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; } - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; + value = value.replace( rxhtmlTag, "<$1>" ); - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; - // 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 ); + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } - // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; + elem = 0; - // Only use addEventListener if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - } - } + // If using innerHTML throws an exception, use the fallback method + } catch( e ) {} } - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } + if ( elem ) { + this.empty().append( value ); } + }, null, value, arguments.length ); + }, - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); + 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 ); } + }); - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, - // Nullify elem to prevent memory leaks in IE - elem = null; + detach: function( selector ) { + return this.remove( selector, true ); }, - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { + domManip: function( args, callback ) { - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.hasData( elem ) && data_priv.get( elem ); + // Flatten any nested arrays + args = concat.apply( [], args ); - if ( !elemData || !(events = elemData.events) ) { - return; + var fragment, first, scripts, hasScripts, node, doc, + 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 ); + }); } - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( core_rnotwhite ) || [""]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; - // 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; + if ( fragment.childNodes.length === 1 ) { + fragment = first; } - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; + // 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 ( ( 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 ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( scripts, getAll( node, "script" ) ); + } } - } - } - // 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 ); + callback.call( this[ i ], node, i ); } - delete events[ type ]; + 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 || "" ) && + !data_priv.access( 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.textContent.replace( rcleanScript, "" ) ); + } + } + } + } } } - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - data_priv.remove( elem, "events" ); + return this; + } +}); + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: QtWebKit + // .get() because push.apply(_, arraylike) throws + push.apply( ret, elems.get() ); } - }, - trigger: function( event, data, elem, onlyHandlers ) { + return this.pushStack( ret ); + }; +}); - var i, cur, tmp, bubbleType, ontype, handle, special, - eventPath = [ elem || document ], - type = core_hasOwn.call( event, "type" ) ? event.type : event, - namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - cur = tmp = elem = elem || document; +var iframe, + elemdisplay = {}; - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } +/** + * 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 style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? - 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; + // 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 + style.display : jQuery.css( elem[ 0 ], "display" ); - // 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 ); + // 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(); - // 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; + return display; +} - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); + if ( !display ) { + display = actualDisplay( nodeName, doc ); - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { - // 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 ) ) { + // Use the already-created iframe if possible + iframe = (iframe || jQuery( "