Ext.namespace('Ext.ux.form');

Ext.ux.form.CheckboxCombo = Ext.extend(Ext.form.TriggerField, {
	/**
     * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
     * the dropdown list (defaults to undefined, with no header element)
     */

    // private
    defaultAutoCreate: {tag: "input", type: "text", size: "24", autocomplete: "off"},
    
	/**
     * @cfg {String} listClass The CSS class to add to the predefined 'x-checkboxcombo-list' class
     * applied the dropdown list element (defaults to '').
     */
    listClass: '',
    
	/**
     * @cfg {String} listEmptyText The empty text to display in the data view if no items are found.
     * (defaults to '')
     */
    listEmptyText: '',
    
	/**
     * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always
     * get the class 'x-form-trigger' and triggerClass will be appended if specified
     * (defaults to 'x-form-arrow-trigger' which displays a downward arrow icon).
     */
    triggerClass: 'x-form-arrow-trigger',
    
	/**
     * @cfg {Boolean/String} shadow true or "sides" for the default effect, "frame" for
     * 4-way shadow, and "drop" for bottom-right
     */
    shadow: 'sides',
    
	/**
     * @cfg {String/Array} listAlign A valid anchor position value. See {@link Ext.Element#alignTo} for details
     * on supported anchor positions and offsets. To specify x/y offsets as well, this value
     * may be specified as an Array of {@link Ext.Element#alignTo} method arguments.

     * 
	[ 'tl-bl?', [6,0] ]
	(defaults to 'tl-bl?')
     */
    listAlign: 'tl-bl?',
    
	/**
     * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown
     * (defaults to 300)
     */
    maxHeight: 300,
    
	/**
     * @cfg {Number} minHeight The minimum height in pixels of the dropdown list when the list is constrained by its
     * distance to the viewport edges (defaults to 90)
     */
    minHeight: 90,
    
	/**
     * @cfg {Boolean} selectOnFocus true to select any existing text in the field immediately on focus.
     * Only applies when {@link Ext.form.TriggerField#editable editable} = true (defaults to
     * false).
     */
    selectOnFocus: false,
	
	/**
     * @cfg {Boolean} false to prevent the user from typing text directly into the field, the field will only respond to a click on the trigger to set the value. (defaults to false).
     */
    editable: false,
    
	/**
     * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
     * when {@link #mode} = 'remote' (defaults to 'Loading...')
     */
    loadingText: 'Loading...',
    
	/**
     * @cfg {Boolean} resizable true to add a resize handle to the bottom of the dropdown list
     * (creates an {@link Ext.Resizable} with 'se' {@link Ext.Resizable#pinned pinned} handles).
     * Defaults to false.
     */
    resizable: false,
    
	/**
     * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if
     * {@link #resizable} = true (defaults to 8)
     */
    handleHeight: 8,
    
	/**
     * @cfg {String} mode Acceptable values are 'remote' (Default) or 'local'
     */
    mode: 'remote',
    
	/**
     * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will
     * be ignored if {@link #listWidth} has a higher value)
     */
    minListWidth: 70,
    
    
	/**
     * @cfg {Boolean} lazyInit true to not initialize the list for this combo until the field is focused
     * (defaults to true)
     */
    lazyInit: true,

    
	/**
     * @cfg {Boolean} submitValue False to clear the name attribute on the field so that it is not submitted during a form post.
     * If a hiddenName is specified, setting this to true will cause both the hidden field and the element to be submitted.
     * Defaults to undefined.
     */
    submitValue: undefined,
	
	id:'dump_value',
    firstEntryExclusive: false,
    
    // private
    initComponent: function() {
        Ext.ux.form.CheckboxCombo.superclass.initComponent.call(this);
        this.addEvents(
            'expand',
            'collapse'
        );
		
		//auto-configure store from local array data
        if (this.store) {
            this.store = Ext.StoreMgr.lookup(this.store);
            if (this.store.autoCreated){
                this.displayField = this.valueField = 'field1';
                if (!this.store.expandData){
                    this.displayField = 'field2';
                }
                this.mode = 'local';
            }
        }

        this.selectedIndex = -1;
    },

    // private
    onRender: function(ct, position){
        if (this.hiddenName && !Ext.isDefined(this.submitValue)){
            this.submitValue = false;
        }
        Ext.ux.form.CheckboxCombo.superclass.onRender.call(this, ct, position);
        if (this.hiddenName){
            this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id: (this.hiddenId||this.hiddenName)}, 'before', true);
        }
        if (Ext.isGecko){
            this.el.dom.setAttribute('autocomplete', 'off');
        }
//        this.initList();
        if (!this.lazyInit){
            this.initList();
        } else {
            this.on('focus', this.initList, this, {single: true});
        }
    },

    // private
    initValue: function() {
        Ext.ux.form.CheckboxCombo.superclass.initValue.call(this);
        if (this.hiddenField){
            this.hiddenField.value = Ext.value(Ext.isDefined(this.hiddenValue) ? this.hiddenValue : this.value, '');
        }
    },

    // private
    initList: function() {
        if (!this.list){
            var cls = 'x-checkboxcombo-list',
                listParent = Ext.getDom(this.getListParent() || Ext.getBody()),
                zindex = parseInt(Ext.fly(listParent).getStyle('z-index') ,10);

            if (this.ownerCt && !zindex){
                this.findParentBy(function(ct){
                    zindex = parseInt(ct.getPositionEl().getStyle('z-index'), 10);
                    return !!zindex;
                });
            }

            this.list = new Ext.Layer({
                parentEl: listParent,
                shadow: this.shadow,
                cls: [cls, this.listClass].join(' '),
                constrain:false,
                zindex: (zindex || 12000) + 5
            });

            var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
            this.list.setWidth(lw);
			this.list.swallowEvent('mousewheel');
            this.assetHeight = 0;
            if (this.syncFont !== false){
                this.list.setStyle('font-size', this.el.getStyle('font-size'));
            }

            this.innerList = this.list.createChild({cls:cls + '-inner'});
            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
			this.mon(this.innerList, 'mouseover', this.onListOver, this, {delegate: '.x-form-item'});
			this.mon(this.innerList, 'mousemove', this.onListMove, this, {delegate: '.x-form-item'});
			this.mon(this.innerList, 'click', this.onListClick, this, {delegate: '.x-form-item'});
			
			var cbheight = 'auto';
            if (this.maxHeight != 300) {
	            cbheight = this.maxHeight;
            }
            var autoScroll = true;
            if (Ext.isIE) {
            //if (Ext.isIE && !Ext.isIE8) {	//to test in ie8
            	autoScroll = false;
            }
			this.cbgroup = new Ext.form.CheckboxGroup({
				renderTo: this.innerList,
				columns: 1,
				//autoHeight: true,
				height:cbheight,
				autoScroll:autoScroll,
				border: false,
				items: [{}]
			});
	        this.bindStore(this.store, true);

            if (this.resizable){
                this.resizer = new Ext.Resizable(this.list,  {
                   pinned:true, handles:'se'
                });
                this.mon(this.resizer, 'resize', function(r, w, h){
                    this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
                    this.listWidth = w;
                    this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
                    this.restrictHeight();
                }, this);

                this['innerList'].setStyle('margin-bottom', this.handleHeight+'px');
            }
        }
    },

    getListParent: function() {
        return document.body;
    },

    
	/**
     * Returns the store associated with this combo.
     * @return {Ext.data.Store} The store
     */
    getStore: function() {
        return this.store;
    },
	
    // private
    bindStore: function(store, initial){
        if (this.store && !initial){
            if (this.store !== store && this.store.autoDestroy){
                this.store.destroy();
            } else {
                this.store.un('beforeload', this.onBeforeLoad, this);
                this.store.un('load', this.onLoad, this);
                this.store.un('exception', this.collapse, this);
            }
            if (!store){
                this.store = null;
            }
        }
        if (store){
            if (!initial) {
                this.lastQuery = null;
            }

            this.store = Ext.StoreMgr.lookup(store);
            this.store.on({
                scope: this,
                beforeload: this.onBeforeLoad,
                load: this.onLoad,
                exception: this.collapse
            });
        }
		
		this.removeCheckboxes();
		this.addCheckboxes();
    },

    // private
    initEvents: function() {
        Ext.ux.form.CheckboxCombo.superclass.initEvents.call(this);

        this.keyNav = new Ext.KeyNav(this.el, {
            'up': function(e){
                this.inKeyMode = true;
				this.selectPrev();
            },
            'down': function(e){
                if (!this.isExpanded()){
                    this.onTriggerClick();
                } else {
					this.inKeyMode = true;
                    this.selectNext();
				}
            },
			'enter': function(e) {
				this.onListEnter();
			},
            'esc': function(e){
                this.collapse();
            },
            'tab': function(e){
                this.collapse();
                return true;
            },
            scope: this,
            doRelay: function(e, h, hname){
                if (hname == 'down' || this.scope.isExpanded()){
                    // this MUST be called before ComboBox#fireKey()
                    var relay = Ext.KeyNav.prototype.doRelay.apply(this, arguments);
                    if (!Ext.isIE && Ext.EventManager.useKeydown){
                        // call Combo#fireKey() for browsers which use keydown event (except IE)
                        this.scope.fireKey(e);
                    }
                    return relay;
                }
                return true;
            },

            forceKeyDown: true,
            defaultEventAction: 'stopEvent'
        });
        if (!this.enableKeyEvents){
            this.mon(this.el, 'keyup', this.onKeyUp, this);
        }
    },

    // private
    onDestroy: function() {
        this.bindStore(null);
        Ext.destroy(
            this.resizer,
            this.cbgroup,
            this.list
        );
        Ext.destroyMembers(this, 'hiddenField');
        Ext.ux.form.CheckboxCombo.superclass.onDestroy.call(this);
    },

    // private
    fireKey: function(e){
        if (!this.isExpanded()) {
            Ext.ux.form.CheckboxCombo.superclass.fireKey.call(this, e);
        }
    },

    // private
    onResize: function(w, h){
        Ext.ux.form.CheckboxCombo.superclass.onResize.apply(this, arguments);
        if (this.isVisible() && this.list){
            this.doResize(w);
        } else {
            this.bufferSize = w;
        }
    },

    doResize: function(w){
        if (!Ext.isDefined(this.listWidth)){
            var lw = Math.max(w, this.minListWidth);
            this.list.setWidth(lw);
            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
        }
    },

    // private
    onEnable: function() {
        Ext.ux.form.CheckboxCombo.superclass.onEnable.apply(this, arguments);
        if (this.hiddenField){
            this.hiddenField.disabled = false;
        }
    },

    // private
    onDisable: function() {
        Ext.ux.form.CheckboxCombo.superclass.onDisable.apply(this, arguments);
        if (this.hiddenField){
            this.hiddenField.disabled = true;
        }
    },

    // private
    onBeforeLoad: function() {
        if (!this.hasFocus){
            return;
        }
		
		this.removeCheckboxes();
		
        //this.innerList.update(this.loadingText ? ' ' + this.loadingText + ' ' : '');
        //this.restrictHeight();
        //this.selectedIndex = -1;
    },

    // private
    onLoad: function() {
        if (!this.hasFocus){
            return;
        }
        if (this.store.getCount() > 0 || this.listEmptyText){
			this.addCheckboxes();
            this.expand();
            this.restrictHeight();
        } else {
            this.collapse();
        }

    },

    // inherit docs
    getName: function() {
        var hf = this.hiddenField;
        return hf && hf.name ? hf.name : this.hiddenName || Ext.ux.form.CheckboxCombo.superclass.getName.call(this);
    },
	
	// private
    assertValue: function() {
		var cbval = this.cbgroup.getValue();
		if (cbval) {
			var vals = [];
			Ext.each(this.cbgroup.getValue(), function(cb) {
				vals.push(cb.inputValue);
			});
			this.setValue(vals);
		} else {
			this.clearValue();
		}
    },
	
	/**
     * Sets the specified value into the field.  If the value finds a match, the corresponding record text
     * will be displayed in the field.  If the value does not match the data value of an existing item,
     * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
     * Otherwise the field will be blank (although the value will still be set).
     * @param {String} value The value to match
     * @return {Ext.form.Field} this
     */
    setValue: function(vals) {
		if (typeof vals == 'string') {
			vals = vals.split(',');
		}
		
		var text = [];

		Ext.each(vals, function(v) {
			if (this.valueField){
				var r = this.findRecord(this.valueField, v);
				if (r) {
					text.push(r.data[this.displayField]);
					r.checked = true;
				}
			}
		}, this);
		
		if (this.hiddenField) {
            this.hiddenField.value = Ext.value(vals.join(','), '');
        }
		
		this.lastSelectionText = text.join(', ');
		Ext.ux.form.CheckboxCombo.superclass.setValue.call(this, text.join(', '));
		this.value = vals.join(',');
		return this;
    },  
	
	/**
     * Returns the currently selected field value or empty string if no value is set.
     * @return {String} value The selected value
     */
    getValue: function() {
        if (this.valueField){
            return Ext.isDefined(this.value) ? this.value : '';
        } else {
            return Ext.ux.form.CheckboxCombo.superclass.getValue.call(this);
        }
    },
    
	/**
     * Clears any text/value currently set in the field
     */
    clearValue: function() {
        if (this.hiddenField){
            this.hiddenField.value = '';
        }
        this.setRawValue('');
        this.lastSelectionText = '';
        this.applyEmptyText();
        this.value = '';
    },

    // private
    findRecord: function(prop, value){
		var record;
		if (this.store.getCount() > 0) {
			record = this.store.getAt(this.store.find(prop, value));
			return (record ? record : false);
		}
    },
	
	// private
    onListMove: function(e, t) {
        this.inKeyMode = false;
    },
	
	// private
	onListOver: function(e, t) {
		var target = e.getTarget('div.x-form-item');
		if (target) {
			target = Ext.get(target);
			target.radioClass('x-checkboxcombo-item-over');
		}
    },
	
	// private
	onListClick: function(e, t) {
		if (Ext.get(e.getTarget()).dom.tagName == 'INPUT' || Ext.get(e.getTarget()).dom.tagName == 'LABEL') {
			return;
		}
		
		var target = e.getTarget('div.x-form-item');
		if (target) {
			target = Ext.get(target);
			var cb = target.child('input');
			cb = Ext.getCmp(cb.id);
			cb.setValue(cb.getValue() ? false : true);
		}
    },
	
	// private
	onListEnter: function(e, t) {
		var target = Ext.DomQuery.selectNode('.x-checkboxcombo-item-over', this.list.dom);
		if (target) {
			target = Ext.get(target);
			var cb = target.child('input');
			cb = Ext.getCmp(cb.id);
			cb.setValue(cb.getValue() ? false : true);
		}
    },

    // private
    restrictHeight: function() {
        this.innerList.dom.style.height = '';
        var inner = this.innerList.dom,
            pad = this.list.getFrameWidth('tb') + (this.resizable ? this.handleHeight : 0) + this.assetHeight,
            h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight),
            ha = this.getPosition()[1]-Ext.getBody().getScroll().top,
            hb = Ext.lib.Dom.getViewHeight()-ha-this.getSize().height,
            space = Math.max(ha, hb, this.minHeight || 0)-this.list.shadowOffset-pad-5;
            
        h = Math.min(h, space, this.maxHeight);
        this.innerList.setHeight(h);
        this.list.beginUpdate();
        this.list.setHeight(h+pad);
        this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
        this.list.endUpdate();
    },

    
	/**
     * Returns true if the dropdown list is expanded, else false.
     */
    isExpanded: function() {
        return this.list && this.list.isVisible();
    },
	
	// private
    selectNext: function() {
        var ct = this.store.getCount();
        if (ct > 0) {
			var el = Ext.DomQuery.selectNode('.x-checkboxcombo-item-over', this.list.dom);
			if (!el) {
				this.innerList.child('.x-form-item').radioClass('x-checkboxcombo-item-over');
			} else {
				Ext.get(el).next().radioClass('x-checkboxcombo-item-over');
			}
        }
    },

    // private
    selectPrev: function() {
		var ct = this.store.getCount();
        if (ct > 0) {
			var el = Ext.DomQuery.selectNode('.x-checkboxcombo-item-over', this.list.dom);
			if (!el) {
				this.innerList.child('.x-form-item').radioClass('x-checkboxcombo-item-over');
			} else {
				Ext.get(el).prev().radioClass('x-checkboxcombo-item-over');
			}
        }
    },

    // private
    validateBlur: function() {
        return !this.list || !this.list.isVisible();
    },

    // private
    beforeBlur: function() {
        this.assertValue();
    },

    // private
    postBlur: function() {
        Ext.ux.form.CheckboxCombo.superclass.postBlur.call(this);
        this.collapse();
        this.inKeyMode = false;
    },
    
	/**
     * Hides the dropdown list if it is currently expanded. Fires the {@link #collapse} event on completion.
     */
    collapse: function() {
        if (!this.isExpanded()){
            return;
        }
        this.list.hide();
        Ext.getDoc().un('mousewheel', this.collapseIf, this);
        Ext.getDoc().un('mousedown', this.collapseIf, this);
		this.beforeBlur();
        this.fireEvent('collapse', this);
    },

    // private
    collapseIf: function(e){
        if (!e.within(this.wrap) && !e.within(this.list)){
            this.collapse();
        }
    },
    
	/**
     * Expands the dropdown list if it is currently hidden. Fires the {@link #expand} event on completion.
     */
    expand: function() {
        if (this.isExpanded() || !this.hasFocus){
            return;
        }
        if (this.bufferSize){
            this.doResize(this.bufferSize);
            delete this.bufferSize;
        }
        this.list.alignTo.apply(this.list, [this.el].concat(this.listAlign));
        this.list.show();
        if (Ext.isGecko2){
            this.innerList.setOverflow('auto'); // necessary for FF 2.0/Mac
        }
        this.mon(Ext.getDoc(), {
            scope: this,
            mousewheel: this.collapseIf,
            mousedown: this.collapseIf
        });
        this.fireEvent('expand', this);
    },

    removeCheckboxes: function() {
		// Remove any existing checkboxes
		for (var i=0; i < this.cbgroup.columns; i++){
			if (this.cbgroup.panel.getComponent(i).items.length > 0){
				this.cbgroup.panel.getComponent(i).items.each(function(cb) {
					cb.destroy();
				});
			}
		}
	},
	
	addCheckboxes: function() {
		// Add new checkboxes from store
		var count = 0;
		Ext.each(this.store.data.items, function(rec) {
			var checkbox = new Ext.form.Checkbox({
				id:this.id+'-checkbox'+count,
				name: rec.data.id + '-checkbox', 
				boxLabel: rec.data[this.displayField], 
				inputValue: rec.data[this.valueField], 
				checked: (rec.checked ? rec.checked : false)
			});
			if (this.firstEntryExclusive) {
				if (count == 0) {
					checkbox.on('check', function(cb, checked){
						if (checked) {
							for (var cb_count = 0; ; cb_count++) {
								if (cb_count != 0) {
									var checkbox = Ext.getCmp(this.id+'-checkbox'+cb_count);
									if (checkbox) {
										checkbox.setValue(false);
									} else {
										return;
									}
								}
							}
						}
					}, this);
				} else {
					checkbox.on('check', function(cb, checked){
						if (checked) {
							Ext.getCmp(this.id+'-checkbox0').setValue(false);
						}
					}, this);
				}
			}
			var col = this.cbgroup.panel.items.get(0);
			this.cbgroup.items.add(checkbox);
			col.add(checkbox);
			this.cbgroup.panel.doLayout();
			count++;
		}, this);
	},
	
	/**
     * @method onTriggerClick
     * @hide
     */
    // private
    // Implements the default empty TriggerField.onTriggerClick function
    onTriggerClick: function() {
        if (this.readOnly || this.disabled){
            return;
        }
        if (this.isExpanded()){
            this.collapse();
            this.el.focus();
        } else {
            this.onFocus({});
            this.expand();
            this.el.focus();
        }
    },
	
	// A renderer for displaying the values in a grid
	gridRenderer: function(value) {
		if (typeof value == 'string') {
			value = value.split(',');
		}
		
		var text = [];
	
		Ext.each(value, function(v) {
			if (this.valueField){
				var r = this.findRecord(this.valueField, v);
				if (r) {
					text.push(r.data[this.displayField]);
				}
			}
		}, this);
		
		return text.join(', ');
	}
});
Ext.reg('checkboxcombo', Ext.ux.form.CheckboxCombo);
