Table3.html 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217
  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-view-Table'>/**
  19. </span> * This class encapsulates the user interface for a tabular data set.
  20. * It acts as a centralized manager for controlling the various interface
  21. * elements of the view. This includes handling events, such as row and cell
  22. * level based DOM events. It also reacts to events from the underlying {@link Ext.selection.Model}
  23. * to provide visual feedback to the user.
  24. *
  25. * This class does not provide ways to manipulate the underlying data of the configured
  26. * {@link Ext.data.Store}.
  27. *
  28. * This is the base class for both {@link Ext.grid.View} and {@link Ext.tree.View} and is not
  29. * to be used directly.
  30. */
  31. Ext.define('Ext.view.Table', {
  32. extend: 'Ext.view.View',
  33. alias: 'widget.tableview',
  34. uses: [
  35. 'Ext.view.TableLayout',
  36. 'Ext.view.TableChunker',
  37. 'Ext.util.DelayedTask',
  38. 'Ext.util.MixedCollection'
  39. ],
  40. componentLayout: 'tableview',
  41. baseCls: Ext.baseCSSPrefix + 'grid-view',
  42. // row
  43. itemSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-row',
  44. // cell
  45. cellSelector: 'td.' + Ext.baseCSSPrefix + 'grid-cell',
  46. // keep a separate rowSelector, since we may need to select the actual row elements
  47. rowSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-row',
  48. <span id='Ext-view-Table-cfg-firstCls'> /**
  49. </span> * @cfg {String} [firstCls='x-grid-cell-first']
  50. * A CSS class to add to the *first* cell in every row to enable special styling for the first column.
  51. * If no styling is needed on the first column, this may be configured as `null`.
  52. */
  53. firstCls: Ext.baseCSSPrefix + 'grid-cell-first',
  54. <span id='Ext-view-Table-cfg-lastCls'> /**
  55. </span> * @cfg {String} [lastCls='x-grid-cell-last']
  56. * A CSS class to add to the *last* cell in every row to enable special styling for the last column.
  57. * If no styling is needed on the last column, this may be configured as `null`.
  58. */
  59. lastCls: Ext.baseCSSPrefix + 'grid-cell-last',
  60. headerRowSelector: 'tr.' + Ext.baseCSSPrefix + 'grid-header-row',
  61. selectedItemCls: Ext.baseCSSPrefix + 'grid-row-selected',
  62. selectedCellCls: Ext.baseCSSPrefix + 'grid-cell-selected',
  63. focusedItemCls: Ext.baseCSSPrefix + 'grid-row-focused',
  64. overItemCls: Ext.baseCSSPrefix + 'grid-row-over',
  65. altRowCls: Ext.baseCSSPrefix + 'grid-row-alt',
  66. rowClsRe: new RegExp('(?:^|\\s*)' + Ext.baseCSSPrefix + 'grid-row-(first|last|alt)(?:\\s+|$)', 'g'),
  67. cellRe: new RegExp(Ext.baseCSSPrefix + 'grid-cell-([^\\s]+) ', ''),
  68. // cfg docs inherited
  69. trackOver: true,
  70. <span id='Ext-view-Table-method-getRowClass'> /**
  71. </span> * Override this function to apply custom CSS classes to rows during rendering. This function should return the
  72. * CSS class name (or empty string '' for none) that will be added to the row's wrapping div. To apply multiple
  73. * class names, simply return them space-delimited within the string (e.g. 'my-class another-class').
  74. * Example usage:
  75. *
  76. * viewConfig: {
  77. * getRowClass: function(record, rowIndex, rowParams, store){
  78. * return record.get(&quot;valid&quot;) ? &quot;row-valid&quot; : &quot;row-error&quot;;
  79. * }
  80. * }
  81. *
  82. * @param {Ext.data.Model} record The record corresponding to the current row.
  83. * @param {Number} index The row index.
  84. * @param {Object} rowParams **DEPRECATED.** For row body use the
  85. * {@link Ext.grid.feature.RowBody#getAdditionalData getAdditionalData} method of the rowbody feature.
  86. * @param {Ext.data.Store} store The store this grid is bound to
  87. * @return {String} a CSS class name to add to the row.
  88. * @method
  89. */
  90. getRowClass: null,
  91. <span id='Ext-view-Table-cfg-stripeRows'> /**
  92. </span> * @cfg {Boolean} stripeRows
  93. * True to stripe the rows.
  94. *
  95. * This causes the CSS class **`x-grid-row-alt`** to be added to alternate rows of
  96. * the grid. A default CSS rule is provided which sets a background color, but you can override this
  97. * with a rule which either overrides the **background-color** style using the `!important`
  98. * modifier, or which uses a CSS selector of higher specificity.
  99. */
  100. stripeRows: true,
  101. <span id='Ext-view-Table-cfg-markDirty'> /**
  102. </span> * @cfg {Boolean} markDirty
  103. * True to show the dirty cell indicator when a cell has been modified.
  104. */
  105. markDirty : true,
  106. <span id='Ext-view-Table-cfg-enableTextSelection'> /**
  107. </span> * @cfg {Boolean} enableTextSelection
  108. * True to enable text selections.
  109. */
  110. <span id='Ext-view-Table-property-initialTpl'> /**
  111. </span> * @private
  112. * Simple initial tpl for TableView just to satisfy the validation within AbstractView.initComponent.
  113. */
  114. initialTpl: '&lt;div&gt;&lt;/div&gt;',
  115. initComponent: function() {
  116. var me = this,
  117. scroll = me.scroll;
  118. <span id='Ext-view-Table-property-table'> /**
  119. </span> * @private
  120. * @property {Ext.dom.AbstractElement.Fly} table
  121. * A flyweight Ext.Element which encapsulates a reference to the transient `&lt;table&gt;` element within this View.
  122. * *Note that the `dom` reference will not be present until the first data refresh*
  123. */
  124. me.table = new Ext.dom.Element.Fly();
  125. me.table.id = me.id + 'gridTable';
  126. // Scrolling within a TableView is controlled by the scroll config of its owning GridPanel
  127. // It must see undefined in this property in order to leave the scroll styles alone at afterRender time
  128. me.autoScroll = undefined;
  129. // Convert grid scroll config to standard Component scrolling configurations.
  130. if (scroll === true || scroll === 'both') {
  131. me.autoScroll = true;
  132. } else if (scroll === 'horizontal') {
  133. me.overflowX = 'auto';
  134. } else if (scroll === 'vertical') {
  135. me.overflowY = 'auto';
  136. }
  137. me.selModel.view = me;
  138. me.headerCt.view = me;
  139. me.headerCt.markDirty = me.markDirty;
  140. // Features need a reference to the grid.
  141. me.initFeatures(me.grid);
  142. delete me.grid;
  143. // The real tpl is generated, but AbstractView.initComponent insists upon the presence of a fully instantiated XTemplate at construction time.
  144. me.tpl = me.getTpl('initialTpl');
  145. me.callParent();
  146. },
  147. <span id='Ext-view-Table-method-moveColumn'> /**
  148. </span> * @private
  149. * Move a grid column from one position to another
  150. * @param {Number} fromIdx The index from which to move columns
  151. * @param {Number} toIdx The index at which to insert columns.
  152. * @param {Number} [colsToMove=1] The number of columns to move beginning at the `fromIdx`
  153. */
  154. moveColumn: function(fromIdx, toIdx, colsToMove) {
  155. var me = this,
  156. fragment = (colsToMove &gt; 1) ? document.createDocumentFragment() : undefined,
  157. destinationCellIdx = toIdx,
  158. colCount = me.getGridColumns().length,
  159. lastIdx = colCount - 1,
  160. doFirstLastClasses = (me.firstCls || me.lastCls) &amp;&amp; (toIdx === 0 || toIdx == colCount || fromIdx === 0 || fromIdx == lastIdx),
  161. i,
  162. j,
  163. rows, len, tr, headerRows;
  164. if (me.rendered) {
  165. // Use select here. In most cases there will only be one row. In
  166. // the case of a grouping grid, each group also has a header.
  167. headerRows = me.el.query(me.headerRowSelector);
  168. rows = me.el.query(me.rowSelector);
  169. if (toIdx &gt; fromIdx &amp;&amp; fragment) {
  170. destinationCellIdx -= colsToMove;
  171. }
  172. // Move the column sizing header to match
  173. for (i = 0, len = headerRows.length; i &lt; len; ++i) {
  174. tr = headerRows[i];
  175. if (fragment) {
  176. for (j = 0; j &lt; colsToMove; j++) {
  177. fragment.appendChild(tr.cells[fromIdx]);
  178. }
  179. tr.insertBefore(fragment, tr.cells[destinationCellIdx] || null);
  180. } else {
  181. tr.insertBefore(tr.cells[fromIdx], tr.cells[destinationCellIdx] || null);
  182. }
  183. }
  184. for (i = 0, len = rows.length; i &lt; len; i++) {
  185. tr = rows[i];
  186. // Keep first cell class and last cell class correct *only if needed*
  187. if (doFirstLastClasses) {
  188. if (fromIdx === 0) {
  189. Ext.fly(tr.cells[0]).removeCls(me.firstCls);
  190. Ext.fly(tr.cells[1]).addCls(me.firstCls);
  191. } else if (fromIdx === lastIdx) {
  192. Ext.fly(tr.cells[lastIdx]).removeCls(me.lastCls);
  193. Ext.fly(tr.cells[lastIdx - 1]).addCls(me.lastCls);
  194. }
  195. if (toIdx === 0) {
  196. Ext.fly(tr.cells[0]).removeCls(me.firstCls);
  197. Ext.fly(tr.cells[fromIdx]).addCls(me.firstCls);
  198. } else if (toIdx === colCount) {
  199. Ext.fly(tr.cells[lastIdx]).removeCls(me.lastCls);
  200. Ext.fly(tr.cells[fromIdx]).addCls(me.lastCls);
  201. }
  202. }
  203. if (fragment) {
  204. for (j = 0; j &lt; colsToMove; j++) {
  205. fragment.appendChild(tr.cells[fromIdx]);
  206. }
  207. tr.insertBefore(fragment, tr.cells[destinationCellIdx] || null);
  208. } else {
  209. tr.insertBefore(tr.cells[fromIdx], tr.cells[destinationCellIdx] || null);
  210. }
  211. }
  212. me.setNewTemplate();
  213. }
  214. },
  215. // scroll the view to the top
  216. scrollToTop: Ext.emptyFn,
  217. <span id='Ext-view-Table-method-addElListener'> /**
  218. </span> * Add a listener to the main view element. It will be destroyed with the view.
  219. * @private
  220. */
  221. addElListener: function(eventName, fn, scope){
  222. this.mon(this, eventName, fn, scope, {
  223. element: 'el'
  224. });
  225. },
  226. <span id='Ext-view-Table-method-getGridColumns'> /**
  227. </span> * Get the columns used for generating a template via TableChunker.
  228. * See {@link Ext.grid.header.Container#getGridColumns}.
  229. * @private
  230. */
  231. getGridColumns: function() {
  232. return this.headerCt.getGridColumns();
  233. },
  234. <span id='Ext-view-Table-method-getHeaderAtIndex'> /**
  235. </span> * Get a leaf level header by index regardless of what the nesting
  236. * structure is.
  237. * @private
  238. * @param {Number} index The index
  239. */
  240. getHeaderAtIndex: function(index) {
  241. return this.headerCt.getHeaderAtIndex(index);
  242. },
  243. <span id='Ext-view-Table-method-getCell'> /**
  244. </span> * Get the cell (td) for a particular record and column.
  245. * @param {Ext.data.Model} record
  246. * @param {Ext.grid.column.Column} column
  247. * @private
  248. */
  249. getCell: function(record, column) {
  250. var row = this.getNode(record);
  251. return Ext.fly(row).down(column.getCellSelector());
  252. },
  253. <span id='Ext-view-Table-method-getFeature'> /**
  254. </span> * Get a reference to a feature
  255. * @param {String} id The id of the feature
  256. * @return {Ext.grid.feature.Feature} The feature. Undefined if not found
  257. */
  258. getFeature: function(id) {
  259. var features = this.featuresMC;
  260. if (features) {
  261. return features.get(id);
  262. }
  263. },
  264. <span id='Ext-view-Table-method-initFeatures'> /**
  265. </span> * Initializes each feature and bind it to this view.
  266. * @private
  267. */
  268. initFeatures: function(grid) {
  269. var me = this,
  270. i,
  271. features,
  272. feature,
  273. len;
  274. me.featuresMC = new Ext.util.MixedCollection();
  275. features = me.features = me.constructFeatures();
  276. len = features ? features.length : 0;
  277. for (i = 0; i &lt; len; i++) {
  278. feature = features[i];
  279. // inject a reference to view and grid - Features need both
  280. feature.view = me;
  281. feature.grid = grid;
  282. me.featuresMC.add(feature);
  283. feature.init();
  284. }
  285. },
  286. <span id='Ext-view-Table-method-constructFeatures'> /**
  287. </span> * @private
  288. * Converts the features array as configured, into an array of instantiated Feature objects.
  289. *
  290. * This is borrowed by Lockable which clones and distributes Features to both child grids of a locking grid.
  291. *
  292. * Must have no side effects other than Feature instantiation.
  293. *
  294. * MUST NOT update the this.features property, and MUST NOT update the instantiated Features.
  295. */
  296. constructFeatures: function() {
  297. var me = this,
  298. features = me.features,
  299. feature,
  300. result,
  301. i = 0, len;
  302. if (features) {
  303. result = [];
  304. len = features.length;
  305. for (; i &lt; len; i++) {
  306. feature = features[i];
  307. if (!feature.isFeature) {
  308. feature = Ext.create('feature.' + feature.ftype, feature);
  309. }
  310. result[i] = feature;
  311. }
  312. }
  313. return result;
  314. },
  315. <span id='Ext-view-Table-method-attachEventsForFeatures'> /**
  316. </span> * Gives features an injection point to attach events to the markup that
  317. * has been created for this view.
  318. * @private
  319. */
  320. attachEventsForFeatures: function() {
  321. var features = this.features,
  322. ln = features.length,
  323. i = 0;
  324. for (; i &lt; ln; i++) {
  325. if (features[i].isFeature) {
  326. features[i].attachEvents();
  327. }
  328. }
  329. },
  330. afterRender: function() {
  331. var me = this;
  332. me.callParent();
  333. if (!me.enableTextSelection) {
  334. me.el.unselectable();
  335. }
  336. me.attachEventsForFeatures();
  337. },
  338. // Private template method implemented starting at the AbstractView class.
  339. onViewScroll: function(e, t) {
  340. this.callParent(arguments);
  341. this.fireEvent('bodyscroll', e, t);
  342. },
  343. <span id='Ext-view-Table-method-prepareData'> /**
  344. </span> * Uses the headerCt (Which is the repository of all information relating to Column definitions)
  345. * to transform data from dataIndex keys in a record to headerId keys in each header and then run
  346. * them through each feature to get additional data for variables they have injected into the view template.
  347. * @private
  348. */
  349. prepareData: function(data, idx, record) {
  350. var me = this,
  351. result = me.headerCt.prepareData(data, idx, record, me, me.ownerCt),
  352. features = me.features,
  353. ln = features.length,
  354. i = 0,
  355. feature;
  356. for (; i &lt; ln; i++) {
  357. feature = features[i];
  358. if (feature.isFeature) {
  359. Ext.apply(result, feature.getAdditionalData(data, idx, record, result, me));
  360. }
  361. }
  362. return result;
  363. },
  364. // TODO: Refactor headerCt dependency here to colModel
  365. collectData: function(records, startIndex) {
  366. var me = this,
  367. preppedRecords = me.callParent(arguments),
  368. headerCt = me.headerCt,
  369. fullWidth = headerCt.getFullWidth(),
  370. features = me.features,
  371. ln = features.length,
  372. o = {
  373. rows: preppedRecords,
  374. fullWidth: fullWidth
  375. },
  376. i = 0,
  377. feature,
  378. j = 0,
  379. jln,
  380. rowParams,
  381. rec,
  382. cls;
  383. jln = preppedRecords.length;
  384. // process row classes, rowParams has been deprecated and has been moved
  385. // to the individual features that implement the behavior.
  386. if (me.getRowClass) {
  387. for (; j &lt; jln; j++) {
  388. rowParams = {};
  389. rec = preppedRecords[j];
  390. cls = rec.rowCls || '';
  391. rec.rowCls = this.getRowClass(records[j], j, rowParams, me.store) + ' ' + cls;
  392. //&lt;debug&gt;
  393. if (rowParams.alt) {
  394. Ext.Error.raise(&quot;The getRowClass alt property is no longer supported.&quot;);
  395. }
  396. if (rowParams.tstyle) {
  397. Ext.Error.raise(&quot;The getRowClass tstyle property is no longer supported.&quot;);
  398. }
  399. if (rowParams.cells) {
  400. Ext.Error.raise(&quot;The getRowClass cells property is no longer supported.&quot;);
  401. }
  402. if (rowParams.body) {
  403. Ext.Error.raise(&quot;The getRowClass body property is no longer supported. Use the getAdditionalData method of the rowbody feature.&quot;);
  404. }
  405. if (rowParams.bodyStyle) {
  406. Ext.Error.raise(&quot;The getRowClass bodyStyle property is no longer supported.&quot;);
  407. }
  408. if (rowParams.cols) {
  409. Ext.Error.raise(&quot;The getRowClass cols property is no longer supported.&quot;);
  410. }
  411. //&lt;/debug&gt;
  412. }
  413. }
  414. // currently only one feature may implement collectData. This is to modify
  415. // what's returned to the view before its rendered
  416. for (; i &lt; ln; i++) {
  417. feature = features[i];
  418. if (feature.isFeature &amp;&amp; feature.collectData &amp;&amp; !feature.disabled) {
  419. o = feature.collectData(records, preppedRecords, startIndex, fullWidth, o);
  420. break;
  421. }
  422. }
  423. return o;
  424. },
  425. // Private. Called when the table changes height.
  426. // For example, see examples/grid/group-summary-grid.html
  427. // If we have flexed column headers, we need to update the header layout
  428. // because it may have to accommodate (or cease to accommodate) a vertical scrollbar.
  429. // Only do this on platforms which have a space-consuming scrollbar.
  430. // Only do it when vertical scrolling is enabled.
  431. refreshSize: function() {
  432. var me = this,
  433. cmp;
  434. // On every update of the layout system due to data update, capture the table's DOM in our private flyweight
  435. me.table.attach(me.el.child('table', true));
  436. if (!me.hasLoadingHeight) {
  437. cmp = me.up('tablepanel');
  438. // Suspend layouts in case the superclass requests a layout. We might too, so they
  439. // must be coalescsed.
  440. Ext.suspendLayouts();
  441. me.callParent();
  442. // If the OS displays scrollbars, and we are overflowing vertically, ensure the
  443. // HeaderContainer accounts for the scrollbar.
  444. if (cmp &amp;&amp; Ext.getScrollbarSize().width &amp;&amp; (me.autoScroll || me.overflowY)) {
  445. cmp.updateLayout();
  446. }
  447. Ext.resumeLayouts(true);
  448. }
  449. },
  450. <span id='Ext-view-Table-method-setNewTemplate'> /**
  451. </span> * Set a new template based on the current columns displayed in the grid.
  452. * @private
  453. */
  454. setNewTemplate: function() {
  455. var me = this,
  456. columns = me.headerCt.getColumnsForTpl(true);
  457. // Template generation requires the rowCount as well as the column definitions and features.
  458. me.tpl = me.getTableChunker().getTableTpl({
  459. rowCount: me.store.getCount(),
  460. columns: columns,
  461. features: me.features,
  462. enableTextSelection: me.enableTextSelection
  463. });
  464. },
  465. <span id='Ext-view-Table-method-getTableChunker'> /**
  466. </span> * Returns the configured chunker or default of Ext.view.TableChunker
  467. */
  468. getTableChunker: function() {
  469. return this.chunker || Ext.view.TableChunker;
  470. },
  471. <span id='Ext-view-Table-method-addRowCls'> /**
  472. </span> * Adds a CSS Class to a specific row.
  473. * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model
  474. * representing this row
  475. * @param {String} cls
  476. */
  477. addRowCls: function(rowInfo, cls) {
  478. var row = this.getNode(rowInfo);
  479. if (row) {
  480. Ext.fly(row).addCls(cls);
  481. }
  482. },
  483. <span id='Ext-view-Table-method-removeRowCls'> /**
  484. </span> * Removes a CSS Class from a specific row.
  485. * @param {HTMLElement/String/Number/Ext.data.Model} rowInfo An HTMLElement, index or instance of a model
  486. * representing this row
  487. * @param {String} cls
  488. */
  489. removeRowCls: function(rowInfo, cls) {
  490. var row = this.getNode(rowInfo);
  491. if (row) {
  492. Ext.fly(row).removeCls(cls);
  493. }
  494. },
  495. // GridSelectionModel invokes onRowSelect as selection changes
  496. onRowSelect : function(rowIdx) {
  497. this.addRowCls(rowIdx, this.selectedItemCls);
  498. },
  499. // GridSelectionModel invokes onRowDeselect as selection changes
  500. onRowDeselect : function(rowIdx) {
  501. var me = this;
  502. me.removeRowCls(rowIdx, me.selectedItemCls);
  503. me.removeRowCls(rowIdx, me.focusedItemCls);
  504. },
  505. onCellSelect: function(position) {
  506. var cell = this.getCellByPosition(position, true);
  507. if (cell) {
  508. Ext.fly(cell).addCls(this.selectedCellCls);
  509. }
  510. },
  511. onCellDeselect: function(position) {
  512. var cell = this.getCellByPosition(position, true);
  513. if (cell) {
  514. Ext.fly(cell).removeCls(this.selectedCellCls);
  515. }
  516. },
  517. onCellFocus: function(position) {
  518. this.focusCell(position);
  519. },
  520. getCellByPosition: function(position, returnDom) {
  521. if (position) {
  522. var node = this.getNode(position.row),
  523. header = this.headerCt.getHeaderAtIndex(position.column);
  524. if (header &amp;&amp; node) {
  525. return Ext.fly(node).down(header.getCellSelector(), returnDom);
  526. }
  527. }
  528. return false;
  529. },
  530. // GridSelectionModel invokes onRowFocus to 'highlight'
  531. // the last row focused
  532. onRowFocus: function(rowIdx, highlight, supressFocus) {
  533. var me = this;
  534. if (highlight) {
  535. me.addRowCls(rowIdx, me.focusedItemCls);
  536. if (!supressFocus) {
  537. me.focusRow(rowIdx);
  538. }
  539. //this.el.dom.setAttribute('aria-activedescendant', row.id);
  540. } else {
  541. me.removeRowCls(rowIdx, me.focusedItemCls);
  542. }
  543. },
  544. <span id='Ext-view-Table-method-focusRow'> /**
  545. </span> * Focuses a particular row and brings it into view. Will fire the rowfocus event.
  546. * @param {HTMLElement/String/Number/Ext.data.Model} rowIdx
  547. * An HTMLElement template node, index of a template node, the id of a template node or the
  548. * record associated with the node.
  549. */
  550. focusRow: function(rowIdx) {
  551. var me = this,
  552. row = me.getNode(rowIdx),
  553. el = me.el,
  554. adjustment = 0,
  555. panel = me.ownerCt,
  556. rowRegion,
  557. elTop,
  558. elBottom,
  559. record;
  560. if (row &amp;&amp; el) {
  561. // Viewable region must not include scrollbars, so use
  562. // DOM clientHeight to determine height
  563. elTop = el.getY();
  564. elBottom = elTop + el.dom.clientHeight;
  565. rowRegion = Ext.fly(row).getRegion();
  566. // row is above
  567. if (rowRegion.top &lt; elTop) {
  568. adjustment = rowRegion.top - elTop;
  569. // row is below
  570. } else if (rowRegion.bottom &gt; elBottom) {
  571. adjustment = rowRegion.bottom - elBottom;
  572. }
  573. record = me.getRecord(row);
  574. rowIdx = me.store.indexOf(record);
  575. if (adjustment) {
  576. panel.scrollByDeltaY(adjustment);
  577. }
  578. me.fireEvent('rowfocus', record, row, rowIdx);
  579. }
  580. },
  581. focusCell: function(position) {
  582. var me = this,
  583. cell = me.getCellByPosition(position),
  584. el = me.el,
  585. adjustmentY = 0,
  586. adjustmentX = 0,
  587. elRegion = el.getRegion(),
  588. panel = me.ownerCt,
  589. cellRegion,
  590. record;
  591. // Viewable region must not include scrollbars, so use
  592. // DOM client dimensions
  593. elRegion.bottom = elRegion.top + el.dom.clientHeight;
  594. elRegion.right = elRegion.left + el.dom.clientWidth;
  595. if (cell) {
  596. cellRegion = cell.getRegion();
  597. // cell is above
  598. if (cellRegion.top &lt; elRegion.top) {
  599. adjustmentY = cellRegion.top - elRegion.top;
  600. // cell is below
  601. } else if (cellRegion.bottom &gt; elRegion.bottom) {
  602. adjustmentY = cellRegion.bottom - elRegion.bottom;
  603. }
  604. // cell is left
  605. if (cellRegion.left &lt; elRegion.left) {
  606. adjustmentX = cellRegion.left - elRegion.left;
  607. // cell is right
  608. } else if (cellRegion.right &gt; elRegion.right) {
  609. adjustmentX = cellRegion.right - elRegion.right;
  610. }
  611. if (adjustmentY) {
  612. panel.scrollByDeltaY(adjustmentY);
  613. }
  614. if (adjustmentX) {
  615. panel.scrollByDeltaX(adjustmentX);
  616. }
  617. el.focus();
  618. me.fireEvent('cellfocus', record, cell, position);
  619. }
  620. },
  621. <span id='Ext-view-Table-method-scrollByDelta'> /**
  622. </span> * Scrolls by delta. This affects this individual view ONLY and does not
  623. * synchronize across views or scrollers.
  624. * @param {Number} delta
  625. * @param {String} [dir] Valid values are scrollTop and scrollLeft. Defaults to scrollTop.
  626. * @private
  627. */
  628. scrollByDelta: function(delta, dir) {
  629. dir = dir || 'scrollTop';
  630. var elDom = this.el.dom;
  631. elDom[dir] = (elDom[dir] += delta);
  632. },
  633. // private
  634. onUpdate : function(store, record, operation, changedFieldNames) {
  635. var me = this,
  636. index,
  637. newRow, newAttrs, attLen, i, attName, oldRow, oldRowDom,
  638. oldCells, newCells, len, i,
  639. columns, overItemCls,
  640. isHovered, row,
  641. // See if an editing plugin is active.
  642. isEditing = me.editingPlugin &amp;&amp; me.editingPlugin.editing;
  643. if (me.viewReady) {
  644. index = me.store.indexOf(record);
  645. columns = me.headerCt.getGridColumns();
  646. overItemCls = me.overItemCls;
  647. // If we have columns which may *need* updating (think lockable grid child with all columns either locked or unlocked)
  648. // and the changed record is within our view, then update the view
  649. if (columns.length &amp;&amp; index &gt; -1) {
  650. newRow = me.bufferRender([record], index)[0];
  651. oldRow = me.all.item(index);
  652. if (oldRow) {
  653. oldRowDom = oldRow.dom;
  654. isHovered = oldRow.hasCls(overItemCls);
  655. // Copy new row attributes across. Use IE-specific method if possible.
  656. if (oldRowDom.mergeAttributes) {
  657. oldRowDom.mergeAttributes(newRow, true);
  658. } else {
  659. newAttrs = newRow.attributes;
  660. attLen = newAttrs.length;
  661. for (i = 0; i &lt; attLen; i++) {
  662. attName = newAttrs[i].name;
  663. if (attName !== 'id') {
  664. oldRowDom.setAttribute(attName, newAttrs[i].value);
  665. }
  666. }
  667. }
  668. if (isHovered) {
  669. oldRow.addCls(overItemCls);
  670. }
  671. // Replace changed cells in the existing row structure with the new version from the rendered row.
  672. oldCells = oldRow.query(me.cellSelector);
  673. newCells = Ext.fly(newRow).query(me.cellSelector);
  674. len = newCells.length;
  675. // row is the element that contains the cells. This will be a different element from oldRow when using a rowwrap feature
  676. row = oldCells[0].parentNode;
  677. for (i = 0; i &lt; len; i++) {
  678. // If the field at this column index was changed, or column has a custom renderer
  679. // (which means value could rely on any other changed field) the update the cell's content.
  680. if (me.shouldUpdateCell(columns[i], changedFieldNames)) {
  681. // If an editor plugin is active, we carefully replace just the *contents* of the cell.
  682. if (isEditing) {
  683. Ext.fly(oldCells[i]).syncContent(newCells[i]);
  684. }
  685. // Otherwise, we simply replace whole TDs with a new version
  686. else {
  687. row.insertBefore(newCells[i], oldCells[i]);
  688. row.removeChild(oldCells[i]);
  689. }
  690. }
  691. }
  692. }
  693. me.fireEvent('itemupdate', record, index, newRow);
  694. }
  695. }
  696. },
  697. shouldUpdateCell: function(column, changedFieldNames){
  698. // Though this may not be the most efficient, a renderer could be dependent on any field in the
  699. // store, so we must always update the cell
  700. if (column.hasCustomRenderer) {
  701. return true;
  702. }
  703. return !changedFieldNames || Ext.Array.contains(changedFieldNames, column.dataIndex);
  704. },
  705. <span id='Ext-view-Table-method-refresh'> /**
  706. </span> * Refreshes the grid view. Saves and restores the scroll state, generates a new template, stripes rows and
  707. * invalidates the scrollers.
  708. */
  709. refresh: function() {
  710. var me = this;
  711. me.setNewTemplate();
  712. me.callParent(arguments);
  713. me.doStripeRows(0);
  714. me.headerCt.setSortState();
  715. },
  716. clearViewEl: function() {
  717. this.callParent();
  718. delete this.table.dom;
  719. },
  720. processItemEvent: function(record, row, rowIndex, e) {
  721. var me = this,
  722. cell = e.getTarget(me.cellSelector, row),
  723. cellIndex = cell ? cell.cellIndex : -1,
  724. map = me.statics().EventMap,
  725. selModel = me.getSelectionModel(),
  726. type = e.type,
  727. result;
  728. if (type == 'keydown' &amp;&amp; !cell &amp;&amp; selModel.getCurrentPosition) {
  729. // CellModel, otherwise we can't tell which cell to invoke
  730. cell = me.getCellByPosition(selModel.getCurrentPosition());
  731. if (cell) {
  732. cell = cell.dom;
  733. cellIndex = cell.cellIndex;
  734. }
  735. }
  736. result = me.fireEvent('uievent', type, me, cell, rowIndex, cellIndex, e, record, row);
  737. if (result === false || me.callParent(arguments) === false) {
  738. return false;
  739. }
  740. // Don't handle cellmouseenter and cellmouseleave events for now
  741. if (type == 'mouseover' || type == 'mouseout') {
  742. return true;
  743. }
  744. if(!cell) {
  745. // if the element whose event is being processed is not an actual cell (for example if using a rowbody
  746. // feature and the rowbody element's event is being processed) then do not fire any &quot;cell&quot; events
  747. return true;
  748. }
  749. return !(
  750. // We are adding cell and feature events
  751. (me['onBeforeCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) ||
  752. (me.fireEvent('beforecell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false) ||
  753. (me['onCell' + map[type]](cell, cellIndex, record, row, rowIndex, e) === false) ||
  754. (me.fireEvent('cell' + type, me, cell, cellIndex, record, row, rowIndex, e) === false)
  755. );
  756. },
  757. processSpecialEvent: function(e) {
  758. var me = this,
  759. map = me.statics().EventMap,
  760. features = me.features,
  761. ln = features.length,
  762. type = e.type,
  763. i, feature, prefix, featureTarget,
  764. beforeArgs, args,
  765. panel = me.ownerCt;
  766. me.callParent(arguments);
  767. if (type == 'mouseover' || type == 'mouseout') {
  768. return;
  769. }
  770. for (i = 0; i &lt; ln; i++) {
  771. feature = features[i];
  772. if (feature.hasFeatureEvent) {
  773. featureTarget = e.getTarget(feature.eventSelector, me.getTargetEl());
  774. if (featureTarget) {
  775. prefix = feature.eventPrefix;
  776. // allows features to implement getFireEventArgs to change the
  777. // fireEvent signature
  778. beforeArgs = feature.getFireEventArgs('before' + prefix + type, me, featureTarget, e);
  779. args = feature.getFireEventArgs(prefix + type, me, featureTarget, e);
  780. if (
  781. // before view event
  782. (me.fireEvent.apply(me, beforeArgs) === false) ||
  783. // panel grid event
  784. (panel.fireEvent.apply(panel, beforeArgs) === false) ||
  785. // view event
  786. (me.fireEvent.apply(me, args) === false) ||
  787. // panel event
  788. (panel.fireEvent.apply(panel, args) === false)
  789. ) {
  790. return false;
  791. }
  792. }
  793. }
  794. }
  795. return true;
  796. },
  797. onCellMouseDown: Ext.emptyFn,
  798. onCellMouseUp: Ext.emptyFn,
  799. onCellClick: Ext.emptyFn,
  800. onCellDblClick: Ext.emptyFn,
  801. onCellContextMenu: Ext.emptyFn,
  802. onCellKeyDown: Ext.emptyFn,
  803. onBeforeCellMouseDown: Ext.emptyFn,
  804. onBeforeCellMouseUp: Ext.emptyFn,
  805. onBeforeCellClick: Ext.emptyFn,
  806. onBeforeCellDblClick: Ext.emptyFn,
  807. onBeforeCellContextMenu: Ext.emptyFn,
  808. onBeforeCellKeyDown: Ext.emptyFn,
  809. <span id='Ext-view-Table-method-expandToFit'> /**
  810. </span> * Expands a particular header to fit the max content width.
  811. * This will ONLY expand, not contract.
  812. * @private
  813. */
  814. expandToFit: function(header) {
  815. if (header) {
  816. var maxWidth = this.getMaxContentWidth(header);
  817. delete header.flex;
  818. header.setWidth(maxWidth);
  819. }
  820. },
  821. <span id='Ext-view-Table-method-getMaxContentWidth'> /**
  822. </span> * Returns the max contentWidth of the header's text and all cells
  823. * in the grid under this header.
  824. * @private
  825. */
  826. getMaxContentWidth: function(header) {
  827. var cellSelector = header.getCellInnerSelector(),
  828. cells = this.el.query(cellSelector),
  829. i = 0,
  830. ln = cells.length,
  831. maxWidth = header.el.dom.scrollWidth,
  832. scrollWidth;
  833. for (; i &lt; ln; i++) {
  834. scrollWidth = cells[i].scrollWidth;
  835. if (scrollWidth &gt; maxWidth) {
  836. maxWidth = scrollWidth;
  837. }
  838. }
  839. return maxWidth;
  840. },
  841. getPositionByEvent: function(e) {
  842. var me = this,
  843. cellNode = e.getTarget(me.cellSelector),
  844. rowNode = e.getTarget(me.itemSelector),
  845. record = me.getRecord(rowNode),
  846. header = me.getHeaderByCell(cellNode);
  847. return me.getPosition(record, header);
  848. },
  849. getHeaderByCell: function(cell) {
  850. if (cell) {
  851. var m = cell.className.match(this.cellRe);
  852. if (m &amp;&amp; m[1]) {
  853. return Ext.getCmp(m[1]);
  854. }
  855. }
  856. return false;
  857. },
  858. <span id='Ext-view-Table-method-walkCells'> /**
  859. </span> * @param {Object} position The current row and column: an object containing the following properties:
  860. *
  861. * - row - The row index
  862. * - column - The column index
  863. *
  864. * @param {String} direction 'up', 'down', 'right' and 'left'
  865. * @param {Ext.EventObject} e event
  866. * @param {Boolean} preventWrap Set to true to prevent wrap around to the next or previous row.
  867. * @param {Function} verifierFn A function to verify the validity of the calculated position.
  868. * When using this function, you must return true to allow the newPosition to be returned.
  869. * @param {Object} scope Scope to run the verifierFn in
  870. * @returns {Object} newPosition An object containing the following properties:
  871. *
  872. * - row - The row index
  873. * - column - The column index
  874. *
  875. * @private
  876. */
  877. walkCells: function(pos, direction, e, preventWrap, verifierFn, scope) {
  878. // Caller (probably CellModel) had no current position. This can happen
  879. // if the main el is focused and any navigation key is presssed.
  880. if (!pos) {
  881. return;
  882. }
  883. var me = this,
  884. row = pos.row,
  885. column = pos.column,
  886. rowCount = me.store.getCount(),
  887. firstCol = me.getFirstVisibleColumnIndex(),
  888. lastCol = me.getLastVisibleColumnIndex(),
  889. newPos = {row: row, column: column},
  890. activeHeader = me.headerCt.getHeaderAtIndex(column);
  891. // no active header or its currently hidden
  892. if (!activeHeader || activeHeader.hidden) {
  893. return false;
  894. }
  895. e = e || {};
  896. direction = direction.toLowerCase();
  897. switch (direction) {
  898. case 'right':
  899. // has the potential to wrap if its last
  900. if (column === lastCol) {
  901. // if bottom row and last column, deny right
  902. if (preventWrap || row === rowCount - 1) {
  903. return false;
  904. }
  905. if (!e.ctrlKey) {
  906. // otherwise wrap to nextRow and firstCol
  907. newPos.row = row + 1;
  908. newPos.column = firstCol;
  909. }
  910. // go right
  911. } else {
  912. if (!e.ctrlKey) {
  913. newPos.column = column + me.getRightGap(activeHeader);
  914. } else {
  915. newPos.column = lastCol;
  916. }
  917. }
  918. break;
  919. case 'left':
  920. // has the potential to wrap
  921. if (column === firstCol) {
  922. // if top row and first column, deny left
  923. if (preventWrap || row === 0) {
  924. return false;
  925. }
  926. if (!e.ctrlKey) {
  927. // otherwise wrap to prevRow and lastCol
  928. newPos.row = row - 1;
  929. newPos.column = lastCol;
  930. }
  931. // go left
  932. } else {
  933. if (!e.ctrlKey) {
  934. newPos.column = column + me.getLeftGap(activeHeader);
  935. } else {
  936. newPos.column = firstCol;
  937. }
  938. }
  939. break;
  940. case 'up':
  941. // if top row, deny up
  942. if (row === 0) {
  943. return false;
  944. // go up
  945. } else {
  946. if (!e.ctrlKey) {
  947. newPos.row = row - 1;
  948. } else {
  949. newPos.row = 0;
  950. }
  951. }
  952. break;
  953. case 'down':
  954. // if bottom row, deny down
  955. if (row === rowCount - 1) {
  956. return false;
  957. // go down
  958. } else {
  959. if (!e.ctrlKey) {
  960. newPos.row = row + 1;
  961. } else {
  962. newPos.row = rowCount - 1;
  963. }
  964. }
  965. break;
  966. }
  967. if (verifierFn &amp;&amp; verifierFn.call(scope || window, newPos) !== true) {
  968. return false;
  969. } else {
  970. return newPos;
  971. }
  972. },
  973. getFirstVisibleColumnIndex: function() {
  974. var firstVisibleHeader = this.getHeaderCt().getVisibleGridColumns()[0];
  975. return firstVisibleHeader ? firstVisibleHeader.getIndex() : -1;
  976. },
  977. getLastVisibleColumnIndex: function() {
  978. var visHeaders = this.getHeaderCt().getVisibleGridColumns(),
  979. lastHeader = visHeaders[visHeaders.length - 1];
  980. return lastHeader.getIndex();
  981. },
  982. getHeaderCt: function() {
  983. return this.headerCt;
  984. },
  985. // TODO: have this use the new Ext.grid.CellContext class
  986. getPosition: function(record, header) {
  987. var me = this,
  988. store = me.store,
  989. gridCols = me.headerCt.getGridColumns();
  990. return {
  991. row: store.indexOf(record),
  992. column: Ext.Array.indexOf(gridCols, header)
  993. };
  994. },
  995. <span id='Ext-view-Table-method-getRightGap'> /**
  996. </span> * Determines the 'gap' between the closest adjacent header to the right
  997. * that is not hidden.
  998. * @private
  999. */
  1000. getRightGap: function(activeHeader) {
  1001. var headerCt = this.getHeaderCt(),
  1002. headers = headerCt.getGridColumns(),
  1003. activeHeaderIdx = Ext.Array.indexOf(headers, activeHeader),
  1004. i = activeHeaderIdx + 1,
  1005. nextIdx;
  1006. for (; i &lt;= headers.length; i++) {
  1007. if (!headers[i].hidden) {
  1008. nextIdx = i;
  1009. break;
  1010. }
  1011. }
  1012. return nextIdx - activeHeaderIdx;
  1013. },
  1014. beforeDestroy: function() {
  1015. if (this.rendered) {
  1016. this.el.removeAllListeners();
  1017. }
  1018. this.callParent(arguments);
  1019. },
  1020. <span id='Ext-view-Table-method-getLeftGap'> /**
  1021. </span> * Determines the 'gap' between the closest adjacent header to the left
  1022. * that is not hidden.
  1023. * @private
  1024. */
  1025. getLeftGap: function(activeHeader) {
  1026. var headerCt = this.getHeaderCt(),
  1027. headers = headerCt.getGridColumns(),
  1028. activeHeaderIdx = Ext.Array.indexOf(headers, activeHeader),
  1029. i = activeHeaderIdx - 1,
  1030. prevIdx;
  1031. for (; i &gt;= 0; i--) {
  1032. if (!headers[i].hidden) {
  1033. prevIdx = i;
  1034. break;
  1035. }
  1036. }
  1037. return prevIdx - activeHeaderIdx;
  1038. },
  1039. // after adding a row stripe rows from then on
  1040. onAdd: function(ds, records, index) {
  1041. this.callParent(arguments);
  1042. this.doStripeRows(index);
  1043. },
  1044. // after removing a row stripe rows from then on
  1045. onRemove: function(ds, records, index) {
  1046. this.callParent(arguments);
  1047. this.doStripeRows(index);
  1048. },
  1049. <span id='Ext-view-Table-method-doStripeRows'> /**
  1050. </span> * Stripes rows from a particular row index.
  1051. * @param {Number} startRow
  1052. * @param {Number} [endRow] argument specifying the last row to process.
  1053. * By default process up to the last row.
  1054. * @private
  1055. */
  1056. doStripeRows: function(startRow, endRow) {
  1057. var me = this,
  1058. rows,
  1059. rowsLn,
  1060. i,
  1061. row;
  1062. // ensure stripeRows configuration is turned on
  1063. if (me.rendered &amp;&amp; me.stripeRows) {
  1064. rows = me.getNodes(startRow, endRow);
  1065. for (i = 0, rowsLn = rows.length; i &lt; rowsLn; i++) {
  1066. row = rows[i];
  1067. // Remove prior applied row classes.
  1068. row.className = row.className.replace(me.rowClsRe, ' ');
  1069. startRow++;
  1070. // Every odd row will get an additional cls
  1071. if (startRow % 2 === 0) {
  1072. row.className += (' ' + me.altRowCls);
  1073. }
  1074. }
  1075. }
  1076. }
  1077. });
  1078. </pre>
  1079. </body>
  1080. </html>