    /*
     * Note that this control will most likely remain as an example, and not as a core Ext form
     * control.  However, the API will be changing in a future release and so should not yet be
     * treated as a final, stable API at this time.
     */

    /**
     * A control that allows selection of between two Ext.ux.form.MultiSelect controls.
     */
    Ext.define('Ext.ux.form.ItemSelector', {
        extend: 'Ext.ux.form.MultiSelect',
        alias: ['widget.itemselectorfield', 'widget.itemselector'],
        alternateClassName: ['Ext.ux.ItemSelector'],
        requires: [
            'Ext.button.Button',
            'Ext.ux.form.MultiSelect'
        ],

        /**
         * @cfg {Boolean} [hideNavIcons=false] True to hide the navigation icons
         */
        hideNavIcons:false,

        /**
         * @cfg {Array} buttons Defines the set of buttons that should be displayed in between the ItemSelector
         * fields. Defaults to <tt>['top', 'up', 'add', 'remove', 'down', 'bottom']</tt>. These names are used
         * to build the button CSS class names, and to look up the button text labels in {@link #buttonsText}.
         * This can be overridden with a custom Array to change which buttons are displayed or their order.
         */
        buttons: ['top', 'up', 'add', 'remove', 'down', 'bottom'],

        /**
         * @cfg {Object} buttonsText The tooltips for the {@link #buttons}.
         * Labels for buttons.
         */
        buttonsText: {
            top: "Move to Top",
            up: "Move Up",
            add: "Add to Selected",
            remove: "Remove from Selected",
            down: "Move Down",
            bottom: "Move to Bottom"
        },

        initComponent: function() {
            var me = this;

            me.ddGroup = me.id + '-dd';
            me.callParent();

            // bindStore must be called after the fromField has been created because
            // it copies records from our configured Store into the fromField's Store
            me.bindStore(me.store);
        },

        createList: function(){
            var me = this;

            return Ext.create('Ext.ux.form.MultiSelect', {
                submitValue: false,
                flex: 1,
                dragGroup: me.ddGroup,
                dropGroup: me.ddGroup,
                store: {
                    model: me.store.model,
                    data: []
                },
                displayField: me.displayField,
                disabled: me.disabled,
                listeners: {
                    boundList: {
                        scope: me,
                        itemdblclick: me.onItemDblClick,
                        drop: me.syncValue
                    }
                }
            });
        },

        setupItems: function() {
            var me = this;

            me.fromField = me.createList();
            me.toField = me.createList();

            return {
                layout: {
                    type: 'hbox',
                    align: 'stretch'
                },
                items: [
                    me.fromField,
                    {
                        xtype: 'container',
                        margins: '0 4',
                        width: 22,
                        layout: {
                            type: 'vbox',
                            pack: 'center'
                        },
                        items: me.createButtons()
                    },
                    me.toField
                ]
            };
        },

        createButtons: function(){
            var me = this,
                buttons = [];

            if (!me.hideNavIcons) {
                Ext.Array.forEach(me.buttons, function(name) {
                    buttons.push({
                        xtype: 'button',
                        tooltip: me.buttonsText[name],
                        handler: me['on' + Ext.String.capitalize(name) + 'BtnClick'],
                        cls: Ext.baseCSSPrefix + 'form-itemselector-btn',
                        iconCls: Ext.baseCSSPrefix + 'form-itemselector-' + name,
                        navBtn: true,
                        scope: me,
                        margin: '4 0 0 0'
                    });
                });
            }
            return buttons;
        },

        getSelections: function(list){
            var store = list.getStore(),
                selections = list.getSelectionModel().getSelection();

            return Ext.Array.sort(selections, function(a, b){
                a = store.indexOf(a);
                b = store.indexOf(b);

                if (a < b) {
                    return -1;
                } else if (a > b) {
                    return 1;
                }
                return 0;
            });
        },

        onTopBtnClick : function() {
            var list = this.toField.boundList,
                store = list.getStore(),
                selected = this.getSelections(list);

            store.suspendEvents();
            store.remove(selected, true);
            store.insert(0, selected);
            store.resumeEvents();
            list.refresh();
            this.syncValue();
            list.getSelectionModel().select(selected);
        },

        onBottomBtnClick : function() {
            var list = this.toField.boundList,
                store = list.getStore(),
                selected = this.getSelections(list);

            store.suspendEvents();
            store.remove(selected, true);
            store.add(selected);
            store.resumeEvents();
            list.refresh();
            this.syncValue();
            list.getSelectionModel().select(selected);
        },

        onUpBtnClick : function() {
            var list = this.toField.boundList,
                store = list.getStore(),
                selected = this.getSelections(list),
                i = 0,
                len = selected.length,
                index = store.getCount();

            // Find index of first selection
            for (; i < len; ++i) {
                index = Math.min(index, store.indexOf(selected[i]));
            }
            // If first selection is not at the top, move the whole lot up
            if (index > 0) {
                store.suspendEvents();
                store.remove(selected, true);
                store.insert(index - 1, selected);
                store.resumeEvents();
                list.refresh();
                this.syncValue();
                list.getSelectionModel().select(selected);
            }
        },

        onDownBtnClick : function() {
            var list = this.toField.boundList,
                store = list.getStore(),
                selected = this.getSelections(list),
                i = 0,
                len = selected.length,
                index = 0;

            // Find index of last selection
            for (; i < len; ++i) {
                index = Math.max(index, store.indexOf(selected[i]));
            }
            // If last selection is not at the bottom, move the whole lot down
            if (index < store.getCount() - 1) {
                store.suspendEvents();
                store.remove(selected, true);
                store.insert(index + 2 - len, selected);
                store.resumeEvents();
                list.refresh();
                this.syncValue();
                list.getSelectionModel().select(selected);
            }
        },

        onAddBtnClick : function() {
            var me = this,
                fromList = me.fromField.boundList,
                selected = this.getSelections(fromList);

            fromList.getStore().remove(selected);
            this.toField.boundList.getStore().add(selected);
            this.syncValue();
        },

        onRemoveBtnClick : function() {
            var me = this,
                toList = me.toField.boundList,
                selected = this.getSelections(toList);

            toList.getStore().remove(selected);
            this.fromField.boundList.getStore().add(selected);
            this.syncValue();
        },

        syncValue: function() {
            this.setValue(this.toField.store.getRange());
        },

        onItemDblClick: function(view, rec){
            var me = this,
                from = me.fromField.store,
                to = me.toField.store,
                current,
                destination;

            if (view === me.fromField.boundList) {
                current = from;
                destination = to;
            } else {
                current = to;
                destination = from;
            }
            current.remove(rec);
            destination.add(rec);
            me.syncValue();
        },

        setValue: function(value){
            var me = this,
                fromStore = me.fromField.store,
                toStore = me.toField.store,
                selected;

            // Wait for from store to be loaded
            if (!me.fromField.store.getCount()) {
                me.fromField.store.on({
                    load: Ext.Function.bind(me.setValue, me, [value]),
                    single: true
                });
                return;
            }

            value = me.setupValue(value);
            me.mixins.field.setValue.call(me, value);

            selected = me.getRecordsForValue(value);

            Ext.Array.forEach(toStore.getRange(), function(rec){
                if (!Ext.Array.contains(selected, rec)) {
                    // not in the selected group, remove it from the toStore
                    toStore.remove(rec);
                    fromStore.add(rec);
                }
            });
            toStore.removeAll();

            Ext.Array.forEach(selected, function(rec){
                // In the from store, move it over
                if (fromStore.indexOf(rec) > -1) {
                    fromStore.remove(rec);
                }
                toStore.add(rec);
            });
        },

        onBindStore: function(store, initial) {
            var me = this;

            if (me.fromField) {
                me.fromField.store.removeAll()
                me.toField.store.removeAll();

                // Add everything to the from field as soon as the Store is loaded
                if (store.getCount()) {
                    me.populateFromStore(store);
                } else {
                    me.store.on('load', me.populateFromStore, me);
                }
            }
        },

        populateFromStore: function(store) {
            this.fromField.store.add(store.getRange());

            // setValue wait for the from Store to be loaded
            this.fromField.store.fireEvent('load', this.fromField.store);
        },

        onEnable: function(){
            var me = this;

            me.callParent();
            me.fromField.enable();
            me.toField.enable();

            Ext.Array.forEach(me.query('[navBtn]'), function(btn){
                btn.enable();
            });
        },

        onDisable: function(){
            var me = this;

            me.callParent();
            me.fromField.disable();
            me.toField.disable();

            Ext.Array.forEach(me.query('[navBtn]'), function(btn){
                btn.disable();
            });
        },

        onDestroy: function(){
            this.bindStore(null);
            this.callParent();
        }
    });