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>
|