Model2.html 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-selection-Model'>/**
  19. </span> * Tracks what records are currently selected in a databound component.
  20. *
  21. * This is an abstract class and is not meant to be directly used. Databound UI widgets such as
  22. * {@link Ext.grid.Panel Grid} and {@link Ext.tree.Panel Tree} should subclass Ext.selection.Model
  23. * and provide a way to binding to the component.
  24. *
  25. * The abstract methods `onSelectChange` and `onLastFocusChanged` should be implemented in these
  26. * subclasses to update the UI widget.
  27. */
  28. Ext.define('Ext.selection.Model', {
  29. extend: 'Ext.util.Observable',
  30. alternateClassName: 'Ext.AbstractSelectionModel',
  31. requires: ['Ext.data.StoreManager'],
  32. mixins: {
  33. bindable: 'Ext.util.Bindable'
  34. },
  35. // lastSelected
  36. <span id='Ext-selection-Model-cfg-mode'> /**
  37. </span> * @cfg {String} mode
  38. * Mode of selection. Valid values are:
  39. *
  40. * - **SINGLE** - Only allows selecting one item at a time. Use {@link #allowDeselect} to allow
  41. * deselecting that item. This is the default.
  42. * - **SIMPLE** - Allows simple selection of multiple items one-by-one. Each click in grid will either
  43. * select or deselect an item.
  44. * - **MULTI** - Allows complex selection of multiple items using Ctrl and Shift keys.
  45. */
  46. <span id='Ext-selection-Model-cfg-allowDeselect'> /**
  47. </span> * @cfg {Boolean} allowDeselect
  48. * Allow users to deselect a record in a DataView, List or Grid.
  49. * Only applicable when the {@link #mode} is 'SINGLE'.
  50. */
  51. allowDeselect: false,
  52. <span id='Ext-selection-Model-property-selected'> /**
  53. </span> * @property {Ext.util.MixedCollection} [selected=undefined]
  54. * A MixedCollection that maintains all of the currently selected records.
  55. * @readonly
  56. */
  57. selected: null,
  58. <span id='Ext-selection-Model-cfg-pruneRemoved'> /**
  59. </span> * @cfg {Boolean} pruneRemoved
  60. * Prune records when they are removed from the store from the selection.
  61. * This is a private flag. For an example of its usage, take a look at
  62. * Ext.selection.TreeModel.
  63. */
  64. pruneRemoved: true,
  65. constructor: function(cfg) {
  66. var me = this;
  67. cfg = cfg || {};
  68. Ext.apply(me, cfg);
  69. me.addEvents(
  70. <span id='Ext-selection-Model-event-selectionchange'> /**
  71. </span> * @event
  72. * Fired after a selection change has occurred
  73. * @param {Ext.selection.Model} this
  74. * @param {Ext.data.Model[]} selected The selected records
  75. */
  76. 'selectionchange',
  77. <span id='Ext-selection-Model-event-focuschange'> /**
  78. </span> * @event
  79. * Fired when a row is focused
  80. * @param {Ext.selection.Model} this
  81. * @param {Ext.data.Model} oldFocused The previously focused record
  82. * @param {Ext.data.Model} newFocused The newly focused record
  83. */
  84. 'focuschange'
  85. );
  86. me.modes = {
  87. SINGLE: true,
  88. SIMPLE: true,
  89. MULTI: true
  90. };
  91. // sets this.selectionMode
  92. me.setSelectionMode(cfg.mode || me.mode);
  93. // maintains the currently selected records.
  94. me.selected = new Ext.util.MixedCollection();
  95. me.callParent(arguments);
  96. },
  97. // binds the store to the selModel.
  98. bindStore: function(store, initial){
  99. var me = this;
  100. me.mixins.bindable.bindStore.apply(me, arguments);
  101. if(me.store &amp;&amp; !initial) {
  102. me.refresh();
  103. }
  104. },
  105. getStoreListeners: function() {
  106. var me = this;
  107. return {
  108. add: me.onStoreAdd,
  109. clear: me.onStoreClear,
  110. remove: me.onStoreRemove,
  111. update: me.onStoreUpdate
  112. };
  113. },
  114. <span id='Ext-selection-Model-method-selectAll'> /**
  115. </span> * Selects all records in the view.
  116. * @param {Boolean} suppressEvent True to suppress any select events
  117. */
  118. selectAll: function(suppressEvent) {
  119. var me = this,
  120. selections = me.store.getRange(),
  121. i = 0,
  122. len = selections.length,
  123. start = me.getSelection().length;
  124. me.bulkChange = true;
  125. for (; i &lt; len; i++) {
  126. me.doSelect(selections[i], true, suppressEvent);
  127. }
  128. delete me.bulkChange;
  129. // fire selection change only if the number of selections differs
  130. me.maybeFireSelectionChange(me.getSelection().length !== start);
  131. },
  132. <span id='Ext-selection-Model-method-deselectAll'> /**
  133. </span> * Deselects all records in the view.
  134. * @param {Boolean} suppressEvent True to suppress any deselect events
  135. */
  136. deselectAll: function(suppressEvent) {
  137. var me = this,
  138. selections = me.getSelection(),
  139. i = 0,
  140. len = selections.length,
  141. start = me.getSelection().length;
  142. me.bulkChange = true;
  143. for (; i &lt; len; i++) {
  144. me.doDeselect(selections[i], suppressEvent);
  145. }
  146. delete me.bulkChange;
  147. // fire selection change only if the number of selections differs
  148. me.maybeFireSelectionChange(me.getSelection().length !== start);
  149. },
  150. // Provides differentiation of logic between MULTI, SIMPLE and SINGLE
  151. // selection modes. Requires that an event be passed so that we can know
  152. // if user held ctrl or shift.
  153. selectWithEvent: function(record, e, keepExisting) {
  154. var me = this;
  155. switch (me.selectionMode) {
  156. case 'MULTI':
  157. if (e.ctrlKey &amp;&amp; me.isSelected(record)) {
  158. me.doDeselect(record, false);
  159. } else if (e.shiftKey &amp;&amp; me.lastFocused) {
  160. me.selectRange(me.lastFocused, record, e.ctrlKey);
  161. } else if (e.ctrlKey) {
  162. me.doSelect(record, true, false);
  163. } else if (me.isSelected(record) &amp;&amp; !e.shiftKey &amp;&amp; !e.ctrlKey &amp;&amp; me.selected.getCount() &gt; 1) {
  164. me.doSelect(record, keepExisting, false);
  165. } else {
  166. me.doSelect(record, false);
  167. }
  168. break;
  169. case 'SIMPLE':
  170. if (me.isSelected(record)) {
  171. me.doDeselect(record);
  172. } else {
  173. me.doSelect(record, true);
  174. }
  175. break;
  176. case 'SINGLE':
  177. // if allowDeselect is on and this record isSelected, deselect it
  178. if (me.allowDeselect &amp;&amp; me.isSelected(record)) {
  179. me.doDeselect(record);
  180. // select the record and do NOT maintain existing selections
  181. } else {
  182. me.doSelect(record, false);
  183. }
  184. break;
  185. }
  186. },
  187. <span id='Ext-selection-Model-method-selectRange'> /**
  188. </span> * Selects a range of rows if the selection model {@link #isLocked is not locked}.
  189. * All rows in between startRow and endRow are also selected.
  190. * @param {Ext.data.Model/Number} startRow The record or index of the first row in the range
  191. * @param {Ext.data.Model/Number} endRow The record or index of the last row in the range
  192. * @param {Boolean} keepExisting (optional) True to retain existing selections
  193. */
  194. selectRange : function(startRow, endRow, keepExisting, dir){
  195. var me = this,
  196. store = me.store,
  197. selectedCount = 0,
  198. i,
  199. tmp,
  200. dontDeselect,
  201. records = [];
  202. if (me.isLocked()){
  203. return;
  204. }
  205. if (!keepExisting) {
  206. me.deselectAll(true);
  207. }
  208. if (!Ext.isNumber(startRow)) {
  209. startRow = store.indexOf(startRow);
  210. }
  211. if (!Ext.isNumber(endRow)) {
  212. endRow = store.indexOf(endRow);
  213. }
  214. // swap values
  215. if (startRow &gt; endRow){
  216. tmp = endRow;
  217. endRow = startRow;
  218. startRow = tmp;
  219. }
  220. for (i = startRow; i &lt;= endRow; i++) {
  221. if (me.isSelected(store.getAt(i))) {
  222. selectedCount++;
  223. }
  224. }
  225. if (!dir) {
  226. dontDeselect = -1;
  227. } else {
  228. dontDeselect = (dir == 'up') ? startRow : endRow;
  229. }
  230. for (i = startRow; i &lt;= endRow; i++){
  231. if (selectedCount == (endRow - startRow + 1)) {
  232. if (i != dontDeselect) {
  233. me.doDeselect(i, true);
  234. }
  235. } else {
  236. records.push(store.getAt(i));
  237. }
  238. }
  239. me.doMultiSelect(records, true);
  240. },
  241. <span id='Ext-selection-Model-method-select'> /**
  242. </span> * Selects a record instance by record instance or index.
  243. * @param {Ext.data.Model[]/Number} records An array of records or an index
  244. * @param {Boolean} [keepExisting=false] True to retain existing selections
  245. * @param {Boolean} [suppressEvent=false] True to not fire a select event
  246. */
  247. select: function(records, keepExisting, suppressEvent) {
  248. // Automatically selecting eg store.first() or store.last() will pass undefined, so that must just return;
  249. if (Ext.isDefined(records)) {
  250. this.doSelect(records, keepExisting, suppressEvent);
  251. }
  252. },
  253. <span id='Ext-selection-Model-method-deselect'> /**
  254. </span> * Deselects a record instance by record instance or index.
  255. * @param {Ext.data.Model[]/Number} records An array of records or an index
  256. * @param {Boolean} [suppressEvent=false] True to not fire a deselect event
  257. */
  258. deselect: function(records, suppressEvent) {
  259. this.doDeselect(records, suppressEvent);
  260. },
  261. doSelect: function(records, keepExisting, suppressEvent) {
  262. var me = this,
  263. record;
  264. if (me.locked || !me.store) {
  265. return;
  266. }
  267. if (typeof records === &quot;number&quot;) {
  268. records = [me.store.getAt(records)];
  269. }
  270. if (me.selectionMode == &quot;SINGLE&quot; &amp;&amp; records) {
  271. record = records.length ? records[0] : records;
  272. me.doSingleSelect(record, suppressEvent);
  273. } else {
  274. me.doMultiSelect(records, keepExisting, suppressEvent);
  275. }
  276. },
  277. doMultiSelect: function(records, keepExisting, suppressEvent) {
  278. var me = this,
  279. selected = me.selected,
  280. change = false,
  281. i = 0,
  282. len, record;
  283. if (me.locked) {
  284. return;
  285. }
  286. records = !Ext.isArray(records) ? [records] : records;
  287. len = records.length;
  288. if (!keepExisting &amp;&amp; selected.getCount() &gt; 0) {
  289. if (me.doDeselect(me.getSelection(), suppressEvent) === false) {
  290. return;
  291. }
  292. // TODO - coalesce the selectionchange event in deselect w/the one below...
  293. }
  294. function commit () {
  295. selected.add(record);
  296. change = true;
  297. }
  298. for (; i &lt; len; i++) {
  299. record = records[i];
  300. if (keepExisting &amp;&amp; me.isSelected(record)) {
  301. continue;
  302. }
  303. me.lastSelected = record;
  304. me.onSelectChange(record, true, suppressEvent, commit);
  305. }
  306. if (!me.preventFocus) {
  307. me.setLastFocused(record, suppressEvent);
  308. }
  309. // fire selchange if there was a change and there is no suppressEvent flag
  310. me.maybeFireSelectionChange(change &amp;&amp; !suppressEvent);
  311. },
  312. // records can be an index, a record or an array of records
  313. doDeselect: function(records, suppressEvent) {
  314. var me = this,
  315. selected = me.selected,
  316. i = 0,
  317. len, record,
  318. attempted = 0,
  319. accepted = 0;
  320. if (me.locked || !me.store) {
  321. return false;
  322. }
  323. if (typeof records === &quot;number&quot;) {
  324. records = [me.store.getAt(records)];
  325. } else if (!Ext.isArray(records)) {
  326. records = [records];
  327. }
  328. function commit () {
  329. ++accepted;
  330. selected.remove(record);
  331. }
  332. len = records.length;
  333. for (; i &lt; len; i++) {
  334. record = records[i];
  335. if (me.isSelected(record)) {
  336. if (me.lastSelected == record) {
  337. me.lastSelected = selected.last();
  338. }
  339. ++attempted;
  340. me.onSelectChange(record, false, suppressEvent, commit);
  341. }
  342. }
  343. // fire selchange if there was a change and there is no suppressEvent flag
  344. me.maybeFireSelectionChange(accepted &gt; 0 &amp;&amp; !suppressEvent);
  345. return accepted === attempted;
  346. },
  347. doSingleSelect: function(record, suppressEvent) {
  348. var me = this,
  349. changed = false,
  350. selected = me.selected;
  351. if (me.locked) {
  352. return;
  353. }
  354. // already selected.
  355. // should we also check beforeselect?
  356. if (me.isSelected(record)) {
  357. return;
  358. }
  359. function commit () {
  360. me.bulkChange = true;
  361. if (selected.getCount() &gt; 0 &amp;&amp; me.doDeselect(me.lastSelected, suppressEvent) === false) {
  362. delete me.bulkChange;
  363. return false;
  364. }
  365. delete me.bulkChange;
  366. selected.add(record);
  367. me.lastSelected = record;
  368. changed = true;
  369. }
  370. me.onSelectChange(record, true, suppressEvent, commit);
  371. if (changed) {
  372. if (!suppressEvent) {
  373. me.setLastFocused(record);
  374. }
  375. me.maybeFireSelectionChange(!suppressEvent);
  376. }
  377. },
  378. <span id='Ext-selection-Model-method-setLastFocused'> /**
  379. </span> * Sets a record as the last focused record. This does NOT mean
  380. * that the record has been selected.
  381. * @param {Ext.data.Model} record
  382. */
  383. setLastFocused: function(record, supressFocus) {
  384. var me = this,
  385. recordBeforeLast = me.lastFocused;
  386. me.lastFocused = record;
  387. // Only call the changed method if in fact the selected record *has* changed.
  388. if (record !== recordBeforeLast) {
  389. me.onLastFocusChanged(recordBeforeLast, record, supressFocus);
  390. }
  391. },
  392. <span id='Ext-selection-Model-method-isFocused'> /**
  393. </span> * Determines if this record is currently focused.
  394. * @param {Ext.data.Model} record
  395. */
  396. isFocused: function(record) {
  397. return record === this.getLastFocused();
  398. },
  399. // fire selection change as long as true is not passed
  400. // into maybeFireSelectionChange
  401. maybeFireSelectionChange: function(fireEvent) {
  402. var me = this;
  403. if (fireEvent &amp;&amp; !me.bulkChange) {
  404. me.fireEvent('selectionchange', me, me.getSelection());
  405. }
  406. },
  407. <span id='Ext-selection-Model-method-getLastSelected'> /**
  408. </span> * Returns the last selected record.
  409. */
  410. getLastSelected: function() {
  411. return this.lastSelected;
  412. },
  413. getLastFocused: function() {
  414. return this.lastFocused;
  415. },
  416. <span id='Ext-selection-Model-method-getSelection'> /**
  417. </span> * Returns an array of the currently selected records.
  418. * @return {Ext.data.Model[]} The selected records
  419. */
  420. getSelection: function() {
  421. return this.selected.getRange();
  422. },
  423. <span id='Ext-selection-Model-method-getSelectionMode'> /**
  424. </span> * Returns the current selectionMode.
  425. * @return {String} The selectionMode: 'SINGLE', 'MULTI' or 'SIMPLE'.
  426. */
  427. getSelectionMode: function() {
  428. return this.selectionMode;
  429. },
  430. <span id='Ext-selection-Model-method-setSelectionMode'> /**
  431. </span> * Sets the current selectionMode.
  432. * @param {String} selMode 'SINGLE', 'MULTI' or 'SIMPLE'.
  433. */
  434. setSelectionMode: function(selMode) {
  435. selMode = selMode ? selMode.toUpperCase() : 'SINGLE';
  436. // set to mode specified unless it doesnt exist, in that case
  437. // use single.
  438. this.selectionMode = this.modes[selMode] ? selMode : 'SINGLE';
  439. },
  440. <span id='Ext-selection-Model-method-isLocked'> /**
  441. </span> * Returns true if the selections are locked.
  442. * @return {Boolean}
  443. */
  444. isLocked: function() {
  445. return this.locked;
  446. },
  447. <span id='Ext-selection-Model-method-setLocked'> /**
  448. </span> * Locks the current selection and disables any changes from happening to the selection.
  449. * @param {Boolean} locked True to lock, false to unlock.
  450. */
  451. setLocked: function(locked) {
  452. this.locked = !!locked;
  453. },
  454. <span id='Ext-selection-Model-method-isSelected'> /**
  455. </span> * Returns true if the specified row is selected.
  456. * @param {Ext.data.Model/Number} record The record or index of the record to check
  457. * @return {Boolean}
  458. */
  459. isSelected: function(record) {
  460. record = Ext.isNumber(record) ? this.store.getAt(record) : record;
  461. return this.selected.indexOf(record) !== -1;
  462. },
  463. <span id='Ext-selection-Model-method-hasSelection'> /**
  464. </span> * Returns true if there are any a selected records.
  465. * @return {Boolean}
  466. */
  467. hasSelection: function() {
  468. return this.selected.getCount() &gt; 0;
  469. },
  470. refresh: function() {
  471. var me = this,
  472. store = me.store,
  473. toBeSelected = [],
  474. oldSelections = me.getSelection(),
  475. len = oldSelections.length,
  476. selection,
  477. change,
  478. i = 0,
  479. lastFocused = me.getLastFocused();
  480. // Not been bound yet.
  481. if (!store) {
  482. return;
  483. }
  484. // check to make sure that there are no records
  485. // missing after the refresh was triggered, prune
  486. // them from what is to be selected if so
  487. for (; i &lt; len; i++) {
  488. selection = oldSelections[i];
  489. if (!me.pruneRemoved || store.indexOf(selection) !== -1) {
  490. toBeSelected.push(selection);
  491. }
  492. }
  493. // there was a change from the old selected and
  494. // the new selection
  495. if (me.selected.getCount() != toBeSelected.length) {
  496. change = true;
  497. }
  498. me.clearSelections();
  499. if (store.indexOf(lastFocused) !== -1) {
  500. // restore the last focus but supress restoring focus
  501. me.setLastFocused(lastFocused, true);
  502. }
  503. if (toBeSelected.length) {
  504. // perform the selection again
  505. me.doSelect(toBeSelected, false, true);
  506. }
  507. me.maybeFireSelectionChange(change);
  508. },
  509. <span id='Ext-selection-Model-method-clearSelections'> /**
  510. </span> * A fast reset of the selections without firing events, updating the ui, etc.
  511. * For private usage only.
  512. * @private
  513. */
  514. clearSelections: function() {
  515. // reset the entire selection to nothing
  516. this.selected.clear();
  517. this.lastSelected = null;
  518. this.setLastFocused(null);
  519. },
  520. // when a record is added to a store
  521. onStoreAdd: Ext.emptyFn,
  522. // when a store is cleared remove all selections
  523. // (if there were any)
  524. onStoreClear: function() {
  525. if (this.selected.getCount &gt; 0) {
  526. this.clearSelections();
  527. this.maybeFireSelectionChange(true);
  528. }
  529. },
  530. // prune records from the SelectionModel if
  531. // they were selected at the time they were
  532. // removed.
  533. onStoreRemove: function(store, record, index) {
  534. var me = this,
  535. selected = me.selected;
  536. if (me.locked || !me.pruneRemoved) {
  537. return;
  538. }
  539. if (selected.remove(record)) {
  540. if (me.lastSelected == record) {
  541. me.lastSelected = null;
  542. }
  543. if (me.getLastFocused() == record) {
  544. me.setLastFocused(null);
  545. }
  546. me.maybeFireSelectionChange(true);
  547. }
  548. },
  549. <span id='Ext-selection-Model-method-getCount'> /**
  550. </span> * Returns the count of selected records.
  551. * @return {Number} The number of selected records
  552. */
  553. getCount: function() {
  554. return this.selected.getCount();
  555. },
  556. // cleanup.
  557. destroy: Ext.emptyFn,
  558. // if records are updated
  559. onStoreUpdate: Ext.emptyFn,
  560. <span id='Ext-selection-Model-method-onStoreLoad'> /**
  561. </span> * @abstract
  562. * @private
  563. */
  564. onStoreLoad: Ext.emptyFn,
  565. // @abstract
  566. onSelectChange: Ext.emptyFn,
  567. // @abstract
  568. onLastFocusChanged: function(oldFocused, newFocused) {
  569. this.fireEvent('focuschange', this, oldFocused, newFocused);
  570. },
  571. // @abstract
  572. onEditorKey: Ext.emptyFn,
  573. // @abstract
  574. bindComponent: Ext.emptyFn,
  575. // @abstract
  576. beforeViewRender: Ext.emptyFn
  577. });
  578. </pre>
  579. </body>
  580. </html>