    /** * A general picker class. {@link Ext.picker.Slot}s are used to organize multiple scrollable slots into a single picker. {@link #slots} is * the only necessary configuration. * * The {@link #slots} configuration with a few key values: * * - `name`: The name of the slot (will be the key when using {@link #getValues} in this {@link Ext.picker.Picker}). * - `title`: The title of this slot (if {@link #useTitles} is set to `true`). * - `data`/`store`: The data or store to use for this slot. * * Remember, {@link Ext.picker.Slot} class extends from {@link Ext.dataview.DataView}. * * ## Examples * *     @example miniphone preview *     var picker = Ext.create('Ext.Picker', { *         slots: [ *             { *                 name : 'limit_speed', *                 title: 'Speed', *                 data : [ *                     {text: '50 KB/s', value: 50}, *                     {text: '100 KB/s', value: 100}, *                     {text: '200 KB/s', value: 200}, *                     {text: '300 KB/s', value: 300} *                 ] *             } *         ] *     }); *     Ext.Viewport.add(picker); *     picker.show(); * * You can also customize the top toolbar on the {@link Ext.picker.Picker} by changing the {@link #doneButton} and {@link #cancelButton} configurations: * *     @example miniphone preview *     var picker = Ext.create('Ext.Picker', { *         doneButton: 'I\'m done!', *         cancelButton: false, *         slots: [ *             { *                 name : 'limit_speed', *                 title: 'Speed', *                 data : [ *                     {text: '50 KB/s', value: 50}, *                     {text: '100 KB/s', value: 100}, *                     {text: '200 KB/s', value: 200}, *                     {text: '300 KB/s', value: 300} *                 ] *             } *         ] *     }); *     Ext.Viewport.add(picker); *     picker.show(); * * Or by passing a custom {@link #toolbar} configuration: * *     @example miniphone preview *     var picker = Ext.create('Ext.Picker', { *         doneButton: false, *         cancelButton: false, *         toolbar: { *             ui: 'light', *             title: 'My Picker!' *         }, *         slots: [ *             { *                 name : 'limit_speed', *                 title: 'Speed', *                 data : [ *                     {text: '50 KB/s', value: 50}, *                     {text: '100 KB/s', value: 100}, *                     {text: '200 KB/s', value: 200}, *                     {text: '300 KB/s', value: 300} *                 ] *             } *         ] *     }); *     Ext.Viewport.add(picker); *     picker.show(); */Ext.define('Ext.picker.Picker', {    extend: 'Ext.Sheet',    alias : 'widget.picker',    alternateClassName: 'Ext.Picker',    requires: ['Ext.picker.Slot', 'Ext.TitleBar', 'Ext.data.Model', 'Ext.util.InputBlocker'],     isPicker: true,     /**     * @event pick     * Fired when a slot has been picked     * @param {Ext.Picker} this This Picker.     * @param {Object} values The values of this picker's slots, in `{name:'value'}` format.     * @param {Ext.picker.Slot} slot An instance of Ext.Picker.Slot that has been picked.     */     /**     * @event change     * Fired when the value of this picker has changed the Done button has been pressed.     * @param {Ext.picker.Picker} this This Picker.     * @param {Object} values The values of this picker's slots, in `{name:'value'}` format.     */     /**     * @event cancel     * Fired when the cancel button is tapped and the values are reverted back to     * what they were.     * @param {Ext.Picker} this This Picker.     */     config: {        /**         * @cfg         * @inheritdoc         */        baseCls: Ext.baseCSSPrefix + 'picker',         /**         * @cfg {String/Mixed} doneButton         * Can be either:         *         * - A {String} text to be used on the Done button.         * - An {Object} as config for {@link Ext.Button}.         * - `false` or `null` to hide it.         * @accessor         */        doneButton: true,         /**         * @cfg {String/Mixed} cancelButton         * Can be either:         *         * - A {String} text to be used on the Cancel button.         * - An {Object} as config for {@link Ext.Button}.         * - `false` or `null` to hide it.         * @accessor         */        cancelButton: true,         /**         * @cfg {Boolean} useTitles          * Generate a title header for each individual slot and use         * the title configuration of the slot.         * @accessor         */        useTitles: false,         /**         * @cfg {Array} slots          * An array of slot configurations.         *         * - `name` {String} - Name of the slot         * - `data` {Array} - An array of text/value pairs in the format `{text: 'myKey', value: 'myValue'}`         * - `title` {String} - Title of the slot. This is used in conjunction with `useTitles: true`.         *         * @accessor         */        slots: null,         /**         * @cfg {String/Number} value The value to initialize the picker with.         * @accessor         */        value: null,         /**         * @cfg {Number} height          * The height of the picker.         * @accessor         */        height: 220,         /**         * @cfg         * @inheritdoc         */        layout: {            type : 'hbox',            align: 'stretch'        },         /**         * @cfg         * @hide         */        centered: false,         /**         * @cfg         * @inheritdoc         */        left : 0,         /**         * @cfg         * @inheritdoc         */        right: 0,         /**         * @cfg         * @inheritdoc         */        bottom: 0,         /**         * @private         */        defaultType: 'pickerslot',         toolbarPosition: 'top',         /**         * @cfg {Ext.TitleBar/Ext.Toolbar/Object} toolbar         * The toolbar which contains the {@link #doneButton} and {@link #cancelButton} buttons.         * You can override this if you wish, and add your own configurations. Just ensure that you take into account         * the {@link #doneButton} and {@link #cancelButton} configurations.         *         * The default xtype is a {@link Ext.TitleBar}:         *         *     toolbar: {         *         items: [         *             {         *                 xtype: 'button',         *                 text: 'Left',         *                 align: 'left'         *             },         *             {         *                 xtype: 'button',         *                 text: 'Right',         *                 align: 'left'         *             }         *         ]         *     }         *         * Or to use a {@link Ext.Toolbar instead}:         *         *     toolbar: {         *         xtype: 'toolbar',         *         items: [         *             {         *                 xtype: 'button',         *                 text: 'Left'         *             },         *             {         *                 xtype: 'button',         *                 text: 'Left Two'         *             }         *         ]         *     }         *         * @accessor         */        toolbar: {            xtype: 'titlebar'        }    },     floated: true,     initialize: function() {        var me = this,            clsPrefix = Ext.baseCSSPrefix,            innerElement = this.innerElement;         //insert the mask, and the picker bar         this.mask = innerElement.insertFirst({            cls: clsPrefix + 'picker-mask'        });         this.bar = this.mask.createChild({            cls: clsPrefix + 'picker-bar'        });         me.on({            scope   : this,            delegate: 'pickerslot',            slotpick: 'onSlotPick'        });    },     /**     * @private     */    applyToolbar: function(config) {        if (config === true) {            config = {};        }         Ext.applyIf(config, {            docked: this.getToolbarPosition()        });         return Ext.factory(config, 'Ext.TitleBar', this.getToolbar());    },     /**     * @private     */    updateToolbar: function(newToolbar, oldToolbar) {        if (newToolbar) {            this.add(newToolbar);        }         if (oldToolbar) {            this.remove(oldToolbar);        }    },     /**     * Updates the {@link #doneButton} configuration. Will change it into a button when appropriate, or just update the text if needed.     * @param {Object} config      * @return {Object}     */    applyDoneButton: function(config) {        if (config) {            if (Ext.isBoolean(config)) {                config = {};            }             if (typeof config == "string") {                config = {                    text: config                };            }             Ext.applyIf(config, {                ui: 'action',                align: 'right',                text: 'Done'            });        }         return Ext.factory(config, 'Ext.Button', this.getDoneButton());    },     updateDoneButton: function(newDoneButton, oldDoneButton) {        var toolbar = this.getToolbar();         if (newDoneButton) {            toolbar.add(newDoneButton);            newDoneButton.on('tap', this.onDoneButtonTap, this);        } else if (oldDoneButton) {            toolbar.remove(oldDoneButton);        }    },     /**     * Updates the {@link #cancelButton} configuration. Will change it into a button when appropriate, or just update the text if needed.     * @param {Object} config      * @return {Object}     */    applyCancelButton: function(config) {        if (config) {            if (Ext.isBoolean(config)) {                config = {};            }             if (typeof config == "string") {                config = {                    text: config                };            }             Ext.applyIf(config, {                align: 'left',                text: 'Cancel'            });        }         return Ext.factory(config, 'Ext.Button', this.getCancelButton());    },     updateCancelButton: function(newCancelButton, oldCancelButton) {        var toolbar = this.getToolbar();         if (newCancelButton) {            toolbar.add(newCancelButton);            newCancelButton.on('tap', this.onCancelButtonTap, this);        } else if (oldCancelButton) {            toolbar.remove(oldCancelButton);        }    },     /**     * @private     */    updateUseTitles: function(useTitles) {        var innerItems = this.getInnerItems(),            ln = innerItems.length,            cls = Ext.baseCSSPrefix + 'use-titles',            i, innerItem;         //add a cls onto the picker         if (useTitles) {            this.addCls(cls);        } else {            this.removeCls(cls);        }         //show the time on each of the slots         for (i = 0; i < ln; i++) {            innerItem = innerItems[i];             if (innerItem.isSlot) {                innerItem.setShowTitle(useTitles);            }        }    },     applySlots: function(slots) {        //loop through each of the slots and add a reference to this picker         if (slots) {            var ln = slots.length,                i;             for (i = 0; i < ln; i++) {                slots[i].picker = this;            }        }         return slots;    },     /**     * Adds any new {@link #slots} to this picker, and removes existing {@link #slots}     * @private     */    updateSlots: function(newSlots) {        var bcss = Ext.baseCSSPrefix,            innerItems;         this.removeAll();         if (newSlots) {            this.add(newSlots);        }         innerItems = this.getInnerItems();        if (innerItems.length > 0) {            innerItems[0].addCls(bcss + 'first');            innerItems[innerItems.length - 1].addCls(bcss + 'last');        }         this.updateUseTitles(this.getUseTitles());    },     /**     * @private     * Called when the done button has been tapped.     */    onDoneButtonTap: function() {        var oldValue = this._value,            newValue = this.getValue(true);         if (newValue != oldValue) {            this.fireEvent('change', this, newValue);        }         this.hide();        Ext.util.InputBlocker.unblockInputs();    },     /**     * @private     * Called when the cancel button has been tapped.     */    onCancelButtonTap: function() {        this.fireEvent('cancel', this);        this.hide();        Ext.util.InputBlocker.unblockInputs();    },     /**     * @private     * Called when a slot has been picked.     */    onSlotPick: function(slot) {        this.fireEvent('pick', this, this.getValue(true), slot);    },     show: function() {        if (this.getParent() === undefined) {            Ext.Viewport.add(this);        }         this.callParent(arguments);         if (!this.isHidden()) {            this.setValue(this._value);        }        Ext.util.InputBlocker.blockInputs();    },     /**     * Sets the values of the pickers slots.     * @param {Object} values The values in a {name:'value'} format.     * @param {Boolean} animated `true` to animate setting the values.     * @return {Ext.Picker} this This picker.     */    setValue: function(values, animated) {        var me = this,            slots = me.getInnerItems(),            ln = slots.length,            key, slot, loopSlot, i, value;         if (!values) {            values = {};            for (i = 0; i < ln; i++) {                //set the value to false so the slot will return null when getValue is called                 values[slots[i].config.name] = null;            }        }         for (key in values) {            slot = null;            value = values[key];            for (i = 0; i < slots.length; i++) {                loopSlot = slots[i];                if (loopSlot.config.name == key) {                    slot = loopSlot;                    break;                }            }             if (slot) {                if (animated) {                    slot.setValueAnimated(value);                } else {                    slot.setValue(value);                }            }        }         me._values = me._value = values;         return me;    },     setValueAnimated: function(values) {        this.setValue(values, true);    },     /**     * Returns the values of each of the pickers slots     * @return {Object} The values of the pickers slots     */    getValue: function(useDom) {        var values = {},            items = this.getItems().items,            ln = items.length,            item, i;         if (useDom) {            for (i = 0; i < ln; i++) {                item = items[i];                if (item && item.isSlot) {                    values[item.getName()] = item.getValue(useDom);                }            }             this._values = values;        }         return this._values;    },     /**     * Returns the values of each of the pickers slots.     * @return {Object} The values of the pickers slots.     */    getValues: function() {        return this.getValue();    },     doDestroy: function() {        var me = this;         me.mask = me.bar = Ext.destroy(me.mask, me.bar);                me.callParent();    }});