// vim: ts=4:sw=4:nu:fdc=4:nospell /** * Search plugin for Ext.grid.GridPanel, Ext.grid.EditorGrid ver. 2.x or subclasses of them * * @author Ing. Jozef Sakalos * @copyright (c) 2008, by Ing. Jozef Sakalos * @date 17. January 2008 * @version $Id: Ext.ux.grid.Search.js 220 2008-04-29 21:46:51Z jozo $ * * @license Ext.ux.grid.Search is licensed under the terms of * the Open Source LGPL 3.0 license. Commercial use is permitted to the extent * that the code/component(s) do NOT become part of another Open Source or Commercially * licensed development library or toolkit without explicit permission. * * License details: http://www.gnu.org/licenses/lgpl.html */ /*global Ext */ Ext.ns('Ext.ux.grid'); /** * @class Ext.ux.grid.Search * @extends Ext.util.Observable * @param {Object} config configuration object * @constructor */ Ext.ux.grid.Search = function(config) { Ext.apply(this, config); Ext.ux.grid.Search.superclass.constructor.call(this); }; // eo constructor Ext.extend(Ext.ux.grid.Search, Ext.util.Observable, { /** * cfg {Boolean} autoFocus true to try to focus the input field on each store load (defaults to undefined) */ /** * @cfg {String} searchText Text to display on menu button */ searchText:'Search' /** * @cfg {String} searchTipText Text to display as input tooltip. Set to '' for no tooltip */ ,searchTipText:'Type the search-term and press ENTER' /** * @cfg {String} selectAllText Text to display on menu item that selects all fields */ ,selectAllText:'select all' ,myid: '' /** * @cfg {String} position Where to display the search controls. Valid values are top and bottom (defaults to bottom) * Corresponding toolbar has to exist at least with mimimum configuration tbar:[] for position:top or bbar:[] * for position bottom. Plugin does NOT create any toolbar. */ ,position:'bottom' /** * @cfg {String} iconCls Icon class for menu button (defaults to icon-magnifier) */ ,iconCls:'icon-magnifier' /** * @cfg {String/Array} checkIndexes Which indexes to check by default. Can be either 'all' for all indexes * or array of dataIndex names, e.g. ['persFirstName', 'persLastName'] */ ,checkIndexes: 'all' /** * @cfg {Array} disableIndexes Array of index names to disable (not show in the menu), e.g. ['persTitle', 'persTitle2'] */ ,disableIndexes:[] /** * @cfg {String} dateFormat how to format date values. If undefined (the default) * date is formatted as configured in colummn model */ ,dateFormat:undefined /** * @cfg {Boolean} showSelectAll Select All item is shown in menu if true (defaults to true) */ ,showSelectAll:true /** * @cfg {String} menuStyle Valid values are 'checkbox' and 'radio'. If menuStyle is radio * then only one field can be searched at a time and selectAll is automatically switched off. */ ,menuStyle:'checkbox' /** * @cfg {Number} minChars minimum characters to type before the request is made. If undefined (the default) * the trigger field shows magnifier icon and you need to click it or press enter for search to start. If it * is defined and greater than 0 then maginfier is not shown and search starts after minChars are typed. */ /** * @cfg {String} minCharsTipText Tooltip to display if minChars is > 0 */ ,minCharsTipText:'Enter at least {0} character(s)' /** * @cfg {String} mode Use 'remote' for remote stores or 'local' for local stores. If mode is local * no data requests are sent to server the grid's store is filtered instead (defaults to 'remote') */ ,mode:'remote' ,no_button:'false' /** * @cfg {Array} readonlyIndexes Array of index names to disable (show in menu disabled), e.g. ['persTitle', 'persTitle2'] */ /** * @cfg {Number} width Width of input field in pixels (defaults to 100) */ ,width:100 /** * @cfg {String} xtype xtype is usually not used to instantiate this plugin but you have a chance to identify it */ ,xtype:'gridsearch' /** * @cfg {Object} paramNames Params name map (defaults to {fields:'fields', query:'query'} */ ,paramNames: { fields:'fields' ,query:'query' ,from:'quick_search' } /** * @cfg {int} position * The starting position inside the toolbar */ ,tbPosition: 15 /** * @cfg {String} shortcutKey Key to fucus the input field (defaults to r = Sea_r_ch). Empty string disables shortcut */ ,shortcutKey:'r' /** * @cfg {String} shortcutModifier Modifier for shortcutKey. Valid values: alt, ctrl, shift (defaults to alt) */ ,shortcutModifier:'alt' /** * @cfg {String} align 'left' or 'right' (defaults to 'left') */ /** * @cfg {Number} minLength force user to type this many character before he can make a search */ /** * @cfg {Ext.Panel/String} toolbarContainer Panel (or id of the panel) which contains toolbar we want to render * search controls to (defaults to this.grid, the grid this plugin is plugged-in into) */ // {{{ /** * private * @param {Ext.grid.GridPanel/Ext.grid.EditorGrid} grid reference to grid this plugin is used for */ ,init:function(grid) { this.grid = grid; // setup toolbar container if id was given if('string' === typeof this.toolbarContainer) { this.toolbarContainer = Ext.getCmp(this.toolbarContainer); } // do our processing after grid render and reconfigure grid.onRender = grid.onRender.createSequence(this.onRender, this); grid.reconfigure = grid.reconfigure.createSequence(this.reconfigure, this); } // eo function init // }}} // {{{ /** * private add plugin controls to existing toolbar and calls reconfigure */ ,onRender:function() { var panel = this.toolbarContainer || this.grid; var tb = 'bottom' === this.position ? panel.bottomToolbar : panel.topToolbar; // add menu this.menu = new Ext.menu.Menu(); // handle position if('right' === this.align) { tb.insert(this.tbPosition, '->'); } else { tb.insert(this.tbPosition, '-'); } this.tbPosition++; if(this.no_button=="false") { // add menu button tb.insert(this.tbPosition, { text:this.searchText ,menu:this.menu ,iconCls:this.iconCls }); this.tbPosition++; /*tb.add({ text:this.searchText ,menu:this.menu ,iconCls:this.iconCls });*/ } else { // add menu button tb.insert(this.tbPosition, this.searchText ); this.tbPosition++; } // add input field (TwinTriggerField in fact) this.field = new Ext.form.TwinTriggerField({ id: this.myid ,width:this.width ,selectOnFocus:undefined === this.selectOnFocus ? true : this.selectOnFocus ,trigger1Class:'x-form-clear-trigger' ,trigger2Class:this.minChars ? 'x-hidden' : 'x-form-search-trigger' ,onTrigger1Click:this.minChars ? this.onTriggerClear.createDelegate(this) : this.onTriggerClear.createDelegate(this) ,onTrigger2Click:this.onTriggerSearch.createDelegate(this) ,minLength:this.minLength }); // install event handlers on input field this.field.on('render', function() { this.field.el.dom.qtip = this.minChars ? String.format(this.minCharsTipText, this.minChars) : this.searchTipText; if(this.minChars) { this.field.el.on({scope:this, buffer:600, keyup:this.onKeyUp}); } // install key map var map = new Ext.KeyMap(this.field.el, [{ key:Ext.EventObject.ENTER ,scope:this ,fn:this.onTriggerSearch },{ key:Ext.EventObject.ESC ,scope:this ,fn:this.onTriggerClear }]); map.stopEvent = true; }, this, {single:true}); tb.insert(this.tbPosition, this.field); // reconfigure this.reconfigure(); // keyMap if(this.shortcutKey && this.shortcutModifier) { var shortcutEl = this.grid.getEl(); var shortcutCfg = [{ key:this.shortcutKey ,scope:this ,stopEvent:true ,fn:function() { this.field.focus(); } }]; shortcutCfg[0][this.shortcutModifier] = true; this.keymap = new Ext.KeyMap(shortcutEl, shortcutCfg); } if(true === this.autoFocus) { this.grid.store.on({scope:this, load:function(){this.field.focus();}}); } } // eo function onRender // }}} // {{{ /** * field el keypup event handler. Triggers the search * @private */ ,onKeyUp:function() { var length = this.field.getValue().toString().length; if(0 === length || this.minChars <= length) { this.onTriggerSearch(); } } // eo function onKeyUp // }}} // {{{ /** * private Clear Trigger click handler */ ,onTriggerClear:function() { if(this.field.getValue()) { this.field.setValue(''); this.field.focus(); this.onTriggerSearch(); } } // eo function onTriggerClear // }}} // {{{ /** * private Search Trigger click handler (executes the search, local or remote) */ ,onTriggerSearch:function() { if(!this.field.isValid()) { return; } var val = this.field.getValue(); var store = this.grid.store; // grid's store filter if('local' === this.mode) { store.clearFilter(); if(val) { store.filterBy(function(r) { var retval = false; this.menu.items.each(function(item) { if(!item.checked || retval) { return; } var rv = r.get(item.dataIndex); rv = rv instanceof Date ? rv.format(this.dateFormat || r.fields.get(item.dataIndex).dateFormat) : rv; var re = new RegExp(val, 'gi'); retval = re.test(rv); }, this); if(retval) { return true; } return retval; }, this); } else { } } // ask server to filter records else { // clear start (necessary if we have paging) if(store.lastOptions && store.lastOptions.params) { store.lastOptions.params[store.paramNames.start] = 0; } if(Ext.getCmp("mp3_list").getStore() == store) { if(this.paramNames.from=="fulltext") filter_setting("fulltext_search",0); else filter_setting("quick_search",0); } // get fields to search array var fields = []; this.menu.items.each(function(item) { if(item.checked) { fields.push(item.dataIndex); } }); // add fields and query to baseParams of store delete(store.baseParams[this.paramNames.fields]); delete(store.baseParams[this.paramNames.query]); if (store.lastOptions && store.lastOptions.params) { delete(store.lastOptions.params[this.paramNames.fields]); delete(store.lastOptions.params[this.paramNames.query]); } if(fields.length) { store.baseParams[this.paramNames.fields] = Ext.encode(fields); store.baseParams[this.paramNames.query] = val; } // reload store store.reload(); } } // eo function onTriggerSearch // }}} // {{{ /** * @param {Boolean} true to disable search (TwinTriggerField), false to enable */ ,setDisabled:function() { this.field.setDisabled.apply(this.field, arguments); } // eo function setDisabled // }}} // {{{ /** * Enable search (TwinTriggerField) */ ,enable:function() { this.setDisabled(false); } // eo function enable // }}} // {{{ /** * Enable search (TwinTriggerField) */ ,disable:function() { this.setDisabled(true); } // eo function disable // }}} // {{{ /** * private (re)configures the plugin, creates menu items from column model */ ,reconfigure:function() { // {{{ // remove old items var menu = this.menu; menu.removeAll(); // add Select All item plus separator if(this.showSelectAll && 'radio' !== this.menuStyle) { menu.add(new Ext.menu.CheckItem({ text:this.selectAllText ,checked:!(this.checkIndexes instanceof Array) ,hideOnClick:false ,handler:function(item) { var checked = ! item.checked; item.parentMenu.items.each(function(i) { if(item !== i && i.setChecked && !i.disabled) { i.setChecked(checked); } }); } }),'-'); } // }}} // {{{ // add new items var cm = this.grid.colModel; var group = undefined; if('radio' === this.menuStyle) { group = 'g' + (new Date).getTime(); } Ext.each(cm.config, function(config) { var disable = false; if(config.header && config.dataIndex) { Ext.each(this.disableIndexes, function(item) { disable = disable ? disable : item === config.dataIndex; }); if(!disable) { menu.add(new Ext.menu.CheckItem({ text:config.header ,listeners: { checkchange: function() { var checked = new Array; menu.items.each(function(item) { if(item.checked) { checked[checked.length] = item.dataIndex; } }, this); cp.set("query_fields",checked); } } ,hideOnClick:false ,group:group ,checked:'all' === this.checkIndexes ,dataIndex:config.dataIndex })); } } }, this); // }}} // {{{ // check items if(this.checkIndexes instanceof Array) { Ext.each(this.checkIndexes, function(di) { var item = menu.items.find(function(itm) { return itm.dataIndex === di; }); if(item) { item.setChecked(true, true); } }, this); } // }}} // {{{ // disable items if(this.readonlyIndexes instanceof Array) { Ext.each(this.readonlyIndexes, function(di) { var item = menu.items.find(function(itm) { return itm.dataIndex === di; }); if(item) { item.disable(); } }, this); } // }}} } // eo function reconfigure // }}} }); // eo extend /* * Ext JS Library 2.0 * Copyright(c) 2006-2007, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license * * MODIFIED: SGB [12.12.07] * Added support for a new config option, remoteDataMethod, * including getter and setter functions, and minor mods * to the beforeExpand and expandRow functions */ Ext.grid.RowExpander = function(config){ Ext.apply(this, config); Ext.grid.RowExpander.superclass.constructor.call(this); if(this.tpl){ if(typeof this.tpl == 'string'){ this.tpl = new Ext.Template(this.tpl); } this.tpl.compile(); } this.state = {}; this.bodyContent = {}; this.addEvents({ beforeexpand : true, expand: true, beforecollapse: true, collapse: true }); }; Ext.extend(Ext.grid.RowExpander, Ext.util.Observable, { header: "", width: 20, sortable: false, fixed:true, dataIndex: '', id: 'expander', lazyRender : true, enableCaching: true, getRowClass : function(record, rowIndex, p, ds){ p.cols = p.cols-1; var content = this.bodyContent[record.id]; if(!content && !this.lazyRender){ content = this.getBodyContent(record, rowIndex); } if(content){ p.body = content; } return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed'; }, init : function(grid){ this.grid = grid; var view = grid.getView(); view.getRowClass = this.getRowClass.createDelegate(this); view.enableRowBody = true; grid.on('render', function(){ view.mainBody.on('mousedown', this.onMouseDown, this); }, this); }, getBodyContent : function(record, index){ if(!this.enableCaching){ return this.tpl.apply(record.data); } var content = this.bodyContent[record.id]; if(!content){ content = this.tpl.apply(record.data); this.bodyContent[record.id] = content; } return content; }, // Setter and Getter methods for the remoteDataMethod property setRemoteDataMethod : function (fn){ this.remoteDataMethod = fn; }, getRemoteDataMethod : function (record, index){ if(!this.remoteDataMethod){ return; } return this.remoteDataMethod.call(this,record,index); }, onMouseDown : function(e, t){ if(t.className == 'x-grid3-row-expander'){ e.stopEvent(); var row = e.getTarget('.x-grid3-row'); this.toggleRow(row); } }, renderer : function(v, p, record){ p.cellAttr = 'rowspan="2"'; return '
 
'; }, beforeExpand : function(record, body, rowIndex){ if(this.fireEvent('beforexpand', this, record, body, rowIndex) !== false){ // If remoteDataMethod is defined then we'll need a div, with a unique ID, // to place the content if(this.remoteDataMethod){ this.tpl = new Ext.Template("
<\div>"); } if(this.tpl && this.lazyRender){ body.innerHTML = this.getBodyContent(record, rowIndex); } return true; }else{ return false; } }, toggleRow : function(row){ if(typeof row == 'number'){ row = this.grid.view.getRow(row); } this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row); }, expandRow : function(row){ if(typeof row == 'number'){ row = this.grid.view.getRow(row); } var record = this.grid.store.getAt(row.rowIndex); var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row); if(this.beforeExpand(record, body, row.rowIndex)){ this.state[record.id] = true; Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded'); if(this.fireEvent('expand', this, record, body, row.rowIndex) !== false){ // If the expand event is successful then get the remoteDataMethod this.getRemoteDataMethod(record,row.rowIndex); } } }, collapseRow : function(row){ if(typeof row == 'number'){ row = this.grid.view.getRow(row); } var record = this.grid.store.getAt(row.rowIndex); var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true); if(this.fireEvent('beforcollapse', this, record, body, row.rowIndex) !== false){ this.state[record.id] = false; Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed'); this.fireEvent('collapse', this, record, body, row.rowIndex); } } }); Ext.namespace('Ext.ux.dd'); Ext.ux.dd.GridReorderDropTarget = function(grid, config) { this.target = new Ext.dd.DropTarget(grid.getEl(), { ddGroup: grid.ddGroup || 'GridDD' || 'PlaylistDDGroup' ,grid: grid ,gridDropTarget: this ,notifyDrop: function(dd, e, data){ var pl = Ext.getCmp("playlist_selectionID"); if(pl.selectedIndex>=0) { var userID = pl.store.data.items[pl.selectedIndex].data.userID; if(!(1 || pl.getValue()==0 || (userID == 2 && 1))) return false; } if(dd.grid.id=="mp3_list") { // Generic function to add records. function addRow(record, index, allItems) { var store = grid.getStore(); // Search for duplicates var foundItem = store.find('title', record.data.title); // if not found if (foundItem == -1) { store.add(record); // Call a sort dynamically //store.sort('title', 'ASC'); } } // Loop through the selections Ext.each(dd.dragData.selections ,addRow); if(Ext.getCmp("playlistID").getStore().getCount() && Ext.getCmp("playlist_selectionID").getValue()>=0) { if(Ext.getCmp("playlist_clear_buttonID")) Ext.getCmp("playlist_clear_buttonID").enable(); } else { if(Ext.getCmp("playlist_clear_buttonID")) Ext.getCmp("playlist_clear_buttonID").disable(); } player_obj.save_playlist(); return(true); } else { // Remove drag lines. The If condition prevents null error when // drop occurs without dragging out of the selection area. if (this.currentRowEl) { this.currentRowEl.removeClass("grid-row-insert-below"); this.currentRowEl.removeClass("grid-row-insert-above"); } // determine the row var t = Ext.lib.Event.getTarget(e); var rindex = this.grid.getView().findRowIndex(t); if (rindex === false) return false; if (rindex == data.rowIndex) return false; // fire the before move/copy event if (this.gridDropTarget.fireEvent(this.copy?'beforerowcopy':'beforerowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections, 123) === false) return false; // update the store var ds = this.grid.getStore(); // Changes for multiselction by Spirit var selections = new Array(); var keys = ds.data.keys; for (key in keys) { for(i = 0; i < data.selections.length; i++) { if (keys[key]==data.selections[i].id) { // Exit to prevent drop of selected records on itself. if (rindex == key) return false; selections.push(data.selections[i]); } } } if (!this.copy) { for(i = 0; i < data.selections.length; i++) { ds.remove(ds.getById(data.selections[i].id)); } } if (rindex > data.rowIndex && data.selections.length > 1) { rindex = rindex - (data.selections.length - 1); } for(i = selections.length-1; i>=0; i--) { var insertIndex = rindex; // Logic (convoluted) depending on if rows were moved up or down. if (rindex > data.rowIndex && this.rowPosition < 0) insertIndex--; if (rindex < data.rowIndex && this.rowPosition > 0) insertIndex++; ds.insert(insertIndex, selections[i]); } // re-select the row(s) sm = this.grid.getSelectionModel(); if (sm) sm.selectRecords(data.selections); // fire the after move/copy event this.gridDropTarget.fireEvent(this.copy?'afterrowcopy':'afterrowmove', this.gridDropTarget, data.rowIndex, rindex, data.selections); return true; } } ,notifyOver: function(dd, e, data) { var pl = Ext.getCmp("playlist_selectionID"); if(pl.selectedIndex>=0) { var userID = pl.store.data.items[pl.selectedIndex].data.userID; if(!(1 || pl.getValue()==0 || (userID == 2 && 1))) return false; } if(Ext.getCmp("playlist_selectionID").getValue()<0) return false; if(dd.grid.id=="playlistID") { var t = Ext.lib.Event.getTarget(e); var rindex = this.grid.getView().findRowIndex(t); // Similar to the code in notifyDrop. Filters for selected rows and // quits function if any one row matches the current selected row. var ds = this.grid.getStore(); var keys = ds.data.keys; for (key in keys) { for(i = 0; i < data.selections.length; i++) { if (keys[key]==data.selections[i].id) { if (rindex == key) { if (this.currentRowEl) { this.currentRowEl.removeClass("grid-row-insert-below"); this.currentRowEl.removeClass("grid-row-insert-above"); } return this.dropNotAllowed; } } } } // If on first row, remove upper line. Prevents negative // index error as a result of rindex going negative. if (rindex < 0 || rindex === false) { this.currentRowEl.removeClass("grid-row-insert-above"); return this.dropNotAllowed; } try { var currentRow = this.grid.getView().getRow(rindex); // Find position of row relative to page (adjusting for grid's scroll position) var resolvedRow = new Ext.Element(currentRow).getY() - this.grid.getView().scroller.dom.scrollTop; var rowHeight = currentRow.offsetHeight; // Cursor relative to a row. -ve value implies cursor is above the // row's middle and +ve value implues cursor is below the row's middle. this.rowPosition = e.getPageY() - resolvedRow - (rowHeight/2); // Clear drag line. if (this.currentRowEl) { this.currentRowEl.removeClass("grid-row-insert-below"); this.currentRowEl.removeClass("grid-row-insert-above"); } if (this.rowPosition > 0) { // If the pointer is on the bottom half of the row. this.currentRowEl = new Ext.Element(currentRow); this.currentRowEl.addClass("grid-row-insert-below"); } else { // If the pointer is on the top half of the row. if (rindex-1 >= 0) { var previousRow = this.grid.getView().getRow(rindex-1); this.currentRowEl = new Ext.Element(previousRow); this.currentRowEl.addClass("grid-row-insert-below"); } else { // If the pointer is on the top half of the first row. this.currentRowEl.addClass("grid-row-insert-above"); } } } catch (err) { console.warn(err); rindex = false; } return (rindex === false)? this.dropNotAllowed : this.dropAllowed; } else { return this.dropAllowed; } } ,notifyOut: function(dd, e, data) { // Remove drag lines when pointer leaves the gridView. if (this.currentRowEl) { this.currentRowEl.removeClass("grid-row-insert-above"); this.currentRowEl.removeClass("grid-row-insert-below"); } } }); if (config) { Ext.apply(this.target, config); if (config.listeners) Ext.apply(this,{listeners: config.listeners}); } this.addEvents({ "beforerowmove": true ,"afterrowmove": true ,"beforerowcopy": true ,"afterrowcopy": true }); Ext.ux.dd.GridReorderDropTarget.superclass.constructor.call(this); }; Ext.extend(Ext.ux.dd.GridReorderDropTarget, Ext.util.Observable, { getTarget: function() { return this.target; } ,getGrid: function() { return this.target.grid; } ,getCopy: function() { return this.target.copy?true:false; } ,setCopy: function(b) { this.target.copy = b?true:false; } }); // vim: ts=4:sw=4:nu:fdc=4:nospell /*global Ext */ /** * @class Ext.ux.grid.RowActions * @extends Ext.util.Observable * * RowActions plugin for Ext grid. Contains renderer for icons and fires events when an icon is clicked. * CSS rules from Ext.ux.RowActions.css are mandatory * * Important general information: Actions are identified by iconCls. Wherever an action * is referenced (event argument, callback argument), the iconCls of clicked icon is used. * In other words, action identifier === iconCls. * * @author Ing. Jozef Sakáloš * @copyright (c) 2008, by Ing. Jozef Sakáloš * @date 22. March 2008 * @version 1.0 * @revision $Id: Ext.ux.grid.RowActions.js 713 2009-05-18 23:40:07Z jozo $ * * @license Ext.ux.grid.RowActions is licensed under the terms of * the Open Source LGPL 3.0 license. Commercial use is permitted to the extent * that the code/component(s) do NOT become part of another Open Source or Commercially * licensed development library or toolkit without explicit permission. * *

License details: http://www.gnu.org/licenses/lgpl.html

* * @forum 29961 * @demo http://rowactions.extjs.eu * @download * * * @donate *
* * * * *
*/ Ext.ns('Ext.ux.grid'); // add RegExp.escape if it has not been already added if('function' !== typeof RegExp.escape) { RegExp.escape = function(s) { if('string' !== typeof s) { return s; } // Note: if pasting from forum, precede ]/\ with backslash manually return s.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1'); }; // eo function escape } /** * Creates new RowActions plugin * @constructor * @param {Object} config A config object */ Ext.ux.grid.RowActions = function(config) { Ext.apply(this, config); // {{{ this.addEvents( /** * @event beforeaction * Fires before action event. Return false to cancel the subsequent action event. * @param {Ext.grid.GridPanel} grid * @param {Ext.data.Record} record Record corresponding to row clicked * @param {String} action Identifies the action icon clicked. Equals to icon css class name. * @param {Integer} rowIndex Index of clicked grid row * @param {Integer} colIndex Index of clicked grid column that contains all action icons */ 'beforeaction' /** * @event action * Fires when icon is clicked * @param {Ext.grid.GridPanel} grid * @param {Ext.data.Record} record Record corresponding to row clicked * @param {String} action Identifies the action icon clicked. Equals to icon css class name. * @param {Integer} rowIndex Index of clicked grid row * @param {Integer} colIndex Index of clicked grid column that contains all action icons */ ,'action' /** * @event beforegroupaction * Fires before group action event. Return false to cancel the subsequent groupaction event. * @param {Ext.grid.GridPanel} grid * @param {Array} records Array of records in this group * @param {String} action Identifies the action icon clicked. Equals to icon css class name. * @param {String} groupId Identifies the group clicked */ ,'beforegroupaction' /** * @event groupaction * Fires when icon in a group header is clicked * @param {Ext.grid.GridPanel} grid * @param {Array} records Array of records in this group * @param {String} action Identifies the action icon clicked. Equals to icon css class name. * @param {String} groupId Identifies the group clicked */ ,'groupaction' ); // }}} // call parent Ext.ux.grid.RowActions.superclass.constructor.call(this); }; Ext.extend(Ext.ux.grid.RowActions, Ext.util.Observable, { // configuration options // {{{ /** * @cfg {Array} actions Mandatory. Array of action configuration objects. The action * configuration object recognizes the following options: * */ /** * @cfg {String} actionEvent Event to trigger actions, e.g. click, dblclick, mouseover (defaults to 'click') */ actionEvent:'click' /** * @cfg {Boolean} autoWidth true to calculate field width for iconic actions only (defaults to true). * If true, the width is calculated as {@link #widthSlope} * number of actions + {@link #widthIntercept}. */ ,autoWidth:true /** * @cfg {String} dataIndex - Do not touch! * @private */ ,dataIndex:'' /** * @cfg {Array} groupActions Array of action to use for group headers of grouping grids. * These actions support static icons, texts and tooltips same way as {@link #actions}. There is one * more action config option recognized: * */ /** * @cfg {Object} callbacks iconCls keyed object that contains callback functions. For example: *
	 * callbacks:{
	 *      'icon-open':function(...) {...}
	 *     ,'icon-save':function(...) {...}
	 * }
	 * 
*/ /** * @cfg {String} header Actions column header */ ,header:'' /** * @cfg {Boolean} isColumn * Tell ColumnModel that we are column. Do not touch! * @private */ ,isColumn:true /** * @cfg {Boolean} keepSelection * Set it to true if you do not want action clicks to affect selected row(s) (defaults to false). * By default, when user clicks an action icon the clicked row is selected and the action events are fired. * If this option is true then the current selection is not affected, only the action events are fired. */ ,keepSelection:false /** * @cfg {Boolean} menuDisabled No sense to display header menu for this column * @private */ ,menuDisabled:true /** * @cfg {Boolean} sortable Usually it has no sense to sort by this column * @private */ ,sortable:false /** * @cfg {String} tplGroup Template for group actions * @private */ ,tplGroup: '' +'
ux-action-right ' +'{cls}" style="{style}" qtip="{qtip}">{text}
' +'
' /** * @cfg {String} tplRow Template for row actions * @private */ ,tplRow: '
' +'' +'
' +'ux-row-action-text" style="{hide}{style}" qtip="{qtip}">' +'{text}
' +'
' +'
' /** * @cfg {String} hideMode How to hide hidden icons. Valid values are: 'visibility' and 'display' * (defaluts to 'visibility'). If the mode is visibility the hidden icon is not visible but there * is still blank space occupied by the icon. In display mode, the visible icons are shifted taking * the space of the hidden icon. */ ,hideMode:'visiblity' /** * @cfg {Number} widthIntercept Constant used for auto-width calculation (defaults to 4). * See {@link #autoWidth} for explanation. */ ,widthIntercept:4 /** * @cfg {Number} widthSlope Constant used for auto-width calculation (defaults to 21). * See {@link #autoWidth} for explanation. */ ,widthSlope:21 // }}} // methods // {{{ /** * Init function * @param {Ext.grid.GridPanel} grid Grid this plugin is in */ ,init:function(grid) { this.grid = grid; // the actions column must have an id for Ext 3.x this.id = this.id || Ext.id(); // for Ext 3.x compatibility var lookup = grid.getColumnModel().lookup; delete(lookup[undefined]); lookup[this.id] = this; // {{{ // setup template if(!this.tpl) { this.tpl = this.processActions(this.actions); } // eo template setup // }}} // calculate width if(this.autoWidth) { this.width = this.widthSlope * this.actions.length + this.widthIntercept; this.fixed = true; } // body click handler var view = grid.getView(); var cfg = {scope:this}; cfg[this.actionEvent] = this.onClick; grid.afterRender = grid.afterRender.createSequence(function() { view.mainBody.on(cfg); grid.on('destroy', this.purgeListeners, this); }, this); // setup renderer if(!this.renderer) { this.renderer = function(value, cell, record, row, col, store) { cell.css += (cell.css ? ' ' : '') + 'ux-row-action-cell'; return this.tpl.apply(this.getData(value, cell, record, row, col, store)); }.createDelegate(this); } // actions in grouping grids support if(view.groupTextTpl && this.groupActions) { view.interceptMouse = view.interceptMouse.createInterceptor(function(e) { if(e.getTarget('.ux-grow-action-item')) { return false; } }); view.groupTextTpl = '
' + view.groupTextTpl +'
' +this.processActions(this.groupActions, this.tplGroup).apply() ; } // cancel click if(true === this.keepSelection) { grid.processEvent = grid.processEvent.createInterceptor(function(name, e) { if('mousedown' === name) { return !this.getAction(e); } }, this); } } // eo function init // }}} // {{{ /** * Returns data to apply to template. Override this if needed. * @param {Mixed} value * @param {Object} cell object to set some attributes of the grid cell * @param {Ext.data.Record} record from which the data is extracted * @param {Number} row row index * @param {Number} col col index * @param {Ext.data.Store} store object from which the record is extracted * @return {Object} data to apply to template */ ,getData:function(value, cell, record, row, col, store) { return record.data || {}; } // eo function getData // }}} // {{{ /** * Processes actions configs and returns template. * @param {Array} actions * @param {String} template Optional. Template to use for one action item. * @return {String} * @private */ ,processActions:function(actions, template) { var acts = []; // actions loop Ext.each(actions, function(a, i) { // save callback if(a.iconCls && 'function' === typeof (a.callback || a.cb)) { this.callbacks = this.callbacks || {}; this.callbacks[a.iconCls] = a.callback || a.cb; } // data for intermediate template var o = { cls:a.iconIndex ? '{' + a.iconIndex + '}' : (a.iconCls ? a.iconCls : '') ,qtip:a.qtipIndex ? '{' + a.qtipIndex + '}' : (a.tooltip || a.qtip ? a.tooltip || a.qtip : '') ,text:a.textIndex ? '{' + a.textIndex + '}' : (a.text ? a.text : '') ,hide:a.hideIndex ? '' + ('display' === this.hideMode ? 'display:none' :'visibility:hidden') + ';' : (a.hide ? ('display' === this.hideMode ? 'display:none' :'visibility:hidden;') : '') ,align:a.align || 'right' ,style:a.style ? a.style : '' }; acts.push(o); }, this); // eo actions loop var xt = new Ext.XTemplate(template || this.tplRow); return new Ext.XTemplate(xt.apply({actions:acts})); } // eo function processActions // }}} ,getAction:function(e) { var action = false; var t = e.getTarget('.ux-row-action-item'); if(t) { action = t.className.replace(/ux-row-action-item /, ''); if(action) { action = action.replace(/ ux-row-action-text/, ''); action = action.trim(); } } return action; } // eo function getAction // {{{ /** * Grid body actionEvent event handler * @private */ ,onClick:function(e, target) { var view = this.grid.getView(); // handle row action click var row = e.getTarget('.x-grid3-row'); var col = view.findCellIndex(target.parentNode.parentNode); var action = this.getAction(e); // var t = e.getTarget('.ux-row-action-item'); // if(t) { // action = this.getAction(t); // action = t.className.replace(/ux-row-action-item /, ''); // if(action) { // action = action.replace(/ ux-row-action-text/, ''); // action = action.trim(); // } // } if(false !== row && false !== col && false !== action) { var record = this.grid.store.getAt(row.rowIndex); // call callback if any if(this.callbacks && 'function' === typeof this.callbacks[action]) { this.callbacks[action](this.grid, record, action, row.rowIndex, col); } // fire events if(true !== this.eventsSuspended && false === this.fireEvent('beforeaction', this.grid, record, action, row.rowIndex, col)) { return; } else if(true !== this.eventsSuspended) { this.fireEvent('action', this.grid, record, action, row.rowIndex, col); } } // handle group action click t = e.getTarget('.ux-grow-action-item'); if(t) { // get groupId var group = view.findGroup(target); var groupId = group ? group.id.replace(/ext-gen[0-9]+-gp-/, '') : null; // get matching records var records; if(groupId) { var re = new RegExp(RegExp.escape(groupId)); records = this.grid.store.queryBy(function(r) { return r._groupId.match(re); }); records = records ? records.items : []; } action = t.className.replace(/ux-grow-action-item (ux-action-right )*/, ''); // call callback if any if('function' === typeof this.callbacks[action]) { this.callbacks[action](this.grid, records, action, groupId); } // fire events if(true !== this.eventsSuspended && false === this.fireEvent('beforegroupaction', this.grid, records, action, groupId)) { return false; } this.fireEvent('groupaction', this.grid, records, action, groupId); } } // eo function onClick // }}} }); // registre xtype Ext.reg('rowactions', Ext.ux.grid.RowActions); // eof Ext.ns('Ext.ux.grid'); Ext.ux.grid.ExplorerView = Ext.extend(Ext.grid.GridView, { /** * @cfg {Ext.Template} Template to use when rendering rows, null if default grid row rendering */ rowTemplate: null, /** * Changes the current row-template and refreshes the view. * @param {Ext.Template} template Use this template, set to null if you want grid default row rendering. */ changeTemplate: function(template) { this.rowTemplate = template; this.initTemplates(); this.refresh(); }, initTemplates: function() { Ext.ux.grid.ExplorerView.superclass.initTemplates.call(this); // Store original row template if (!this.templates.orgrow) this.templates.orgrow = this.templates.row; if (this.rowTemplate != null) this.templates.row = this.rowTemplate.compile(); else this.templates.row = this.templates.orgrow; }, doRender: function(cs, rs, ds, startRow, colCount, stripe){ if (this.rowTemplate == null) { // Let GridView class handle "normal" rows return Ext.ux.grid.ExplorerView.superclass.doRender.apply( this, arguments); } else { var ts = this.templates, rt = ts.row; var buf = []; for(var j = 0, len = rs.length; j < len; j++){ var rowIndex = (j+startRow); rs[j].data.rowIndex = rowIndex; buf[buf.length] = rt.apply(rs[j].data); } buf[buf.length] = "
"; return buf.join(""); } }, updateAllColumnWidths : function(){ var tw = this.getTotalWidth(); var clen = this.cm.getColumnCount(); var ws = []; for(var i = 0; i < clen; i++){ ws[i] = this.getColumnWidth(i); } this.innerHd.firstChild.firstChild.style.width = tw; for(var i = 0; i < clen; i++){ var hd = this.getHeaderCell(i); hd.style.width = ws[i]; } // If we have specified our own template there wont be columns to resize here. if (this.rowTemplate == null) { var ns = this.getRows(); for(var i = 0, len = ns.length; i < len; i++){ ns[i].style.width = tw; ns[i].firstChild.style.width = tw; var row = ns[i].firstChild.rows[0]; for(var j = 0; j < clen; j++){ row.childNodes[j].style.width = ws[j]; } } } this.onAllColumnWidthsUpdated(ws, tw); }, updateColumnWidth : function(col, width){ var w = this.getColumnWidth(col); var tw = this.getTotalWidth(); this.innerHd.firstChild.firstChild.style.width = tw; var hd = this.getHeaderCell(col); hd.style.width = w; if (this.rowTemplate == null) { var ns = this.getRows(); for(var i = 0, len = ns.length; i < len; i++){ ns[i].style.width = tw; ns[i].firstChild.style.width = tw; ns[i].firstChild.rows[0].childNodes[col].style.width = w; } } this.onColumnWidthUpdated(col, w, tw); }, updateColumnHidden : function(col, hidden){ var tw = this.getTotalWidth(); this.innerHd.firstChild.firstChild.style.width = tw; var display = hidden ? 'none' : ''; var hd = this.getHeaderCell(col); hd.style.display = display; if (this.rowTemplate == null) { var ns = this.getRows(); for(var i = 0, len = ns.length; i < len; i++){ ns[i].style.width = tw; ns[i].firstChild.style.width = tw; ns[i].firstChild.rows[0].childNodes[col].style.display = display; } this.onColumnHiddenUpdated(col, hidden, tw); } delete this.lastViewWidth; this.layout(); } }); // Reg for lazy loading (xtype) Ext.reg('explorerview', Ext.ux.grid.ExplorerView); // Make sure ExplorerView is used in GridPanel Ext.override(Ext.grid.GridPanel, { getView : function(){ if(!this.view){ this.view = new Ext.ux.grid.ExplorerView(this.viewConfig); } return this.view; } }); /** * Ext.ux.grid.GridViewMenuPlugin * Copyright (c) 2008, http://www.siteartwork.de * * Ext.ux.grid.GridViewMenuPlugin is licensed under the terms of the * GNU Open Source LGPL 3.0 * license. * * This program is free software: you can redistribute it and/or modify it under * the terms of the LGPL as published by the Free Software * Foundation, either version 3 of the License, or any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the LGPL License for more * details. * * You should have received a copy of the GNU LGPL along with * this program. If not, see . * */ Ext.namespace('Ext.ux.grid'); /** * Renders a menu button to the upper right corner of the grid this plugin is * bound to. The menu items will represent the column model and hide/show * the columns on click. * * Note that you have to set the enableHdMenu-property of the bound grid to * "false" so this plugin does not interfere with the header menus of the grid's view. * * @class Ext.ux.grid.GridViewMenuPlugin * @extends Object * @constructor * * @author Thorsten Suckow-Homberg */ Ext.ux.grid.GridViewMenuPlugin = Ext.extend(Object, { /** * The {Ext.grid.GridView} this plugin is bound to. * @type {Ext.grid.GridView} * @protected */ _view : null, /** * The menu button that gets rendered to the upper right corner of the * grid's view. * @type {Ext.Element} * @protected */ _menuBtn : null, /** * The menu that will be shown when the menu button gets clicked. * Named after the "colModel" property of the grid's view so this plugin * can easily operate in the scope of the view. * @type {Ext.Menu} */ colMenu : null, /** * The column model of the grid this plugin is bound to. * Named after the "cm" property of the grid's view so this plugin * can easily operate in the scope of the view. * @type {Ext.grid.ColumnModel} */ cm : null, /** * Inits this plugin. * Method is API-only. Will be called automatically from the grid this * plugin is bound to. * * @param {Ext.grid.GridPanel} grid * * @throws {Exception} throws an exception if the plugin recognizes the * grid's "enableHdMenu" property to be set to "true" */ init : function(grid) { if (grid.enableHdMenu === true) { throw("Ext.ux.grid.GridViewMenuPlugin - grid\"s \"enableHdMenu\" property has to be set to \"false\""); } this._view = grid.view; this._view.initElements = this._view.initElements.createSequence( this.initElements, this ); this._view.initData = this._view.initData.createSequence( this.initData, this ); this._view.destroy = this._view.destroy.createInterceptor( this._destroy, this ); this.colMenu = new Ext.menu.Menu(); this.colMenu.on("beforeshow", this._beforeColMenuShow, this); this.colMenu.on("itemclick", this._handleHdMenuClick, this); }, // -------- listeners /** * Callback for the itemclick event of the menu. * Default implementation calls the view's handleHdMenuClick-method in the * scope of the view itself. * * @param {Ext.menu.BaseItem baseItem} item * @param {Ext.EventObject} e * * @return {Boolean} returns false if hiding the column represented by the * column is not allowed, otherwise true * * @protected */ _handleHdMenuClick : function(item, e) { return this._view.handleHdMenuClick(item, e); }, /** * Listener for the beforeshow-event of the menu. * Default implementation calls the view's beforeColMenuShow-method * in the scope of this plugin. * * Overwrite this for custom behavior. * * @param {Ext.menu.Menu} menu * * @protected */ _beforeColMenuShow : function(menu) { this._view.beforeColMenuShow.call(this, menu); }, /** * Listener for the click event of the menuBtn element. * Used internally to show the menu. * * @param {Ext.EventObject} e * @param {HtmlElement} t * * @protected */ _handleHdDown : function(e, t) { if(Ext.fly(t).hasClass('x-grid3-hd-btn')){ e.stopEvent(); this.colMenu.show(t, "tr-br?"); } }, // -------- helpers /** * Builds the element that gets added to teh grid's header for showing * the menu. * The default implementation will render the menu button into the upper * right corner of the grid. * Overwrite for custom behavior. * * @return {Ext.Element} * * @protected */ _getMenuButton : function() { var a = document.createElement('a'); a.className = 'ext-ux-grid-gridviewmenuplugin-menuBtn x-grid3-hd-btn'; a.href = '#'; return new Ext.Element(a); }, /** * Sequenced function for storing the view's cm property, * Called in the scope of this plugin. */ initData : function() { this.cm = this._view.cm; }, /** * Sequenced function for adding the menuBtn to the grid's header. * Called in the scope of this plugin. */ initElements : function() { this.menuBtn = this._getMenuButton(); this._view.mainHd.dom.appendChild(this.menuBtn.dom); this.menuBtn.on("click", this._handleHdDown, this); //this.menuBtn.dom.style.height = (this._view.mainHd.dom.offsetHeight-1)+'px'; this.menuBtn.dom.style.height = '22px'; }, /** * Hooks into the view's destroy method and removes the menu and the menu * button. * * @protected */ _destroy : function() { if(this.colMenu){ this.colMenu.removeAll(); Ext.menu.MenuMgr.unregister(this.colMenu); this.colMenu.getEl().remove(); delete this.colMenu; } if(this._menuBtn){ this._menuBtn.remove(); delete this._menuBtn; } } }); Ext.override(Ext.ux.grid.GridViewMenuPlugin, { init : function(grid) { if (grid.enableHdMenu === true) { throw("Ext.ux.grid.GridViewMenuPlugin - grid\"s \"enableHdMenu\" property has to be set to \"false\""); } var v = this._view = grid.getView(); v.afterMethod('initElements', this.initElements, this); v.afterMethod('initData', this.initData, this); v.afterMethod('onLayout', this._onLayout, this); // auto-size plugin on view layout v.beforeMethod('destroy', this._destroy, this); this.colMenu = new Ext.menu.Menu({ listeners: { scope: this, beforeshow: this._beforeColMenuShow, itemclick: this._handleHdMenuClick } }); this.colMenu.override({ show : function(el, pos, parentMenu){ this.parentMenu = parentMenu; if(!this.el){ this.render(); } this.fireEvent("beforeshow", this); // show menu and constrain to viewport if necessary // ( + minor offset adjustments for pixel perfection) this.showAt( this.el.getAlignToXY(el, pos || this.defaultAlign, [Ext.isSafari? 2 : 1, 0]), parentMenu, true // true to constrain ); } }); }, _beforeColMenuShow : function(menu) { this._view.beforeColMenuShow.call(this, menu); // menu title tweak this.colMenu.insert(0, new Ext.menu.Separator()); this.colMenu.insert(0, new Ext.menu.TextItem({ text: String.format( '{1}', Ext.BLANK_IMAGE_URL, this._view.columnsText ), style: 'line-height:16px;padding:3px 21px 3px 27px;' })); }, /** * Hooks into the view's onLayout method and fits the menu to the grid header * @param vw * @param vh */ _onLayout: function(vw, vh) { //this.menuBtn.dom.style.height = (this._view.mainHd.dom.offsetHeight - 1) + 'px'; this.menuBtn.dom.style.height = '22px'; } }); Ext.namespace('Ext.ux.Andrie'); /** * @class Ext.ux.Andrie.pPageSize * @extends Ext.PagingToolbar * A combobox control that glues itself to a PagingToolbar's pageSize configuration property. * @constructor * Create a new PageSize plugin. * @param {Object} config Configuration options * @author Andrei Neculau - andrei.neculau@gmail.com / http://andreineculau.wordpress.com * @version 0.6 */ Ext.ux.Andrie.pPageSize = function(config){ Ext.apply(this, config); }; Ext.extend(Ext.ux.Andrie.pPageSize, Ext.util.Observable, { /** * @cfg {String} beforeText * Text to display before the comboBox */ beforeText: 'Show', /** * @cfg {String} afterText * Text to display after the comboBox */ afterText: 'items', /** * @cfg {Mixed} addBefore * Toolbar item(s) to add before the PageSizer */ addBefore: '-', /** * @cfg {Mixed} addAfter * Toolbar item(s) to be added after the PageSizer */ addAfter: null, /** * @cfg {Bool} dynamic * True for dynamic variations, false for static ones */ dynamic: false, /** * @cfg {Array} variations * Variations used for determining pageSize options */ variations: [5, 10, 20, 50, 100, 200, 500, 1000], /** * @cfg {Object} comboCfg * Combo config object that overrides the defaults */ comboCfg: undefined, init: function(pagingToolbar){ this.pagingToolbar = pagingToolbar; this.pagingToolbar.pageSizeCombo = this; this.pagingToolbar.setPageSize = this.setPageSize.createDelegate(this); this.pagingToolbar.getPageSize = function(){ return this.pageSize; } this.pagingToolbar.on('render', this.onRender, this); }, //private addSize:function(value){ if (value>0){ this.sizes.push([value]); } }, //private updateStore: function(){ if (this.dynamic) { var middleValue = this.pagingToolbar.pageSize, start; middleValue = (middleValue > 0) ? middleValue : 1; this.sizes = []; var v = this.variations; for (var i = 0, len = v.length; i < len; i++) { this.addSize(middleValue - v[v.length - 1 - i]); } this.addToStore(middleValue); for (var i = 0, len = v.length; i < len; i++) { this.addSize(middleValue + v[i]); } }else{ if (!this.staticSizes){ this.sizes = []; var v = this.variations; var middleValue = 0; for (var i = 0, len = v.length; i < len; i++) { this.addSize(middleValue + v[i]); } this.staticSizes = this.sizes.slice(0); }else{ this.sizes = this.staticSizes.slice(0); } } this.combo.store.loadData(this.sizes); this.combo.collapse(); this.combo.setValue(this.pagingToolbar.pageSize); }, setPageSize:function(value, forced){ var pt = this.pagingToolbar; this.combo.collapse(); value = parseInt(value) || parseInt(this.combo.getValue()); value = (value>0)?value:1; if (value == pt.pageSize){ return; }else if (value < pt.pageSize){ pt.pageSize = value; var ap = Math.round(pt.cursor/value)+1; var cursor = (ap-1)*value; var store = pt.store; if (cursor > store.getTotalCount()) { this.pagingToolbar.pageSize = value; this.pagingToolbar.doLoad(cursor-value); }else{ store.suspendEvents(); for (var i = 0, len = cursor - pt.cursor; i < len; i++) { store.remove(store.getAt(0)); } while (store.getCount() > value) { store.remove(store.getAt(store.getCount() - 1)); } store.resumeEvents(); store.fireEvent('datachanged', store); pt.cursor = cursor; var d = pt.getPageData(); console.log(pt); if(pt.afterTextItem) pt.afterTextItem.el.innerHTML = String.format(pt.afterPageText, d.pages); pt.setPagePosition(ap); pt.first.setDisabled(ap == 1); pt.prev.setDisabled(ap == 1); pt.next.setDisabled(ap == d.pages); pt.last.setDisabled(ap == d.pages); pt.updateInfo(); } }else{ this.pagingToolbar.pageSize = value; this.pagingToolbar.doLoad(Math.floor(this.pagingToolbar.cursor/this.pagingToolbar.pageSize) * this.pagingToolbar.pageSize); } this.updateStore(); }, //private onRender: function(){ this.combo = Ext.ComponentMgr.create(Ext.applyIf(this.comboCfg||{}, { store:new Ext.data.SimpleStore({ fields:['pageSize'], data:[] }), displayField:'pageSize', valueField:'pageSize', id:'pageSizeID', mode:'local', triggerAction:'all', width:50, xtype:'combo' })); this.combo.on('select', this.setPageSize, this); this.updateStore(); if (this.addBefore){ this.pagingToolbar.add(this.addBefore); } if (this.beforeText){ this.pagingToolbar.add(this.beforeText); } this.pagingToolbar.add(this.combo); if (this.afterText){ this.pagingToolbar.add(this.afterText); } if (this.addAfter){ this.pagingToolbar.add(this.addAfter); } } }) Ext.ns('Ext.ux.grid'); Ext.ux.grid.RateColumnPlugin = function(config){ Ext.apply(this, config); if(!this.id){ this.id = Ext.id(null, 'rating-'); } } Ext.apply(Ext.ux.grid.RateColumnPlugin.prototype, { tickSize: 20, selectedCls: 'rating-selected', unselectedCls: 'rating-unselected', roundToTick: true, init: function(grid){ this.grid = grid; grid.on('render', function(c){ c.getView().mainBody.on('mousedown', this.onMouseDown, this, {delegate: '.' + this.id, stopEvent: true}); }, this); }, onMouseDown: function(e, t){ var value = (e.getXY()[0] - Ext.fly(t).getX()) / this.tickSize; if (value < this.zeroSensitivity) { value = 0} //<--- added this line if(this.roundToTick){ value = Math.ceil(value); } var view = this.grid.getView(); var rowIndex = view.findRowIndex(t); var colIndex = view.findCellIndex(t); var dataIndex = this.grid.getColumnModel().getDataIndex(colIndex); this.grid.getStore().getAt(rowIndex).set(dataIndex, value); var record = this.grid.getStore().getAt(rowIndex); var ID = record.get('ID'); }, createRenderer: function(count){ return function(value, count){ return '
' + '
 
'; }.createDelegate(this, [count || 5], 1); } });