/* * Ext JS Library 2.1 * Copyright(c) 2006-2008, Ext JS, LLC. * licensing@extjs.com * * http://extjs.com/license */ /** * CheckedColumn */ Ext.grid.CheckColumn = function(config){ Ext.apply(this, config); if(!this.id){ this.id = Ext.id(); } this.renderer = this.renderer.createDelegate(this); }; Ext.grid.CheckColumn.prototype ={ init : function(grid){ this.grid = grid; this.grid.on('render', function(){ var view = this.grid.getView(); view.mainBody.on('mousedown', this.onMouseDown, this); }, this); }, onMouseDown : function(e, t){ if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){ e.stopEvent(); var index = this.grid.getView().findRowIndex(t); var record = this.grid.store.getAt(index); record.set(this.dataIndex, !record.data[this.dataIndex]); } }, renderer : function(v, p, record){ p.css += ' x-grid3-check-col-td'; return '
A DnD-enabled version of {@link Ext.DataView}. Drag/drop is implemented by adding * {@link Ext.data.Record}s to the target DDView. If copying is not being performed, * the original {@link Ext.data.Record} is removed from the source DDView.
* @constructor * Create a new DDView * @param {Object} config The configuration properties. */ Ext.ux.DDView = function(config) { if (!config.itemSelector) { var tpl = config.tpl; if (this.classRe.test(tpl)) { config.tpl = tpl.replace(this.classRe, 'class=$1x-combo-list-item $2$1'); } else { config.tpl = tpl.replace(this.tagRe, '$1 class="x-combo-list-item" $2'); } config.itemSelector = ".x-combo-list-item"; } Ext.ux.DDView.superclass.constructor.call(this, Ext.apply(config, { border: false })); }; Ext.extend(Ext.ux.DDView, Ext.DataView, { /** * @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone (defaults to undefined). */ /** * @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone (defaults to undefined). */ /** * @cfg {Boolean} copy Causes drag operations to copy nodes rather than move (defaults to false). */ /** * @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move (defaults to false). */ /** * @cfg {String} sortDir Sort direction for the view, 'ASC' or 'DESC' (defaults to 'ASC'). */ sortDir: 'ASC', // private isFormField: true, classRe: /class=(['"])(.*)\1/, tagRe: /(<\w*)(.*?>)/, reset: Ext.emptyFn, clearInvalid: Ext.form.Field.prototype.clearInvalid, // private afterRender: function() { Ext.ux.DDView.superclass.afterRender.call(this); if (this.dragGroup) { this.setDraggable(this.dragGroup.split(",")); } if (this.dropGroup) { this.setDroppable(this.dropGroup.split(",")); } if (this.deletable) { this.setDeletable(); } this.isDirtyFlag = false; this.addEvents( "drop" ); }, // private validate: function() { return true; }, // private destroy: function() { this.purgeListeners(); this.getEl().removeAllListeners(); this.getEl().remove(); if (this.dragZone) { if (this.dragZone.destroy) { this.dragZone.destroy(); } } if (this.dropZone) { if (this.dropZone.destroy) { this.dropZone.destroy(); } } }, /** * Allows this class to be an Ext.form.Field so it can be found using {@link Ext.form.BasicForm#findField}. */ getName: function() { return this.name; }, /** * Loads the View from a JSON string representing the Records to put into the Store. * @param {String} value The JSON string */ setValue: function(v) { if (!this.store) { throw "DDView.setValue(). DDView must be constructed with a valid Store"; } var data = {}; data[this.store.reader.meta.root] = v ? [].concat(v) : []; this.store.proxy = new Ext.data.MemoryProxy(data); this.store.load(); }, /** * Returns the view's data value as a list of ids. * @return {String} A parenthesised list of the ids of the Records in the View, e.g. (1,3,8). */ getValue: function() { var result = '('; this.store.each(function(rec) { result += rec.id + ','; }); return result.substr(0, result.length - 1) + ')'; }, getIds: function() { var i = 0, result = new Array(this.store.getCount()); this.store.each(function(rec) { result[i++] = rec.id; }); return result; }, /** * Returns true if the view's data has changed, else false. * @return {Boolean} */ isDirty: function() { return this.isDirtyFlag; }, /** * Part of the Ext.dd.DropZone interface. If no target node is found, the * whole Element becomes the target, and this causes the drop gesture to append. */ getTargetFromEvent : function(e) { var target = e.getTarget(); while ((target !== null) && (target.parentNode != this.el.dom)) { target = target.parentNode; } if (!target) { target = this.el.dom.lastChild || this.el.dom; } return target; }, /** * Create the drag data which consists of an object which has the property "ddel" as * the drag proxy element. */ getDragData : function(e) { var target = this.findItemFromChild(e.getTarget()); if(target) { if (!this.isSelected(target)) { delete this.ignoreNextClick; this.onItemClick(target, this.indexOf(target), e); this.ignoreNextClick = true; } var dragData = { sourceView: this, viewNodes: [], records: [], copy: this.copy || (this.allowCopy && e.ctrlKey) }; if (this.getSelectionCount() == 1) { var i = this.getSelectedIndexes()[0]; var n = this.getNode(i); dragData.viewNodes.push(dragData.ddel = n); dragData.records.push(this.store.getAt(i)); dragData.repairXY = Ext.fly(n).getXY(); } else { dragData.ddel = document.createElement('div'); dragData.ddel.className = 'multi-proxy'; this.collectSelection(dragData); } return dragData; } return false; }, // override the default repairXY. getRepairXY : function(e){ return this.dragData.repairXY; }, // private collectSelection: function(data) { data.repairXY = Ext.fly(this.getSelectedNodes()[0]).getXY(); if (this.preserveSelectionOrder === true) { Ext.each(this.getSelectedIndexes(), function(i) { var n = this.getNode(i); var dragNode = n.cloneNode(true); dragNode.id = Ext.id(); data.ddel.appendChild(dragNode); data.records.push(this.store.getAt(i)); data.viewNodes.push(n); }, this); } else { var i = 0; this.store.each(function(rec){ if (this.isSelected(i)) { var n = this.getNode(i); var dragNode = n.cloneNode(true); dragNode.id = Ext.id(); data.ddel.appendChild(dragNode); data.records.push(this.store.getAt(i)); data.viewNodes.push(n); } i++; }, this); } }, /** * Specify to which ddGroup items in this DDView may be dragged. * @param {String} ddGroup The DD group name to assign this view to. */ setDraggable: function(ddGroup) { if (ddGroup instanceof Array) { Ext.each(ddGroup, this.setDraggable, this); return; } if (this.dragZone) { this.dragZone.addToGroup(ddGroup); } else { this.dragZone = new Ext.dd.DragZone(this.getEl(), { containerScroll: true, ddGroup: ddGroup }); // Draggability implies selection. DragZone's mousedown selects the element. if (!this.multiSelect) { this.singleSelect = true; } // Wire the DragZone's handlers up to methods in *this* this.dragZone.getDragData = this.getDragData.createDelegate(this); this.dragZone.getRepairXY = this.getRepairXY; this.dragZone.onEndDrag = this.onEndDrag; } }, /** * Specify from which ddGroup this DDView accepts drops. * @param {String} ddGroup The DD group name from which to accept drops. */ setDroppable: function(ddGroup) { if (ddGroup instanceof Array) { Ext.each(ddGroup, this.setDroppable, this); return; } if (this.dropZone) { this.dropZone.addToGroup(ddGroup); } else { this.dropZone = new Ext.dd.DropZone(this.getEl(), { owningView: this, containerScroll: true, ddGroup: ddGroup }); // Wire the DropZone's handlers up to methods in *this* this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this); this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this); this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this); this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this); this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this); } }, // private getDropPoint : function(e, n, dd){ if (n == this.el.dom) { return "above"; } var t = Ext.lib.Dom.getY(n), b = t + n.offsetHeight; var c = t + (b - t) / 2; var y = Ext.lib.Event.getPageY(e); if(y <= c) { return "above"; }else{ return "below"; } }, // private isValidDropPoint: function(pt, n, data) { if (!data.viewNodes || (data.viewNodes.length != 1)) { return true; } var d = data.viewNodes[0]; if (d == n) { return false; } if ((pt == "below") && (n.nextSibling == d)) { return false; } if ((pt == "above") && (n.previousSibling == d)) { return false; } return true; }, // private onNodeEnter : function(n, dd, e, data){ if (this.highlightColor && (data.sourceView != this)) { this.el.highlight(this.highlightColor); } return false; }, // private onNodeOver : function(n, dd, e, data){ var dragElClass = this.dropNotAllowed; var pt = this.getDropPoint(e, n, dd); if (this.isValidDropPoint(pt, n, data)) { if (this.appendOnly || this.sortField) { return "x-tree-drop-ok-below"; } // set the insert point style on the target node if (pt) { var targetElClass; if (pt == "above"){ dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above"; targetElClass = "x-view-drag-insert-above"; } else { dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below"; targetElClass = "x-view-drag-insert-below"; } if (this.lastInsertClass != targetElClass){ Ext.fly(n).replaceClass(this.lastInsertClass, targetElClass); this.lastInsertClass = targetElClass; } } } return dragElClass; }, // private onNodeOut : function(n, dd, e, data){ this.removeDropIndicators(n); }, // private onNodeDrop : function(n, dd, e, data){ if (this.fireEvent("drop", this, n, dd, e, data) === false) { return false; } var pt = this.getDropPoint(e, n, dd); var insertAt = (this.appendOnly || (n == this.el.dom)) ? this.store.getCount() : n.viewIndex; if (pt == "below") { insertAt++; } // Validate if dragging within a DDView if (data.sourceView == this) { // If the first element to be inserted below is the target node, remove it if (pt == "below") { if (data.viewNodes[0] == n) { data.viewNodes.shift(); } } else { // If the last element to be inserted above is the target node, remove it if (data.viewNodes[data.viewNodes.length - 1] == n) { data.viewNodes.pop(); } } // Nothing to drop... if (!data.viewNodes.length) { return false; } // If we are moving DOWN, then because a store.remove() takes place first, // the insertAt must be decremented. if (insertAt > this.store.indexOf(data.records[0])) { insertAt--; } } // Dragging from a Tree. Use the Tree's recordFromNode function. if (data.node instanceof Ext.tree.TreeNode) { var r = data.node.getOwnerTree().recordFromNode(data.node); if (r) { data.records = [ r ]; } } if (!data.records) { alert("Programming problem. Drag data contained no Records"); return false; } for (var i = 0; i < data.records.length; i++) { var r = data.records[i]; var dup = this.store.getById(r.id); if (dup && (dd != this.dragZone)) { if(!this.allowDup && !this.allowTrash){ Ext.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1); return true } var x=new Ext.data.Record(); r.id=x.id; delete x; } if (data.copy) { this.store.insert(insertAt++, r.copy()); } else { if (data.sourceView) { data.sourceView.isDirtyFlag = true; data.sourceView.store.remove(r); } if(!this.allowTrash)this.store.insert(insertAt++, r); } if(this.sortField){ this.store.sort(this.sortField, this.sortDir); } this.isDirtyFlag = true; } this.dragZone.cachedTarget = null; return true; }, // private onEndDrag: function(data, e) { var d = Ext.get(this.dragData.ddel); if (d && d.hasClass("multi-proxy")) { d.remove(); //delete this.dragData.ddel; } }, // private removeDropIndicators : function(n){ if(n){ Ext.fly(n).removeClass([ "x-view-drag-insert-above", "x-view-drag-insert-left", "x-view-drag-insert-right", "x-view-drag-insert-below"]); this.lastInsertClass = "_noclass"; } }, /** * Add a delete option to the DDView's context menu. * @param {String} imageUrl The URL of the "delete" icon image. */ setDeletable: function(imageUrl) { if (!this.singleSelect && !this.multiSelect) { this.singleSelect = true; } var c = this.getContextMenu(); this.contextMenu.on("itemclick", function(item) { switch (item.id) { case "delete": this.remove(this.getSelectedIndexes()); break; } }, this); this.contextMenu.add({ icon: imageUrl || AU.resolveUrl("/images/delete.gif"), id: "delete", text: AU.getMessage("deleteItem") }); }, /** * Return the context menu for this DDView. * @return {Ext.menu.Menu} The context menu */ getContextMenu: function() { if (!this.contextMenu) { // Create the View's context menu this.contextMenu = new Ext.menu.Menu({ id: this.id + "-contextmenu" }); this.el.on("contextmenu", this.showContextMenu, this); } return this.contextMenu; }, /** * Disables the view's context menu. */ disableContextMenu: function() { if (this.contextMenu) { this.el.un("contextmenu", this.showContextMenu, this); } }, // private showContextMenu: function(e, item) { item = this.findItemFromChild(e.getTarget()); if (item) { e.stopEvent(); this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true); this.contextMenu.showAt(e.getXY()); } }, /** * Remove {@link Ext.data.Record}s at the specified indices. * @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove. */ remove: function(selectedIndices) { selectedIndices = [].concat(selectedIndices); for (var i = 0; i < selectedIndices.length; i++) { var rec = this.store.getAt(selectedIndices[i]); this.store.remove(rec); } }, /** * Double click fires the {@link #dblclick} event. Additionally, if this DDView is draggable, and there is only one other * related DropZone that is in another DDView, it drops the selected node on that DDView. */ onDblClick : function(e){ var item = this.findItemFromChild(e.getTarget()); if(item){ if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) { return false; } if (this.dragGroup) { var targets = Ext.dd.DragDropMgr.getRelated(this.dragZone, true); // Remove instances of this View's DropZone while (targets.indexOf(this.dropZone) !== -1) { targets.remove(this.dropZone); } // If there's only one other DropZone, and it is owned by a DDView, then drop it in if ((targets.length == 1) && (targets[0].owningView)) { this.dragZone.cachedTarget = null; var el = Ext.get(targets[0].getEl()); var box = el.getBox(true); targets[0].onNodeDrop(el.dom, { target: el.dom, xy: [box.x, box.y + box.height - 1] }, null, this.getDragData(e)); } } } }, // private onItemClick : function(item, index, e){ // The DragZone's mousedown->getDragData already handled selection if (this.ignoreNextClick) { delete this.ignoreNextClick; return; } if(this.fireEvent("beforeclick", this, index, item, e) === false){ return false; } if(this.multiSelect || this.singleSelect){ if(this.multiSelect && e.shiftKey && this.lastSelection){ this.select(this.getNodes(this.indexOf(this.lastSelection), index), false); } else if (this.isSelected(item) && e.ctrlKey) { this.deselect(item); }else{ this.deselect(item); this.select(item, this.multiSelect && e.ctrlKey); this.lastSelection = item; } e.preventDefault(); } return true; } }); /* * 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 = '