/* * Ext JS Library 2.1 * Copyright(c) 2006-2008, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ /* * Ext JS Library 2.2 * Copyright(c) 2006-2008, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ /* * Note that this control should still be treated as an example and that the API will most likely * change once it is ported into the Ext core as a standard form control. This is still planned * for a future release, so this should not yet be treated as a final, stable API at this time. */ /** * @class Ext.ux.MultiSelect * @extends Ext.form.Field * A control that allows selection and form submission of multiple list items. The MultiSelect control * depends on the Ext.ux.DDView class to provide drag/drop capability both within the list and also * between multiple MultiSelect controls (see the Ext.ux.ItemSelector). * * @history * 2008-06-19 bpm Original code contributed by Toby Stuart * 2008-06-19 bpm Docs and demo code clean up * * @constructor * Create a new MultiSelect * @param {Object} config Configuration options */ Ext.ux.Multiselect = Ext.extend(Ext.form.Field, { /** * @cfg {String} legend Wraps the object with a fieldset and specified legend. */ /** * @cfg {Store} store The {@link Ext.data.Store} used by the underlying Ext.ux.DDView. */ /** * @cfg {Ext.ux.DDView} view The Ext.ux.DDView used to render the multiselect list. */ /** * @cfg {String/Array} dragGroup The ddgroup name(s) for the DDView's DragZone (defaults to undefined). */ /** * @cfg {String/Array} dropGroup The ddgroup name(s) for the DDView's DropZone (defaults to undefined). */ /** * @cfg {Object/Array} tbar The top toolbar of the control. This can be a {@link Ext.Toolbar} object, a * toolbar config, or an array of buttons/button configs to be added to the toolbar. */ /** * @cfg {String} fieldName The name of the field to sort by when sorting is enabled. */ /** * @cfg {String} appendOnly True if the list should only allow append drops when drag/drop is enabled * (use for lists which are sorted, defaults to false). */ appendOnly: false, /** * @cfg {Array} dataFields Inline data definition when not using a pre-initialised store. Known to cause problems * in some browswers for very long lists. Use store for large datasets. */ dataFields: [], /** * @cfg {Array} data Inline data when not using a pre-initialised store. Known to cause problems in some * browswers for very long lists. Use store for large datasets. */ data: [], /** * @cfg {Number} width Width in pixels of the control (defaults to 100). */ width: 100, /** * @cfg {Number} height Height in pixels of the control (defaults to 100). */ height: 100, /** * @cfg {String/Number} displayField Name/Index of the desired display field in the dataset (defaults to 0). */ displayField: 0, /** * @cfg {String/Number} valueField Name/Index of the desired value field in the dataset (defaults to 1). */ valueField: 1, /** * @cfg {Boolean} allowBlank True to require at least one item in the list to be selected, false to allow no * selection (defaults to true). */ allowBlank: true, /** * @cfg {Number} minLength Minimum number of selections allowed (defaults to 0). */ minLength: 0, /** * @cfg {Number} maxLength Maximum number of selections allowed (defaults to Number.MAX_VALUE). */ maxLength: Number.MAX_VALUE, /** * @cfg {String} blankText Default text displayed when the control contains no items (defaults to the same value as * {@link Ext.form.TextField#blankText}. */ blankText: Ext.form.TextField.prototype.blankText, /** * @cfg {String} minLengthText Validation message displayed when {@link #minLength} is not met (defaults to 'Minimum {0} * item(s) required'). The {0} token will be replaced by the value of {@link #minLength}. */ minLengthText: 'Minimum {0} item(s) required', /** * @cfg {String} maxLengthText Validation message displayed when {@link #maxLength} is not met (defaults to 'Maximum {0} * item(s) allowed'). The {0} token will be replaced by the value of {@link #maxLength}. */ maxLengthText: 'Maximum {0} item(s) allowed', /** * @cfg {String} delimiter The string used to delimit between items when set or returned as a string of values * (defaults to ','). */ delimiter: ',', // DDView settings copy: false, allowDup: false, allowTrash: false, focusClass: undefined, sortDir: 'ASC', // private defaultAutoCreate: {tag: "div"}, // private initComponent: function() { Ext.ux.Multiselect.superclass.initComponent.call(this); this.addEvents({ 'dblclick': true, 'click': true, 'change': true, 'drop': true }); }, // private onRender: function(ct, position) { Ext.ux.Multiselect.superclass.onRender.call(this, ct, position); var cls = 'ux-mselect'; var fs = new Ext.form.FieldSet({ renderTo: this.el, title: this.legend, height: this.height, width: this.width, style: "padding:0;", tbar: this.tbar }); //if(!this.legend)fs.el.down('.'+fs.headerCls).remove(); fs.body.addClass(cls); var tpl = '
'; if (!this.store) { this.store = new Ext.data.SimpleStore({ fields: this.dataFields, data: this.data }); } this.view = new Ext.ux.DDView({ multiSelect: true, store: this.store, selectedClass: cls + "-selected", tpl: tpl, allowDup: this.allowDup, copy: this.copy, allowTrash: this.allowTrash, dragGroup: this.dragGroup, dropGroup: this.dropGroup, itemSelector: "." + cls + "-item", isFormField: false, applyTo: fs.body, appendOnly: this.appendOnly, sortField: this.sortField, sortDir: this.sortDir }); fs.add(this.view); this.view.on('click', this.onViewClick, this); this.view.on('beforeClick', this.onViewBeforeClick, this); this.view.on('dblclick', this.onViewDblClick, this); this.view.on('drop', function(ddView, n, dd, e, data) { return this.fireEvent("drop", ddView, n, dd, e, data); }, this); this.hiddenName = this.name; var hiddenTag = {tag: "input", type: "hidden", value: "", name: this.name}; if (this.isFormField) { this.hiddenField = this.el.createChild(hiddenTag); } else { this.hiddenField = Ext.get(document.body).createChild(hiddenTag); } fs.doLayout(); }, // private initValue: Ext.emptyFn, // private onViewClick: function(vw, index, node, e) { var arrayIndex = this.preClickSelections.indexOf(index); if (arrayIndex !== -1) { this.preClickSelections.splice(arrayIndex, 1); this.view.clearSelections(true); this.view.select(this.preClickSelections); } this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value); this.hiddenField.dom.value = this.getValue(); this.fireEvent('click', this, e); this.validate(); }, // private onViewBeforeClick: function(vw, index, node, e) { this.preClickSelections = this.view.getSelectedIndexes(); if (this.disabled) { return false; } }, // private onViewDblClick: function(vw, index, node, e) { return this.fireEvent('dblclick', vw, index, node, e); }, /** * Returns an array of data values for the selected items in the list. The values will be separated * by {@link #delimiter}. * @return {Array} value An array of string data values */ getValue: function(valueField) { var returnArray = []; var selectionsArray = this.view.getSelectedIndexes(); if (selectionsArray.length === 0) { return ''; } for (var i = 0; i < selectionsArray.length; i++) { returnArray.push(this.store.getAt(selectionsArray[i]).get(((valueField != null) ? valueField : this.valueField))); } return returnArray.join(this.delimiter); }, /** * Sets a delimited string (using {@link #delimiter}) or array of data values into the list. * @param {String/Array} values The values to set */ setValue: function(values) { var index; var selections = []; this.view.clearSelections(); this.hiddenField.dom.value = ''; if (!values || (values === '')) { return; } if (!(values instanceof Array)) { values = values.split(this.delimiter); } for (var i = 0; i < values.length; i++) { index = this.view.store.indexOf(this.view.store.query(this.valueField, new RegExp('^' + values[i] + '$', "i")).itemAt(0)); selections.push(index); } this.view.select(selections); this.hiddenField.dom.value = this.getValue(); this.validate(); }, // inherit docs reset: function() { this.setValue(''); }, // inherit docs getRawValue: function(valueField) { var tmp = this.getValue(valueField); if (tmp.length) { tmp = tmp.split(this.delimiter); } else { tmp = []; } return tmp; }, // inherit docs setRawValue: function(values) { setValue(values); }, // inherit docs validateValue: function(value) { if (value.length < 1) { // if it has no value if (this.allowBlank) { this.clearInvalid(); return true; } else { this.markInvalid(this.blankText); return false; } } if (value.length < this.minLength) { this.markInvalid(String.format(this.minLengthText, this.minLength)); return false; } if (value.length > this.maxLength) { this.markInvalid(String.format(this.maxLengthText, this.maxLength)); return false; } return true; } }); Ext.reg("multiselect", Ext.ux.Multiselect); /** * Ext.ux.grid.ProgressColumn - Ext.ux.grid.ProgressColumn is a grid plugin that * shows a progress bar for a number between 0 and 100 to indicate some sort of * progress. The plugin supports all the normal cell/column operations including * sorting, editing, dragging, and hiding. It also supports special progression * coloring or standard Ext.ProgressBar coloring for the bar. * * @author Benjamin Runnels * @copyright (c) 2008, by Benjamin Runnels * @date 06 June 2008 * @version 1.1 * * @license Ext.ux.grid.ProgressColumn 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 */ /** * 22/07/2014: ceiling support backported from version 1.2, by Kai Sommerfeld * 01/08/2014: tvh_renderer fcn added by Jaroslav Kysela */ Ext.namespace('Ext.ux.grid'); Ext.ux.grid.ProgressColumn = function(config) { Ext.apply(this, config); this.renderer = this.renderer.createDelegate(this); this.addEvents('action'); Ext.ux.grid.ProgressColumn.superclass.constructor.call(this); }; Ext.extend(Ext.ux.grid.ProgressColumn, Ext.util.Observable, { /** * @cfg {Integer} upper limit for full progress indicator (defaults to 100) */ ceiling : 100, /** * @cfg {String} colored determines whether use special progression coloring * or the standard Ext.ProgressBar coloring for the bar (defaults to * false) */ textPst: '%', /** * @cfg {String} colored determines whether use special progression coloring * or the standard Ext.ProgressBar coloring for the bar (defaults to * false) */ colored: false, /** * @cfg {String} actionEvent Event to trigger actions, e.g. click, dblclick, * mouseover (defaults to 'dblclick') */ actionEvent: 'dblclick', init: function(grid) { this.grid = grid; this.view = grid.getView(); if (this.editor && grid.isEditor) { var cfg = { scope: this }; cfg[this.actionEvent] = this.onClick; grid.afterRender = grid.afterRender.createSequence(function() { this.view.mainBody.on(cfg); }, this); } }, onClick: function(e, target) { var rowIndex = e.getTarget('.x-grid3-row').rowIndex; var colIndex = this.view.findCellIndex(target.parentNode.parentNode); var t = e.getTarget('.x-progress-text'); if (t) { this.grid.startEditing(rowIndex, colIndex); } }, tvh_renderer: function (v, p, record) { return v; }, renderer: function(v, p, record) { v = this.tvh_renderer(v, p, record); if (typeof v === "string") return v; // custom string var style = ''; var textClass = (v < (this.ceiling / 1.818)) ? 'x-progress-text-back' : 'x-progress-text-front' + (Ext.isIE6 ? '-ie6' : ''); var value = v / this.ceiling * 100; value = value.toFixed(0); //ugly hack to deal with IE6 issue var text = String.format('
{2}
', textClass, Ext.id(), value + this.textPst ); text = (v < (this.ceiling / 1.031)) ? text.substring(0, text.length - 6) : text.substr(6); if (this.colored == true) { if (v <= this.ceiling && v > (this.ceiling * 0.66)) style = '-green'; if (v < (this.ceiling * 0.67) && v > (this.ceiling * 0.33)) style = '-orange'; if (v < (this.ceiling * 0.34)) style = '-red'; } p.css += ' x-grid3-progresscol'; return String.format( '
{2}
' + '
', style, value, text ); } }); //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 747 2009-09-03 23:30:52Z 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 {Boolean} editable - Do not touch! * Must be false to prevent errors in editable grids */ , editable: false /** * @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: 'visibility' /** * @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.ux.form.LovCombo, List of Values Combo * * @author Ing. Jozef Sakáloš * @copyright (c) 2008, by Ing. Jozef Sakáloš * @date 16. April 2008 * @version $Id: Ext.ux.form.LovCombo.js 285 2008-06-06 09:22:20Z jozo $ * * @license Ext.ux.form.LovCombo.js 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 */ // 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 } // create namespace Ext.ns('Ext.ux.form'); /** * * @class Ext.ux.form.LovCombo * @extends Ext.form.ComboBox */ Ext.ux.form.LovCombo = Ext.extend(Ext.form.ComboBox, { // {{{ // configuration options /** * @cfg {String} checkField name of field used to store checked state. * It is automatically added to existing fields. * Change it only if it collides with your normal field. */ checkField: 'checked' /** * @cfg {String} separator separator to use between values and texts */ , separator: ',' /** * @cfg {String/Array} tpl Template for items. * Change it only if you know what you are doing. */ // }}} // {{{ , initComponent: function() { // template with checkbox if (!this.tpl) { this.tpl = '' + '
' + '' + '
{' + (this.displayField || 'text') + '}
' + '
' + '
' ; } // call parent Ext.ux.form.LovCombo.superclass.initComponent.apply(this, arguments); // install internal event handlers this.on({ scope: this , beforequery: this.onBeforeQuery , blur: this.onRealBlur }); // remove selection from input field this.onLoad = this.onLoad.createSequence(function() { if (this.el) { var v = this.el.dom.value; this.el.dom.value = ''; this.el.dom.value = v; } }); } // e/o function initComponent // }}} // {{{ /** * Disables default tab key bahavior * @private */ , initEvents: function() { Ext.ux.form.LovCombo.superclass.initEvents.apply(this, arguments); // disable default tab handling - does no good this.keyNav.tab = false; } // eo function initEvents // }}} // {{{ /** * clears value */ , clearValue: function() { this.value = ''; this.setRawValue(this.value); this.store.clearFilter(); this.store.each(function(r) { r.set(this.checkField, false); }, this); if (this.hiddenField) { this.hiddenField.value = ''; } this.applyEmptyText(); } // eo function clearValue // }}} // {{{ /** * @return {String} separator (plus space) separated list of selected displayFields * @private */ , getCheckedDisplay: function() { var re = new RegExp(this.separator, "g"); return this.getCheckedValue(this.displayField).replace(re, this.separator + ' '); } // eo function getCheckedDisplay // }}} // {{{ /** * @return {String} separator separated list of selected valueFields * @private */ , getCheckedValue: function(field) { field = field || this.valueField; var c = []; // store may be filtered so get all records var snapshot = this.store.snapshot || this.store.data; snapshot.each(function(r) { if (r.get(this.checkField)) { c.push(r.get(field)); } }, this); return c.join(this.separator); } // eo function getCheckedValue // }}} // {{{ /** * beforequery event handler - handles multiple selections * @param {Object} qe query event * @private */ , onBeforeQuery: function(qe) { qe.query = qe.query.replace(new RegExp(this.getCheckedDisplay() + '[ ' + this.separator + ']*'), ''); } // eo function onBeforeQuery // }}} // {{{ /** * blur event handler - runs only when real blur event is fired */ , onRealBlur: function() { this.list.hide(); var rv = this.getRawValue(); var rva = rv.split(new RegExp(RegExp.escape(this.separator) + ' *')); var va = []; var snapshot = this.store.snapshot || this.store.data; // iterate through raw values and records and check/uncheck items Ext.each(rva, function(v) { snapshot.each(function(r) { if (v === r.get(this.displayField)) { va.push(r.get(this.valueField)); } }, this); }, this); this.setValue(va.join(this.separator)); this.store.clearFilter(); } // eo function onRealBlur // }}} // {{{ /** * Combo's onSelect override * @private * @param {Ext.data.Record} record record that has been selected in the list * @param {Number} index index of selected (clicked) record */ , onSelect: function(record, index) { if (this.fireEvent('beforeselect', this, record, index) !== false) { // toggle checked field record.set(this.checkField, !record.get(this.checkField)); // display full list if (this.store.isFiltered()) { this.doQuery(this.allQuery); } // set (update) value and fire event this.setValue(this.getCheckedValue()); this.fireEvent('select', this, record, index); } } // eo function onSelect // }}} // {{{ /** * Sets the value of the LovCombo * @param {Mixed} v value */ , setValue: function(v) { if (v) { v = '' + v; if (this.valueField) { this.store.clearFilter(); this.store.each(function(r) { var checked = !(!v.match( '(^|' + this.separator + ')' + RegExp.escape(r.get(this.valueField)) + '(' + this.separator + '|$)')) ; r.set(this.checkField, checked); }, this); this.value = this.getCheckedValue(); this.setRawValue(this.getCheckedDisplay()); if (this.hiddenField) { this.hiddenField.value = this.value; } } else { this.value = v; this.setRawValue(v); if (this.hiddenField) { this.hiddenField.value = v; } } if (this.el) { this.el.removeClass(this.emptyClass); } } else { this.clearValue(); } } // eo function setValue // }}} // {{{ /** * Selects all items */ , selectAll: function() { this.store.each(function(record) { // toggle checked field record.set(this.checkField, true); }, this); //display full list this.doQuery(this.allQuery); this.setValue(this.getCheckedValue()); } // eo full selectAll // }}} // {{{ /** * Deselects all items. Synonym for clearValue */ , deselectAll: function() { this.clearValue(); } // eo full deselectAll // }}} }); // eo extend // register xtype Ext.reg('lovcombo', Ext.ux.form.LovCombo);