BufferView.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*
  2. * Ext JS Library 3.3.1
  3. * Copyright(c) 2006-2010 Sencha Inc.
  4. * licensing@sencha.com
  5. * http://www.sencha.com/license
  6. */
  7. Ext.ns('Ext.ux.grid');
  8. /**
  9. * @class Ext.ux.grid.BufferView
  10. * @extends Ext.grid.GridView
  11. * A custom GridView which renders rows on an as-needed basis.
  12. */
  13. Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, {
  14. /**
  15. * @cfg {Number} rowHeight
  16. * The height of a row in the grid.
  17. */
  18. rowHeight: 19,
  19. /**
  20. * @cfg {Number} borderHeight
  21. * The combined height of border-top and border-bottom of a row.
  22. */
  23. borderHeight: 2,
  24. /**
  25. * @cfg {Boolean/Number} scrollDelay
  26. * The number of milliseconds before rendering rows out of the visible
  27. * viewing area. Defaults to 100. Rows will render immediately with a config
  28. * of false.
  29. */
  30. scrollDelay: 100,
  31. /**
  32. * @cfg {Number} cacheSize
  33. * The number of rows to look forward and backwards from the currently viewable
  34. * area. The cache applies only to rows that have been rendered already.
  35. */
  36. cacheSize: 20,
  37. /**
  38. * @cfg {Number} cleanDelay
  39. * The number of milliseconds to buffer cleaning of extra rows not in the
  40. * cache.
  41. */
  42. cleanDelay: 500,
  43. initTemplates : function(){
  44. Ext.ux.grid.BufferView.superclass.initTemplates.call(this);
  45. var ts = this.templates;
  46. // empty div to act as a place holder for a row
  47. ts.rowHolder = new Ext.Template(
  48. '<div class="x-grid3-row {alt}" style="{tstyle}"></div>'
  49. );
  50. ts.rowHolder.disableFormats = true;
  51. ts.rowHolder.compile();
  52. ts.rowBody = new Ext.Template(
  53. '<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
  54. '<tbody><tr>{cells}</tr>',
  55. (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
  56. '</tbody></table>'
  57. );
  58. ts.rowBody.disableFormats = true;
  59. ts.rowBody.compile();
  60. },
  61. getStyleRowHeight : function(){
  62. return Ext.isBorderBox ? (this.rowHeight + this.borderHeight) : this.rowHeight;
  63. },
  64. getCalculatedRowHeight : function(){
  65. return this.rowHeight + this.borderHeight;
  66. },
  67. getVisibleRowCount : function(){
  68. var rh = this.getCalculatedRowHeight(),
  69. visibleHeight = this.scroller.dom.clientHeight;
  70. return (visibleHeight < 1) ? 0 : Math.ceil(visibleHeight / rh);
  71. },
  72. getVisibleRows: function(){
  73. var count = this.getVisibleRowCount(),
  74. sc = this.scroller.dom.scrollTop,
  75. start = (sc === 0 ? 0 : Math.floor(sc/this.getCalculatedRowHeight())-1);
  76. return {
  77. first: Math.max(start, 0),
  78. last: Math.min(start + count + 2, this.ds.getCount()-1)
  79. };
  80. },
  81. doRender : function(cs, rs, ds, startRow, colCount, stripe, onlyBody){
  82. var ts = this.templates,
  83. ct = ts.cell,
  84. rt = ts.row,
  85. rb = ts.rowBody,
  86. last = colCount-1,
  87. rh = this.getStyleRowHeight(),
  88. vr = this.getVisibleRows(),
  89. tstyle = 'width:'+this.getTotalWidth()+';height:'+rh+'px;',
  90. // buffers
  91. buf = [],
  92. cb,
  93. c,
  94. p = {},
  95. rp = {tstyle: tstyle},
  96. r;
  97. for (var j = 0, len = rs.length; j < len; j++) {
  98. r = rs[j]; cb = [];
  99. var rowIndex = (j+startRow),
  100. visible = rowIndex >= vr.first && rowIndex <= vr.last;
  101. if (visible) {
  102. for (var i = 0; i < colCount; i++) {
  103. c = cs[i];
  104. p.id = c.id;
  105. p.css = i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
  106. p.attr = p.cellAttr = "";
  107. p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
  108. p.style = c.style;
  109. if (p.value === undefined || p.value === "") {
  110. p.value = "&#160;";
  111. }
  112. if (r.dirty && typeof r.modified[c.name] !== 'undefined') {
  113. p.css += ' x-grid3-dirty-cell';
  114. }
  115. cb[cb.length] = ct.apply(p);
  116. }
  117. }
  118. var alt = [];
  119. if(stripe && ((rowIndex+1) % 2 === 0)){
  120. alt[0] = "x-grid3-row-alt";
  121. }
  122. if(r.dirty){
  123. alt[1] = " x-grid3-dirty-row";
  124. }
  125. rp.cols = colCount;
  126. if(this.getRowClass){
  127. alt[2] = this.getRowClass(r, rowIndex, rp, ds);
  128. }
  129. rp.alt = alt.join(" ");
  130. rp.cells = cb.join("");
  131. buf[buf.length] = !visible ? ts.rowHolder.apply(rp) : (onlyBody ? rb.apply(rp) : rt.apply(rp));
  132. }
  133. return buf.join("");
  134. },
  135. isRowRendered: function(index){
  136. var row = this.getRow(index);
  137. return row && row.childNodes.length > 0;
  138. },
  139. syncScroll: function(){
  140. Ext.ux.grid.BufferView.superclass.syncScroll.apply(this, arguments);
  141. this.update();
  142. },
  143. // a (optionally) buffered method to update contents of gridview
  144. update: function(){
  145. if (this.scrollDelay) {
  146. if (!this.renderTask) {
  147. this.renderTask = new Ext.util.DelayedTask(this.doUpdate, this);
  148. }
  149. this.renderTask.delay(this.scrollDelay);
  150. }else{
  151. this.doUpdate();
  152. }
  153. },
  154. onRemove : function(ds, record, index, isUpdate){
  155. Ext.ux.grid.BufferView.superclass.onRemove.apply(this, arguments);
  156. if(isUpdate !== true){
  157. this.update();
  158. }
  159. },
  160. doUpdate: function(){
  161. if (this.getVisibleRowCount() > 0) {
  162. var g = this.grid,
  163. cm = g.colModel,
  164. ds = g.store,
  165. cs = this.getColumnData(),
  166. vr = this.getVisibleRows(),
  167. row;
  168. for (var i = vr.first; i <= vr.last; i++) {
  169. // if row is NOT rendered and is visible, render it
  170. if(!this.isRowRendered(i) && (row = this.getRow(i))){
  171. var html = this.doRender(cs, [ds.getAt(i)], ds, i, cm.getColumnCount(), g.stripeRows, true);
  172. row.innerHTML = html;
  173. }
  174. }
  175. this.clean();
  176. }
  177. },
  178. // a buffered method to clean rows
  179. clean : function(){
  180. if(!this.cleanTask){
  181. this.cleanTask = new Ext.util.DelayedTask(this.doClean, this);
  182. }
  183. this.cleanTask.delay(this.cleanDelay);
  184. },
  185. doClean: function(){
  186. if (this.getVisibleRowCount() > 0) {
  187. var vr = this.getVisibleRows();
  188. vr.first -= this.cacheSize;
  189. vr.last += this.cacheSize;
  190. var i = 0, rows = this.getRows();
  191. // if first is less than 0, all rows have been rendered
  192. // so lets clean the end...
  193. if(vr.first <= 0){
  194. i = vr.last + 1;
  195. }
  196. for(var len = this.ds.getCount(); i < len; i++){
  197. // if current row is outside of first and last and
  198. // has content, update the innerHTML to nothing
  199. if ((i < vr.first || i > vr.last) && rows[i].innerHTML) {
  200. rows[i].innerHTML = '';
  201. }
  202. }
  203. }
  204. },
  205. removeTask: function(name){
  206. var task = this[name];
  207. if(task && task.cancel){
  208. task.cancel();
  209. this[name] = null;
  210. }
  211. },
  212. destroy : function(){
  213. this.removeTask('cleanTask');
  214. this.removeTask('renderTask');
  215. Ext.ux.grid.BufferView.superclass.destroy.call(this);
  216. },
  217. layout: function(){
  218. Ext.ux.grid.BufferView.superclass.layout.call(this);
  219. this.update();
  220. }
  221. });