text:
package org.jsoup.nodes;

import org.jsoup.helper.ChangeNotifyingArrayList;
import org.jsoup.helper.Validate;
import org.jsoup.internal.StringUtil;
import org.jsoup.parser.ParseSettings;
import org.jsoup.parser.Tag;
import org.jsoup.select.Collector;
import org.jsoup.select.Elements;
import org.jsoup.select.Evaluator;
import org.jsoup.select.NodeFilter;
import org.jsoup.select.NodeTraversor;
import org.jsoup.select.NodeVisitor;
import org.jsoup.select.QueryParser;
import org.jsoup.select.Selector;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import static org.jsoup.internal.Normalizer.normalize;

/**
 * A HTML element consists of a tag name, attributes, and child nodes (including text nodes and
 * other elements).
 *
 * From an Element, you can extract data, traverse the node graph, and manipulate the HTML.
 *
 * @author Jonathan Hedley, jonathan@hedley.net
 */
public class Element extends Node {
    private static final List<Node> EMPTY_NODES = Collections.emptyList();
    private static final Pattern classSplit = Pattern.compile("\\s+");
    private static final String baseUriKey = Attributes.internalKey("baseUri");
    private Tag tag;
    private WeakReference<List<Element>> shadowChildrenRef; // points to child elements shadowed from node children
    List<Node> childNodes;
    private Attributes attributes;

    /**
     * Create a new, standalone element.
     * @param tag tag name
     */
    public Element(String tag) {
        this(Tag.valueOf(tag), "", null);
    }

    /**
     * Create a new, standalone Element. (Standalone in that is has no parent.)
     *
     * @param tag tag of this element
     * @param baseUri the base URI (optional, may be null to inherit from parent, or "" to clear parent's)
     * @param attributes initial attributes (optional, may be null)
     * @see #appendChild(Node)
     * @see #appendElement(String)
     */
    public Element(Tag tag, String baseUri, Attributes attributes) {
        Validate.notNull(tag);
        childNodes = EMPTY_NODES;
        this.attributes = attributes;
        this.tag = tag;
        if (baseUri != null)
            this.setBaseUri(baseUri);
    }

    /**
     * Create a new Element from a Tag and a base URI.
     *
     * @param tag element tag
     * @param baseUri the base URI of this element. Optional, and will inherit from its parent, if any.
     * @see Tag#valueOf(String, ParseSettings)
     */
    public Element(Tag tag, String baseUri) {
        this(tag, baseUri, null);
    }

    protected List<Node> ensureChildNodes() {
        if (childNodes == EMPTY_NODES) {
            childNodes = new NodeList(this, 4);
        }
        return childNodes;
    }

    @Override
    protected boolean hasAttributes() {
        return attributes != null;
    }

    @Override
    public Attributes attributes() {
        if (!hasAttributes())
            attributes = new Attributes();
        return attributes;
    }

    @Override
    public String baseUri() {
        return searchUpForAttribute(this, baseUriKey);
    }

    private static String searchUpForAttribute(final Element start, final String key) {
        Element el = start;
        while (el != null) {
            if (el.hasAttributes() && el.attributes.hasKey(key))
                return el.attributes.get(key);
            el = el.parent();
        }
        return "";
    }

    @Override
    protected void doSetBaseUri(String baseUri) {
        attributes().put(baseUriKey, baseUri);
    }

    @Override
    public int childNodeSize() {
        return childNodes.size();
    }

    @Override
    public String nodeName() {
        return tag.getName();
    }

    /**
     * Get the name of the tag for this element. E.g. {@code div}. If you are using {@link ParseSettings#preserveCase
     * case preserving parsing}, this will return the source's original case.
     *
     * @return the tag name
     */
    public String tagName() {
        return tag.getName();
    }

    /**
     * Get the normalized name of this Element's tag. This will always be the lowercased version of the tag, regardless
     * of the tag case preserving setting of the parser. For e.g., {@code
} and {@code
} both have a
     * normal name of {@code div}.
     * @return normal name
     */
    public String normalName() {
        return tag.normalName();
    }

    /**
     * Change the tag of this element. For example, convert a {@code } to a {@code
} with
     * {@code el.tagName("div");}.
     *
     * @param tagName new tag name for this element
     * @return this element, for chaining
     */
    public Element tagName(String tagName) {
        Validate.notEmpty(tagName, "Tag name must not be empty.");
        tag = Tag.valueOf(tagName, NodeUtils.parser(this).settings()); // maintains the case option of the original parse
        return this;
    }

    /**
     * Get the Tag for this element.
     *
     * @return the tag object
     */
    public Tag tag() {
        return tag;
    }

    /**
     * Test if this element is a block-level element. (E.g. {@code
 == true} or an inline element
     * {@code
 == false}).
     *
     * @return true if block, false if not (and thus inline)
     */
    public boolean isBlock() {
        return tag.isBlock();
    }

    /**
     * Get the {@code id} attribute of this element.
     *
     * @return The id attribute, if present, or an empty string if not.
     */
    public String id() {
        return hasAttributes() ? attributes.getIgnoreCase("id") :"";
    }

    /**
     * Set an attribute value on this element. If this element already has an attribute with the
     * key, its value is updated; otherwise, a new attribute is added.
     *
     * @return this element
     */
    public Element attr(String attributeKey, String attributeValue) {
        super.attr(attributeKey, attributeValue);
        return this;
    }

    /**
     * Set a boolean attribute value on this element. Setting to true sets the attribute value to "" and
     * marks the attribute as boolean so no value is written out. Setting to false removes the attribute
     * with the same key if it exists.
     *
     * @param attributeKey the attribute key
     * @param attributeValue the attribute value
     *
     * @return this element
     */
    public Element attr(String attributeKey, boolean attributeValue) {
        attributes().put(attributeKey, attributeValue);
        return this;
    }

    /**
     * Get this element's HTML5 custom data attributes. Each attribute in the element that has a key
     * starting with "data-" is included the dataset.
     *

     * E.g., the element {@code
...} has the dataset
     * {@code package=jsoup, language=java}.
     *

     * This map is a filtered view of the element's attribute map. Changes to one map (add, remove, update) are reflected
     * in the other map.
     *

     * You can find elements that have data attributes using the {@code [^data-]} attribute key prefix selector.
     * @return a map of {@code key=value} custom data attributes.
     */
    public Map<String, String> dataset() {
        return attributes().dataset();
    }

    @Override
    public final Element parent() {
        return (Element) parentNode;
    }

    /**
     * Get this element's parent and ancestors, up to the document root.
     * @return this element's stack of parents, closest first.
     */
    public Elements parents() {
        Elements parents = new Elements();
        accumulateParents(this, parents);
        return parents;
    }

    private static void accumulateParents(Element el, Elements parents) {
        Element parent = el.parent();
        if (parent != null && !parent.tagName().equals("#root")) {
            parents.add(parent);
            accumulateParents(parent, parents);
        }
    }

    /**
     * Get a child element of this element, by its 0-based index number.
     *

     * Note that an element can have both mixed Nodes and Elements as children. This method inspects
     * a filtered list of children that are elements, and the index is based on that filtered list.
     *

     *
     * @param index the index number of the element to retrieve
     * @return the child element, if it exists, otherwise throws an {@code IndexOutOfBoundsException}
     * @see #childNode(int)
     */
    public Element child(int index) {
        return childElementsList().get(index);
    }

    /**
     * Get the number of child nodes of this element that are elements.
     *

     * This method works on the same filtered list like {@link #child(int)}. Use {@link #childNodes()} and {@link
     * #childNodeSize()} to get the unfiltered Nodes (e.g. includes TextNodes etc.)
     *

     *
     * @return the number of child nodes that are elements
     * @see #children()
     * @see #child(int)
     */
    public int childrenSize() {
        return childElementsList().size();
    }

    /**
     * Get this element's child elements.
     *

     * This is effectively a filter on {@link #childNodes()} to get Element nodes.
     *

     * @return child elements. If this element has no children, returns an empty list.
     * @see #childNodes()
     */
    public Elements children() {
        return new Elements(childElementsList());
    }

    /**
     * Maintains a shadow copy of this element's child elements. If the nodelist is changed, this cache is invalidated.
     * TODO - think about pulling this out as a helper as there are other shadow lists (like in Attributes) kept around.
     * @return a list of child elements
     */
    private List<Element> childElementsList() {
        List<Element> children;
        if (shadowChildrenRef == null || (children = shadowChildrenRef.get()) == null) {
            final int size = childNodes.size();
            children = new ArrayList<>(size);
            //noinspection ForLoopReplaceableByForEach (beacause it allocates an Iterator which is wasteful here)
            for (int i = 0; i < size; i++) {
                final Node node = childNodes.get(i);
                if (node instanceof Element)
                    children.add((Element) node);
            }
            shadowChildrenRef = new WeakReference<>(children);
        }
        return children;
    }

    /**
     * Clears the cached shadow child elements.
     */
    @Override
    void nodelistChanged() {
        super.nodelistChanged();
        shadowChildrenRef = null;
    }

    /**
     * Get this element's child text nodes. The list is unmodifiable but the text nodes may be manipulated.
     *

     * This is effectively a filter on {@link #childNodes()} to get Text nodes.
     * @return child text nodes. If this element has no text nodes, returns an
     * empty list.
     *

     * For example, with the input HTML: {@code
One Two Three
 Four
} with the {@code p} element selected:
     *

     *
{@code p.text()} = {@code "One Two Three Four"}

     *
{@code p.ownText()} = {@code "One Three Four"}

     *
{@code p.children()} = {@code Elements[,
]}

     *
{@code p.childNodes()} = {@code List<Node>["One ", , " Three ",
, " Four"]}

     *
{@code p.textNodes()} = {@code List<TextNode>["One ", " Three ", " Four"]}

     *

     */
    public List<TextNode> textNodes() {
        List<TextNode> textNodes = new ArrayList<>();
        for (Node node : childNodes) {
            if (node instanceof TextNode)
                textNodes.add((TextNode) node);
        }
        return Collections.unmodifiableList(textNodes);
    }

    /**
     * Get this element's child data nodes. The list is unmodifiable but the data nodes may be manipulated.
     *

     * This is effectively a filter on {@link #childNodes()} to get Data nodes.
     *

     * @return child data nodes. If this element has no data nodes, returns an
     * empty list.
     * @see #data()
     */
    public List<DataNode> dataNodes() {
        List<DataNode> dataNodes = new ArrayList<>();
        for (Node node : childNodes) {
            if (node instanceof DataNode)
                dataNodes.add((DataNode) node);
        }
        return Collections.unmodifiableList(dataNodes);
    }

    /**
     * Find elements that match the {@link Selector} CSS query, with this element as the starting context. Matched elements
     * may include this element, or any of its children.
     *
This method is generally more powerful to use than the DOM-type {@code getElementBy*} methods, because
     * multiple filters can be combined, e.g.:

     *

     *
{@code el.select("a[href]")} - finds links ({@code a} tags with {@code href} attributes)
     *
{@code el.select("a[href*=example.com]")} - finds links pointing to example.com (loosely)
     *

     *
See the query syntax documentation in {@link org.jsoup.select.Selector}.

     *
Also known as {@code querySelectorAll()} in the Web DOM.

     *
     * @param cssQuery a {@link Selector} CSS-like query
     * @return an {@link Elements} list containing elements that match the query (empty if none match)
     * @see Selector selector query syntax
     * @see QueryParser#parse(String)
     * @throws Selector.SelectorParseException (unchecked) on an invalid CSS query.
     */
    public Elements select(String cssQuery) {
        return Selector.select(cssQuery, this);
    }

    /**
     * Find elements that match the supplied Evaluator. This has the same functionality as {@link #select(String)}, but
     * may be useful if you are running the same query many times (on many documents) and want to save the overhead of
     * repeatedly parsing the CSS query.
     * @param evaluator an element evaluator
     * @return an {@link Elements} list containing elements that match the query (empty if none match)
     */
    public Elements select(Evaluator evaluator) {
        return Selector.select(evaluator, this);
    }

    /**
     * Find the first Element that matches the {@link Selector} CSS query, with this element as the starting context.
     *
This is effectively the same as calling {@code element.select(query).first()}, but is more efficient as query
     * execution stops on the first hit.

     *
Also known as {@code querySelector()} in the Web DOM.

     * @param cssQuery cssQuery a {@link Selector} CSS-like query
     * @return the first matching element, or {@code null} if there is no match.
     */
    public Element selectFirst(String cssQuery) {
        return Selector.selectFirst(cssQuery, this);
    }

    /**
     * Finds the first Element that matches the supplied Evaluator, with this element as the starting context, or
     * {@code null} if none match.
     *
     * @param evaluator an element evaluator
     * @return the first matching element (walking down the tree, starting from this element), or {@code null} if none
     *     matchn.
     */
    public Element selectFirst(Evaluator evaluator) {
        return Collector.findFirst(evaluator, this);
    }

    /**
     * Checks if this element matches the given {@link Selector} CSS query. Also knows as {@code matches()} in the Web
     * DOM.
     *
     * @param cssQuery a {@link Selector} CSS query
     * @return if this element matches the query
     */
    public boolean is(String cssQuery) {
        return is(QueryParser.parse(cssQuery));
    }

    /**
     * Check if this element matches the given evaluator.
     * @param evaluator an element evaluator
     * @return if this element matches
     */
    public boolean is(Evaluator evaluator) {
        return evaluator.matches(this.root(), this);
    }

    /**
     * Find the closest element up the tree of parents that matches the specified CSS query. Will return itself, an
     * ancestor, or {@code null} if there is no such matching element.
     * @param cssQuery a {@link Selector} CSS query
     * @return the closest ancestor element (possibly itself) that matches the provided evaluator. {@code null} if not
     * found.
     */
    public Element closest(String cssQuery) {
        return closest(QueryParser.parse(cssQuery));
    }

    /**
     * Find the closest element up the tree of parents that matches the specified evaluator. Will return itself, an
     * ancestor, or {@code null} if there is no such matching element.
     * @param evaluator a query evaluator
     * @return the closest ancestor element (possibly itself) that matches the provided evaluator. {@code null} if not
     * found.
     */
    public Element closest(Evaluator evaluator) {
        Validate.notNull(evaluator);
        Element el = this;
        final Element root = root();
        do {
            if (evaluator.matches(root, el))
                return el;
            el = el.parent();
        } while (el != null);
        return null;
    }

    /**
     * Add a node child node to this element.
     *
     * @param child node to add.
     * @return this element, so that you can add more child nodes or elements.
     */
    public Element appendChild(Node child) {
        Validate.notNull(child);

        // was - Node#addChildren(child). short-circuits an array create and a loop.
        reparentChild(child);
        ensureChildNodes();
        childNodes.add(child);
        child.setSiblingIndex(childNodes.size() - 1);
        return this;
    }

    /**
     * Add this element to the supplied parent element, as its next child.
     *
     * @param parent element to which this element will be appended
     * @return this element, so that you can continue modifying the element
     */
    public Element appendTo(Element parent) {
        Validate.notNull(parent);
        parent.appendChild(this);
        return this;
    }

    /**
     * Add a node to the start of this element's children.
     *
     * @param child node to add.
     * @return this element, so that you can add more child nodes or elements.
     */
    public Element prependChild(Node child) {
        Validate.notNull(child);

        addChildren(0, child);
        return this;
    }

    /**
     * Inserts the given child nodes into this element at the specified index. Current nodes will be shifted to the
     * right. The inserted nodes will be moved from their current parent. To prevent moving, copy the nodes first.
     *
     * @param index 0-based index to insert children at. Specify {@code 0} to insert at the start, {@code -1} at the
     * end
     * @param children child nodes to insert
     * @return this element, for chaining.
     */
    public Element insertChildren(int index, Collection<? extends Node> children) {
        Validate.notNull(children, "Children collection to be inserted must not be null.");
        int currentSize = childNodeSize();
        if (index < 0) index += currentSize +1; // roll around
        Validate.isTrue(index >= 0 && index <= currentSize, "Insert position out of bounds.");

        ArrayList<Node> nodes = new ArrayList<>(children);
        Node[] nodeArray = nodes.toArray(new Node[0]);
        addChildren(index, nodeArray);
        return this;
    }

    /**
     * Inserts the given child nodes into this element at the specified index. Current nodes will be shifted to the
     * right. The inserted nodes will be moved from their current parent. To prevent moving, copy the nodes first.
     *
     * @param index 0-based index to insert children at. Specify {@code 0} to insert at the start, {@code -1} at the
     * end
     * @param children child nodes to insert
     * @return this element, for chaining.
     */
    public Element insertChildren(int index, Node... children) {
        Validate.notNull(children, "Children collection to be inserted must not be null.");
        int currentSize = childNodeSize();
        if (index < 0) index += currentSize +1; // roll around
        Validate.isTrue(index >= 0 && index <= currentSize, "Insert position out of bounds.");

        addChildren(index, children);
        return this;
    }

    /**
     * Create a new element by tag name, and add it as the last child.
     *
     * @param tagName the name of the tag (e.g. {@code div}).
     * @return the new element, to allow you to add content to it, e.g.:
     *  {@code parent.appendElement("h1").attr("id", "header").text("Welcome");}
     */
    public Element appendElement(String tagName) {
        Element child = new Element(Tag.valueOf(tagName, NodeUtils.parser(this).settings()), baseUri());
        appendChild(child);
        return child;
    }

    /**
     * Create a new element by tag name, and add it as the first child.
     *
     * @param tagName the name of the tag (e.g. {@code div}).
     * @return the new element, to allow you to add content to it, e.g.:
     *  {@code parent.prependElement("h1").attr("id", "header").text("Welcome");}
     */
    public Element prependElement(String tagName) {
        Element child = new Element(Tag.valueOf(tagName, NodeUtils.parser(this).settings()), baseUri());
        prependChild(child);
        return child;
    }

    /**
     * Create and append a new TextNode to this element.
     *
     * @param text the unencoded text to add
     * @return this element
     */
    public Element appendText(String text) {
        Validate.notNull(text);
        TextNode node = new TextNode(text);
        appendChild(node);
        return this;
    }

    /**
     * Create and prepend a new TextNode to this element.
     *
     * @param text the unencoded text to add
     * @return this element
     */
    public Element prependText(String text) {
        Validate.notNull(text);
        TextNode node = new TextNode(text);
        prependChild(node);
        return this;
    }

    /**
     * Add inner HTML to this element. The supplied HTML will be parsed, and each node appended to the end of the children.
     * @param html HTML to add inside this element, after the existing HTML
     * @return this element
     * @see #html(String)
     */
    public Element append(String html) {
        Validate.notNull(html);
        List<Node> nodes = NodeUtils.parser(this).parseFragmentInput(html, this, baseUri());
        addChildren(nodes.toArray(new Node[0]));
        return this;
    }

    /**
     * Add inner HTML into this element. The supplied HTML will be parsed, and each node prepended to the start of the element's children.
     * @param html HTML to add inside this element, before the existing HTML
     * @return this element
     * @see #html(String)
     */
    public Element prepend(String html) {
        Validate.notNull(html);
        List<Node> nodes = NodeUtils.parser(this).parseFragmentInput(html, this, baseUri());
        addChildren(0, nodes.toArray(new Node[0]));
        return this;
    }

    /**
     * Insert the specified HTML into the DOM before this element (as a preceding sibling).
     *
     * @param html HTML to add before this element
     * @return this element, for chaining
     * @see #after(String)
     */
    @Override
    public Element before(String html) {
        return (Element) super.before(html);
    }

    /**
     * Insert the specified node into the DOM before this node (as a preceding sibling).
     * @param node to add before this element
     * @return this Element, for chaining
     * @see #after(Node)
     */
    @Override
    public Element before(Node node) {
        return (Element) super.before(node);
    }

    /**
     * Insert the specified HTML into the DOM after this element (as a following sibling).
     *
     * @param html HTML to add after this element
     * @return this element, for chaining
     * @see #before(String)
     */
    @Override
    public Element after(String html) {
        return (Element) super.after(html);
    }

    /**
     * Insert the specified node into the DOM after this node (as a following sibling).
     * @param node to add after this element
     * @return this element, for chaining
     * @see #before(Node)
     */
    @Override
    public Element after(Node node) {
        return (Element) super.after(node);
    }

    /**
     * Remove all of the element's child nodes. Any attributes are left as-is.
     * @return this element
     */
    @Override
    public Element empty() {
        childNodes.clear();
        return this;
    }

    /**
     * Wrap the supplied HTML around this element.
     *
     * @param html HTML to wrap around this element, e.g. {@code

}. Can be arbitrarily deep.
     * @return this element, for chaining.
     */
    @Override
    public Element wrap(String html) {
        return (Element) super.wrap(html);
    }

    /**
     * Get a CSS selector that will uniquely select this element.
     *

     * If the element has an ID, returns #id;
     * otherwise returns the parent (if any) CSS selector, followed by {@literal '>'},
     * followed by a unique selector for the element (tag.class.class:nth-child(n)).
     *

     *
     * @return the CSS Path that can be used to retrieve the element in a selector.
     */
    public String cssSelector() {
        if (id().length() > 0)
            return "#" + id();

        // Translate HTML namespace ns:tag to CSS namespace syntax ns|tag
        String tagName = tagName().replace(':', '|');
        StringBuilder selector = new StringBuilder(tagName);
        String classes = StringUtil.join(classNames(), ".");
        if (classes.length() > 0)
            selector.append('.').append(classes);

        if (parent() == null || parent() instanceof Document) // don't add Document to selector, as will always have a html node
            return selector.toString();

        selector.insert(0, " > ");
        if (parent().select(selector.toString()).size() > 1)
            selector.append(String.format(
                ":nth-child(%d)", elementSiblingIndex() + 1));

        return parent().cssSelector() + selector.toString();
    }

    /**
     * Get sibling elements. If the element has no sibling elements, returns an empty list. An element is not a sibling
     * of itself, so will not be included in the returned list.
     * @return sibling elements
     */
    public Elements siblingElements() {
        if (parentNode == null)
            return new Elements(0);

        List<Element> elements = parent().childElementsList();
        Elements siblings = new Elements(elements.size() - 1);
        for (Element el: elements)
            if (el != this)
                siblings.add(el);
        return siblings;
    }

    /**
     * Gets the next sibling element of this element. E.g., if a {@code div} contains two {@code p}s,
     * the {@code nextElementSibling} of the first {@code p} is the second {@code p}.
     *

     * This is similar to {@link #nextSibling()}, but specifically finds only Elements
     *

     * @return the next element, or null if there is no next element
     * @see #previousElementSibling()
     */
    public Element nextElementSibling() {
        if (parentNode == null) return null;
        List<Element> siblings = parent().childElementsList();
        int index = indexInList(this, siblings);
        if (siblings.size() > index+1)
            return siblings.get(index+1);
        else
            return null;
    }

    /**
     * Get each of the sibling elements that come after this element.
     *
     * @return each of the element siblings after this element, or an empty list if there are no next sibling elements
     */
    public Elements nextElementSiblings() {
        return nextElementSiblings(true);
    }

    /**
     * Gets the previous element sibling of this element.
     * @return the previous element, or null if there is no previous element
     * @see #nextElementSibling()
     */
    public Element previousElementSibling() {
        if (parentNode == null) return null;
        List<Element> siblings = parent().childElementsList();
        int index = indexInList(this, siblings);
        if (index > 0)
            return siblings.get(index-1);
        else
            return null;
    }

    /**
     * Get each of the element siblings before this element.
     *
     * @return the previous element siblings, or an empty list if there are none.
     */
    public Elements previousElementSiblings() {
        return nextElementSiblings(false);
    }

    private Elements nextElementSiblings(boolean next) {
        Elements els = new Elements();
        if (parentNode == null)
            return  els;
        els.add(this);
        return next ?  els.nextAll() : els.prevAll();
    }

    /**
     * Gets the first element sibling of this element.
     * @return the first sibling that is an element (aka the parent's first element child)
     */
    public Element firstElementSibling() {
        // todo: should firstSibling() exclude this?
        List<Element> siblings = parent().childElementsList();
        return siblings.size() > 1 ? siblings.get(0) : null;
    }

    /**
     * Get the list index of this element in its element sibling list. I.e. if this is the first element
     * sibling, returns 0.
     * @return position in element sibling list
     */
    public int elementSiblingIndex() {
       if (parent() == null) return 0;
       return indexInList(this, parent().childElementsList());
    }

    /**
     * Gets the last element sibling of this element
     * @return the last sibling that is an element (aka the parent's last element child)
     */
    public Element lastElementSibling() {
        List<Element> siblings = parent().childElementsList();
        return siblings.size() > 1 ? siblings.get(siblings.size() - 1) : null;
    }

    private static <E extends Element> int indexInList(Element search, List<E> elements) {
        final int size = elements.size();
        for (int i = 0; i < size; i++) {
            if (elements.get(i) == search)
                return i;
        }
        return 0;
    }

    // DOM type methods

    /**
     * Finds elements, including and recursively under this element, with the specified tag name.
     * @param tagName The tag name to search for (case insensitively).
     * @return a matching unmodifiable list of elements. Will be empty if this element and none of its children match.
     */
    public Elements getElementsByTag(String tagName) {
        Validate.notEmpty(tagName);
        tagName = normalize(tagName);

        return Collector.collect(new Evaluator.Tag(tagName), this);
    }

    /**
     * Find an element by ID, including or under this element.
     *

     * Note that this finds the first matching ID, starting with this element. If you search down from a different
     * starting point, it is possible to find a different element by ID. For unique element by ID within a Document,
     * use {@link Document#getElementById(String)}
     * @param id The ID to search for.
     * @return The first matching element by ID, starting with this element, or null if none found.
     */
    public Element getElementById(String id) {
        Validate.notEmpty(id);

        Elements elements = Collector.collect(new Evaluator.Id(id), this);
        if (elements.size() > 0)
            return elements.get(0);
        else
            return null;
    }

    /**
     * Find elements that have this class, including or under this element. Case insensitive.
     *

     * Elements can have multiple classes (e.g. {@code
}. This method
     * checks each class, so you can find the above with {@code el.getElementsByClass("header");}.
     *
     * @param className the name of the class to search for.
     * @return elements with the supplied class name, empty if none
     * @see #hasClass(String)
     * @see #classNames()
     */
    public Elements getElementsByClass(String className) {
        Validate.notEmpty(className);

        return Collector.collect(new Evaluator.Class(className), this);
    }

    /**
     * Find elements that have a named attribute set. Case insensitive.
     *
     * @param key name of the attribute, e.g. {@code href}
     * @return elements that have this attribute, empty if none
     */
    public Elements getElementsByAttribute(String key) {
        Validate.notEmpty(key);
        key = key.trim();

        return Collector.collect(new Evaluator.Attribute(key), this);
    }

    /**
     * Find elements that have an attribute name starting with the supplied prefix. Use {@code data-} to find elements
     * that have HTML5 datasets.
     * @param keyPrefix name prefix of the attribute e.g. {@code data-}
     * @return elements that have attribute names that start with with the prefix, empty if none.
     */
    public Elements getElementsByAttributeStarting(String keyPrefix) {
        Validate.notEmpty(keyPrefix);
        keyPrefix = keyPrefix.trim();

        return Collector.collect(new Evaluator.AttributeStarting(keyPrefix), this);
    }

    /**
     * Find elements that have an attribute with the specific value. Case insensitive.
     *
     * @param key name of the attribute
     * @param value value of the attribute
     * @return elements that have this attribute with this value, empty if none
     */
    public Elements getElementsByAttributeValue(String key, String value) {
        return Collector.collect(new Evaluator.AttributeWithValue(key, value), this);
    }

    /**
     * Find elements that either do not have this attribute, or have it with a different value. Case insensitive.
     *
     * @param key name of the attribute
     * @param value value of the attribute
     * @return elements that do not have a matching attribute
     */
    public Elements getElementsByAttributeValueNot(String key, String value) {
        return Collector.collect(new Evaluator.AttributeWithValueNot(key, value), this);
    }

    /**
     * Find elements that have attributes that start with the value prefix. Case insensitive.
     *
     * @param key name of the attribute
     * @param valuePrefix start of attribute value
     * @return elements that have attributes that start with the value prefix
     */
    public Elements getElementsByAttributeValueStarting(String key, String valuePrefix) {
        return Collector.collect(new Evaluator.AttributeWithValueStarting(key, valuePrefix), this);
    }

    /**
     * Find elements that have attributes that end with the value suffix. Case insensitive.
     *
     * @param key name of the attribute
     * @param valueSuffix end of the attribute value
     * @return elements that have attributes that end with the value suffix
     */
    public Elements getElementsByAttributeValueEnding(String key, String valueSuffix) {
        return Collector.collect(new Evaluator.AttributeWithValueEnding(key, valueSuffix), this);
    }

    /**
     * Find elements that have attributes whose value contains the match string. Case insensitive.
     *
     * @param key name of the attribute
     * @param match substring of value to search for
     * @return elements that have attributes containing this text
     */
    public Elements getElementsByAttributeValueContaining(String key, String match) {
        return Collector.collect(new Evaluator.AttributeWithValueContaining(key, match), this);
    }

    /**
     * Find elements that have attributes whose values match the supplied regular expression.
     * @param key name of the attribute
     * @param pattern compiled regular expression to match against attribute values
     * @return elements that have attributes matching this regular expression
     */
    public Elements getElementsByAttributeValueMatching(String key, Pattern pattern) {
        return Collector.collect(new Evaluator.AttributeWithValueMatching(key, pattern), this);

    }

    /**
     * Find elements that have attributes whose values match the supplied regular expression.
     * @param key name of the attribute
     * @param regex regular expression to match against attribute values. You can use embedded flags (such as (?i) and (?m) to control regex options.
     * @return elements that have attributes matching this regular expression
     */
    public Elements getElementsByAttributeValueMatching(String key, String regex) {
        Pattern pattern;
        try {
            pattern = Pattern.compile(regex);
        } catch (PatternSyntaxException e) {
            throw new IllegalArgumentException("Pattern syntax error: " + regex, e);
        }
        return getElementsByAttributeValueMatching(key, pattern);
    }

    /**
     * Find elements whose sibling index is less than the supplied index.
     * @param index 0-based index
     * @return elements less than index
     */
    public Elements getElementsByIndexLessThan(int index) {
        return Collector.collect(new Evaluator.IndexLessThan(index), this);
    }

    /**
     * Find elements whose sibling index is greater than the supplied index.
     * @param index 0-based index
     * @return elements greater than index
     */
    public Elements getElementsByIndexGreaterThan(int index) {
        return Collector.collect(new Evaluator.IndexGreaterThan(index), this);
    }

    /**
     * Find elements whose sibling index is equal to the supplied index.
     * @param index 0-based index
     * @return elements equal to index
     */
    public Elements getElementsByIndexEquals(int index) {
        return Collector.collect(new Evaluator.IndexEquals(index), this);
    }

    /**
     * Find elements that contain the specified string. The search is case insensitive. The text may appear directly
     * in the element, or in any of its descendants.
     * @param searchText to look for in the element's text
     * @return elements that contain the string, case insensitive.
     * @see Element#text()
     */
    public Elements getElementsContainingText(String searchText) {
        return Collector.collect(new Evaluator.ContainsText(searchText), this);
    }

    /**
     * Find elements that directly contain the specified string. The search is case insensitive. The text must appear directly
     * in the element, not in any of its descendants.
     * @param searchText to look for in the element's own text
     * @return elements that contain the string, case insensitive.
     * @see Element#ownText()
     */
    public Elements getElementsContainingOwnText(String searchText) {
        return Collector.collect(new Evaluator.ContainsOwnText(searchText), this);
    }

    /**
     * Find elements whose text matches the supplied regular expression.
     * @param pattern regular expression to match text against
     * @return elements matching the supplied regular expression.
     * @see Element#text()
     */
    public Elements getElementsMatchingText(Pattern pattern) {
        return Collector.collect(new Evaluator.Matches(pattern), this);
    }

    /**
     * Find elements whose text matches the supplied regular expression.
     * @param regex regular expression to match text against. You can use embedded flags (such as (?i) and (?m) to control regex options.
     * @return elements matching the supplied regular expression.
     * @see Element#text()
     */
    public Elements getElementsMatchingText(String regex) {
        Pattern pattern;
        try {
            pattern = Pattern.compile(regex);
        } catch (PatternSyntaxException e) {
            throw new IllegalArgumentException("Pattern syntax error: " + regex, e);
        }
        return getElementsMatchingText(pattern);
    }

    /**
     * Find elements whose own text matches the supplied regular expression.
     * @param pattern regular expression to match text against
     * @return elements matching the supplied regular expression.
     * @see Element#ownText()
     */
    public Elements getElementsMatchingOwnText(Pattern pattern) {
        return Collector.collect(new Evaluator.MatchesOwn(pattern), this);
    }

    /**
     * Find elements whose own text matches the supplied regular expression.
     * @param regex regular expression to match text against. You can use embedded flags (such as (?i) and (?m) to control regex options.
     * @return elements matching the supplied regular expression.
     * @see Element#ownText()
     */
    public Elements getElementsMatchingOwnText(String regex) {
        Pattern pattern;
        try {
            pattern = Pattern.compile(regex);
        } catch (PatternSyntaxException e) {
            throw new IllegalArgumentException("Pattern syntax error: " + regex, e);
        }
        return getElementsMatchingOwnText(pattern);
    }

    /**
     * Find all elements under this element (including self, and children of children).
     *
     * @return all elements
     */
    public Elements getAllElements() {
        return Collector.collect(new Evaluator.AllElements(), this);
    }

    /**
     * Gets the combined text of this element and all its children. Whitespace is normalized and trimmed.
     *

     * For example, given HTML {@code
Hello  there now!
}, {@code p.text()} returns {@code "Hello there now!"}
     *
     * @return unencoded, normalized text, or empty string if none.
     * @see #wholeText() if you don't want the text to be normalized.
     * @see #ownText()
     * @see #textNodes()
     */
    public String text() {
        final StringBuilder accum = StringUtil.borrowBuilder();
        NodeTraversor.traverse(new NodeVisitor() {
            public void head(Node node, int depth) {
                if (node instanceof TextNode) {
                    TextNode textNode = (TextNode) node;
                    appendNormalisedText(accum, textNode);
                } else if (node instanceof Element) {
                    Element element = (Element) node;
                    if (accum.length() > 0 &&
                        (element.isBlock() || element.tag.getName().equals("br")) &&
                        !TextNode.lastCharIsWhitespace(accum))
                        accum.append(' ');
                }
            }

            public void tail(Node node, int depth) {
                // make sure there is a space between block tags and immediately following text nodes
One
Two should be "One Two".
                if (node instanceof Element) {
                    Element element = (Element) node;
                    if (element.isBlock() && (node.nextSibling() instanceof TextNode) && !TextNode.lastCharIsWhitespace(accum))
                        accum.append(' ');
                }

            }
        }, this);

        return StringUtil.releaseBuilder(accum).trim();
    }

    /**
     * Get the (unencoded) text of all children of this element, including any newlines and spaces present in the
     * original.
     *
     * @return unencoded, un-normalized text
     * @see #text()
     */
    public String wholeText() {
        final StringBuilder accum = StringUtil.borrowBuilder();
        NodeTraversor.traverse(new NodeVisitor() {
            public void head(Node node, int depth) {
                if (node instanceof TextNode) {
                    TextNode textNode = (TextNode) node;
                    accum.append(textNode.getWholeText());
                }
            }

            public void tail(Node node, int depth) {
            }
        }, this);

        return StringUtil.releaseBuilder(accum);
    }

    /**
     * Gets the text owned by this element only; does not get the combined text of all children.
     *

     * For example, given HTML {@code
Hello there now!
}, {@code p.ownText()} returns {@code "Hello now!"},
     * whereas {@code p.text()} returns {@code "Hello there now!"}.
     * Note that the text within the {@code b} element is not returned, as it is not a direct child of the {@code p} element.
     *
     * @return unencoded text, or empty string if none.
     * @see #text()
     * @see #textNodes()
     */
    public String ownText() {
        StringBuilder sb = StringUtil.borrowBuilder();
        ownText(sb);
        return StringUtil.releaseBuilder(sb).trim();
    }

    private void ownText(StringBuilder accum) {
        for (Node child : childNodes) {
            if (child instanceof TextNode) {
                TextNode textNode = (TextNode) child;
                appendNormalisedText(accum, textNode);
            } else if (child instanceof Element) {
                appendWhitespaceIfBr((Element) child, accum);
            }
        }
    }

    private static void appendNormalisedText(StringBuilder accum, TextNode textNode) {
        String text = textNode.getWholeText();

        if (preserveWhitespace(textNode.parentNode) || textNode instanceof CDataNode)
            accum.append(text);
        else
            StringUtil.appendNormalisedWhitespace(accum, text, TextNode.lastCharIsWhitespace(accum));
    }

    private static void appendWhitespaceIfBr(Element element, StringBuilder accum) {
        if (element.tag.getName().equals("br") && !TextNode.lastCharIsWhitespace(accum))
            accum.append(" ");
    }

    static boolean preserveWhitespace(Node node) {
        // looks only at this element and five levels up, to prevent recursion & needless stack searches
        if (node instanceof Element) {
            Element el = (Element) node;
            int i = 0;
            do {
                if (el.tag.preserveWhitespace())
                    return true;
                el = el.parent();
                i++;
            } while (i < 6 && el != null);
        }
        return false;
    }

    /**
     * Set the text of this element. Any existing contents (text or elements) will be cleared
     * @param text unencoded text
     * @return this element
     */
    public Element text(String text) {
        Validate.notNull(text);

        empty();
        TextNode textNode = new TextNode(text);
        appendChild(textNode);

        return this;
    }

    /**
     Test if this element has any text content (that is not just whitespace).
     @return true if element has non-blank text content.
     */
    public boolean hasText() {
        for (Node child: childNodes) {
            if (child instanceof TextNode) {
                TextNode textNode = (TextNode) child;
                if (!textNode.isBlank())
                    return true;
            } else if (child instanceof Element) {
                Element el = (Element) child;
                if (el.hasText())
                    return true;
            }
        }
        return false;
    }

    /**
     * Get the combined data of this element. Data is e.g. the inside of a {@code script} tag. Note that data is NOT the
     * text of the element. Use {@link #text()} to get the text that would be visible to a user, and {@link #data()}
     * for the contents of scripts, comments, CSS styles, etc.
     *
     * @return the data, or empty string if none
     *
     * @see #dataNodes()
     */
    public String data() {
        StringBuilder sb = StringUtil.borrowBuilder();

        for (Node childNode : childNodes) {
            if (childNode instanceof DataNode) {
                DataNode data = (DataNode) childNode;
                sb.append(data.getWholeData());
            } else if (childNode instanceof Comment) {
                Comment comment = (Comment) childNode;
                sb.append(comment.getData());
            } else if (childNode instanceof Element) {
                Element element = (Element) childNode;
                String elementData = element.data();
                sb.append(elementData);
            } else if (childNode instanceof CDataNode) {
                // this shouldn't really happen because the html parser won't see the cdata as anything special when parsing script.
                // but incase another type gets through.
                CDataNode cDataNode = (CDataNode) childNode;
                sb.append(cDataNode.getWholeText());
            }
        }
        return StringUtil.releaseBuilder(sb);
    }

    /**
     * Gets the literal value of this element's "class" attribute, which may include multiple class names, space
     * separated. (E.g. on <div class="header gray"> returns, "header gray")
     * @return The literal class attribute, or empty string if no class attribute set.
     */
    public String className() {
        return attr("class").trim();
    }

    /**
     * Get all of the element's class names. E.g. on element {@code
},
     * returns a set of two elements {@code "header", "gray"}. Note that modifications to this set are not pushed to
     * the backing {@code class} attribute; use the {@link #classNames(java.util.Set)} method to persist them.
     * @return set of classnames, empty if no class attribute
     */
    public Set<String> classNames() {
    	String[] names = classSplit.split(className());
    	Set<String> classNames = new LinkedHashSet<>(Arrays.asList(names));
    	classNames.remove(""); // if classNames() was empty, would include an empty class

        return classNames;
    }

    /**
     Set the element's {@code class} attribute to the supplied class names.
     @param classNames set of classes
     @return this element, for chaining
     */
    public Element classNames(Set<String> classNames) {
        Validate.notNull(classNames);
        if (classNames.isEmpty()) {
            attributes().remove("class");
        } else {
            attributes().put("class", StringUtil.join(classNames, " "));
        }
        return this;
    }

    /**
     * Tests if this element has a class. Case insensitive.
     * @param className name of class to check for
     * @return true if it does, false if not
     */
    // performance sensitive
    public boolean hasClass(String className) {
        if (!hasAttributes())
            return false;

        final String classAttr = attributes.getIgnoreCase("class");
        final int len = classAttr.length();
        final int wantLen = className.length();

        if (len == 0 || len < wantLen) {
            return false;
        }

        // if both lengths are equal, only need compare the className with the attribute
        if (len == wantLen) {
            return className.equalsIgnoreCase(classAttr);
        }

        // otherwise, scan for whitespace and compare regions (with no string or arraylist allocations)
        boolean inClass = false;
        int start = 0;
        for (int i = 0; i < len; i++) {
            if (Character.isWhitespace(classAttr.charAt(i))) {
                if (inClass) {
                    // white space ends a class name, compare it with the requested one, ignore case
                    if (i - start == wantLen && classAttr.regionMatches(true, start, className, 0, wantLen)) {
                        return true;
                    }
                    inClass = false;
                }
            } else {
                if (!inClass) {
                    // we're in a class name : keep the start of the substring
                    inClass = true;
                    start = i;
                }
            }
        }

        // check the last entry
        if (inClass && len - start == wantLen) {
            return classAttr.regionMatches(true, start, className, 0, wantLen);
        }

        return false;
    }

    /**
     Add a class name to this element's {@code class} attribute.
     @param className class name to add
     @return this element
     */
    public Element addClass(String className) {
        Validate.notNull(className);

        Set<String> classes = classNames();
        classes.add(className);
        classNames(classes);

        return this;
    }

    /**
     Remove a class name from this element's {@code class} attribute.
     @param className class name to remove
     @return this element
     */
    public Element removeClass(String className) {
        Validate.notNull(className);

        Set<String> classes = classNames();
        classes.remove(className);
        classNames(classes);

        return this;
    }

    /**
     Toggle a class name on this element's {@code class} attribute: if present, remove it; otherwise add it.
     @param className class name to toggle
     @return this element
     */
    public Element toggleClass(String className) {
        Validate.notNull(className);

        Set<String> classes = classNames();
        if (classes.contains(className))
            classes.remove(className);
        else
            classes.add(className);
        classNames(classes);

        return this;
    }

    /**
     * Get the value of a form element (input, textarea, etc).
     * @return the value of the form element, or empty string if not set.
     */
    public String val() {
        if (normalName().equals("textarea"))
            return text();
        else
            return attr("value");
    }

    /**
     * Set the value of a form element (input, textarea, etc).
     * @param value value to set
     * @return this element (for chaining)
     */
    public Element val(String value) {
        if (normalName().equals("textarea"))
            text(value);
        else
            attr("value", value);
        return this;
    }

    void outerHtmlHead(final Appendable accum, int depth, final Document.OutputSettings out) throws IOException {
        if (out.prettyPrint() && isFormatAsBlock(out) && !isInlineable(out)) {
            if (accum instanceof StringBuilder) {
                if (((StringBuilder) accum).length() > 0)
                    indent(accum, depth, out);
            } else {
                indent(accum, depth, out);
            }
        }
        accum.append('<').append(tagName());
        if (attributes != null) attributes.html(accum, out);

        // selfclosing includes unknown tags, isEmpty defines tags that are always empty
        if (childNodes.isEmpty() && tag.isSelfClosing()) {
            if (out.syntax() == Document.OutputSettings.Syntax.html && tag.isEmpty())
                accum.append('>');
            else
                accum.append(" />"); //  in html,  in xml
        }
        else
            accum.append('>');
    }

    void outerHtmlTail(Appendable accum, int depth, Document.OutputSettings out) throws IOException {
        if (!(childNodes.isEmpty() && tag.isSelfClosing())) {
            if (out.prettyPrint() && (!childNodes.isEmpty() && (
                    tag.formatAsBlock() || (out.outline() && (childNodes.size()>1 || (childNodes.size()==1 && !(childNodes.get(0) instanceof TextNode))))
            )))
                indent(accum, depth, out);
            accum.append("</").append(tagName()).append('>');
        }
    }

    /**
     * Retrieves the element's inner HTML. E.g. on a {@code
} with one empty {@code
}, would return
     * {@code

}. (Whereas {@link #outerHtml()} would return {@code

}.)
     *
     * @return String of HTML.
     * @see #outerHtml()
     */
    public String html() {
        StringBuilder accum = StringUtil.borrowBuilder();
        html(accum);
        String html = StringUtil.releaseBuilder(accum);
        return NodeUtils.outputSettings(this).prettyPrint() ? html.trim() : html;
    }

    @Override
    public <T extends Appendable> T html(T appendable) {
        final int size = childNodes.size();
        for (int i = 0; i < size; i++)
            childNodes.get(i).outerHtml(appendable);

        return appendable;
    }

    /**
     * Set this element's inner HTML. Clears the existing HTML first.
     * @param html HTML to parse and set into this element
     * @return this element
     * @see #append(String)
     */
    public Element html(String html) {
        empty();
        append(html);
        return this;
    }

    @Override
    public Element clone() {
        return (Element) super.clone();
    }

    @Override
    public Element shallowClone() {
        // simpler than implementing a clone version with no child copy
        return new Element(tag, baseUri(), attributes == null ? null : attributes.clone());
    }

    @Override
    protected Element doClone(Node parent) {
        Element clone = (Element) super.doClone(parent);
        clone.attributes = attributes != null ? attributes.clone() : null;
        clone.childNodes = new NodeList(clone, childNodes.size());
        clone.childNodes.addAll(childNodes); // the children then get iterated and cloned in Node.clone
        clone.setBaseUri(baseUri());

        return clone;
    }

    // overrides of Node for call chaining
    @Override
    public Element clearAttributes() {
        if (attributes != null) {
            super.clearAttributes();
            attributes = null;
        }

        return this;
    }

    @Override
    public Element removeAttr(String attributeKey) {
        return (Element) super.removeAttr(attributeKey);
    }

    @Override
    public Element root() {
        return (Element) super.root(); // probably a document, but always at least an element
    }

    @Override
    public Element traverse(NodeVisitor nodeVisitor) {
        return  (Element) super.traverse(nodeVisitor);
    }

    @Override
    public Element filter(NodeFilter nodeFilter) {
        return  (Element) super.filter(nodeFilter);
    }

    private static final class NodeList extends ChangeNotifyingArrayList<Node> {
        private final Element owner;

        NodeList(Element owner, int initialCapacity) {
            super(initialCapacity);
            this.owner = owner;
        }

        public void onContentsChanged() {
            owner.nodelistChanged();
        }
    }

    private boolean isFormatAsBlock(Document.OutputSettings out) {
        return tag.formatAsBlock() || (parent() != null && parent().tag().formatAsBlock()) || out.outline();
    }

    private boolean isInlineable(Document.OutputSettings out) {
        return tag().isInline()
            && !tag().isEmpty()
            && parent().isBlock()
            && previousSibling() != null
            && !out.outline();
    }
}

redpajama_set_name: RedPajamaGithub


Bad bench marks with 8800GTS why??
How to identify Graphics Card?
Geforce 9400 GT on windows XP?
XFX 8800GT with COD, or 9600GT?
Can any one solve the problem of Nvidia SLI for Multi-Monitoring?
I bought 8800GT cards but I got 9600GTs instead!!!
Which one is better,7300GT or X1650Pro??


July 19, 2018 Three Earn Spring NCAA PostGraduate Scholarships Three NCAC student-athletes have earned NCAA postgraduate scholarships from the NCAA.
June 6, 2018 Wabash Tabs McCreary As Cross Country Coach Tyler McCreary joins the Wabash College athletics program as head cross country and associate head track and field coach for the Little Giants.
December 21, 2015 Allegheny Names Mourer Head XC Coach Allegheny College has announced the hiring of Ben Mourer to the position of head men's and women's cross country coach.
August 21, 2015 Wabash Names Young Cross Country Coach Dr. Colin Young, a former Division III national champion, will take over the reins of the Little Giants' cross country program as its next head coach.
July 23, 2015 Hiram Announces Suspension Of Cross Country The Hiram College Athletic Department has announced the suspension of the men's and women's cross country programs, effective in the fall of 2015.
October 13, 2014 NCAC Weekly Release (10-13-14) - Cross Country North Coast cross country teams were all idle last week, but they will return to the circuit this weekend with all 10 schools in action.
October 14, 2013 NCAC Weekly Release (10-14-13) - Cross Country Cross Country teams in the North Cost were idle last week, but will return to the circuit this weekend with all 10 schools in action.
September 30, 2013 NCAC Weekly Release (9-30-13) - Cross Country Cross Country teams in the North Cost were idle last week, but will return to the circuit this weekend with all 10 schools in action.
September 23, 2013 NCAC Weekly Release (9-23-13) - Cross Country NCAC cross country teams battled wet conditions last weekend throughout the region as seven of the league's 10 squads were in action.


text:
Marvin Wilson execution goes ahead in Texas
Lawyers fail in supreme court bid to have death sentence commuted on grounds of mental impairment
Marvin Wilson has been executed by the state of Texas. Photograph: AP
Associated Press in Huntsville
Tue 7 Aug 2012 22.22 EDT
A Texas man convicted of killing a police informant has been executed after the US supreme court rejected arguments that he was too mentally impaired to receive the death penalty.
Marvin Wilson, 54, was pronounced dead 14 minutes after his lethal injection began at the state prison in Huntsville on Tuesday night. Wilson's attorneys had argued that he should have been exempt from capital punishment because of his low IQ.
Before the lethal drugs were administered Wilson smiled and raised his head from the death-chamber gurney, nodding to his three sisters and son as they watched through a window a few metres away. He told them several times that he loved them and asked that they give his mother "a big hug".
"Y'all do understand that I came here a sinner and leaving a saint," he said. "Take me home Jesus, take me home Lord, take me home Lord!"
In their appeal to the supreme court Wilson's attorneys had pointed to a psychological test conducted in 2004 that put his IQ at 61, below the generally accepted minimum competency standard of 70. But lower courts agreed with state attorneys, who argued that Wilson's claim was based on a single possibly faulty test and that his mental impairment claim was not supported by other tests and assessments over the years.
The supreme court denied his request for a stay of execution less than two hours before his lethal injection began. Lead defence attorney Lee Kovarsky said he was "gravely disappointed and saddened" by the ruling, calling it "outrageous that the state of Texas continues to utilise unscientific guidelines ... to determine which citizens with intellectual disability are exempt from execution".
Wilson was convicted of murdering 21-year-old Jerry Williams in November 1992. Wilson, a convicted robber, had been arrested for possessing cocaine and believed it was Williams who told police, the trial heard.
In Wilson's supreme court appeal Kovarsky said Wilson's language and math skills "never progressed beyond an elementary school level", that he read and wrote below a second-grade level and that he was unable to manage his finances, pay bills or hold down a job.
The supreme court issued a ruling in 2002 outlawing the execution of the mentally impaired but left it to states to determine what constitutes mental impairment. Kovarsky argued that Texas had deliberately chosen a definition that made such an exemption "virtually unobtainable" in the state.

redpajama_set_name: RedPajamaCommonCrawl


Could your morning routine use a makeover? Many of us do not view the morning as a sacred time to ease into our day. We often have a million and one things to do and our time is consumed with trying to save the world all before 9 am.
Your morning doesn't have to be this way. Transforming your morning routine will require a bit of planning but it's so incredibly worth it and one of the best things you can do for your body and spirit. When you wake up and jump into the day without ease you may be neglecting your spirit's needs. Getting into the practice of a beautiful morning routine will make the world of difference in how you start your day and how your day treats you. We all know those days where our morning starts in chaos and feels just like one faux pas after another ensues and you wind up defeated wishing you could hit the restart button.
It's true that we all don't have a ton of time in the morning to ease into the day but we want to show you a few things you can do in the morning (some only take 5 minutes) that can make all the difference in how you approach your day. As you build your morning routine practice you can add more things that feel good to you and pretty soon you will be starting your days with ease and grace.
This in turn makes you more productive in your day and overall a better caregiver to yourself and everyone else in your life. Remember it is not selfish to take care of you first.
Lemon water. If you do nothing else, make sure you drink a large glass of water with some lemon first thing in the morning. This will rehydrate you and make your water easier to drink. Not a fan of lemon? Pick another fruit that calls to you and add it to your water.
Deep breathing. Take 5 minutes even before you get out of bed. Put one hand on your heart and take about 10 deep breaths and focus on nothing but your breath.
Set one intention for the day. You can think of your intention or write it down. One sentence and you are done! This conscious act sets the wheels in motion to make this intention happen. Don't underestimate the power in writing your goals down.
Think of 5 things that make you blissfully happy or laugh. Spend 5 minutes thinking of those things and getting into a state filled with good vibes.
Have you tried any of these morning transformers? Do you have other things you do to kick off your morning? Why don't you share them with us and then go conquer your day!
Benefits Of Lemon: When Life Gives You Lemons.


text:
SKETCHES - Ana Marie Pamintuan (The Philippine Star) - August 3, 2017 - 4:00pm
If certain members of the House of Representatives have committed improprieties or broken the law in the Bureau of Customs or proposed to do so, BOC Commissioner Nicanor Faeldon should go ahead with his threat and expose them.
This is required by laws on corruption and conduct governing public officials and employees. Failure to report even an attempt or proposal to break the law is an act of omission that makes both parties liable.
Of course in reality, laws governing acts of omission are violated with impunity at all levels of government in this country. Especially if the illegal proposal is made by a superior. Ordinary government employees have enough problems just trying to make ends meet, and if there are crooks in their midst, they know enough to just live and let live.
Faeldon, however, is not just another government employee. And he has come out in the open with a sweeping accusation that he must back with evidence – like the written job endorsement from Speaker Pantaleon Alvarez that was leaked shortly after he was described on Facebook by Faeldon's aide as an "imbecile."
Yesterday Faeldon didn't look like he was about to back down. While apologizing to the House for making a sweeping accusation, he said his threat of exposure was directed at 10 congressmen.
Even if there are "only" 10 influence peddlers, the Customs chief is duty-bound to name names and specify their purported impropriety or criminal act, as dared yesterday by senators. Especially if the 10 include several House leaders.
Faeldon, of course, will have to be ready for the return fire. And when you head an agency as rotten as the BOC, you can be sure your enemies can gather plenty of ammunition against you.
On the same day that the threat of an exposé was made, a witness was forced to admit before the House inquiry that he gave BOC officials P27,000 as grease money for every shipping container waved through the "green lane" at the Port of Manila.
The amount is on top of the legitimate fee of P40,000 per container, according to Customs broker Mark Taguba, who imported a shipment from China in May that was found to contain 605 kilos of shabu valued at P6.4 billion. Taguba said he handled the release of 500 shipments through the BOC from March to May this year alone, although Customs officials said the actual figure is 630. That's P17 million in bribes in just three months.
Taguba, who claims he did not know the shipment on May 23 included shabu, could face either Oplan Double Barrel or life in prison for large-scale drug trafficking. But his cooperation in the House probe might lighten his penalty.
Under pressure from congressmen, Taguba also admitted that the recipient of his payoffs was at the House probe. The BOC official remained unnamed as of yesterday.
If Taguba is telling the truth, that BOC official deserves Tokhang more than the penny ante drug users who have been killed.
At this point, listening to the testimonies at the parallel probes on the shabu smuggling being conducted by the House and Senate, you can see how corruption has burrowed like a termite into the foundations of the BOC.
It's an infestation that needs to be exterminated. The BOC has become a milking cow of crooks, who have managed to sabotage every effort to stop corruption. Computerization? Computer wires are painted with sardine sauce so they are gnawed away by rats. CCTV? Disabled. Computerized processing to eliminate personal discretion in assessment and processing? New steps or requirements are imposed to keep the discretion intact, so that there will always be a reason to extract facilitation fees.
Red tape, corruption and sheer inefficiency in the BOC are among the biggest hindrances to doing business in this country. Those trying to promote Pinoy entrepreneurship must first fix the mess at Customs.
One encounter with BOC crooks, especially for startups and micro entrepreneurs, is enough to make a budding businessman give up and opt instead to be an overseas Filipino worker. It can discourage returning OFWs from investing their hard-earned money even in home-based enterprises. They must run a gauntlet just to bring in a few kilos of specialty food items while the well connected can have luxury vehicles and shipping containers, contents unknown, waved through the BOC green lane.
"Micro enterprise" under the law is a business activity whose total assets do not exceed P3 million. They are not spared from shakedowns in the BOC. Enterprises with assets of less than P1 million do not stand a chance of importing anything.
With the creation of the ASEAN Economic Community, we should be seeing a tariff-free flow of goods and services within the region. For competitive enterprises, the free market should be a boon. Even home-based businesses at the bottom of micro enterprises can benefit.
With no more need to personally assess the value of shipments and required tariffs and duties, Customs personnel can focus on intercepting contraband such as prohibited drugs, guns and ammunition, biological hazards and weapons of mass destruction.
The government will lose revenue, but this must be weighed against the boost to business, job creation and overall national productivity and competitiveness as Customs processes are simplified and opportunities for graft are plugged. From new or expanded enterprises and their employees, taxes can be collected.
Those in power or with the right connections have no problem getting their shipments through Customs. This poses unfair competition and throws normal market forces awry, which also discourage investments. It gives undue advantage to the nation's power elite and helps perpetuate yawning income disparities in this country.
President Duterte paints himself as a non-member of the elite, and he has made noises about leveling the playing field. If he is serious in this mission, exterminating the termites at Customs is indispensable for success.
UNHRC: What we have here is a PR problem
SHOOTING STRAIGHT - By Bobit S. Avila | July 18, 2019 - 12:00am
Frankly speaking, I'm for leaving the United Nations Human Rights Council (UNHRC) for it has become a PR problem to the Philippines.
Destined for war
BREAKTHROUGH - By Elfren S. Cruz | July 18, 2019 - 12:00am
China and the United States are currently on a collision course for war – unless both parties take difficult and painful actions to avert it.
FIRST PERSON - By Alex Magno | July 18, 2019 - 12:00am
When Nicolas Sarkozy was president of France, among his major problems was a very high youth unemployment rate.
Going back to the dark ages?
FROM THE STANDS - By Domini M. Torrevillas | July 18, 2019 - 12:00am
During a recent interview with the press, former president, former Manila mayor, and now citizen Joseph Estrada addressed the allegations of newly elected Manila Mayor Isko Moreno about the way the ex-mayor supposedly...
Philippines, US dialogue on regional security
POSTSCRIPT - By Federico D. Pascual Jr. | July 18, 2019 - 12:00am
IT IS reassuring that despite President Duterte's dalliance with China, the Philippines sat this week for serious talks with its only defense partner, the United States, on economic relations and regional...
EDITORIAL - Cleaning up the BOC
Many employees of the Bureau of Customs have civil service eligibility and can be removed only for cause.
We can avoid a road to ruin
AUSSIE DIPLOMACY MATTERS - By Steven J. Robinson | July 18, 2019 - 12:00am
When you look at the state of the world, you might be worried that we are on a road to ruin.
EDITORIAL - National dengue alert
The provinces of Guimaras and Capiz have declared outbreaks of dengue. Since the start of the year, the National Disaster Risk Reduction and Management Council has recorded 89 deaths out of 15,803 reported cases...
Charter-Change: For federal or selfish politics?
GOTCHA - By Jarius Bondoc | July 17, 2019 - 12:00am
President Rody Duterte's selection of two Speakers was not only to end squabbling among his political pals. It is seen also to seal Congress' rewrite of the Constitution to his liking. No permanent friends...
Pulling out
SKETCHES - By Ana Marie Pamintuan | July 17, 2019 - 12:00am
As of last night, the word from the secretary of foreign affairs was that the Philippines would not pull out of the United Nations Human Rights Council (UNHRC) or cut diplomatic ties with Iceland.


Dressing up your home for the Summer? Want to add some stunning beach decor to your home or beach home? Then this Time to Relax MatMates Doormat will make the perfect addition to your home. Created using a dye sublimation proces that forces the ink deep into the polyester top surface, this mat is fade and stain resistant. Its gorgeous colors and stunning beauty will fit in just about any environment including both inside and outside your home. Made with a recycled rubber back,this mat is slip resistant and so it will not slid around.
Display your mat in the kitchen, at the front door, back door, deck door, in the bathroom and any other places you can think of.
This summer beach Time to Relax MatMate doormat is 18" x 30" x 3/16" when used as a stand-alone mat. You can view how your mat will look as a stand alone mat or in the various trays that we currently offer by clicking on the images above.


A top destination, for a multitude of reasons, China's Beijing has soared in terms of popularity in recent years. Its growing popularity is largely due to its multitude of attractions, as well as its growing number of air connections.
Top sites to see in Beijing range from the most touristic destinations to less so but definitely worth seeing sites. There are, in fact, some top sites to see in Beijing that have not become top on the tourist trail, yet they are impressive and enticing to any traveller. Low cost airline tickets have made China a lot more accessible to the West, with some providers offering return tickets as low as £350.
Beijing conjures up images of the famed Tiananmen Square, the Great Wall and the Forbidden City. These celebrated sites are iconic for unique reasons. Take the Forbidden City, for example, one of the most famous sites to visit in the world. The magnanimous palace compound of China's yesteryear, the Forbidden City is a perfect example of great historical preservation.
Previously home to 24 emperors of the Ming and the Qing dynasties, it is now known as one of the most important palaces in the world and is a UNESCO World Heritage site. It is an impressive and awe-inspiring site which is host to over 90 palaces and covers over 178 acres. This is one Beijing attraction which is definitely worth a visit.
The huge open space of Tiananmen Square is impressive to witness. Located in the geographical centre of Beijing, this vast square is decorated with ornate and beautiful flowers. Many people visit the square to see the raising of the national flag, which is quite a spectacle. There are three musts-sees within Tiananmen Square: Tiananmen Tower, the Great Hall of the People and the Mao Zedong Memorial Hall.
No trip to China is ever free of the mention of the Great Wall. From Beijing, you can take a trip to cover some of the expanse of the Great Wall. Its popularity is totally warranted. After all, what is not impressive about this magnificent architectural accomplishment? The longest structure built in the world, and declared as one of the Seven Wonders of the World, the Great Wall is beyond majestic.
Having mentioned the great tourist sites of Beijing that must definitely be seen, it is also worth discovering other sites, attractions and things to do in Beijing. Why not take the afternoon to visit the Summer Palace, or perhaps adventure to a pearl or silk factory to find out more about China's famous exported goods.
For fun sites, why not head to Beijing Zoo and see the Panda House? Beijing Zoo is huge and host to an array of different exhibitions and shows. The Panda House is the most popular. The animal spaces are large, taking into account the size of the animals. The entirety of the zoo is arranged and decorated in an old Chinese fashion and is fun to see.


Accomplish in seconds what would take an experienced analyst 30-60 minutes. Check your analytics settings and data for issues. Makes sure you are following best practices. Over 30 different types of checks result in a detailed report of up to 60 lines. Use it now: Sign In or Start Free Trial.
Detailed Google Analytics Audit in 30 Seconds!
The tool works by analysing your Google Analytics property and profile settings as well as actual data gathered. It does over 30 different types of checks to make sure your Google Analytics setup is on the right track. Running the tool can produce up to 60 lines of detailed issue descriptions and recommendations for fixes and solutions to the problems found.
In this video we present the Google Analytics Health Status tool from Analytics-Toolkit.com.
This is the ultimate audit tool for your Google Analytics setup. You can use it when taking on a new client to make sure their tracking doesn't have major issues. It's also a good idea to routinely check the status of all websites, just in case the latest site update broke your tracking. In short – use this tool on every analytics account you work with, in order to make sure you are working with good data.
The tool checks your settings, does analysis on your data and looks for signs of issues. It currently does more than 30 different types of checks in a matter of seconds. This would normally take a trained web analyst at least half an hour to complete.
The checks are split in several groups – general, site search, goal tracking, AdWords campaign tracking, e-commerce transaction tracking and more.
You get a detailed audit report containing status and description for each check performed. When an issue is detected, you are presented with a detailed description and recommended fixes.
Now, let's proceed with an example. First, select the view you want to check. Then, tick all options that apply to the website. In this example it is an e-commerce site, they are advertising on Google AdWords and they also have other advertising campaigns.
Then, press "Check Health". The results usually take around 10 seconds to appear.
First, we see the General section. It contains checks for the Google Analytics code and its placement, whether visits are tracked, whether site speed sample rate is good enough and others.
As you can see, the messages are informative and propose fixes, where such are not self-explanatory. In this case the tool suggests the usage of the siteSpeedSampleRate variable to fix the issue.
Then follows the Google Adwords section. As we can see campaigns are properly tagged and cost data is imported, but there is a big issue with discrepancies between ad clicks and visits to the site. This is not a definite fail, but since it is still something that can be a sign for several different issues, our tool shows a WARNing sign, so that you can consider investigating further.
You can see some other passes and fails in the site goals and e-commerce sections. Not all our checks print out a message. Some of them will only display something when they produce a fail or a warning.
That was our Google Analytics health status tool. We hope it's useful in helping you follow best practices and evaluating your analytics data integrity.
What is covered in the checks?
The checks cover a few categories of property/profile setup as well as data quality issues: general tracking issues, general data quality issues, site search setup, goal setup, campaign tracking, AdWords tracking, E-Commerce tracking & data quality. Some checks are silent (display a result only if they detect an issue). You can validate any Google Analytics setup in mere seconds.


Hot-firing tests of the new Vinci cryogenic upper-stage engine reached a new high on Wednesday with a successful test lasting 60 seconds. This is the longest duration test since trials began and the first carried out under full test conditions.
To meet these conditions the turbo-pumps ran at full revolution and full pressure was reached in both the thrust chamber and in the turbo-pumps. During previous tests, the engine was shut down at between 25% to 50% pressure level, well before these conditions were reached, to protect the hardware.
Vinci, developed under ESA's Ariane-5 Plus launcher programme, is a new upper-stage launcher engine. This re-ignitable cryogenic engine uses a more efficient expander cycle, which does not require a gas generator to drive its two turbo-pumps: one for liquid hydrogen (LH2) and one for liquid oxygen (LOX). Vinci will provide 18 tonnes of thrust in vacuum with a specific impulse of 465 sec. The technologies involved in developing this new upper stage engine are of great importance for future developments in the European propulsion sector.
Wednesday's ignition was as smooth as in previous tests and when the supersonic flame front stabilised it was thumbs up from all present for a long duration run. Vinci behaved as predicted and performed flawlessly; adding an important milestone to the engine's development.
Since the first test in May this year, lasting just one second, the length of the firing has been gradually increased until full establishment of the combustion process and stable 60 seconds run in Wednesday's trial. This test, with all systems running at nominal conditions and for a longer duration, provided the certainty that the design objectives have been achieved.
"The fact that we were able to perform a long duration test after only a few initial runs with this new launcher engine is a remarkable European success. Particularly as the tests have been performed in the new DLR test facility, which is of high complexity and unique in Europe" said Jean-Marc Ruault of the French Space Agency, CNES Vinci Project Manager.
Vinci engine M-1 will now be taken out of the test chamber to undergo detailed inspection for characterisation of its mechanical status and validation of lifetime predictions. Data gathered during the tests will also be evaluated during the next few weeks by the industry teams involved in the project.
From September a second development engine, Vinci M-2, will be used for further testing and characterisation of engine behaviour. This second engine has been modified based on experience gained during manufacturing, integration and previous tests.
The trials are being carried out in the new P4.1 test stand of the German Space Agency (DLR) in Lampoldshausen, Germany; a facility built to perform hot-firing tests under near-realistic space conditions. It is unique in Europe both for its size and because it allows continuous hot-firing tests of upper-stage engines. The test stand can provide up to 20 tonnes of thrust at a pressure level of below 200 mbar and typically at 60 mbar. Maximum test duration is 10 minutes.
"All our thanks go to the European industry team around the prime contractor SNECMA for the constant effort to reach this milestone, and to DLR for establishing the test facility," said Ruault.


text:
Top Investments in Hessen 2020
Google is Building New Data Centers in Hessen
The rapid digitalization developments in everyday life as well as in science and research call for an expansion of the technical infrastructure in the form of high-performance data centers.
Google is expanding with three projects in the FrankfurtRheinMain region and is looking for more space. The company has purchased two properties in Erlensee/ Main-Kinzig County (250,000 square meters, 50-60 jobs) and in Dietzenbach/ Offenbach County (around 58,000 square meters). The construction of data centers is planned at both locations. The US project developer Hillwood was commissioned for the construction in Erlensee and the laying of power lines has already begun.
Additionally, Google rented 4,600 square meters of office space for around 200 employees in April 2020 at Opernplatz in the Global Tower.
Almost forty percent of all capacities in German colocation data centers are located in Hessen, and Frankfurt has DE-CIX, one of the world's largest internet nodes. The generally good accessibility of the region also makes Hessen attractive for large IT companies such as Google.
US Online-Company Wayfair Invests in New Logistics Center in Lich
Wayfair, one of the world's largest online destinations for the home and every items which has something to do with the home, decided to invest in Hessen.
This greenfield investment of EUR 100 Million on a total area of 200,000 square meters will create 90,000 square meters floor space for logistics. About 500 new employees will be hired.
Wayfair Inc.. founded in 2002, is an American e-commerce company that sells furniture and home-goods. Their digital platform offers 14 million items from more than 11,000 global suppliers. The online company is headquartered in Boston, Massachusetts.
Construction site for Wayfair © Manfred Henss
AMG Invests in Battery Pilot Plant
The Dutch AMG Advanced Metallurgical Group N.V. announced in November 2020, that AMG Lithium GmbH Frankfurt has invested in a solid state battery (SSB) pilot plant to complement its state-of-the-art battery materials laboratory located in Frankfurt, Germany.
SSBs are on the verge of revolutionizing the lithium battery market for electric vehicles by enabling an increase in energy density on the battery cell level. In addition, compared to conventional lithium-ion batteries, SSBs have better safety characteristics due to their utilization of solid electrolytes. Our team of experts has developed a unique proprietary process to manufacture customized sulfidic solid electrolyte materials.
AMG is a global company with about 3,000 employees worldwide.
Volvo Dietzenbach Modernized
Volvo is strengthening its presence in Germany: After extensive new construction and renovation work, the traditional Volvo location in Dietzenbach, Hessen, with the Consumer Relations Center and the Competence Center is now fully functional.
On the 24,100 square meter area with two building complexes and large open space, a new service center has been built over the past year and a half and the existing training center has been extensively modernized.
With an investment in the high single-digit millions and the establishment of around 200 jobs, the German importer of the Swedish premium automobile brand is setting the course for future tasks in these important areas as well.
Swedish Klarna Opens New Office in Giessen
Internet service provider Klarna has inaugurated its new rooms in the old telegraph office and in a new building beside it in Giessen. After decades of vacancy, the old building is being used again, and thus, history and future are connected there in a special way. Due to the corona crisis, employees followed the opening from their home office via video conference – the internet service provider decided not to hold a celebration because of the pandemic.
The rooms at the previous location in Linde not far from Gießen had not enough space. A technology mile is now being built in the Gießen train station area, also with the arrival of Klarna. The Internet service provider not only establishes new technologies, but also forms of work, such as the "New Work philosophy".
Klarna is one of the globally responsible payment providers and a licensed bank that maintains the payment experience for buyers and merchants. Headquarter of Klarna is Stockholm, Sweden.
Klarna opening © Falk
Posco Europe Moves to Hessen
After more than a quarter of a century in North Rhine-Westphalia, POSCO moved its office to FrankfurtRheinMain, following LG Electronics' similar move three years ago. Now all famous Korean companies are united in FrankfurtRheinMain. From its location in Eschborn the company will monitor the European market and its European subsidiaries. The number of jobs created is 30.
Founded in 1968, Pohang Iron and Steel Company (POSCO) is a South Korean steel company with headquarters in Pohang. The company is the 4th largest in Korea by turnover (2019) and one of the largest steel producers in the world. It has several locations worldwide and employs about 30,000 people.
Invest in Hessen
A New German Unicorn in Personio New German Hydrogen Initiative


text:
/* @flow */

var DispatcherInstance = require('../dispatcher/DispatcherInstance.js');
var DispatcherInterface = require('../dispatcher/DispatcherInterface.js');
var StoreFacade = require('./StoreFacade.js');

var invariant = require('../invariant.js');

function emptyGetter() {
  return null;
}

var HINT_LINK =
  'Learn more about defining stores:' +
  ' https://github.com/HubSpot/general-store#create-a-store';

class StoreDefinition {

  _facade: ?StoreFacade;
  _getter: ?Function;
  _responses: {[key: string]: (data: any) => void};

  constructor() {
    this._facade = null;
    this._getter = null;
    this._responses = {};
  }

  defineGet(
    getter: () => any
  ): StoreDefinition {
    invariant(
      !this.isRegistered(),
      'StoreDefinition.defineGet: this store definition cannot be modified' +
      ' because is has already been registered with a dispatcher. %s',
      HINT_LINK
    );
    invariant(
      typeof getter === 'function',
      'StoreDefinition.defineGet: expected getter to be a function but got' +
      ' "%s" instead. %s',
      getter,
      HINT_LINK
    );
    this._getter = getter;
    return this;
  }

  defineResponseTo(
    actionTypes: Array<string> | string,
    response: (data: any) => void
  ): StoreDefinition {
    invariant(
      !this.isRegistered(),
      'StoreDefinition.defineResponseTo: this store definition cannot be' +
      ' modified because is has already been registered with a dispatcher. %s',
      HINT_LINK
    );
    [].concat(actionTypes).forEach(actionType => {
      return this._setResponse(actionType, response);
    });
    return this;
  }

  isRegistered(): bool {
    return this._facade instanceof StoreFacade;
  }

  register(dispatcher: ?Dispatcher): StoreFacade {
    dispatcher = dispatcher || DispatcherInstance.get();
    invariant(
      typeof dispatcher === 'object',
      'StoreDefinition.register: you haven\'t provide a dispatcher instance.' +
      ' You can pass an instance to' +
      ' GeneralStore.define().register(dispatcher) or use' +
      ' GeneralStore.DispatcherInstance.set(dispatcher) to set a global' +
      ' instance.' +
      ' https://github.com/HubSpot/general-store#default-dispatcher-instance'
    );
    invariant(
      DispatcherInterface.isDispatcher(dispatcher),
      'StoreDefinition.register: Expected dispatcher to be an object' +
      ' with a register method, and an unregister method but got "%s".' +
      ' Learn more about the dispatcher interface:' +
      ' https://github.com/HubSpot/general-store#dispatcher-interface',
      dispatcher
    );
    invariant(
      typeof this._getter === 'function',
      'StoreDefinition.register: a store cannot be registered without a' +
      ' getter. Use GeneralStore.define().defineGet(getter) to define a' +
      ' getter. %s',
      HINT_LINK
    );
    var facade =
      this._facade || new StoreFacade(
        this._getter || emptyGetter,
        this._responses,
        dispatcher
      );
    if (this._facade === null) {
      this._facade = facade;
    }
    return facade;
  }

  _setResponse(
    actionType: string,
    response: (data: any) => void
  ): void {
    invariant(
      typeof actionType === 'string',
      'StoreDefinition.defineResponseTo: expected actionType to be a string' +
      ' but got "%s" instead. %s',
      actionType,
      HINT_LINK
    );
    invariant(
      !this._responses.hasOwnProperty(actionType),
      'StoreDefinition.defineResponseTo: conflicting resposes for actionType' +
      ' "%s". Only one response can be defined per actionType per Store. %s',
      actionType,
      HINT_LINK
    );
    invariant(
      typeof response === 'function',
      'StoreDefinition.defineResponseTo: expected response to be a function' +
      ' but got "%s" instead. %s',
      response
    );
    this._responses[actionType] = response;
  }

}

module.exports = StoreDefinition;