//@tag dom,core
//@define Ext.Element-all
//@define Ext.Element-traversal
//@require Ext.Element-style

/**
 * @class Ext.dom.Element
 */
Ext.dom.Element.addMembers({
    getParent: function() {
        return Ext.get(this.dom.parentNode);
    },

    getFirstChild: function() {
        return Ext.get(this.dom.firstElementChild);
    },

    /**
     * この要素が、渡された要素の先祖である場合に`true`を返します。
     * @param {HTMLElement/String} element チェックする要素。
     * @return {Boolean} この要素が渡された要素の先祖である場合は`true`、`それ以外`は`false`。
     */
    contains: function(element) {
        if (!element) {
            return false;
        }

        var dom = Ext.getDom(element);

        // we need el-contains-itself logic here because isAncestor does not do that:
        return (dom === this.dom) || this.self.isAncestor(this.dom, dom);
    },

    /**
     * このノードから、そしてその親ノードから渡されたシンプルセレクタと一致するものを検索します。 (例:div.som-classまたはspan:first-child)
     * @param {String} simpleSelector テストするシンプルセレクタ。
     * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
     * 検索する最大の深さを数値または要素で指定します(デフォルトは`50またはdocument.body`)。
     * @param {Boolean} returnEl (optional) `true`に設定すると、DOMノードの代わりにExt.Elementオブジェクトを返します。
     * @return {HTMLElement/null} 一致するDOMノード(何も一致しない場合は`null`)。
     */
    findParent: function(simpleSelector, maxDepth, returnEl) {
        var p = this.dom,
            b = document.body,
            depth = 0,
            stopEl;

        maxDepth = maxDepth || 50;
        if (isNaN(maxDepth)) {
            stopEl = Ext.getDom(maxDepth);
            maxDepth = Number.MAX_VALUE;
        }
        while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
            if (Ext.DomQuery.is(p, simpleSelector)) {
                return returnEl ? Ext.get(p) : p;
            }
            depth++;
            p = p.parentNode;
        }
        return null;
    },

    /**
     * 親ノードから渡されたシンプルセレクタと一致するものを検索します。(例えば、div.some-classまたはspan:first-child)
     * @param {String} simpleSelector テストするシンプルセレクタ。
     * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
     * 検索する最大の深さを数値または要素で指定します(デフォルトは`10またはdocument.body`)。
     * @param {Boolean} returnEl (optional) `true`に設定すると、DOMノードの代わりにExt.Elementオブジェクトを返します。
     * @return {HTMLElement/null} 一致するDOMノード(何も一致しない場合は`null`)。
     */
    findParentNode: function(simpleSelector, maxDepth, returnEl) {
        var p = Ext.fly(this.dom.parentNode, '_internal');
        return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
    },

    /**
     * 渡されたシンプルセレクタと一致する親ノードを探すためにDOMを上がっていきます(例:div.some-classまたはspan:first-child)。これは、必ずExt.dom.Elementを返す`findParentNode()`のショートカットです。
     * @param {String} simpleSelector テストするシンプルセレクタ。
     * @param {Number/String/HTMLElement/Ext.Element} maxDepth (optional)
     * 検索する最大の深さを数値または要素で指定します(デフォルトは`10またはdocument.body`)。
     * @return {Ext.dom.Element/null} 一致するDOMノード(何も一致しない場合は`null`)。
     */
    up: function(simpleSelector, maxDepth) {
        return this.findParentNode(simpleSelector, maxDepth, true);
    },

    /**
     * 渡されたCSSセレクタに基づいて要素を選択し、返された値を通じて、1つの文で多数の関係要素に{@link Ext.Element Element}メソッドを適用できるようにします。その要素が検索のルートです。{@link Ext.dom.CompositeElementLite CompositeElementLite}オブジェクト。
     * @param {String/HTMLElement[]} selector CSS セレクタまたは要素の配列
     * @param {Boolean} composite  CompositeElementLiteではなくCompositeElementを返します。デフォルトはfalseです。
     * @return {Ext.dom.CompositeElementLite/Ext.dom.CompositeElement}

     */
    select: function(selector, composite) {
        return Ext.dom.Element.select(selector, composite, this.dom);
    },

    /**
     * 渡されたCSSセレクタに基づき子ノードを選択します(セレクタにはidを含めません)。
     * @param {String} selector CSSセレクタ。
     * @return {HTMLElement[]} 一致したノードの配列。
     */
    query: function(selector) {
        return Ext.DomQuery.select(selector, this.dom);
    },

    /**
     * 渡されたCSSセレクタに基づいて、この要素より下の任意の深さの子を1つ選択します(セレクタにはidを含めません)。
     * @param {String} selector CSSセレクタ。
     * @param {Boolean} [returnDom=false] (optional) `true`に設定すると、Ext.dom.Elementの代わりにDOMノードを返します。
     * @return {HTMLElement/Ext.dom.Element} 子のExt.dom.Element(または`returnDom`が`true`の場合はDOMノード)。
     */
    down: function(selector, returnDom) {
        var n = Ext.DomQuery.selectNode(selector, this.dom);
        return returnDom ? n : Ext.get(n);
    },

    /**
     * 渡されたCSSセレクタに基づいて、単一の*直接*の子を選択します(セレクタにはidを含めません)。
     * @param {String} selector CSSセレクタ。
     * @param {Boolean} [returnDom=false] (optional) `true`に設定すると、Ext.dom.Elementの代わりにDOMノードを返します。
     * @return {HTMLElement/Ext.dom.Element} 子のExt.dom.Element(または`returnDom`が`true`の場合はDOMノード)。
     */
    child: function(selector, returnDom) {
        var node,
            me = this,
            id;
        id = Ext.get(me).id;
        // Escape . or :
        id = id.replace(/[\.:]/g, "\\$0");
        node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
        return returnDom ? node : Ext.get(node);
    },

     /**
     * この要素の親ノードを取得します。 オプションで先祖まで上って行きセレクタと比較します。
     * @param {String} selector (optional) 渡されたシンプルセレクタに一致する親ノードを検索します。
     * @param {Boolean} returnDom (optional) `true`に設定するとExt.dom.Elementの代わりに生のDOMノードを返します。
     * @return {Ext.dom.Element/HTMLElement/null} 親ノード、または`null`。
     */
    parent: function(selector, returnDom) {
        return this.matchNode('parentNode', 'parentNode', selector, returnDom);
    },

     /**
     * テキストノードを飛ばして、次の兄弟を取得します。
     * @param {String} selector (optional) 渡されたシンプルセレクタに一致する次の兄弟要素を検索します。
     * @param {Boolean} returnDom (optional) `true`に設定するとExt.dom.Elementの代わりに生のDOMノードを返します。
     * @return {Ext.dom.Element/HTMLElement/null} 次の兄弟、または`null`。
     */
    next: function(selector, returnDom) {
        return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
    },

    /**
     * テキストノードを飛ばして、前の兄弟を取得します。
     * @param {String} selector (optional) 渡されたシンプルセレクタに一致する前の兄弟を検索します。
     * @param {Boolean} returnDom (optional) `true`に設定するとExt.dom.Elementの代わりに生のDOMノードを返します。
     * @return {Ext.dom.Element/HTMLElement/null} 前の兄弟、または`null`。
     */
    prev: function(selector, returnDom) {
        return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
    },


    /**
     * テキストノードを飛ばして、最初の子要素を取得します。
     * @param {String} selector (optional) 渡されたシンプルセレクタに一致する次の兄弟要素を検索します。
     * @param {Boolean} returnDom (optional) `true`に設定するとExt.dom.Elementの代わりに生のDOMノードを返します。
     * @return {Ext.dom.Element/HTMLElement/null} 最初の子、または`null`。
     */
    first: function(selector, returnDom) {
        return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
    },

    /**
     * テキストノードを飛ばして、最後の子要素を取得します。
     * @param {String} selector (optional) 渡されたシンプルセレクタに一致する前の兄弟を検索します。
     * @param {Boolean} returnDom (optional) `true`に設定するとExt.dom.Elementの代わりに生のDOMノードを返します。
     * @return {Ext.dom.Element/HTMLElement/null} 最後の子、または`null`。
     */
    last: function(selector, returnDom) {
        return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
    },

    matchNode: function(dir, start, selector, returnDom) {
        if (!this.dom) {
            return null;
        }

        var n = this.dom[start];
        while (n) {
            if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
                return !returnDom ? Ext.get(n) : n;
            }
            n = n[dir];
        }
        return null;
    },

    isAncestor: function(element) {
        return this.self.isAncestor.call(this.self, this.dom, element);
    }
});