| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670 | <!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-selection-Model'>/**</span> * Tracks what records are currently selected in a databound component. * * This is an abstract class and is not meant to be directly used. Databound UI widgets such as * {@link Ext.grid.Panel Grid} and {@link Ext.tree.Panel Tree} should subclass Ext.selection.Model * and provide a way to binding to the component. * * The abstract methods `onSelectChange` and `onLastFocusChanged` should be implemented in these * subclasses to update the UI widget. */Ext.define('Ext.selection.Model', {    extend: 'Ext.util.Observable',    alternateClassName: 'Ext.AbstractSelectionModel',    requires: ['Ext.data.StoreManager'],    mixins: {        bindable: 'Ext.util.Bindable'        },    // lastSelected<span id='Ext-selection-Model-cfg-mode'>    /**</span>     * @cfg {String} mode     * Mode of selection.  Valid values are:     *     * - **SINGLE** - Only allows selecting one item at a time.  Use {@link #allowDeselect} to allow     *   deselecting that item.  This is the default.     * - **SIMPLE** - Allows simple selection of multiple items one-by-one. Each click in grid will either     *   select or deselect an item.     * - **MULTI** - Allows complex selection of multiple items using Ctrl and Shift keys.     */<span id='Ext-selection-Model-cfg-allowDeselect'>    /**</span>     * @cfg {Boolean} allowDeselect     * Allow users to deselect a record in a DataView, List or Grid.     * Only applicable when the {@link #mode} is 'SINGLE'.     */    allowDeselect: false,<span id='Ext-selection-Model-property-selected'>    /**</span>     * @property {Ext.util.MixedCollection} [selected=undefined]     * A MixedCollection that maintains all of the currently selected records.     * @readonly     */    selected: null,<span id='Ext-selection-Model-cfg-pruneRemoved'>    /**</span>     * @cfg {Boolean} pruneRemoved     * Prune records when they are removed from the store from the selection.     * This is a private flag. For an example of its usage, take a look at     * Ext.selection.TreeModel.     */    pruneRemoved: true,    constructor: function(cfg) {        var me = this;        cfg = cfg || {};        Ext.apply(me, cfg);        me.addEvents(<span id='Ext-selection-Model-event-selectionchange'>            /**</span>             * @event             * Fired after a selection change has occurred             * @param {Ext.selection.Model} this             * @param {Ext.data.Model[]} selected The selected records             */            'selectionchange',<span id='Ext-selection-Model-event-focuschange'>            /**</span>             * @event             * Fired when a row is focused             * @param {Ext.selection.Model} this             * @param {Ext.data.Model} oldFocused The previously focused record             * @param {Ext.data.Model} newFocused The newly focused record             */            'focuschange'        );        me.modes = {            SINGLE: true,            SIMPLE: true,            MULTI: true        };        // sets this.selectionMode        me.setSelectionMode(cfg.mode || me.mode);        // maintains the currently selected records.        me.selected = new Ext.util.MixedCollection();        me.callParent(arguments);    },    // binds the store to the selModel.    bindStore: function(store, initial){        var me = this;        me.mixins.bindable.bindStore.apply(me, arguments);        if(me.store && !initial) {            me.refresh();        }    },        getStoreListeners: function() {        var me = this;        return {            add: me.onStoreAdd,            clear: me.onStoreClear,            remove: me.onStoreRemove,            update: me.onStoreUpdate            };     },<span id='Ext-selection-Model-method-selectAll'>    /**</span>     * Selects all records in the view.     * @param {Boolean} suppressEvent True to suppress any select events     */    selectAll: function(suppressEvent) {        var me = this,            selections = me.store.getRange(),            i = 0,            len = selections.length,            start = me.getSelection().length;        me.bulkChange = true;        for (; i < len; i++) {            me.doSelect(selections[i], true, suppressEvent);        }        delete me.bulkChange;        // fire selection change only if the number of selections differs        me.maybeFireSelectionChange(me.getSelection().length !== start);    },<span id='Ext-selection-Model-method-deselectAll'>    /**</span>     * Deselects all records in the view.     * @param {Boolean} suppressEvent True to suppress any deselect events     */    deselectAll: function(suppressEvent) {        var me = this,            selections = me.getSelection(),            i = 0,            len = selections.length,            start = me.getSelection().length;        me.bulkChange = true;        for (; i < len; i++) {            me.doDeselect(selections[i], suppressEvent);        }        delete me.bulkChange;        // fire selection change only if the number of selections differs        me.maybeFireSelectionChange(me.getSelection().length !== start);    },    // Provides differentiation of logic between MULTI, SIMPLE and SINGLE    // selection modes. Requires that an event be passed so that we can know    // if user held ctrl or shift.    selectWithEvent: function(record, e, keepExisting) {        var me = this;        switch (me.selectionMode) {            case 'MULTI':                if (e.ctrlKey && me.isSelected(record)) {                    me.doDeselect(record, false);                } else if (e.shiftKey && me.lastFocused) {                    me.selectRange(me.lastFocused, record, e.ctrlKey);                } else if (e.ctrlKey) {                    me.doSelect(record, true, false);                } else if (me.isSelected(record) && !e.shiftKey && !e.ctrlKey && me.selected.getCount() > 1) {                    me.doSelect(record, keepExisting, false);                } else {                    me.doSelect(record, false);                }                break;            case 'SIMPLE':                if (me.isSelected(record)) {                    me.doDeselect(record);                } else {                    me.doSelect(record, true);                }                break;            case 'SINGLE':                // if allowDeselect is on and this record isSelected, deselect it                if (me.allowDeselect && me.isSelected(record)) {                    me.doDeselect(record);                // select the record and do NOT maintain existing selections                } else {                    me.doSelect(record, false);                }                break;        }    },<span id='Ext-selection-Model-method-selectRange'>    /**</span>     * Selects a range of rows if the selection model {@link #isLocked is not locked}.     * All rows in between startRow and endRow are also selected.     * @param {Ext.data.Model/Number} startRow The record or index of the first row in the range     * @param {Ext.data.Model/Number} endRow The record or index of the last row in the range     * @param {Boolean} keepExisting (optional) True to retain existing selections     */    selectRange : function(startRow, endRow, keepExisting, dir){        var me = this,            store = me.store,            selectedCount = 0,            i,            tmp,            dontDeselect,            records = [];        if (me.isLocked()){            return;        }        if (!keepExisting) {            me.deselectAll(true);        }        if (!Ext.isNumber(startRow)) {            startRow = store.indexOf(startRow);        }        if (!Ext.isNumber(endRow)) {            endRow = store.indexOf(endRow);        }        // swap values        if (startRow > endRow){            tmp = endRow;            endRow = startRow;            startRow = tmp;        }        for (i = startRow; i <= endRow; i++) {            if (me.isSelected(store.getAt(i))) {                selectedCount++;            }        }        if (!dir) {            dontDeselect = -1;        } else {            dontDeselect = (dir == 'up') ? startRow : endRow;        }        for (i = startRow; i <= endRow; i++){            if (selectedCount == (endRow - startRow + 1)) {                if (i != dontDeselect) {                    me.doDeselect(i, true);                }            } else {                records.push(store.getAt(i));            }        }        me.doMultiSelect(records, true);    },<span id='Ext-selection-Model-method-select'>    /**</span>     * Selects a record instance by record instance or index.     * @param {Ext.data.Model[]/Number} records An array of records or an index     * @param {Boolean} [keepExisting=false] True to retain existing selections     * @param {Boolean} [suppressEvent=false] True to not fire a select event     */    select: function(records, keepExisting, suppressEvent) {        // Automatically selecting eg store.first() or store.last() will pass undefined, so that must just return;        if (Ext.isDefined(records)) {            this.doSelect(records, keepExisting, suppressEvent);        }    },<span id='Ext-selection-Model-method-deselect'>    /**</span>     * Deselects a record instance by record instance or index.     * @param {Ext.data.Model[]/Number} records An array of records or an index     * @param {Boolean} [suppressEvent=false] True to not fire a deselect event     */    deselect: function(records, suppressEvent) {        this.doDeselect(records, suppressEvent);    },    doSelect: function(records, keepExisting, suppressEvent) {        var me = this,            record;        if (me.locked || !me.store) {            return;        }        if (typeof records === "number") {            records = [me.store.getAt(records)];        }        if (me.selectionMode == "SINGLE" && records) {            record = records.length ? records[0] : records;            me.doSingleSelect(record, suppressEvent);        } else {            me.doMultiSelect(records, keepExisting, suppressEvent);        }    },    doMultiSelect: function(records, keepExisting, suppressEvent) {        var me = this,            selected = me.selected,            change = false,            i = 0,            len, record;        if (me.locked) {            return;        }        records = !Ext.isArray(records) ? [records] : records;        len = records.length;        if (!keepExisting && selected.getCount() > 0) {            if (me.doDeselect(me.getSelection(), suppressEvent) === false) {                return;            }            // TODO - coalesce the selectionchange event in deselect w/the one below...        }        function commit () {            selected.add(record);            change = true;        }        for (; i < len; i++) {            record = records[i];            if (keepExisting && me.isSelected(record)) {                continue;            }            me.lastSelected = record;            me.onSelectChange(record, true, suppressEvent, commit);        }        if (!me.preventFocus) {            me.setLastFocused(record, suppressEvent);        }        // fire selchange if there was a change and there is no suppressEvent flag        me.maybeFireSelectionChange(change && !suppressEvent);    },    // records can be an index, a record or an array of records    doDeselect: function(records, suppressEvent) {        var me = this,            selected = me.selected,            i = 0,            len, record,            attempted = 0,            accepted = 0;        if (me.locked || !me.store) {            return false;        }        if (typeof records === "number") {            records = [me.store.getAt(records)];        } else if (!Ext.isArray(records)) {            records = [records];        }        function commit () {            ++accepted;            selected.remove(record);        }        len = records.length;        for (; i < len; i++) {            record = records[i];            if (me.isSelected(record)) {                if (me.lastSelected == record) {                    me.lastSelected = selected.last();                }                ++attempted;                me.onSelectChange(record, false, suppressEvent, commit);            }        }        // fire selchange if there was a change and there is no suppressEvent flag        me.maybeFireSelectionChange(accepted > 0 && !suppressEvent);        return accepted === attempted;    },    doSingleSelect: function(record, suppressEvent) {        var me = this,            changed = false,            selected = me.selected;        if (me.locked) {            return;        }        // already selected.        // should we also check beforeselect?        if (me.isSelected(record)) {            return;        }        function commit () {            me.bulkChange = true;            if (selected.getCount() > 0 && me.doDeselect(me.lastSelected, suppressEvent) === false) {                delete me.bulkChange;                return false;            }            delete me.bulkChange;            selected.add(record);            me.lastSelected = record;            changed = true;        }        me.onSelectChange(record, true, suppressEvent, commit);        if (changed) {            if (!suppressEvent) {                me.setLastFocused(record);            }            me.maybeFireSelectionChange(!suppressEvent);        }    },<span id='Ext-selection-Model-method-setLastFocused'>    /**</span>     * Sets a record as the last focused record. This does NOT mean     * that the record has been selected.     * @param {Ext.data.Model} record     */    setLastFocused: function(record, supressFocus) {        var me = this,            recordBeforeLast = me.lastFocused;        me.lastFocused = record;                 // Only call the changed method if in fact the selected record *has* changed.        if (record !== recordBeforeLast) {            me.onLastFocusChanged(recordBeforeLast, record, supressFocus);        }    },<span id='Ext-selection-Model-method-isFocused'>    /**</span>     * Determines if this record is currently focused.     * @param {Ext.data.Model} record     */    isFocused: function(record) {        return record === this.getLastFocused();    },    // fire selection change as long as true is not passed    // into maybeFireSelectionChange    maybeFireSelectionChange: function(fireEvent) {        var me = this;        if (fireEvent && !me.bulkChange) {            me.fireEvent('selectionchange', me, me.getSelection());        }    },<span id='Ext-selection-Model-method-getLastSelected'>    /**</span>     * Returns the last selected record.     */    getLastSelected: function() {        return this.lastSelected;    },    getLastFocused: function() {        return this.lastFocused;    },<span id='Ext-selection-Model-method-getSelection'>    /**</span>     * Returns an array of the currently selected records.     * @return {Ext.data.Model[]} The selected records     */    getSelection: function() {        return this.selected.getRange();    },<span id='Ext-selection-Model-method-getSelectionMode'>    /**</span>     * Returns the current selectionMode.     * @return {String} The selectionMode: 'SINGLE', 'MULTI' or 'SIMPLE'.     */    getSelectionMode: function() {        return this.selectionMode;    },<span id='Ext-selection-Model-method-setSelectionMode'>    /**</span>     * Sets the current selectionMode.     * @param {String} selMode 'SINGLE', 'MULTI' or 'SIMPLE'.     */    setSelectionMode: function(selMode) {        selMode = selMode ? selMode.toUpperCase() : 'SINGLE';        // set to mode specified unless it doesnt exist, in that case        // use single.        this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';    },<span id='Ext-selection-Model-method-isLocked'>    /**</span>     * Returns true if the selections are locked.     * @return {Boolean}     */    isLocked: function() {        return this.locked;    },<span id='Ext-selection-Model-method-setLocked'>    /**</span>     * Locks the current selection and disables any changes from happening to the selection.     * @param {Boolean} locked  True to lock, false to unlock.     */    setLocked: function(locked) {        this.locked = !!locked;    },<span id='Ext-selection-Model-method-isSelected'>    /**</span>     * Returns true if the specified row is selected.     * @param {Ext.data.Model/Number} record The record or index of the record to check     * @return {Boolean}     */    isSelected: function(record) {        record = Ext.isNumber(record) ? this.store.getAt(record) : record;        return this.selected.indexOf(record) !== -1;    },<span id='Ext-selection-Model-method-hasSelection'>    /**</span>     * Returns true if there are any a selected records.     * @return {Boolean}     */    hasSelection: function() {        return this.selected.getCount() > 0;    },    refresh: function() {        var me = this,            store = me.store,            toBeSelected = [],            oldSelections = me.getSelection(),            len = oldSelections.length,            selection,            change,            i = 0,            lastFocused = me.getLastFocused();        // Not been bound yet.        if (!store) {            return;        }        // check to make sure that there are no records        // missing after the refresh was triggered, prune        // them from what is to be selected if so        for (; i < len; i++) {            selection = oldSelections[i];            if (!me.pruneRemoved || store.indexOf(selection) !== -1) {                toBeSelected.push(selection);            }        }        // there was a change from the old selected and        // the new selection        if (me.selected.getCount() != toBeSelected.length) {            change = true;        }        me.clearSelections();        if (store.indexOf(lastFocused) !== -1) {            // restore the last focus but supress restoring focus            me.setLastFocused(lastFocused, true);        }        if (toBeSelected.length) {            // perform the selection again            me.doSelect(toBeSelected, false, true);        }        me.maybeFireSelectionChange(change);    },<span id='Ext-selection-Model-method-clearSelections'>    /**</span>     * A fast reset of the selections without firing events, updating the ui, etc.     * For private usage only.     * @private     */    clearSelections: function() {        // reset the entire selection to nothing        this.selected.clear();        this.lastSelected = null;        this.setLastFocused(null);    },    // when a record is added to a store    onStoreAdd: Ext.emptyFn,    // when a store is cleared remove all selections    // (if there were any)    onStoreClear: function() {        if (this.selected.getCount > 0) {            this.clearSelections();            this.maybeFireSelectionChange(true);        }    },    // prune records from the SelectionModel if    // they were selected at the time they were    // removed.    onStoreRemove: function(store, record, index) {        var me = this,            selected = me.selected;        if (me.locked || !me.pruneRemoved) {            return;        }        if (selected.remove(record)) {            if (me.lastSelected == record) {                me.lastSelected = null;            }            if (me.getLastFocused() == record) {                me.setLastFocused(null);            }            me.maybeFireSelectionChange(true);        }    },<span id='Ext-selection-Model-method-getCount'>    /**</span>     * Returns the count of selected records.     * @return {Number} The number of selected records     */    getCount: function() {        return this.selected.getCount();    },    // cleanup.    destroy: Ext.emptyFn,    // if records are updated    onStoreUpdate: Ext.emptyFn,<span id='Ext-selection-Model-method-onStoreLoad'>    /**</span>     * @abstract     * @private     */    onStoreLoad: Ext.emptyFn,    // @abstract    onSelectChange: Ext.emptyFn,    // @abstract    onLastFocusChanged: function(oldFocused, newFocused) {        this.fireEvent('focuschange', this, oldFocused, newFocused);    },    // @abstract    onEditorKey: Ext.emptyFn,    // @abstract    bindComponent: Ext.emptyFn,    // @abstract    beforeViewRender: Ext.emptyFn});</pre></body></html>
 |