| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-ux-form-MultiSelect'>/**</span> * A control that allows selection of multiple items in a list */Ext.define('Ext.ux.form.MultiSelect', {        extend: 'Ext.form.FieldContainer',        mixins: {        bindable: 'Ext.util.Bindable',        field: 'Ext.form.field.Field'        },        alternateClassName: 'Ext.ux.Multiselect',    alias: ['widget.multiselectfield', 'widget.multiselect'],        requires: ['Ext.panel.Panel', 'Ext.view.BoundList', 'Ext.layout.container.Fit'],        uses: ['Ext.view.DragZone', 'Ext.view.DropZone'],        layout: 'fit',    <span id='Ext-ux-form-MultiSelect-cfg-dragGroup'>    /**</span>     * @cfg {String} [dragGroup=""] The ddgroup name for the MultiSelect DragZone.     */<span id='Ext-ux-form-MultiSelect-cfg-dropGroup'>    /**</span>     * @cfg {String} [dropGroup=""] The ddgroup name for the MultiSelect DropZone.     */    <span id='Ext-ux-form-MultiSelect-cfg-title'>    /**</span>     * @cfg {String} [title=""] A title for the underlying panel.     */    <span id='Ext-ux-form-MultiSelect-cfg-ddReorder'>    /**</span>     * @cfg {Boolean} [ddReorder=false] Whether the items in the MultiSelect list are drag/drop reorderable.     */    ddReorder: false,<span id='Ext-ux-form-MultiSelect-cfg-tbar'>    /**</span>     * @cfg {Object/Array} tbar An optional toolbar to be inserted at the top of the control's selection list.     * This can be a {@link Ext.toolbar.Toolbar} object, a toolbar config, or an array of buttons/button configs     * to be added to the toolbar. See {@link Ext.panel.Panel#tbar}.     */<span id='Ext-ux-form-MultiSelect-cfg-appendOnly'>    /**</span>     * @cfg {String} [appendOnly=false] True if the list should only allow append drops when drag/drop is enabled.     * This is useful for lists which are sorted.     */    appendOnly: false,<span id='Ext-ux-form-MultiSelect-cfg-displayField'>    /**</span>     * @cfg {String} [displayField="text"] Name of the desired display field in the dataset.     */    displayField: 'text',<span id='Ext-ux-form-MultiSelect-cfg-valueField'>    /**</span>     * @cfg {String} [valueField="text"] Name of the desired value field in the dataset.     */<span id='Ext-ux-form-MultiSelect-cfg-allowBlank'>    /**</span>     * @cfg {Boolean} [allowBlank=true] False to require at least one item in the list to be selected, true to allow no     * selection.     */    allowBlank: true,<span id='Ext-ux-form-MultiSelect-cfg-minSelections'>    /**</span>     * @cfg {Number} [minSelections=0] Minimum number of selections allowed.     */    minSelections: 0,<span id='Ext-ux-form-MultiSelect-cfg-maxSelections'>    /**</span>     * @cfg {Number} [maxSelections=Number.MAX_VALUE] Maximum number of selections allowed.     */    maxSelections: Number.MAX_VALUE,<span id='Ext-ux-form-MultiSelect-cfg-blankText'>    /**</span>     * @cfg {String} [blankText="This field is required"] Default text displayed when the control contains no items.     */    blankText: 'This field is required',<span id='Ext-ux-form-MultiSelect-cfg-minSelectionsText'>    /**</span>     * @cfg {String} [minSelectionsText="Minimum {0}item(s) required"]      * Validation message displayed when {@link #minSelections} is not met.      * The {0} token will be replaced by the value of {@link #minSelections}.     */    minSelectionsText: 'Minimum {0} item(s) required',    <span id='Ext-ux-form-MultiSelect-cfg-maxSelectionsText'>    /**</span>     * @cfg {String} [maxSelectionsText="Maximum {0}item(s) allowed"]      * Validation message displayed when {@link #maxSelections} is not met     * The {0} token will be replaced by the value of {@link #maxSelections}.     */    maxSelectionsText: 'Minimum {0} item(s) required',<span id='Ext-ux-form-MultiSelect-cfg-delimiter'>    /**</span>     * @cfg {String} [delimiter=","] The string used to delimit the selected values when {@link #getSubmitValue submitting}     * the field as part of a form. If you wish to have the selected values submitted as separate     * parameters rather than a single delimited parameter, set this to <tt>null</tt>.     */    delimiter: ',',<span id='Ext-ux-form-MultiSelect-cfg-store'>    /**</span>     * @cfg {Ext.data.Store/Array} store The data source to which this MultiSelect is bound (defaults to <tt>undefined</tt>).     * Acceptable values for this property are:     * <div class="mdetail-params"><ul>     * <li><b>any {@link Ext.data.Store Store} subclass</b></li>     * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally.     * <div class="mdetail-params"><ul>     * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">     * A 1-dimensional array will automatically be expanded (each array item will be the combo     * {@link #valueField value} and {@link #displayField text})</div></li>     * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">     * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo     * {@link #valueField value}, while the value at index 1 is assumed to be the combo {@link #displayField text}.     * </div></li></ul></div></li></ul></div>     */        ignoreSelectChange: 0,<span id='Ext-ux-form-MultiSelect-cfg-listConfig'>    /**</span>     * @cfg {Object} listConfig     * An optional set of configuration properties that will be passed to the {@link Ext.view.BoundList}'s constructor.     * Any configuration that is valid for BoundList can be included.     */    initComponent: function(){        var me = this;        me.bindStore(me.store, true);        if (me.store.autoCreated) {            me.valueField = me.displayField = 'field1';            if (!me.store.expanded) {                me.displayField = 'field2';            }        }        if (!Ext.isDefined(me.valueField)) {            me.valueField = me.displayField;        }        me.items = me.setupItems();                        me.callParent();        me.initField();        me.addEvents('drop');        },        setupItems: function() {        var me = this;                me.boundList = Ext.create('Ext.view.BoundList', Ext.apply({            deferInitialRefresh: false,            border: false,            multiSelect: true,            store: me.store,            displayField: me.displayField,            disabled: me.disabled        }, me.listConfig));                me.boundList.getSelectionModel().on('selectionchange', me.onSelectChange, me);        return {            border: true,            layout: 'fit',            title: me.title,            tbar: me.tbar,            items: me.boundList        };    },        onSelectChange: function(selModel, selections){        if (!this.ignoreSelectChange) {            this.setValue(selections);        }        },        getSelected: function(){        return this.boundList.getSelectionModel().getSelection();    },        // compare array values    isEqual: function(v1, v2) {        var fromArray = Ext.Array.from,            i = 0,             len;        v1 = fromArray(v1);        v2 = fromArray(v2);        len = v1.length;        if (len !== v2.length) {            return false;        }        for(; i < len; i++) {            if (v2[i] !== v1[i]) {                return false;            }        }        return true;    },        afterRender: function(){        var me = this;                me.callParent();        if (me.selectOnRender) {            ++me.ignoreSelectChange;            me.boundList.getSelectionModel().select(me.getRecordsForValue(me.value));            --me.ignoreSelectChange;            delete me.toSelect;        }                    if (me.ddReorder && !me.dragGroup && !me.dropGroup){            me.dragGroup = me.dropGroup = 'MultiselectDD-' + Ext.id();        }        if (me.draggable || me.dragGroup){            me.dragZone = Ext.create('Ext.view.DragZone', {                view: me.boundList,                ddGroup: me.dragGroup,                dragText: '{0} Item{1}'            });        }        if (me.droppable || me.dropGroup){            me.dropZone = Ext.create('Ext.view.DropZone', {                view: me.boundList,                ddGroup: me.dropGroup,                handleNodeDrop: function(data, dropRecord, position) {                    var view = this.view,                        store = view.getStore(),                        records = data.records,                        index;                    // remove the Models from the source Store                    data.view.store.remove(records);                    index = store.indexOf(dropRecord);                    if (position === 'after') {                        index++;                    }                    store.insert(index, records);                    view.getSelectionModel().select(records);                    me.fireEvent('drop', me, records);                }            });        }    },        isValid : function() {        var me = this,            disabled = me.disabled,            validate = me.forceValidation || !disabled;                            return validate ? me.validateValue(me.value) : disabled;    },        validateValue: function(value) {        var me = this,            errors = me.getErrors(value),            isValid = Ext.isEmpty(errors);                    if (!me.preventMark) {            if (isValid) {                me.clearInvalid();            } else {                me.markInvalid(errors);            }        }        return isValid;    },        markInvalid : function(errors) {        // Save the message and fire the 'invalid' event        var me = this,            oldMsg = me.getActiveError();        me.setActiveErrors(Ext.Array.from(errors));        if (oldMsg !== me.getActiveError()) {            me.updateLayout();        }    },<span id='Ext-ux-form-MultiSelect-method-clearInvalid'>    /**</span>     * Clear any invalid styles/messages for this field.     *     * **Note**: this method does not cause the Field's {@link #validate} or {@link #isValid} methods to return `true`     * if the value does not _pass_ validation. So simply clearing a field's errors will not necessarily allow     * submission of forms submitted with the {@link Ext.form.action.Submit#clientValidation} option set.     */    clearInvalid : function() {        // Clear the message and fire the 'valid' event        var me = this,            hadError = me.hasActiveError();        me.unsetActiveError();        if (hadError) {            me.updateLayout();        }    },        getSubmitData: function() {        var me = this,            data = null,            val;        if (!me.disabled && me.submitValue && !me.isFileUpload()) {            val = me.getSubmitValue();            if (val !== null) {                data = {};                data[me.getName()] = val;            }        }        return data;    },<span id='Ext-ux-form-MultiSelect-method-getSubmitValue'>    /**</span>     * Returns the value that would be included in a standard form submit for this field.     *     * @return {String} The value to be submitted, or null.     */    getSubmitValue: function() {        var me = this,            delimiter = me.delimiter,            val = me.getValue();                    return Ext.isString(delimiter) ? val.join(delimiter) : val;    },        getValue: function(){        return this.value;    },        getRecordsForValue: function(value){        var me = this,            records = [],            all = me.store.getRange(),            valueField = me.valueField,            i = 0,            allLen = all.length,            rec,            j,            valueLen;                    for (valueLen = value.length; i < valueLen; ++i) {            for (j = 0; j < allLen; ++j) {                rec = all[j];                   if (rec.get(valueField) == value[i]) {                    records.push(rec);                }            }            }                    return records;    },        setupValue: function(value){        var delimiter = this.delimiter,            valueField = this.valueField,            i = 0,            out,            len,            item;                    if (Ext.isDefined(value)) {            if (delimiter && Ext.isString(value)) {                value = value.split(delimiter);            } else if (!Ext.isArray(value)) {                value = [value];            }                    for (len = value.length; i < len; ++i) {                item = value[i];                if (item && item.isModel) {                    value[i] = item.get(valueField);                }            }            out = Ext.Array.unique(value);        } else {            out = [];        }        return out;    },        setValue: function(value){        var me = this,            selModel = me.boundList.getSelectionModel();        // Store not loaded yet - we cannot set the value        if (!me.store.getCount()) {            me.store.on({                load: Ext.Function.bind(me.setValue, me, [value]),                single: true            });            return;        }        value = me.setupValue(value);        me.mixins.field.setValue.call(me, value);                if (me.rendered) {            ++me.ignoreSelectChange;            selModel.deselectAll();            selModel.select(me.getRecordsForValue(value));            --me.ignoreSelectChange;        } else {            me.selectOnRender = true;        }    },        clearValue: function(){        this.setValue([]);        },        onEnable: function(){        var list = this.boundList;        this.callParent();        if (list) {            list.enable();        }    },        onDisable: function(){        var list = this.boundList;        this.callParent();        if (list) {            list.disable();        }    },        getErrors : function(value) {        var me = this,            format = Ext.String.format,            errors = [],            numSelected;        value = Ext.Array.from(value || me.getValue());        numSelected = value.length;        if (!me.allowBlank && numSelected < 1) {            errors.push(me.blankText);        }        if (numSelected < me.minSelections) {            errors.push(format(me.minSelectionsText, me.minSelections));        }        if (numSelected > me.maxSelections) {            errors.push(format(me.maxSelectionsText, me.maxSelections));        }        return errors;    },        onDestroy: function(){        var me = this;                me.bindStore(null);        Ext.destroy(me.dragZone, me.dropZone);        me.callParent();    },        onBindStore: function(store){        var boundList = this.boundList;                if (boundList) {            boundList.bindStore(store);        }    }    });</pre></body></html>
 |