    /*!
     * Ext JS Library 3.3.0
     * Copyright(c) 2006-2010 Ext JS, Inc.
     * licensing@extjs.com
     * http://www.extjs.com/license
     */
    Ext.namespace('Ext.ux.menu');

    /**
     * @class Ext.ux.menu.ListMenu
     * @extends Ext.menu.Menu
     * This is a supporting class for {@link Ext.ux.grid.filter.ListFilter}.
     * Although not listed as configuration options for this class, this class
     * also accepts all configuration options from {@link Ext.ux.grid.filter.ListFilter}.
     */
    Ext.ux.menu.ListMenu = Ext.extend(Ext.menu.Menu, {
        /**
         * @cfg {String} labelField
         * Defaults to 'text'.
         */
        labelField :  'text',
        /**
         * @cfg {String} paramPrefix
         * Defaults to 'Loading...'.
         */
        loadingText : 'Loading...',
        /**
         * @cfg {Boolean} loadOnShow
         * Defaults to true.
         */
        loadOnShow : true,
        /**
         * @cfg {Boolean} single
         * Specify true to group all items in this list into a single-select
         * radio button group. Defaults to false.
         */
        single : false,

        constructor : function (cfg) {
            this.selected = [];
            this.addEvents(
                /**
                 * @event checkchange
                 * Fires when there is a change in checked items from this list
                 * @param {Object} item Ext.menu.CheckItem
                 * @param {Object} checked The checked value that was set
                 */
                'checkchange'
            );

            Ext.ux.menu.ListMenu.superclass.constructor.call(this, cfg = cfg || {});

            if(!cfg.store && cfg.options){
                var options = [];
                for(var i=0, len=cfg.options.length; i<len; i++){
                    var value = cfg.options[i];
                    switch(Ext.type(value)){
                        case 'array':  options.push(value); break;
                        case 'object': options.push([value.id, value[this.labelField]]); break;
                        case 'string': options.push([value, value]); break;
                    }
                }

                this.store = new Ext.data.Store({
                    reader: new Ext.data.ArrayReader({id: 0}, ['id', this.labelField]),
                    data:   options,
                    listeners: {
                        'load': this.onLoad,
                        scope:  this
                    }
                });
                this.loaded = true;
            } else {
                this.add({text: this.loadingText, iconCls: 'loading-indicator'});
                this.store.on('load', this.onLoad, this);
            }
        },

        destroy : function () {
            if (this.store) {
                this.store.destroy();
            }
            Ext.ux.menu.ListMenu.superclass.destroy.call(this);
        },

        <div id="method-Ext.ux.menu.ListMenu-show">/**
         * Lists will initially show a 'loading' item while the data is retrieved from the store.
         * In some cases the loaded data will result in a list that goes off the screen to the
         * right (as placement calculations were done with the loading item). This adapter will
         * allow show to be called with no arguments to show with the previous arguments and
         * thus recalculate the width and potentially hang the menu from the left.
         */
        show : function () {
            var lastArgs = null;
            return function(){
                if(arguments.length === 0){
                    Ext.ux.menu.ListMenu.superclass.show.apply(this, lastArgs);
                } else {
                    lastArgs = arguments;
                    if (this.loadOnShow && !this.loaded) {
                        this.store.load();
                    }
                    Ext.ux.menu.ListMenu.superclass.show.apply(this, arguments);
                }
            };
        }(),

        /** @private */
        onLoad : function (store, records) {
            var visible = this.isVisible();
            this.hide(false);

            this.removeAll(true);

            var gid = this.single ? Ext.id() : null;
            for(var i=0, len=records.length; i<len; i++){
                var item = new Ext.menu.CheckItem({
                    text:    records[i].get(this.labelField),
                    group:   gid,
                    checked: this.selected.indexOf(records[i].id) > -1,
                    hideOnClick: false});

                item.itemId = records[i].id;
                item.on('checkchange', this.checkChange, this);

                this.add(item);
            }

            this.loaded = true;

            if (visible) {
                this.show();
            }
            this.fireEvent('load', this, records);
        },

        /**
         * Get the selected items.
         * @return {Array} selected
         */
        getSelected : function () {
            return this.selected;
        },

        /** @private */
        setSelected : function (value) {
            value = this.selected = [].concat(value);

            if (this.loaded) {
                this.items.each(function(item){
                    item.setChecked(false, true);
                    for (var i = 0, len = value.length; i < len; i++) {
                        if (item.itemId == value[i]) {
                            item.setChecked(true, true);
                        }
                    }
                }, this);
            }
        },

        /**
         * Handler for the 'checkchange' event from an check item in this menu
         * @param {Object} item Ext.menu.CheckItem
         * @param {Object} checked The checked value that was set
         */
        checkChange : function (item, checked) {
            var value = [];
            this.items.each(function(item){
                if (item.checked) {
                    value.push(item.itemId);
                }
            },this);
            this.selected = value;

            this.fireEvent('checkchange', item, checked);
        }
    });