// 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:
*
* -
* {Function} callback (optional). Function to call if the action icon is clicked.
* This function is called with same signature as action event and in its original scope.
* If you need to call it in different scope or with another signature use
* createCallback or createDelegate functions. Works for statically defined actions. Use
* callbacks configuration options for store bound actions.
*
* -
* {Function} cb Shortcut for callback.
*
* -
* {String} iconIndex Optional, however either iconIndex or iconCls must be
* configured. Field name of the field of the grid store record that contains
* css class of the icon to show. If configured, shown icons can vary depending
* of the value of this field.
*
* -
* {String} iconCls CSS class of the icon to show. It is ignored if iconIndex is
* configured. Use this if you want static icons that are not base on the values in the record.
*
* -
* {Boolean} hide Optional. True to hide this action while still have a space in
* the grid column allocated to it. IMO, it doesn't make too much sense, use hideIndex instead.
*
* -
* {String} hideIndex Optional. Field name of the field of the grid store record that
* contains hide flag (falsie [null, '', 0, false, undefined] to show, anything else to hide).
*
* -
* {String} qtipIndex Optional. Field name of the field of the grid store record that
* contains tooltip text. If configured, the tooltip texts are taken from the store.
*
* -
* {String} tooltip Optional. Tooltip text to use as icon tooltip. It is ignored if
* qtipIndex is configured. Use this if you want static tooltips that are not taken from the store.
*
* -
* {String} qtip Synonym for tooltip
*
* -
* {String} textIndex Optional. Field name of the field of the grids store record
* that contains text to display on the right side of the icon. If configured, the text
* shown is taken from record.
*
* -
* {String} text Optional. Text to display on the right side of the icon. Use this
* if you want static text that are not taken from record. Ignored if textIndex is set.
*
* -
* {String} style Optional. Style to apply to action icon container.
*
*
*/
/**
* @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:
*
* -
* {String} align Set it to 'left' to place action icon next to the group header text.
* (defaults to undefined = icons are placed at the right side of the group header.
*
*
*/
/**
* @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);
}
});