Ext.form.TreeField = function(config) {
    Ext.form.TreeField.superclass.constructor.call(this, config);
    this.addEvents({ 'expand': true, 'collapse': true, 'beforeselect': true, 'select': true, 'beforequery': true });
};
Ext.extend(Ext.form.TreeField, Ext.form.TriggerField, {
    defaultAutoCreate: { tag: "input", type: "text", size: "24", autocomplete: "off" },
    listWidth: undefined,
    listHeight: 100,
    listClass: '',
    xtype: 'treefield',
    triggerClass: 'x-form-arrow-trigger',
    shadow: 'sides',
    listAlign: 'tl-bl?',
    maxHeight: 300,
    triggerAction: 'query',
    minChars: 4,
    queryDelay: 500,
    selectOnFocus: false,
    queryParam: 'query',
    loadingText: 'Loading...',
    resizable: false,
    handleHeight: 8,
    allQuery: '',
    mode: 'remote',
    minListWidth: 70,
    typeAheadDelay: 250,
    valueNotFoundText: undefined,
    root: undefined,
    animate: true,
    loader: undefined,
    onRender: function(ct, position) {
        Ext.form.TreeField.superclass.onRender.call(this, ct, position);

        if (Ext.isGecko)
            this.el.dom.setAttribute('autocomplete', 'off');

        var cls = 'x-combo-list';
        this.list = new Ext.Layer({ shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain: false, containerScroll: true });

        var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
        this.list.setWidth(lw);
        this.list.swallowEvent('mousewheel');
        this.assetHeight = 0;

        this.innerList = this.list.createChild({ cls: cls + '-inner' });
        this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
        this.innerList.setHeight(this.listHeight);
        this.loader = new Ext.tree.TreeLoader({ preloadChildren: true });
        this.tree = new Ext.tree.TreePanel({ border: false, root: this.root, animate: this.animate === false, enableDD: false, containerScroll: true, rootVisible: false, renderTo: this.innerList, loader: this.loader });
        this.tree.on('click', this.onTreeClick, this);
        this.expand(true);
        if (this.selectedValue) {
            var tn = this.findNode(this.selectedValue);
            this.setValue(tn);
            this.selectedValue = undefined;
        }
        this.tree.getSelectionModel().on("selectionchange", this.selectIntoView, this);
        this.el.dom.setAttribute('readOnly', true);
        this.el.on('mousedown', this.onTriggerClick, this);
        this.el.addClass('x-combo-noedit');
    },
    getSelectedNode: function() { return this.tree.getSelectionModel().getSelectedNode(); },
    initValue: function() { },
    initEvents: function() {
        Ext.form.TreeField.superclass.initEvents.call(this);
        this.keyNav = new Ext.KeyNav(this.el, {
            "up": function(e) { this.selectPrev(); },
            "down": function(e) { if (this.getSelectedNode() == null) this.tree.getRootNode().firstChild.select(); else if (!this.isExpanded()) this.onTriggerClick(); else { this.selectNext(); } },
            "left": function(e) { if (this.getSelectedNode() != null) if (this.getSelectedNode().isExpanded()) this.getSelectedNode().collapse(); else if (this.getSelectedNode().parentNode.attributes.id != "$" && this.getSelectedNode().parentNode != null) this.getSelectedNode().parentNode.select(); else this.selectPrev(); },
            "right": function(e) { if (this.getSelectedNode() != null) { if (this.getSelectedNode().childNodes.length > 0 && !this.getSelectedNode().isExpanded()) this.getSelectedNode().expand(); else if (this.getSelectedNode().parentNode.lastChild.attributes.id != this.getSelectedNode().attributes.id || this.getSelectedNode().childNodes.length > 0) this.selectNext(); } },
            "enter": function(e) { if (this.getSelectedNode() == null) return false; this.onTreeClick(this.getSelectedNode(), e); },
            "esc": function(e) { this.collapse(); },
            "tab": function(e) { if (this.getSelectedNode() == null) return false; this.onTreeClick(this.getSelectedNode(), e); return true; },
            scope: this,
            doRelay: function(foo, bar, hname) {
                if (hname == 'down' || this.scope.isExpanded())
                    return Ext.KeyNav.prototype.doRelay.apply(this, arguments);
                return true;
            },
            forceKeyDown: true
        });
        this.keyNavTree = this.keyNav = new Ext.KeyNav(this.tree.el, {
            "enter": function(e) { this.onTreeClick(this.getSelectedNode(), e); },
            "esc": function(e) { this.collapse(); },
            "tab": function(e) { this.onViewClick(this.getSelectedNode(), e); return true; },
            scope: this
        });
        this.on('blur', this.doForce, this);

    },
    onDestroy: function() {
        if (this.tree) {
            if (!this.tree.root.childNodes)
                this.tree.root.childNodes = [];
            this.tree.destroy();
        }
        if (this.list)
            this.list.destroy();
        Ext.form.TreeField.superclass.onDestroy.call(this);
    },
    fireKey: function(e) {
        if (e.isNavKeyPress() && !this.list.isVisible()) {
            this.fireEvent("specialkey", this, e);
        }
    },
    onResize: function(w, h) {
        Ext.form.TreeField.superclass.onResize.apply(this, arguments);
        if (this.list && this.listWidth === undefined) {
            var lw = Math.max(w, this.minListWidth);
            this.list.setWidth(lw);
            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
        }
    },
    onSelect: function(tn) {
        if (this.fireEvent('beforeselect', this, tn) !== false) {
            this.setValue(tn);
            this.collapse();
            this.fireEvent('select', this, tn);
        }
    },
    getSelectedValue: function() {
        return !this.value ? '' : this.value;
    },
    clearValue: function() {
        this.setRawValue('');
        this.lastSelectionText = '';
        this.applyEmptyText();
        this.value = null;
        this.selectedNode = null;
    },
    setValue: function(tn) {
        if (!tn) {
            tn = { attributes: {} };
            this.selectedNode = undefined;
        }
        else
            this.selectedNode = tn;

        Ext.form.TreeField.superclass.setValue.call(this, tn.text);
        this.lastSelectionText = tn.text;
        this.value = tn.attributes.value;
    },
    findNode: function(value, nodes) {
        if (!nodes)
            nodes = this.root.childNodes;
        if (nodes.length == 0)
            return false;

        for (var x = 0; x < nodes.length; x++)
            if (nodes[x].attributes.value == value)
            return nodes[x];
        else {
            var node = this.findNode(value, nodes[x].childNodes);
            if (node)
                return node;
        }
        return false;
    },
    onTreeClick: function(tn, e) {
        this.onSelect(tn);
        if (tn)
            this.el.focus();
    },
    restrictHeight: function() {
        this.innerList.dom.style.height = '';
        var inner = this.innerList.dom;
        var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
        this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
        this.list.beginUpdate();
        this.list.setHeight(this.innerList.getHeight() + this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight);
        this.list.alignTo(this.el, this.listAlign);
        this.list.endUpdate();
    },
    onEmptyResults: function() {
        this.collapse();
    },
    isExpanded: function() {
        return this.list.isVisible();
    },
    selectByValue: function(v, scrollIntoView) {
        if (v !== undefined && v !== null) {
            var tn = this.findNode(v);
            if (tn)
                this.selectIntoView(null, tn)
        }
        return false;
    },
    selectIntoView: function(model, tn) {
        if (tn)
            this.innerList.scrollChildIntoView(tn.ui.anchor, false);
    },
    selectNext: function() {
        this.tree.getSelectionModel().selectNext();
    },
    selectPrev: function() {
        this.tree.getSelectionModel().selectPrevious();
    },
    validateBlur: function() {
        return !this.list || !this.list.isVisible();
    },
    doForce: function() {
        if (this.el.dom.value.length > 0) {
            this.el.dom.value =
                this.lastSelectionText === undefined ? '' : this.lastSelectionText;
            this.applyEmptyText();
        }
    },
    collapse: function() {
        if (!this.isExpanded())
            return;

        this.list.hide();
        Ext.get(document).un('mousedown', this.collapseIf, this);
        Ext.get(document).un('mousewheel', this.collapseIf, this);
        this.fireEvent('collapse', this);
    },
    collapseIf: function(e) {
        if (!e.within(this.wrap) && !e.within(this.list))
            this.collapse();
    },
    expand: function() {
        if (this.isExpanded() || !this.hasFocus)
            return;

        if (this.selectedNode)
            this.tree.selectPath(this.selectedNode.getPath());
        else
            this.tree.getSelectionModel().clearSelections();
        this.list.alignTo(this.el, this.listAlign);
        this.list.show();
        Ext.get(document).on('mousedown', this.collapseIf, this);
        Ext.get(document).on('mousewheel', this.collapseIf, this);
        this.fireEvent('expand', this);
    },
    onTriggerClick: function() {
        if (this.disabled)
            return;

        this.el.focus();
        if (this.isExpanded())
            this.collapse();
        else {
            this.hasFocus = true;
            this.expand();
        }
    },
    afterCallback: function(rs) {
        var node = this.getSelectedNode();
        this.root.attributes.children = eval(rs["__nodes"]);
        this.loader.load(this.root);
        if (node)
            this.setValue(this.findNode(node.attributes.value));
    }
});
Ext.reg("treefield", Ext.form.TreeField);