Ext.Loader.setConfig({enabled: true}); Ext.Loader.setPath('Ext.ux', '../ux/'); Ext.require([ '*', 'Ext.ux.ajax.JsonSimlet', 'Ext.ux.ajax.SimManager' ]); Ext.define('BigDataSimlet', { extend: 'Ext.ux.ajax.JsonSimlet', data: [], numFields: 10, numRecords: 50*1000, groupSize: 1, getData: function (ctx) { var me = this, data = me.data, groupSize = ctx.params.groupSize || 1, group; if (data.length != me.numRecords || me.lastNumColumns != me.numFields || groupSize !== me.groupSize) { me.currentOrder = null; me.lastNumColumns = me.numFields; me.groupSize = groupSize; data.length = 0; for (var r = 0, k = me.numRecords; r < k; r++) { group = Math.floor((r + groupSize) / groupSize); var rec = { id: 'rec-' + r }; for (var i = 0; i < me.numFields; i++) { if (groupSize > 1 && i === 0) { rec['field' + i] = group; } else { rec['field' + i] = r; } } data.push(rec); } } return this.callParent(arguments); } }); Ext.onReady(function() { Ext.define('StoreInfo', { override: 'Ext.data.Store', prefetch: function(options){ var page = options.page; if (!this.pageCached(page) && !this.pagePending(page)) { logPanel.log('Prefetch rows ' + options.start + '-' + (options.start + options.limit)); } this.callParent(arguments); }, onProxyPrefetch: function(operation) { this.callParent(arguments); logPanel.log('Prefetch returned ' + operation.start + '-' + (operation.start + operation.limit)); } }); Ext.define('CacheInfo', { override: 'Ext.util.LruCache', prune: function(){ this.callParent(arguments); logPanel.log('Page cache contains pages ' + this.getKeys().join(',') + '
    ' + this.getCount() + ' records in cache'); } }); Ext.define('BufferInfo', { override: 'Ext.grid.plugin.BufferedRenderer', setBodyTop: function(bodyTop) { this.callParent(arguments); logPanel.log('Table moved to top: ' + bodyTop); } }); var simlet = new BigDataSimlet(), center, storeCount = 0, summaryTypes = ['count', 'min', 'max', 'sum', 'average'], summaryTypeNames = ['count', 'min', 'max', 'sum', 'avg']; Ext.ux.ajax.SimManager.init({ delay: 300 }).register({ localAjaxSimulator: simlet }); function createStore (numFields, buffered, groupSize) { var fields = [], i, storeConfig; for (i = 0; i < numFields; ++i) { fields.push('field' + i); } simlet.numFields = numFields; storeConfig = { storeId: 'infinite-scroll-store-' + (storeCount++), remoteSort: buffered, // allow the grid to interact with the paging scroller by buffering buffered: buffered, fields: fields, proxy: { // Simulating Ajax. type: 'ajax', url: 'localAjaxSimulator?groupSize=' + groupSize, reader: { root: 'topics', totalProperty: 'totalCount' } } }; if (groupSize) { storeConfig.remoteGroup = buffered; storeConfig.groupField = 'field0'; } return Ext.create('Ext.data.Store', storeConfig); } function createGrid(extraCfg) { var gridConfig = Ext.apply({ title: 'Random data (' + simlet.numRecords + ' records)', border: false, loadMask: true, selModel: { pruneRemoved: false }, columnLines: true, multiSelect: true, columns: [{ xtype: 'rownumberer', width: 50, sortable: false }] }, extraCfg); if (store.groupField) { gridConfig.features = [ Ext.create('Ext.grid.feature.Grouping', { showSummaryRow: true, groupHeaderTpl: [ '{columnName}: {name:this.groupName}', '', ' ({rows.length} Item{[values.rows.length > 1 ? "s" : ""]})', '', { groupName: function(group) { var rowIdx = (group - 1) * simlet.groupSize; return groupColumnRenderer(rowIdx, null, null, rowIdx, 1); } }], id: 'field0Grouping' }) ]; } return Ext.create('Ext.grid.Panel', gridConfig); } var store = createStore(10), grid = createGrid({ store: store }); function makeLabel (ns, cls, name) { var docs = '../..'; //docs = '../../../.build/sdk'; // for development/testing return '' + cls + ' ' + name + ''; } var logPanel = new Ext.Panel({ title: 'Log', region: 'center', autoScroll: true, log: function(m) { if (!this.disabled) { logPanel.body.createChild({ html: m }); logPanel.body.dom.scrollTop = 1000000; } }, tbar: [{ text: 'Logging on', pressed: true, enableToggle: true, toggleHandler: function(btn, pressed) { if (pressed) { btn.setText('Logging on'); logPanel.disabled = false; } else { btn.setText('Logging off'); logPanel.disabled = true; } } }, { text: 'Clear', handler: function() { logPanel.body.update(''); } }] }); var controls = Ext.create('Ext.form.Panel', { region: 'north', split: true, bodyPadding: 5, layout: 'form', height: 375, defaults: { labelWidth: 205 }, items: [{ xtype: 'numberfield', fieldLabel: 'Ajax latency (ms)', itemId: 'latency', value: 1000, minValue: 0, maxValue: 5000 }, { xtype: 'numberfield', fieldLabel: 'Dataset row count', itemId: 'rowCount', value: 50000, minValue: 200 }, { xtype: 'numberfield', fieldLabel: makeLabel('Ext.grid', 'BufferedRenderer', 'trailingBufferZone'), itemId: 'scrollerTrailingBufferZone', value: Ext.grid.plugin.BufferedRenderer.prototype.trailingBufferZone, maxValue: 100 }, { xtype: 'numberfield', fieldLabel: makeLabel('Ext.grid', 'BufferedRenderer', 'leadingBufferZone'), itemId: 'scrollerLeadingBufferZone', value: Ext.grid.plugin.BufferedRenderer.prototype.leadingBufferZone, maxValue: 100, listeners: { change: function(fld, newVal, oldVal) { var nfeField = controls.down('#scrollerNumFromEdge'); nfeField.maxValue = newVal - 1; if (nfeField.getValue() >= newVal) { nfeField.setValue(newVal - 1); } }, buffer: 500 } }, { xtype: 'numberfield', fieldLabel: makeLabel('Ext.grid', 'BufferedRenderer', 'numFromEdge'), itemId: 'scrollerNumFromEdge', value: Ext.grid.plugin.BufferedRenderer.prototype.numFromEdge, maxValue: Ext.data.Store.prototype.leadingBufferZone - 1 }, { xtype: 'numberfield', fieldLabel: 'Num columns', itemId: 'numFields', value: 10, minValue: 1 }, { xtype: 'checkboxfield', fieldLabel: makeLabel('Ext.data', 'Store', 'buffered'), itemId: 'buffered', value: 150, listeners: { change: function(cb, checked) { var pageSize = controls.down('#pageSize'), storeTrailingBufferZone = controls.down('#storeTrailingBufferZone'), storeLeadingBufferZone = controls.down('#storeLeadingBufferZone'), purgePageCount = controls.down('#purgePageCount'), scrollToLoadBuffer = controls.down('#scrollToLoadBuffer'); if (checked) { pageSize.enable(); storeTrailingBufferZone.enable(); storeLeadingBufferZone.enable(); purgePageCount.enable(); scrollToLoadBuffer.enable(); } else { pageSize.disable(); storeTrailingBufferZone.disable(); storeLeadingBufferZone.disable(); purgePageCount.disable(); scrollToLoadBuffer.disable(); } } } }, { xtype: 'numberfield', fieldLabel: makeLabel('Ext.data', 'Store', 'pageSize'), itemId: 'pageSize', value: 150, disabled: true }, { xtype: 'numberfield', fieldLabel: makeLabel('Ext.data', 'Store', 'trailingBufferZone'), itemId: 'storeTrailingBufferZone', value: Ext.data.Store.prototype.trailingBufferZone, disabled: true }, { xtype: 'numberfield', fieldLabel: makeLabel('Ext.data', 'Store', 'leadingBufferZone'), itemId: 'storeLeadingBufferZone', value: Ext.data.Store.prototype.leadingBufferZone, disabled: true }, { xtype: 'numberfield', fieldLabel: makeLabel('Ext.data', 'Store', 'purgePageCount'), itemId: 'purgePageCount', value: Ext.data.Store.prototype.purgePageCount, minValue: 0, disabled: true }, { xtype: 'numberfield', fieldLabel: makeLabel('Ext.grid', 'BufferedRenderer', 'scrollToLoadBuffer'), itemId: 'scrollToLoadBuffer', value: Ext.grid.plugin.BufferedRenderer.prototype.scrollToLoadBuffer, minValue: 0, maxValue: 1000, listeners: { change: function(f, newVal, oldVal) { grid.verticalScroller.scrollToLoadBuffer = newVal; } }, disabled: true }], tbar: [{ text: 'Reload', handler: initializeGrid }], listeners: { boxready: function(panel, width, height) { panel.minHeight = height; } } }); function columnRenderer(v, metadata, record, rowIdx, colIdx, store, view) { return 'col' + colIdx + ', row ' + v + '0.00'; } function groupColumnRenderer(v, metadata, record, rowIdx, colIdx, store, view) { var groupSize = simlet.groupSize, group = Math.floor((rowIdx + groupSize) / groupSize), groupStart = (group - 1) * groupSize + 1, groupEnd = groupStart + (groupSize - 1); return 'rows ' + groupStart + ' to ' + groupEnd; } function scrollTopToBottom(b, maxScroll) { var view = grid.view, el = view.el, br = view.bufferedRenderer, oldSynchronousRender = br.oldSynchronousRender, scrollQuantum = 30, start = new Date().getTime(), nextScroll = function() { el.dom.scrollTop += scrollQuantum; var newPos = el.dom.scrollTop; // At max scroll - we're done if (newPos >= maxScroll) { delete br.handleViewScroll; logPanel.disabled = false; br.synchronousRender = oldSynchronousRender; logPanel.log('Scroll took ' + (new Date().getTime() - start) + ' ms'); b.setText(b.initialConfig.text); } }; // Set up the onRange fetched to immediately scroll more logPanel.disabled = true; br.synchronousRender = true; br.handleViewScroll = Ext.Function.createSequence(br.handleViewScroll, nextScroll); nextScroll(); } function scrollBottomToTop(b) { var view = grid.view, el = view.el, br = view.bufferedRenderer, oldSynchronousRender = br.oldSynchronousRender, scrollQuantum = 30, start = new Date().getTime(), nextScroll = function() { var newPos = el.dom.scrollTop - scrollQuantum; el.dom.scrollTop = newPos; // At min scroll - we're done if (newPos <= 0) { delete br.handleViewScroll; logPanel.disabled = false; br.synchronousRender = oldSynchronousRender; logPanel.log('Scroll took ' + (new Date().getTime() - start) + ' ms'); b.setText(b.initialConfig.text); } }; // Set up the onRange fetched to immediately scroll more logPanel.disabled = true; br.synchronousRender = true; br.handleViewScroll = Ext.Function.createSequence(br.handleViewScroll, nextScroll); nextScroll(); } function initializeGrid () { var numFields = controls.down('#numFields').getValue(), columns = [{ xtype: 'rownumberer', width: 50, sortable: false }], i, rowCount = controls.down('#rowCount').getValue(), groupSize = grid.ownerCt.down('#groupSize').getValue()||0, buffered = controls.down('#buffered').getValue(), columnSpec; Ext.suspendLayouts(); store.removeAll(); grid.destroy(); store.destroy(); logPanel.body.update(''); for (i = 0; i < numFields; ++i) { columnSpec = { text: 'Field ' + (i+1), dataIndex: 'field' + i, renderer: columnRenderer, summaryType: summaryTypes[i % 5], align: 'right', summaryRenderer: function(value, cellValues, record, recordIndex, colIdx, store, view) { cellValues.style = 'text-align:right'; return summaryTypeNames[(colIdx - 1) % 5] + ': ' + Ext.util.Format.number(value, '0.00'); } }; if (i === 0 && groupSize > 1) { delete columnSpec.summaryType; delete columnSpec.summaryRenderer; columnSpec.width = 150; columnSpec.renderer = groupColumnRenderer; } columns.push(columnSpec); } simlet.numRecords = rowCount; store = createStore(numFields, buffered, groupSize); store.pageSize = controls.down('#pageSize').getValue(); store.trailingBufferZone = controls.down('#storeTrailingBufferZone').getValue(); store.leadingBufferZone = controls.down('#storeLeadingBufferZone').getValue(); store.data.maxSize = store.purgePageCount = controls.down('#purgePageCount').getValue(); grid = createGrid({ title: 'Random data (' + simlet.numRecords + ' records)', store: store, columns: columns, plugins: [{ ptype: 'bufferedrenderer', numFromEdge: controls.down('#scrollerNumFromEdge').getValue(), trailingBufferZone: controls.down('#scrollerTrailingBufferZone').getValue(), leadingBufferZone: controls.down('#scrollerLeadingBufferZone').getValue() }] }); Ext.ux.ajax.SimManager.delay = controls.down('#latency').getValue(); center.add(grid); if (buffered) { // Load the first page. It will be diverted through the prefetch buffer. store.loadPage(1); } else { store.load({ limit: rowCount }); } Ext.resumeLayouts(true); } new Ext.Viewport({ layout: { type: 'border', padding: 5 }, items: [{ border: false, listeners: { render: { fn: function(p) { Ext.EventManager.idleEvent.addListener(function() { p.header.removeCls('x-docked-noborder-left x-docked-noborder-right x-docked-noborder-top'); p.updateLayout({isRoot:true}); }, null, {single: true}); }, single: true }, collapse: function() { logPanel.wasDisabled = logPanel.disabled; logPanel.disabled = true; }, expand: function() { logPanel.disabled = logPanel.wasDisabled; } }, title: 'Configuration', collapsible: true, layout: 'border', region: 'west', bodyBorder: false, width: 290, split: true, minWidth: 290, items: [ controls, logPanel ] }, center = Ext.create('Ext.panel.Panel', { region: 'center', layout: 'fit', items: grid, bbar: [{ text: 'Scroll top to bottom', handler: function(scrollButton) { var v = grid.view, el = v.el; if (v.bufferedRenderer.hasOwnProperty('handleViewScroll')) { delete v.bufferedRenderer.handleViewScroll; scrollButton.setText(scrollButton.initialConfig.text); } else { scrollButton.setText('Stop scroll'); v.el.dom.scrollTop = 0; Ext.defer(scrollTopToBottom, controls.down('#latency').getValue() + 200, null, [scrollButton, el.dom.scrollHeight - el.dom.clientHeight]); } }, tooltip: 'Time a full scroll of the grid from top to bottom' }, { text: 'Scroll bottom to top', handler: function(scrollButton) { var v = grid.view, el = v.el; if (v.bufferedRenderer.hasOwnProperty('handleViewScroll')) { delete v.bufferedRenderer.handleViewScroll; scrollButton.setText(scrollButton.initialConfig.text); } else { scrollButton.setText('Stop scroll'); v.el.dom.scrollTop = el.dom.scrollHeight; Ext.defer(scrollBottomToTop, controls.down('#latency').getValue() + 200, null, [scrollButton, el.dom.scrollHeight - el.dom.clientHeight]); } }, tooltip: 'Time a full scroll of the grid bottom to top' }, { xtype: 'numberfield', itemId: 'groupSize', fieldLabel: 'Group size', minValue: 0, maxValue: 10000, width: 140, labelWidth: 70 }] })] }) });