--- /dev/null
+/*!
+ * Ext JS Library 3.0.0
+ * Copyright(c) 2006-2009 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+Ext.ns('Ext.ux.grid');
+
+/**
+ * @class Ext.ux.grid.BufferView
+ * @extends Ext.grid.GridView
+ * A custom GridView which renders rows on an as-needed basis.
+ */
+Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, {
+ /**
+ * @cfg {Number} rowHeight
+ * The height of a row in the grid.
+ */
+ rowHeight: 19,
+
+ /**
+ * @cfg {Number} borderHeight
+ * The combined height of border-top and border-bottom of a row.
+ */
+ borderHeight: 2,
+
+ /**
+ * @cfg {Boolean/Number} scrollDelay
+ * The number of milliseconds before rendering rows out of the visible
+ * viewing area. Defaults to 100. Rows will render immediately with a config
+ * of false.
+ */
+ scrollDelay: 100,
+
+ /**
+ * @cfg {Number} cacheSize
+ * The number of rows to look forward and backwards from the currently viewable
+ * area. The cache applies only to rows that have been rendered already.
+ */
+ cacheSize: 20,
+
+ /**
+ * @cfg {Number} cleanDelay
+ * The number of milliseconds to buffer cleaning of extra rows not in the
+ * cache.
+ */
+ cleanDelay: 500,
+
+ initTemplates : function(){
+ Ext.ux.grid.BufferView.superclass.initTemplates.call(this);
+ var ts = this.templates;
+ // empty div to act as a place holder for a row
+ ts.rowHolder = new Ext.Template(
+ '<div class="x-grid3-row {alt}" style="{tstyle}"></div>'
+ );
+ ts.rowHolder.disableFormats = true;
+ ts.rowHolder.compile();
+
+ ts.rowBody = new Ext.Template(
+ '<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
+ '<tbody><tr>{cells}</tr>',
+ (this.enableRowBody ? '<tr class="x-grid3-row-body-tr" style="{bodyStyle}"><td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on"><div class="x-grid3-row-body">{body}</div></td></tr>' : ''),
+ '</tbody></table>'
+ );
+ ts.rowBody.disableFormats = true;
+ ts.rowBody.compile();
+ },
+
+ getStyleRowHeight : function(){
+ return Ext.isBorderBox ? (this.rowHeight + this.borderHeight) : this.rowHeight;
+ },
+
+ getCalculatedRowHeight : function(){
+ return this.rowHeight + this.borderHeight;
+ },
+
+ getVisibleRowCount : function(){
+ var rh = this.getCalculatedRowHeight();
+ var visibleHeight = this.scroller.dom.clientHeight;
+ return (visibleHeight < 1) ? 0 : Math.ceil(visibleHeight / rh);
+ },
+
+ getVisibleRows: function(){
+ var count = this.getVisibleRowCount();
+ var sc = this.scroller.dom.scrollTop;
+ var start = (sc == 0 ? 0 : Math.floor(sc/this.getCalculatedRowHeight())-1);
+ return {
+ first: Math.max(start, 0),
+ last: Math.min(start + count + 2, this.ds.getCount()-1)
+ };
+ },
+
+ doRender : function(cs, rs, ds, startRow, colCount, stripe, onlyBody){
+ var ts = this.templates, ct = ts.cell, rt = ts.row, rb = ts.rowBody, last = colCount-1;
+ var rh = this.getStyleRowHeight();
+ var vr = this.getVisibleRows();
+ var tstyle = 'width:'+this.getTotalWidth()+';height:'+rh+'px;';
+ // buffers
+ var buf = [], cb, c, p = {}, rp = {tstyle: tstyle}, r;
+ for (var j = 0, len = rs.length; j < len; j++) {
+ r = rs[j]; cb = [];
+ var rowIndex = (j+startRow);
+ var visible = rowIndex >= vr.first && rowIndex <= vr.last;
+ if (visible) {
+ for (var i = 0; i < colCount; i++) {
+ c = cs[i];
+ p.id = c.id;
+ p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
+ p.attr = p.cellAttr = "";
+ p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
+ p.style = c.style;
+ if (p.value == undefined || p.value === "") {
+ p.value = " ";
+ }
+ if (r.dirty && typeof r.modified[c.name] !== 'undefined') {
+ p.css += ' x-grid3-dirty-cell';
+ }
+ cb[cb.length] = ct.apply(p);
+ }
+ }
+ var alt = [];
+ if(stripe && ((rowIndex+1) % 2 == 0)){
+ alt[0] = "x-grid3-row-alt";
+ }
+ if(r.dirty){
+ alt[1] = " x-grid3-dirty-row";
+ }
+ rp.cols = colCount;
+ if(this.getRowClass){
+ alt[2] = this.getRowClass(r, rowIndex, rp, ds);
+ }
+ rp.alt = alt.join(" ");
+ rp.cells = cb.join("");
+ buf[buf.length] = !visible ? ts.rowHolder.apply(rp) : (onlyBody ? rb.apply(rp) : rt.apply(rp));
+ }
+ return buf.join("");
+ },
+
+ isRowRendered: function(index){
+ var row = this.getRow(index);
+ return row && row.childNodes.length > 0;
+ },
+
+ syncScroll: function(){
+ Ext.ux.grid.BufferView.superclass.syncScroll.apply(this, arguments);
+ this.update();
+ },
+
+ // a (optionally) buffered method to update contents of gridview
+ update: function(){
+ if (this.scrollDelay) {
+ if (!this.renderTask) {
+ this.renderTask = new Ext.util.DelayedTask(this.doUpdate, this);
+ }
+ this.renderTask.delay(this.scrollDelay);
+ }else{
+ this.doUpdate();
+ }
+ },
+
+ doUpdate: function(){
+ if (this.getVisibleRowCount() > 0) {
+ var g = this.grid, cm = g.colModel, ds = g.store;
+ var cs = this.getColumnData();
+
+ var vr = this.getVisibleRows();
+ for (var i = vr.first; i <= vr.last; i++) {
+ // if row is NOT rendered and is visible, render it
+ if(!this.isRowRendered(i)){
+ var html = this.doRender(cs, [ds.getAt(i)], ds, i, cm.getColumnCount(), g.stripeRows, true);
+ this.getRow(i).innerHTML = html;
+ }
+ }
+ this.clean();
+ }
+ },
+
+ // a buffered method to clean rows
+ clean : function(){
+ if(!this.cleanTask){
+ this.cleanTask = new Ext.util.DelayedTask(this.doClean, this);
+ }
+ this.cleanTask.delay(this.cleanDelay);
+ },
+
+ doClean: function(){
+ if (this.getVisibleRowCount() > 0) {
+ var vr = this.getVisibleRows();
+ vr.first -= this.cacheSize;
+ vr.last += this.cacheSize;
+
+ var i = 0, rows = this.getRows();
+ // if first is less than 0, all rows have been rendered
+ // so lets clean the end...
+ if(vr.first <= 0){
+ i = vr.last + 1;
+ }
+ for(var len = this.ds.getCount(); i < len; i++){
+ // if current row is outside of first and last and
+ // has content, update the innerHTML to nothing
+ if ((i < vr.first || i > vr.last) && rows[i].innerHTML) {
+ rows[i].innerHTML = '';
+ }
+ }
+ }
+ },
+
+ layout: function(){
+ Ext.ux.grid.BufferView.superclass.layout.call(this);
+ this.update();
+ }
+});
+// We are adding these custom layouts to a namespace that does not
+// exist by default in Ext, so we have to add the namespace first:
+Ext.ns('Ext.ux.layout');
+
+/**
+ * @class Ext.ux.layout.CenterLayout
+ * @extends Ext.layout.FitLayout
+ * <p>This is a very simple layout style used to center contents within a container. This layout works within
+ * nested containers and can also be used as expected as a Viewport layout to center the page layout.</p>
+ * <p>As a subclass of FitLayout, CenterLayout expects to have a single child panel of the container that uses
+ * the layout. The layout does not require any config options, although the child panel contained within the
+ * layout must provide a fixed or percentage width. The child panel's height will fit to the container by
+ * default, but you can specify <tt>autoHeight:true</tt> to allow it to autosize based on its content height.
+ * Example usage:</p>
+ * <pre><code>
+// The content panel is centered in the container
+var p = new Ext.Panel({
+ title: 'Center Layout',
+ layout: 'ux.center',
+ items: [{
+ title: 'Centered Content',
+ width: '75%',
+ html: 'Some content'
+ }]
+});
+
+// If you leave the title blank and specify no border
+// you'll create a non-visual, structural panel just
+// for centering the contents in the main container.
+var p = new Ext.Panel({
+ layout: 'ux.center',
+ border: false,
+ items: [{
+ title: 'Centered Content',
+ width: 300,
+ autoHeight: true,
+ html: 'Some content'
+ }]
+});
+</code></pre>
+ */
+Ext.ux.layout.CenterLayout = Ext.extend(Ext.layout.FitLayout, {
+ // private
+ setItemSize : function(item, size){
+ this.container.addClass('ux-layout-center');
+ item.addClass('ux-layout-center-item');
+ if(item && size.height > 0){
+ if(item.width){
+ size.width = item.width;
+ }
+ item.setSize(size);
+ }
+ }
+});
+
+Ext.Container.LAYOUTS['ux.center'] = Ext.ux.layout.CenterLayout;
+Ext.ns('Ext.ux.grid');\r
+\r
+/**\r
+ * @class Ext.ux.grid.CheckColumn\r
+ * @extends Object\r
+ * GridPanel plugin to add a column with check boxes to a grid.\r
+ * <p>Example usage:</p>\r
+ * <pre><code>\r
+// create the column\r
+var checkColumn = new Ext.grid.CheckColumn({\r
+ header: 'Indoor?',\r
+ dataIndex: 'indoor',\r
+ id: 'check',\r
+ width: 55\r
+});\r
+\r
+// add the column to the column model\r
+var cm = new Ext.grid.ColumnModel([{\r
+ header: 'Foo',\r
+ ...\r
+ },\r
+ checkColumn\r
+]);\r
+\r
+// create the grid\r
+var grid = new Ext.grid.EditorGridPanel({\r
+ ...\r
+ cm: cm,\r
+ plugins: [checkColumn], // include plugin\r
+ ...\r
+});\r
+ * </code></pre>\r
+ * In addition to storing a Boolean value within the record data, this\r
+ * class toggles a css class between <tt>'x-grid3-check-col'</tt> and\r
+ * <tt>'x-grid3-check-col-on'</tt> to alter the background image used for\r
+ * a column.\r
+ */\r
+Ext.ux.grid.CheckColumn = function(config){\r
+ Ext.apply(this, config);\r
+ if(!this.id){\r
+ this.id = Ext.id();\r
+ }\r
+ this.renderer = this.renderer.createDelegate(this);\r
+};\r
+\r
+Ext.ux.grid.CheckColumn.prototype ={\r
+ init : function(grid){\r
+ this.grid = grid;\r
+ this.grid.on('render', function(){\r
+ var view = this.grid.getView();\r
+ view.mainBody.on('mousedown', this.onMouseDown, this);\r
+ }, this);\r
+ },\r
+\r
+ onMouseDown : function(e, t){\r
+ if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){\r
+ e.stopEvent();\r
+ var index = this.grid.getView().findRowIndex(t);\r
+ var record = this.grid.store.getAt(index);\r
+ record.set(this.dataIndex, !record.data[this.dataIndex]);\r
+ }\r
+ },\r
+\r
+ renderer : function(v, p, record){\r
+ p.css += ' x-grid3-check-col-td'; \r
+ return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'"> </div>';\r
+ }\r
+};\r
+\r
+// register ptype\r
+Ext.preg('checkcolumn', Ext.ux.grid.CheckColumn);\r
+\r
+// backwards compat\r
+Ext.grid.CheckColumn = Ext.ux.grid.CheckColumn;Ext.ns('Ext.ux.tree');\r
+\r
+/**\r
+ * @class Ext.ux.tree.ColumnTree\r
+ * @extends Ext.tree.TreePanel\r
+ * \r
+ * @xtype columntree\r
+ */\r
+Ext.ux.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, {\r
+ lines : false,\r
+ borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell\r
+ cls : 'x-column-tree',\r
+\r
+ onRender : function(){\r
+ Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments);\r
+ this.headers = this.header.createChild({cls:'x-tree-headers'});\r
+\r
+ var cols = this.columns, c;\r
+ var totalWidth = 0;\r
+ var scrollOffset = 19; // similar to Ext.grid.GridView default\r
+\r
+ for(var i = 0, len = cols.length; i < len; i++){\r
+ c = cols[i];\r
+ totalWidth += c.width;\r
+ this.headers.createChild({\r
+ cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),\r
+ cn: {\r
+ cls:'x-tree-hd-text',\r
+ html: c.header\r
+ },\r
+ style:'width:'+(c.width-this.borderWidth)+'px;'\r
+ });\r
+ }\r
+ this.headers.createChild({cls:'x-clear'});\r
+ // prevent floats from wrapping when clipped\r
+ this.headers.setWidth(totalWidth+scrollOffset);\r
+ this.innerCt.setWidth(totalWidth);\r
+ }\r
+});\r
+\r
+Ext.reg('columntree', Ext.ux.tree.ColumnTree);\r
+\r
+//backwards compat\r
+Ext.tree.ColumnTree = Ext.ux.tree.ColumnTree;\r
+\r
+\r
+/**\r
+ * @class Ext.ux.tree.ColumnNodeUI\r
+ * @extends Ext.tree.TreeNodeUI\r
+ */\r
+Ext.ux.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, {\r
+ focus: Ext.emptyFn, // prevent odd scrolling behavior\r
+\r
+ renderElements : function(n, a, targetNode, bulkRender){\r
+ this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';\r
+\r
+ var t = n.getOwnerTree();\r
+ var cols = t.columns;\r
+ var bw = t.borderWidth;\r
+ var c = cols[0];\r
+\r
+ var buf = [\r
+ '<li class="x-tree-node"><div ext:tree-node-id="',n.id,'" class="x-tree-node-el x-tree-node-leaf ', a.cls,'">',\r
+ '<div class="x-tree-col" style="width:',c.width-bw,'px;">',\r
+ '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",\r
+ '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">',\r
+ '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on">',\r
+ '<a hidefocus="on" class="x-tree-node-anchor" href="',a.href ? a.href : "#",'" tabIndex="1" ',\r
+ a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", '>',\r
+ '<span unselectable="on">', n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</span></a>",\r
+ "</div>"];\r
+ for(var i = 1, len = cols.length; i < len; i++){\r
+ c = cols[i];\r
+\r
+ buf.push('<div class="x-tree-col ',(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',\r
+ '<div class="x-tree-col-text">',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"</div>",\r
+ "</div>");\r
+ }\r
+ buf.push(\r
+ '<div class="x-clear"></div></div>',\r
+ '<ul class="x-tree-node-ct" style="display:none;"></ul>',\r
+ "</li>");\r
+\r
+ if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){\r
+ this.wrap = Ext.DomHelper.insertHtml("beforeBegin",\r
+ n.nextSibling.ui.getEl(), buf.join(""));\r
+ }else{\r
+ this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));\r
+ }\r
+\r
+ this.elNode = this.wrap.childNodes[0];\r
+ this.ctNode = this.wrap.childNodes[1];\r
+ var cs = this.elNode.firstChild.childNodes;\r
+ this.indentNode = cs[0];\r
+ this.ecNode = cs[1];\r
+ this.iconNode = cs[2];\r
+ this.anchor = cs[3];\r
+ this.textNode = cs[3].firstChild;\r
+ }\r
+});\r
+\r
+//backwards compat\r
+Ext.tree.ColumnNodeUI = Ext.ux.tree.ColumnNodeUI;\r
+/**\r
+ * @class Ext.DataView.LabelEditor\r
+ * @extends Ext.Editor\r
+ * \r
+ */\r
+Ext.DataView.LabelEditor = Ext.extend(Ext.Editor, {\r
+ alignment: "tl-tl",\r
+ hideEl : false,\r
+ cls: "x-small-editor",\r
+ shim: false,\r
+ completeOnEnter: true,\r
+ cancelOnEsc: true,\r
+ labelSelector: 'span.x-editable',\r
+ \r
+ constructor: function(cfg, field){\r
+ Ext.DataView.LabelEditor.superclass.constructor.call(this,\r
+ field || new Ext.form.TextField({\r
+ allowBlank: false,\r
+ growMin:90,\r
+ growMax:240,\r
+ grow:true,\r
+ selectOnFocus:true\r
+ }), cfg\r
+ );\r
+ },\r
+ \r
+ init : function(view){\r
+ this.view = view;\r
+ view.on('render', this.initEditor, this);\r
+ this.on('complete', this.onSave, this);\r
+ },\r
+\r
+ initEditor : function(){\r
+ this.view.on({\r
+ scope: this,\r
+ containerclick: this.doBlur,\r
+ click: this.doBlur\r
+ });\r
+ this.view.getEl().on('mousedown', this.onMouseDown, this, {delegate: this.labelSelector});\r
+ },\r
+ \r
+ doBlur: function(){\r
+ if(this.editing){\r
+ this.field.blur();\r
+ }\r
+ },\r
+\r
+ onMouseDown : function(e, target){\r
+ if(!e.ctrlKey && !e.shiftKey){\r
+ var item = this.view.findItemFromChild(target);\r
+ e.stopEvent();\r
+ var record = this.view.store.getAt(this.view.indexOf(item));\r
+ this.startEdit(target, record.data[this.dataIndex]);\r
+ this.activeRecord = record;\r
+ }else{\r
+ e.preventDefault();\r
+ }\r
+ },\r
+\r
+ onSave : function(ed, value){\r
+ this.activeRecord.set(this.dataIndex, value);\r
+ }\r
+});\r
+\r
+\r
+Ext.DataView.DragSelector = function(cfg){\r
+ cfg = cfg || {};\r
+ var view, proxy, tracker;\r
+ var rs, bodyRegion, dragRegion = new Ext.lib.Region(0,0,0,0);\r
+ var dragSafe = cfg.dragSafe === true;\r
+\r
+ this.init = function(dataView){\r
+ view = dataView;\r
+ view.on('render', onRender);\r
+ };\r
+\r
+ function fillRegions(){\r
+ rs = [];\r
+ view.all.each(function(el){\r
+ rs[rs.length] = el.getRegion();\r
+ });\r
+ bodyRegion = view.el.getRegion();\r
+ }\r
+\r
+ function cancelClick(){\r
+ return false;\r
+ }\r
+\r
+ function onBeforeStart(e){\r
+ return !dragSafe || e.target == view.el.dom;\r
+ }\r
+\r
+ function onStart(e){\r
+ view.on('containerclick', cancelClick, view, {single:true});\r
+ if(!proxy){\r
+ proxy = view.el.createChild({cls:'x-view-selector'});\r
+ }else{\r
+ proxy.setDisplayed('block');\r
+ }\r
+ fillRegions();\r
+ view.clearSelections();\r
+ }\r
+\r
+ function onDrag(e){\r
+ var startXY = tracker.startXY;\r
+ var xy = tracker.getXY();\r
+\r
+ var x = Math.min(startXY[0], xy[0]);\r
+ var y = Math.min(startXY[1], xy[1]);\r
+ var w = Math.abs(startXY[0] - xy[0]);\r
+ var h = Math.abs(startXY[1] - xy[1]);\r
+\r
+ dragRegion.left = x;\r
+ dragRegion.top = y;\r
+ dragRegion.right = x+w;\r
+ dragRegion.bottom = y+h;\r
+\r
+ dragRegion.constrainTo(bodyRegion);\r
+ proxy.setRegion(dragRegion);\r
+\r
+ for(var i = 0, len = rs.length; i < len; i++){\r
+ var r = rs[i], sel = dragRegion.intersect(r);\r
+ if(sel && !r.selected){\r
+ r.selected = true;\r
+ view.select(i, true);\r
+ }else if(!sel && r.selected){\r
+ r.selected = false;\r
+ view.deselect(i);\r
+ }\r
+ }\r
+ }\r
+\r
+ function onEnd(e){\r
+ if (!Ext.isIE) {\r
+ view.un('containerclick', cancelClick, view); \r
+ } \r
+ if(proxy){\r
+ proxy.setDisplayed(false);\r
+ }\r
+ }\r
+\r
+ function onRender(view){\r
+ tracker = new Ext.dd.DragTracker({\r
+ onBeforeStart: onBeforeStart,\r
+ onStart: onStart,\r
+ onDrag: onDrag,\r
+ onEnd: onEnd\r
+ });\r
+ tracker.initEl(view.el);\r
+ }\r
+};Ext.ns('Ext.ux.form');
+
+/**
+ * @class Ext.ux.form.FileUploadField
+ * @extends Ext.form.TextField
+ * Creates a file upload field.
+ * @xtype fileuploadfield
+ */
+Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, {
+ /**
+ * @cfg {String} buttonText The button text to display on the upload button (defaults to
+ * 'Browse...'). Note that if you supply a value for {@link #buttonCfg}, the buttonCfg.text
+ * value will be used instead if available.
+ */
+ buttonText: 'Browse...',
+ /**
+ * @cfg {Boolean} buttonOnly True to display the file upload field as a button with no visible
+ * text field (defaults to false). If true, all inherited TextField members will still be available.
+ */
+ buttonOnly: false,
+ /**
+ * @cfg {Number} buttonOffset The number of pixels of space reserved between the button and the text field
+ * (defaults to 3). Note that this only applies if {@link #buttonOnly} = false.
+ */
+ buttonOffset: 3,
+ /**
+ * @cfg {Object} buttonCfg A standard {@link Ext.Button} config object.
+ */
+
+ // private
+ readOnly: true,
+
+ /**
+ * @hide
+ * @method autoSize
+ */
+ autoSize: Ext.emptyFn,
+
+ // private
+ initComponent: function(){
+ Ext.ux.form.FileUploadField.superclass.initComponent.call(this);
+
+ this.addEvents(
+ /**
+ * @event fileselected
+ * Fires when the underlying file input field's value has changed from the user
+ * selecting a new file from the system file selection dialog.
+ * @param {Ext.ux.form.FileUploadField} this
+ * @param {String} value The file value returned by the underlying file input field
+ */
+ 'fileselected'
+ );
+ },
+
+ // private
+ onRender : function(ct, position){
+ Ext.ux.form.FileUploadField.superclass.onRender.call(this, ct, position);
+
+ this.wrap = this.el.wrap({cls:'x-form-field-wrap x-form-file-wrap'});
+ this.el.addClass('x-form-file-text');
+ this.el.dom.removeAttribute('name');
+
+ this.fileInput = this.wrap.createChild({
+ id: this.getFileInputId(),
+ name: this.name||this.getId(),
+ cls: 'x-form-file',
+ tag: 'input',
+ type: 'file',
+ size: 1
+ });
+
+ var btnCfg = Ext.applyIf(this.buttonCfg || {}, {
+ text: this.buttonText
+ });
+ this.button = new Ext.Button(Ext.apply(btnCfg, {
+ renderTo: this.wrap,
+ cls: 'x-form-file-btn' + (btnCfg.iconCls ? ' x-btn-icon' : '')
+ }));
+
+ if(this.buttonOnly){
+ this.el.hide();
+ this.wrap.setWidth(this.button.getEl().getWidth());
+ }
+
+ this.fileInput.on('change', function(){
+ var v = this.fileInput.dom.value;
+ this.setValue(v);
+ this.fireEvent('fileselected', this, v);
+ }, this);
+ },
+
+ // private
+ getFileInputId: function(){
+ return this.id + '-file';
+ },
+
+ // private
+ onResize : function(w, h){
+ Ext.ux.form.FileUploadField.superclass.onResize.call(this, w, h);
+
+ this.wrap.setWidth(w);
+
+ if(!this.buttonOnly){
+ var w = this.wrap.getWidth() - this.button.getEl().getWidth() - this.buttonOffset;
+ this.el.setWidth(w);
+ }
+ },
+
+ // private
+ onDestroy: function(){
+ Ext.ux.form.FileUploadField.superclass.onDestroy.call(this);
+ Ext.destroy(this.fileInput, this.button, this.wrap);
+ },
+
+
+ // private
+ preFocus : Ext.emptyFn,
+
+ // private
+ getResizeEl : function(){
+ return this.wrap;
+ },
+
+ // private
+ getPositionEl : function(){
+ return this.wrap;
+ },
+
+ // private
+ alignErrorIcon : function(){
+ this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
+ }
+
+});
+
+Ext.reg('fileuploadfield', Ext.ux.form.FileUploadField);
+
+// backwards compat
+Ext.form.FileUploadField = Ext.ux.form.FileUploadField;
+(function(){\r
+Ext.ns('Ext.a11y');\r
+\r
+Ext.a11y.Frame = Ext.extend(Object, {\r
+ initialized: false,\r
+ \r
+ constructor: function(size, color){\r
+ this.setSize(size || 1);\r
+ this.setColor(color || '15428B');\r
+ },\r
+ \r
+ init: function(){\r
+ if (!this.initialized) {\r
+ this.sides = [];\r
+ \r
+ var s, i;\r
+ \r
+ this.ct = Ext.DomHelper.append(document.body, {\r
+ cls: 'x-a11y-focusframe'\r
+ }, true);\r
+ \r
+ for (i = 0; i < 4; i++) {\r
+ s = Ext.DomHelper.append(this.ct, {\r
+ cls: 'x-a11y-focusframe-side',\r
+ style: 'background-color: #' + this.color\r
+ }, true);\r
+ s.visibilityMode = Ext.Element.DISPLAY;\r
+ this.sides.push(s);\r
+ }\r
+ \r
+ this.frameTask = new Ext.util.DelayedTask(function(el){\r
+ var newEl = Ext.get(el);\r
+ if (newEl != this.curEl) {\r
+ var w = newEl.getWidth();\r
+ var h = newEl.getHeight();\r
+ this.sides[0].show().setSize(w, this.size).anchorTo(el, 'tl', [0, -1]);\r
+ this.sides[2].show().setSize(w, this.size).anchorTo(el, 'bl', [0, -1]);\r
+ this.sides[1].show().setSize(this.size, h).anchorTo(el, 'tr', [-1, 0]);\r
+ this.sides[3].show().setSize(this.size, h).anchorTo(el, 'tl', [-1, 0]);\r
+ this.curEl = newEl;\r
+ }\r
+ }, this);\r
+ \r
+ this.unframeTask = new Ext.util.DelayedTask(function(){\r
+ if (this.initialized) {\r
+ this.sides[0].hide();\r
+ this.sides[1].hide();\r
+ this.sides[2].hide();\r
+ this.sides[3].hide();\r
+ this.curEl = null;\r
+ }\r
+ }, this);\r
+ this.initialized = true;\r
+ }\r
+ },\r
+ \r
+ frame: function(el){\r
+ this.init();\r
+ this.unframeTask.cancel();\r
+ this.frameTask.delay(2, false, false, [el]);\r
+ },\r
+ \r
+ unframe: function(){\r
+ this.init();\r
+ this.unframeTask.delay(2);\r
+ },\r
+ \r
+ setSize: function(size){\r
+ this.size = size;\r
+ },\r
+ \r
+ setColor: function(color){\r
+ this.color = color;\r
+ }\r
+});\r
+\r
+Ext.a11y.FocusFrame = new Ext.a11y.Frame(2, '15428B');\r
+Ext.a11y.RelayFrame = new Ext.a11y.Frame(1, '6B8CBF');\r
+\r
+Ext.a11y.Focusable = Ext.extend(Ext.util.Observable, {\r
+ constructor: function(el, relayTo, noFrame, frameEl){\r
+ Ext.a11y.Focusable.superclass.constructor.call(this);\r
+ \r
+ this.addEvents('focus', 'blur', 'left', 'right', 'up', 'down', 'esc', 'enter', 'space');\r
+ \r
+ if (el instanceof Ext.Component) {\r
+ this.el = el.el;\r
+ this.setComponent(el);\r
+ }\r
+ else {\r
+ this.el = Ext.get(el);\r
+ this.setComponent(null);\r
+ }\r
+ \r
+ this.setRelayTo(relayTo)\r
+ this.setNoFrame(noFrame);\r
+ this.setFrameEl(frameEl);\r
+ \r
+ this.init();\r
+ \r
+ Ext.a11y.FocusMgr.register(this);\r
+ },\r
+ \r
+ init: function(){\r
+ this.el.dom.tabIndex = '1';\r
+ this.el.addClass('x-a11y-focusable');\r
+ this.el.on({\r
+ focus: this.onFocus,\r
+ blur: this.onBlur,\r
+ keydown: this.onKeyDown,\r
+ scope: this\r
+ });\r
+ },\r
+ \r
+ setRelayTo: function(relayTo){\r
+ this.relayTo = relayTo ? Ext.a11y.FocusMgr.get(relayTo) : null;\r
+ },\r
+ \r
+ setNoFrame: function(noFrame){\r
+ this.noFrame = (noFrame === true) ? true : false;\r
+ },\r
+ \r
+ setFrameEl: function(frameEl){\r
+ this.frameEl = frameEl && Ext.get(frameEl) || this.el;\r
+ },\r
+ \r
+ setComponent: function(cmp){\r
+ this.component = cmp || null;\r
+ },\r
+ \r
+ onKeyDown: function(e, t){\r
+ var k = e.getKey(), SK = Ext.a11y.Focusable.SpecialKeys, ret, tf;\r
+ \r
+ tf = (t !== this.el.dom) ? Ext.a11y.FocusMgr.get(t, true) : this;\r
+ if (!tf) {\r
+ // this can happen when you are on a focused item within a panel body\r
+ // that is not a Ext.a11y.Focusable\r
+ tf = Ext.a11y.FocusMgr.get(Ext.fly(t).parent('.x-a11y-focusable'));\r
+ }\r
+ \r
+ if (SK[k] !== undefined) {\r
+ ret = this.fireEvent(SK[k], e, t, tf, this);\r
+ }\r
+ if (ret === false || this.fireEvent('keydown', e, t, tf, this) === false) {\r
+ e.stopEvent();\r
+ }\r
+ },\r
+ \r
+ focus: function(){\r
+ this.el.dom.focus();\r
+ },\r
+ \r
+ blur: function(){\r
+ this.el.dom.blur();\r
+ },\r
+ \r
+ onFocus: function(e, t){\r
+ this.el.addClass('x-a11y-focused');\r
+ if (this.relayTo) {\r
+ this.relayTo.el.addClass('x-a11y-focused-relay');\r
+ if (!this.relayTo.noFrame) {\r
+ Ext.a11y.FocusFrame.frame(this.relayTo.frameEl);\r
+ }\r
+ if (!this.noFrame) {\r
+ Ext.a11y.RelayFrame.frame(this.frameEl);\r
+ }\r
+ }\r
+ else {\r
+ if (!this.noFrame) {\r
+ Ext.a11y.FocusFrame.frame(this.frameEl);\r
+ }\r
+ }\r
+ \r
+ this.fireEvent('focus', e, t, this);\r
+ },\r
+ \r
+ onBlur: function(e, t){\r
+ if (this.relayTo) {\r
+ this.relayTo.el.removeClass('x-a11y-focused-relay');\r
+ Ext.a11y.RelayFrame.unframe();\r
+ }\r
+ this.el.removeClass('x-a11y-focused');\r
+ Ext.a11y.FocusFrame.unframe();\r
+ this.fireEvent('blur', e, t, this);\r
+ },\r
+ \r
+ destroy: function(){\r
+ this.el.un('keydown', this.onKeyDown);\r
+ this.el.un('focus', this.onFocus);\r
+ this.el.un('blur', this.onBlur);\r
+ this.el.removeClass('x-a11y-focusable');\r
+ this.el.removeClass('x-a11y-focused');\r
+ if (this.relayTo) {\r
+ this.relayTo.el.removeClass('x-a11y-focused-relay');\r
+ }\r
+ }\r
+});\r
+\r
+Ext.a11y.FocusItem = Ext.extend(Object, {\r
+ constructor: function(el, enableTabbing){\r
+ Ext.a11y.FocusItem.superclass.constructor.call(this);\r
+ \r
+ this.el = Ext.get(el);\r
+ this.fi = new Ext.a11y.Focusable(el);\r
+ this.fi.setComponent(this);\r
+ \r
+ this.fi.on('tab', this.onTab, this);\r
+ \r
+ this.enableTabbing = enableTabbing === true ? true : false;\r
+ },\r
+ \r
+ getEnterItem: function(){\r
+ if (this.enableTabbing) {\r
+ var items = this.getFocusItems();\r
+ if (items && items.length) {\r
+ return items[0];\r
+ }\r
+ }\r
+ },\r
+ \r
+ getFocusItems: function(){\r
+ if (this.enableTabbing) {\r
+ return this.el.query('a, button, input, select');\r
+ }\r
+ return null;\r
+ },\r
+ \r
+ onTab: function(e, t){\r
+ var items = this.getFocusItems(), i;\r
+ \r
+ if (items && items.length && (i = items.indexOf(t)) !== -1) {\r
+ if (e.shiftKey && i > 0) {\r
+ e.stopEvent();\r
+ items[i - 1].focus();\r
+ Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);\r
+ return;\r
+ }\r
+ else \r
+ if (!e.shiftKey && i < items.length - 1) {\r
+ e.stopEvent();\r
+ items[i + 1].focus();\r
+ Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);\r
+ return;\r
+ }\r
+ }\r
+ },\r
+ \r
+ focus: function(){\r
+ if (this.enableTabbing) {\r
+ var items = this.getFocusItems();\r
+ if (items && items.length) {\r
+ items[0].focus();\r
+ Ext.a11y.FocusFrame.frame.defer(20, Ext.a11y.FocusFrame, [this.el]);\r
+ return;\r
+ }\r
+ }\r
+ this.fi.focus();\r
+ },\r
+ \r
+ blur: function(){\r
+ this.fi.blur();\r
+ }\r
+});\r
+\r
+Ext.a11y.FocusMgr = function(){\r
+ var all = new Ext.util.MixedCollection();\r
+ \r
+ return {\r
+ register: function(f){\r
+ all.add(f.el && Ext.id(f.el), f);\r
+ },\r
+ \r
+ unregister: function(f){\r
+ all.remove(f);\r
+ },\r
+ \r
+ get: function(el, noCreate){\r
+ return all.get(Ext.id(el)) || (noCreate ? false : new Ext.a11y.Focusable(el));\r
+ },\r
+ \r
+ all: all\r
+ }\r
+}();\r
+\r
+Ext.a11y.Focusable.SpecialKeys = {};\r
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.LEFT] = 'left';\r
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.RIGHT] = 'right';\r
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.DOWN] = 'down';\r
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.UP] = 'up';\r
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ESC] = 'esc';\r
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.ENTER] = 'enter';\r
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.SPACE] = 'space';\r
+Ext.a11y.Focusable.SpecialKeys[Ext.EventObjectImpl.prototype.TAB] = 'tab';\r
+\r
+// we use the new observeClass method to fire our new initFocus method on components\r
+Ext.util.Observable.observeClass(Ext.Component);\r
+Ext.Component.on('render', function(cmp){\r
+ cmp.initFocus();\r
+ cmp.initARIA();\r
+});\r
+Ext.override(Ext.Component, {\r
+ initFocus: Ext.emptyFn,\r
+ initARIA: Ext.emptyFn\r
+});\r
+\r
+Ext.override(Ext.Container, {\r
+ isFocusable: true,\r
+ noFocus: false,\r
+ \r
+ // private\r
+ initFocus: function(){\r
+ if (!this.fi && !this.noFocus) {\r
+ this.fi = new Ext.a11y.Focusable(this);\r
+ }\r
+ this.mon(this.fi, {\r
+ focus: this.onFocus,\r
+ blur: this.onBlur,\r
+ tab: this.onTab,\r
+ enter: this.onEnter,\r
+ esc: this.onEsc,\r
+ scope: this\r
+ });\r
+ \r
+ if (this.hidden) {\r
+ this.isFocusable = false;\r
+ }\r
+ \r
+ this.on('show', function(){\r
+ this.isFocusable = true;\r
+ }, this);\r
+ this.on('hide', function(){\r
+ this.isFocusable = false;\r
+ }, this);\r
+ },\r
+ \r
+ focus: function(){\r
+ this.fi.focus();\r
+ },\r
+ \r
+ blur: function(){\r
+ this.fi.blur();\r
+ },\r
+ \r
+ enter: function(){\r
+ var eitem = this.getEnterItem();\r
+ if (eitem) {\r
+ eitem.focus();\r
+ }\r
+ },\r
+ \r
+ onFocus: Ext.emptyFn,\r
+ onBlur: Ext.emptyFn,\r
+ \r
+ onTab: function(e, t, tf){\r
+ var rf = tf.relayTo || tf;\r
+ if (rf.component && rf.component !== this) {\r
+ e.stopEvent();\r
+ var item = e.shiftKey ? this.getPreviousFocus(rf.component) : this.getNextFocus(rf.component);\r
+ item.focus();\r
+ }\r
+ },\r
+ \r
+ onEnter: function(e, t, tf){\r
+ // check to see if enter is pressed while "on" the panel\r
+ if (tf.component && tf.component === this) {\r
+ e.stopEvent();\r
+ this.enter();\r
+ }\r
+ e.stopPropagation();\r
+ },\r
+ \r
+ onEsc: function(e, t){\r
+ e.preventDefault();\r
+ \r
+ // check to see if esc is pressed while "inside" the panel\r
+ // or while "on" the panel\r
+ if (t === this.el.dom) {\r
+ // "on" the panel, check if this panel has an owner panel and focus that\r
+ // we dont stop the event in this case so that this same check will be\r
+ // done for this ownerCt\r
+ if (this.ownerCt) {\r
+ this.ownerCt.focus();\r
+ }\r
+ }\r
+ else {\r
+ // we were inside the panel when esc was pressed,\r
+ // so go back "on" the panel\r
+ if (this.ownerCt && this.ownerCt.isFocusable) {\r
+ var si = this.ownerCt.getFocusItems();\r
+ \r
+ if (si && si.getCount() > 1) {\r
+ e.stopEvent();\r
+ }\r
+ }\r
+ this.focus();\r
+ }\r
+ },\r
+ \r
+ getFocusItems: function(){\r
+ return this.items &&\r
+ this.items.filterBy(function(o){\r
+ return o.isFocusable;\r
+ }) ||\r
+ null;\r
+ },\r
+ \r
+ getEnterItem: function(){\r
+ var ci = this.getFocusItems(), length = ci ? ci.getCount() : 0;\r
+ \r
+ if (length === 1) {\r
+ return ci.first().getEnterItem && ci.first().getEnterItem() || ci.first();\r
+ }\r
+ else \r
+ if (length > 1) {\r
+ return ci.first();\r
+ }\r
+ },\r
+ \r
+ getNextFocus: function(current){\r
+ var items = this.getFocusItems(), next = current, i = items.indexOf(current), length = items.getCount();\r
+ \r
+ if (i === length - 1) {\r
+ next = items.first();\r
+ }\r
+ else {\r
+ next = items.get(i + 1);\r
+ }\r
+ return next;\r
+ },\r
+ \r
+ getPreviousFocus: function(current){\r
+ var items = this.getFocusItems(), prev = current, i = items.indexOf(current), length = items.getCount();\r
+ \r
+ if (i === 0) {\r
+ prev = items.last();\r
+ }\r
+ else {\r
+ prev = items.get(i - 1);\r
+ }\r
+ return prev;\r
+ },\r
+ \r
+ getFocusable : function() {\r
+ return this.fi;\r
+ }\r
+});\r
+\r
+Ext.override(Ext.Panel, {\r
+ /**\r
+ * @cfg {Boolean} enableTabbing <tt>true</tt> to enable tabbing. Default is <tt>false</tt>.\r
+ */ \r
+ getFocusItems: function(){\r
+ // items gets all the items inside the body\r
+ var items = Ext.Panel.superclass.getFocusItems.call(this), bodyFocus = null;\r
+ \r
+ if (!items) {\r
+ items = new Ext.util.MixedCollection();\r
+ this.bodyFocus = this.bodyFocus || new Ext.a11y.FocusItem(this.body, this.enableTabbing);\r
+ items.add('body', this.bodyFocus);\r
+ }\r
+ // but panels can also have tbar, bbar, fbar\r
+ if (this.tbar && this.topToolbar) {\r
+ items.insert(0, this.topToolbar);\r
+ }\r
+ if (this.bbar && this.bottomToolbar) {\r
+ items.add(this.bottomToolbar);\r
+ }\r
+ if (this.fbar) {\r
+ items.add(this.fbar);\r
+ }\r
+ \r
+ return items;\r
+ }\r
+});\r
+\r
+Ext.override(Ext.TabPanel, {\r
+ // private\r
+ initFocus: function(){\r
+ Ext.TabPanel.superclass.initFocus.call(this);\r
+ this.mon(this.fi, {\r
+ left: this.onLeft,\r
+ right: this.onRight,\r
+ scope: this\r
+ });\r
+ },\r
+ \r
+ onLeft: function(e){\r
+ if (!this.activeTab) {\r
+ return;\r
+ }\r
+ e.stopEvent();\r
+ var prev = this.items.itemAt(this.items.indexOf(this.activeTab) - 1);\r
+ if (prev) {\r
+ this.setActiveTab(prev);\r
+ }\r
+ return false;\r
+ },\r
+ \r
+ onRight: function(e){\r
+ if (!this.activeTab) {\r
+ return;\r
+ }\r
+ e.stopEvent();\r
+ var next = this.items.itemAt(this.items.indexOf(this.activeTab) + 1);\r
+ if (next) {\r
+ this.setActiveTab(next);\r
+ }\r
+ return false;\r
+ }\r
+});\r
+\r
+Ext.override(Ext.tree.TreeNodeUI, {\r
+ // private\r
+ focus: function(){\r
+ this.node.getOwnerTree().bodyFocus.focus();\r
+ }\r
+});\r
+\r
+Ext.override(Ext.tree.TreePanel, {\r
+ // private\r
+ afterRender : function(){\r
+ Ext.tree.TreePanel.superclass.afterRender.call(this);\r
+ this.root.render();\r
+ if(!this.rootVisible){\r
+ this.root.renderChildren();\r
+ }\r
+ this.bodyFocus = new Ext.a11y.FocusItem(this.body.down('.x-tree-root-ct'));\r
+ this.bodyFocus.fi.setFrameEl(this.body);\r
+ } \r
+});\r
+\r
+Ext.override(Ext.grid.GridPanel, {\r
+ initFocus: function(){\r
+ Ext.grid.GridPanel.superclass.initFocus.call(this);\r
+ this.bodyFocus = new Ext.a11y.FocusItem(this.view.focusEl);\r
+ this.bodyFocus.fi.setFrameEl(this.body);\r
+ }\r
+});\r
+\r
+Ext.override(Ext.Button, {\r
+ isFocusable: true,\r
+ noFocus: false,\r
+ \r
+ initFocus: function(){\r
+ Ext.Button.superclass.initFocus.call(this);\r
+ this.fi = this.fi || new Ext.a11y.Focusable(this.btnEl, null, null, this.el);\r
+ this.fi.setComponent(this);\r
+ \r
+ this.mon(this.fi, {\r
+ focus: this.onFocus,\r
+ blur: this.onBlur,\r
+ scope: this\r
+ });\r
+ \r
+ if (this.menu) {\r
+ this.mon(this.fi, 'down', this.showMenu, this);\r
+ this.on('menuhide', this.focus, this);\r
+ }\r
+ \r
+ if (this.hidden) {\r
+ this.isFocusable = false;\r
+ }\r
+ \r
+ this.on('show', function(){\r
+ this.isFocusable = true;\r
+ }, this);\r
+ this.on('hide', function(){\r
+ this.isFocusable = false;\r
+ }, this);\r
+ },\r
+ \r
+ focus: function(){\r
+ this.fi.focus();\r
+ },\r
+ \r
+ blur: function(){\r
+ this.fi.blur();\r
+ },\r
+ \r
+ onFocus: function(){\r
+ if (!this.disabled) {\r
+ this.el.addClass("x-btn-focus");\r
+ }\r
+ },\r
+ \r
+ onBlur: function(){\r
+ this.el.removeClass("x-btn-focus");\r
+ }\r
+});\r
+\r
+Ext.override(Ext.Toolbar, {\r
+ initFocus: function(){\r
+ Ext.Toolbar.superclass.initFocus.call(this);\r
+ this.mon(this.fi, {\r
+ left: this.onLeft,\r
+ right: this.onRight,\r
+ scope: this\r
+ });\r
+ \r
+ this.on('focus', this.onButtonFocus, this, {\r
+ stopEvent: true\r
+ });\r
+ },\r
+ \r
+ addItem: function(item){\r
+ Ext.Toolbar.superclass.add.apply(this, arguments);\r
+ if (item.rendered && item.fi !== undefined) {\r
+ item.fi.setRelayTo(this.el);\r
+ this.relayEvents(item.fi, ['focus']);\r
+ }\r
+ else {\r
+ item.on('render', function(){\r
+ if (item.fi !== undefined) {\r
+ item.fi.setRelayTo(this.el);\r
+ this.relayEvents(item.fi, ['focus']);\r
+ }\r
+ }, this, {\r
+ single: true\r
+ });\r
+ }\r
+ return item;\r
+ },\r
+ \r
+ onFocus: function(){\r
+ var items = this.getFocusItems();\r
+ if (items && items.getCount() > 0) {\r
+ if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {\r
+ this.lastFocus.focus();\r
+ }\r
+ else {\r
+ items.first().focus();\r
+ }\r
+ }\r
+ },\r
+ \r
+ onButtonFocus: function(e, t, tf){\r
+ this.lastFocus = tf.component || null;\r
+ },\r
+ \r
+ onLeft: function(e, t, tf){\r
+ e.stopEvent();\r
+ this.getPreviousFocus(tf.component).focus();\r
+ },\r
+ \r
+ onRight: function(e, t, tf){\r
+ e.stopEvent();\r
+ this.getNextFocus(tf.component).focus();\r
+ },\r
+ \r
+ getEnterItem: Ext.emptyFn,\r
+ onTab: Ext.emptyFn,\r
+ onEsc: Ext.emptyFn\r
+});\r
+\r
+Ext.override(Ext.menu.BaseItem, {\r
+ initFocus: function(){\r
+ this.fi = new Ext.a11y.Focusable(this, this.parentMenu && this.parentMenu.el || null, true);\r
+ }\r
+});\r
+\r
+Ext.override(Ext.menu.Menu, {\r
+ initFocus: function(){\r
+ this.fi = new Ext.a11y.Focusable(this);\r
+ this.focusEl = this.fi;\r
+ }\r
+});\r
+\r
+Ext.a11y.WindowMgr = new Ext.WindowGroup();\r
+\r
+Ext.apply(Ext.WindowMgr, {\r
+ bringToFront: function(win){\r
+ Ext.a11y.WindowMgr.bringToFront.call(this, win);\r
+ if (win.modal) {\r
+ win.enter();\r
+ }\r
+ else {\r
+ win.focus();\r
+ }\r
+ }\r
+});\r
+\r
+Ext.override(Ext.Window, {\r
+ initFocus: function(){\r
+ Ext.Window.superclass.initFocus.call(this);\r
+ this.on('beforehide', function(){\r
+ Ext.a11y.RelayFrame.unframe();\r
+ Ext.a11y.FocusFrame.unframe();\r
+ });\r
+ }\r
+});\r
+\r
+Ext.override(Ext.form.Field, {\r
+ isFocusable: true,\r
+ noFocus: false,\r
+ \r
+ initFocus: function(){\r
+ this.fi = this.fi || new Ext.a11y.Focusable(this, null, true);\r
+ \r
+ Ext.form.Field.superclass.initFocus.call(this);\r
+ \r
+ if (this.hidden) {\r
+ this.isFocusable = false;\r
+ }\r
+ \r
+ this.on('show', function(){\r
+ this.isFocusable = true;\r
+ }, this);\r
+ this.on('hide', function(){\r
+ this.isFocusable = false;\r
+ }, this);\r
+ }\r
+});\r
+\r
+Ext.override(Ext.FormPanel, {\r
+ initFocus: function(){\r
+ Ext.FormPanel.superclass.initFocus.call(this);\r
+ this.on('focus', this.onFieldFocus, this, {\r
+ stopEvent: true\r
+ });\r
+ },\r
+ \r
+ // private\r
+ createForm: function(){\r
+ delete this.initialConfig.listeners;\r
+ var form = new Ext.form.BasicForm(null, this.initialConfig);\r
+ form.afterMethod('add', this.formItemAdd, this);\r
+ return form;\r
+ },\r
+ \r
+ formItemAdd: function(item){\r
+ item.on('render', function(field){\r
+ field.fi.setRelayTo(this.el);\r
+ this.relayEvents(field.fi, ['focus']);\r
+ }, this, {\r
+ single: true\r
+ });\r
+ },\r
+ \r
+ onFocus: function(){\r
+ var items = this.getFocusItems();\r
+ if (items && items.getCount() > 0) {\r
+ if (this.lastFocus && items.indexOf(this.lastFocus) !== -1) {\r
+ this.lastFocus.focus();\r
+ }\r
+ else {\r
+ items.first().focus();\r
+ }\r
+ }\r
+ },\r
+ \r
+ onFieldFocus: function(e, t, tf){\r
+ this.lastFocus = tf.component || null;\r
+ },\r
+ \r
+ onTab: function(e, t, tf){\r
+ if (tf.relayTo.component === this) {\r
+ var item = e.shiftKey ? this.getPreviousFocus(tf.component) : this.getNextFocus(tf.component);\r
+ \r
+ if (item) {\r
+ ev.stopEvent();\r
+ item.focus();\r
+ return;\r
+ }\r
+ }\r
+ Ext.FormPanel.superclass.onTab.apply(this, arguments);\r
+ },\r
+ \r
+ getNextFocus: function(current){\r
+ var items = this.getFocusItems(), i = items.indexOf(current), length = items.getCount();\r
+ \r
+ return (i < length - 1) ? items.get(i + 1) : false;\r
+ },\r
+ \r
+ getPreviousFocus: function(current){\r
+ var items = this.getFocusItems(), i = items.indexOf(current), length = items.getCount();\r
+ \r
+ return (i > 0) ? items.get(i - 1) : false;\r
+ }\r
+});\r
+\r
+Ext.override(Ext.Viewport, {\r
+ initFocus: function(){\r
+ Ext.Viewport.superclass.initFocus.apply(this);\r
+ this.mon(Ext.get(document), 'focus', this.focus, this);\r
+ this.mon(Ext.get(document), 'blur', this.blur, this);\r
+ this.fi.setNoFrame(true);\r
+ },\r
+ \r
+ onTab: function(e, t, tf, f){\r
+ e.stopEvent();\r
+ \r
+ if (tf === f) {\r
+ items = this.getFocusItems();\r
+ if (items && items.getCount() > 0) {\r
+ items.first().focus();\r
+ }\r
+ }\r
+ else {\r
+ var rf = tf.relayTo || tf;\r
+ var item = e.shiftKey ? this.getPreviousFocus(rf.component) : this.getNextFocus(rf.component);\r
+ item.focus();\r
+ }\r
+ }\r
+});\r
+ \r
+})();/**\r
+ * @class Ext.ux.GMapPanel\r
+ * @extends Ext.Panel\r
+ * @author Shea Frederick\r
+ */\r
+Ext.ux.GMapPanel = Ext.extend(Ext.Panel, {\r
+ initComponent : function(){\r
+ \r
+ var defConfig = {\r
+ plain: true,\r
+ zoomLevel: 3,\r
+ yaw: 180,\r
+ pitch: 0,\r
+ zoom: 0,\r
+ gmapType: 'map',\r
+ border: false\r
+ };\r
+ \r
+ Ext.applyIf(this,defConfig);\r
+ \r
+ Ext.ux.GMapPanel.superclass.initComponent.call(this); \r
+\r
+ },\r
+ afterRender : function(){\r
+ \r
+ var wh = this.ownerCt.getSize();\r
+ Ext.applyIf(this, wh);\r
+ \r
+ Ext.ux.GMapPanel.superclass.afterRender.call(this); \r
+ \r
+ if (this.gmapType === 'map'){\r
+ this.gmap = new GMap2(this.body.dom);\r
+ }\r
+ \r
+ if (this.gmapType === 'panorama'){\r
+ this.gmap = new GStreetviewPanorama(this.body.dom);\r
+ }\r
+ \r
+ if (typeof this.addControl == 'object' && this.gmapType === 'map') {\r
+ this.gmap.addControl(this.addControl);\r
+ }\r
+ \r
+ if (typeof this.setCenter === 'object') {\r
+ if (typeof this.setCenter.geoCodeAddr === 'string'){\r
+ this.geoCodeLookup(this.setCenter.geoCodeAddr);\r
+ }else{\r
+ if (this.gmapType === 'map'){\r
+ var point = new GLatLng(this.setCenter.lat,this.setCenter.lng);\r
+ this.gmap.setCenter(point, this.zoomLevel); \r
+ }\r
+ if (typeof this.setCenter.marker === 'object' && typeof point === 'object'){\r
+ this.addMarker(point,this.setCenter.marker,this.setCenter.marker.clear);\r
+ }\r
+ }\r
+ if (this.gmapType === 'panorama'){\r
+ this.gmap.setLocationAndPOV(new GLatLng(this.setCenter.lat,this.setCenter.lng), {yaw: this.yaw, pitch: this.pitch, zoom: this.zoom});\r
+ }\r
+ }\r
+\r
+ GEvent.bind(this.gmap, 'load', this, function(){\r
+ this.onMapReady();\r
+ });\r
+\r
+ },\r
+ onMapReady : function(){\r
+ this.addMarkers(this.markers);\r
+ this.addMapControls();\r
+ this.addOptions(); \r
+ },\r
+ onResize : function(w, h){\r
+\r
+ if (typeof this.getMap() == 'object') {\r
+ this.gmap.checkResize();\r
+ }\r
+ \r
+ Ext.ux.GMapPanel.superclass.onResize.call(this, w, h);\r
+\r
+ },\r
+ setSize : function(width, height, animate){\r
+ \r
+ if (typeof this.getMap() == 'object') {\r
+ this.gmap.checkResize();\r
+ }\r
+ \r
+ Ext.ux.GMapPanel.superclass.setSize.call(this, width, height, animate);\r
+ \r
+ },\r
+ getMap : function(){\r
+ \r
+ return this.gmap;\r
+ \r
+ },\r
+ getCenter : function(){\r
+ \r
+ return this.getMap().getCenter();\r
+ \r
+ },\r
+ getCenterLatLng : function(){\r
+ \r
+ var ll = this.getCenter();\r
+ return {lat: ll.lat(), lng: ll.lng()};\r
+ \r
+ },\r
+ addMarkers : function(markers) {\r
+ \r
+ if (Ext.isArray(markers)){\r
+ for (var i = 0; i < markers.length; i++) {\r
+ var mkr_point = new GLatLng(markers[i].lat,markers[i].lng);\r
+ this.addMarker(mkr_point,markers[i].marker,false,markers[i].setCenter, markers[i].listeners);\r
+ }\r
+ }\r
+ \r
+ },\r
+ addMarker : function(point, marker, clear, center, listeners){\r
+ \r
+ Ext.applyIf(marker,G_DEFAULT_ICON);\r
+\r
+ if (clear === true){\r
+ this.getMap().clearOverlays();\r
+ }\r
+ if (center === true) {\r
+ this.getMap().setCenter(point, this.zoomLevel);\r
+ }\r
+\r
+ var mark = new GMarker(point,marker);\r
+ if (typeof listeners === 'object'){\r
+ for (evt in listeners) {\r
+ GEvent.bind(mark, evt, this, listeners[evt]);\r
+ }\r
+ }\r
+ this.getMap().addOverlay(mark);\r
+\r
+ },\r
+ addMapControls : function(){\r
+ \r
+ if (this.gmapType === 'map') {\r
+ if (Ext.isArray(this.mapControls)) {\r
+ for(i=0;i<this.mapControls.length;i++){\r
+ this.addMapControl(this.mapControls[i]);\r
+ }\r
+ }else if(typeof this.mapControls === 'string'){\r
+ this.addMapControl(this.mapControls);\r
+ }else if(typeof this.mapControls === 'object'){\r
+ this.getMap().addControl(this.mapControls);\r
+ }\r
+ }\r
+ \r
+ },\r
+ addMapControl : function(mc){\r
+ \r
+ var mcf = window[mc];\r
+ if (typeof mcf === 'function') {\r
+ this.getMap().addControl(new mcf());\r
+ } \r
+ \r
+ },\r
+ addOptions : function(){\r
+ \r
+ if (Ext.isArray(this.mapConfOpts)) {\r
+ var mc;\r
+ for(i=0;i<this.mapConfOpts.length;i++){\r
+ this.addOption(this.mapConfOpts[i]);\r
+ }\r
+ }else if(typeof this.mapConfOpts === 'string'){\r
+ this.addOption(this.mapConfOpts);\r
+ } \r
+ \r
+ },\r
+ addOption : function(mc){\r
+ \r
+ var mcf = this.getMap()[mc];\r
+ if (typeof mcf === 'function') {\r
+ this.getMap()[mc]();\r
+ } \r
+ \r
+ },\r
+ geoCodeLookup : function(addr) {\r
+ \r
+ this.geocoder = new GClientGeocoder();\r
+ this.geocoder.getLocations(addr, this.addAddressToMap.createDelegate(this));\r
+ \r
+ },\r
+ addAddressToMap : function(response) {\r
+ \r
+ if (!response || response.Status.code != 200) {\r
+ Ext.MessageBox.alert('Error', 'Code '+response.Status.code+' Error Returned');\r
+ }else{\r
+ place = response.Placemark[0];\r
+ addressinfo = place.AddressDetails;\r
+ accuracy = addressinfo.Accuracy;\r
+ if (accuracy === 0) {\r
+ Ext.MessageBox.alert('Unable to Locate Address', 'Unable to Locate the Address you provided');\r
+ }else{\r
+ if (accuracy < 7) {\r
+ Ext.MessageBox.alert('Address Accuracy', 'The address provided has a low accuracy.<br><br>Level '+accuracy+' Accuracy (8 = Exact Match, 1 = Vague Match)');\r
+ }else{\r
+ point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);\r
+ if (typeof this.setCenter.marker === 'object' && typeof point === 'object'){\r
+ this.addMarker(point,this.setCenter.marker,this.setCenter.marker.clear,true, this.setCenter.listeners);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ }\r
+ \r
+});\r
+\r
+Ext.reg('gmappanel', Ext.ux.GMapPanel); Ext.ns('Ext.ux.grid');\r
+\r
+/**\r
+ * @class Ext.ux.grid.GroupSummary\r
+ * @extends Ext.util.Observable\r
+ * A GridPanel plugin that enables dynamic column calculations and a dynamically\r
+ * updated grouped summary row.\r
+ */\r
+Ext.ux.grid.GroupSummary = Ext.extend(Ext.util.Observable, {\r
+ /**\r
+ * @cfg {Function} summaryRenderer Renderer example:<pre><code>\r
+summaryRenderer: function(v, params, data){\r
+ return ((v === 0 || v > 1) ? '(' + v +' Tasks)' : '(1 Task)');\r
+},\r
+ * </code></pre>\r
+ */\r
+ /**\r
+ * @cfg {String} summaryType (Optional) The type of\r
+ * calculation to be used for the column. For options available see\r
+ * {@link #Calculations}.\r
+ */\r
+\r
+ constructor : function(config){\r
+ Ext.apply(this, config);\r
+ Ext.ux.grid.GroupSummary.superclass.constructor.call(this);\r
+ },\r
+ init : function(grid){\r
+ this.grid = grid;\r
+ this.cm = grid.getColumnModel();\r
+ this.view = grid.getView();\r
+\r
+ var v = this.view;\r
+ v.doGroupEnd = this.doGroupEnd.createDelegate(this);\r
+\r
+ v.afterMethod('onColumnWidthUpdated', this.doWidth, this);\r
+ v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this);\r
+ v.afterMethod('onColumnHiddenUpdated', this.doHidden, this);\r
+ v.afterMethod('onUpdate', this.doUpdate, this);\r
+ v.afterMethod('onRemove', this.doRemove, this);\r
+\r
+ if(!this.rowTpl){\r
+ this.rowTpl = new Ext.Template(\r
+ '<div class="x-grid3-summary-row" style="{tstyle}">',\r
+ '<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',\r
+ '<tbody><tr>{cells}</tr></tbody>',\r
+ '</table></div>'\r
+ );\r
+ this.rowTpl.disableFormats = true;\r
+ }\r
+ this.rowTpl.compile();\r
+\r
+ if(!this.cellTpl){\r
+ this.cellTpl = new Ext.Template(\r
+ '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',\r
+ '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on">{value}</div>',\r
+ "</td>"\r
+ );\r
+ this.cellTpl.disableFormats = true;\r
+ }\r
+ this.cellTpl.compile();\r
+ },\r
+\r
+ /**\r
+ * Toggle the display of the summary row on/off\r
+ * @param {Boolean} visible <tt>true</tt> to show the summary, <tt>false</tt> to hide the summary.\r
+ */\r
+ toggleSummaries : function(visible){\r
+ var el = this.grid.getGridEl();\r
+ if(el){\r
+ if(visible === undefined){\r
+ visible = el.hasClass('x-grid-hide-summary');\r
+ }\r
+ el[visible ? 'removeClass' : 'addClass']('x-grid-hide-summary');\r
+ }\r
+ },\r
+\r
+ renderSummary : function(o, cs){\r
+ cs = cs || this.view.getColumnData();\r
+ var cfg = this.cm.config;\r
+\r
+ var buf = [], c, p = {}, cf, last = cs.length-1;\r
+ for(var i = 0, len = cs.length; i < len; i++){\r
+ c = cs[i];\r
+ cf = cfg[i];\r
+ p.id = c.id;\r
+ p.style = c.style;\r
+ p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');\r
+ if(cf.summaryType || cf.summaryRenderer){\r
+ p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o);\r
+ }else{\r
+ p.value = '';\r
+ }\r
+ if(p.value == undefined || p.value === "") p.value = " ";\r
+ buf[buf.length] = this.cellTpl.apply(p);\r
+ }\r
+\r
+ return this.rowTpl.apply({\r
+ tstyle: 'width:'+this.view.getTotalWidth()+';',\r
+ cells: buf.join('')\r
+ });\r
+ },\r
+\r
+ /**\r
+ * @private\r
+ * @param {Object} rs\r
+ * @param {Object} cs\r
+ */\r
+ calculate : function(rs, cs){\r
+ var data = {}, r, c, cfg = this.cm.config, cf;\r
+ for(var j = 0, jlen = rs.length; j < jlen; j++){\r
+ r = rs[j];\r
+ for(var i = 0, len = cs.length; i < len; i++){\r
+ c = cs[i];\r
+ cf = cfg[i];\r
+ if(cf.summaryType){\r
+ data[c.name] = Ext.ux.grid.GroupSummary.Calculations[cf.summaryType](data[c.name] || 0, r, c.name, data);\r
+ }\r
+ }\r
+ }\r
+ return data;\r
+ },\r
+\r
+ doGroupEnd : function(buf, g, cs, ds, colCount){\r
+ var data = this.calculate(g.rs, cs);\r
+ buf.push('</div>', this.renderSummary({data: data}, cs), '</div>');\r
+ },\r
+\r
+ doWidth : function(col, w, tw){\r
+ var gs = this.view.getGroups(), s;\r
+ for(var i = 0, len = gs.length; i < len; i++){\r
+ s = gs[i].childNodes[2];\r
+ s.style.width = tw;\r
+ s.firstChild.style.width = tw;\r
+ s.firstChild.rows[0].childNodes[col].style.width = w;\r
+ }\r
+ },\r
+\r
+ doAllWidths : function(ws, tw){\r
+ var gs = this.view.getGroups(), s, cells, wlen = ws.length;\r
+ for(var i = 0, len = gs.length; i < len; i++){\r
+ s = gs[i].childNodes[2];\r
+ s.style.width = tw;\r
+ s.firstChild.style.width = tw;\r
+ cells = s.firstChild.rows[0].childNodes;\r
+ for(var j = 0; j < wlen; j++){\r
+ cells[j].style.width = ws[j];\r
+ }\r
+ }\r
+ },\r
+\r
+ doHidden : function(col, hidden, tw){\r
+ var gs = this.view.getGroups(), s, display = hidden ? 'none' : '';\r
+ for(var i = 0, len = gs.length; i < len; i++){\r
+ s = gs[i].childNodes[2];\r
+ s.style.width = tw;\r
+ s.firstChild.style.width = tw;\r
+ s.firstChild.rows[0].childNodes[col].style.display = display;\r
+ }\r
+ },\r
+\r
+ // Note: requires that all (or the first) record in the\r
+ // group share the same group value. Returns false if the group\r
+ // could not be found.\r
+ refreshSummary : function(groupValue){\r
+ return this.refreshSummaryById(this.view.getGroupId(groupValue));\r
+ },\r
+\r
+ getSummaryNode : function(gid){\r
+ var g = Ext.fly(gid, '_gsummary');\r
+ if(g){\r
+ return g.down('.x-grid3-summary-row', true);\r
+ }\r
+ return null;\r
+ },\r
+\r
+ refreshSummaryById : function(gid){\r
+ var g = document.getElementById(gid);\r
+ if(!g){\r
+ return false;\r
+ }\r
+ var rs = [];\r
+ this.grid.store.each(function(r){\r
+ if(r._groupId == gid){\r
+ rs[rs.length] = r;\r
+ }\r
+ });\r
+ var cs = this.view.getColumnData();\r
+ var data = this.calculate(rs, cs);\r
+ var markup = this.renderSummary({data: data}, cs);\r
+\r
+ var existing = this.getSummaryNode(gid);\r
+ if(existing){\r
+ g.removeChild(existing);\r
+ }\r
+ Ext.DomHelper.append(g, markup);\r
+ return true;\r
+ },\r
+\r
+ doUpdate : function(ds, record){\r
+ this.refreshSummaryById(record._groupId);\r
+ },\r
+\r
+ doRemove : function(ds, record, index, isUpdate){\r
+ if(!isUpdate){\r
+ this.refreshSummaryById(record._groupId);\r
+ }\r
+ },\r
+\r
+ /**\r
+ * Show a message in the summary row.\r
+ * <pre><code>\r
+grid.on('afteredit', function(){\r
+ var groupValue = 'Ext Forms: Field Anchoring';\r
+ summary.showSummaryMsg(groupValue, 'Updating Summary...');\r
+});\r
+ * </code></pre>\r
+ * @param {String} groupValue\r
+ * @param {String} msg Text to use as innerHTML for the summary row.\r
+ */\r
+ showSummaryMsg : function(groupValue, msg){\r
+ var gid = this.view.getGroupId(groupValue);\r
+ var node = this.getSummaryNode(gid);\r
+ if(node){\r
+ node.innerHTML = '<div class="x-grid3-summary-msg">' + msg + '</div>';\r
+ }\r
+ }\r
+});\r
+\r
+//backwards compat\r
+Ext.grid.GroupSummary = Ext.ux.grid.GroupSummary;\r
+\r
+\r
+/**\r
+ * Calculation types for summary row:</p><div class="mdetail-params"><ul>\r
+ * <li><b><tt>sum</tt></b> : <div class="sub-desc"></div></li>\r
+ * <li><b><tt>count</tt></b> : <div class="sub-desc"></div></li>\r
+ * <li><b><tt>max</tt></b> : <div class="sub-desc"></div></li>\r
+ * <li><b><tt>min</tt></b> : <div class="sub-desc"></div></li>\r
+ * <li><b><tt>average</tt></b> : <div class="sub-desc"></div></li>\r
+ * </ul></div>\r
+ * <p>Custom calculations may be implemented. An example of\r
+ * custom <code>summaryType=totalCost</code>:</p><pre><code>\r
+// define a custom summary function\r
+Ext.ux.grid.GroupSummary.Calculations['totalCost'] = function(v, record, field){\r
+ return v + (record.data.estimate * record.data.rate);\r
+};\r
+ * </code></pre>\r
+ * @property Calculations\r
+ */\r
+\r
+Ext.ux.grid.GroupSummary.Calculations = {\r
+ 'sum' : function(v, record, field){\r
+ return v + (record.data[field]||0);\r
+ },\r
+\r
+ 'count' : function(v, record, field, data){\r
+ return data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);\r
+ },\r
+\r
+ 'max' : function(v, record, field, data){\r
+ var v = record.data[field];\r
+ var max = data[field+'max'] === undefined ? (data[field+'max'] = v) : data[field+'max'];\r
+ return v > max ? (data[field+'max'] = v) : max;\r
+ },\r
+\r
+ 'min' : function(v, record, field, data){\r
+ var v = record.data[field];\r
+ var min = data[field+'min'] === undefined ? (data[field+'min'] = v) : data[field+'min'];\r
+ return v < min ? (data[field+'min'] = v) : min;\r
+ },\r
+\r
+ 'average' : function(v, record, field, data){\r
+ var c = data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);\r
+ var t = (data[field+'total'] = ((data[field+'total']||0) + (record.data[field]||0)));\r
+ return t === 0 ? 0 : t / c;\r
+ }\r
+};\r
+Ext.grid.GroupSummary.Calculations = Ext.ux.grid.GroupSummary.Calculations;\r
+\r
+/**\r
+ * @class Ext.ux.grid.HybridSummary\r
+ * @extends Ext.ux.grid.GroupSummary\r
+ * Adds capability to specify the summary data for the group via json as illustrated here:\r
+ * <pre><code>\r
+{\r
+ data: [\r
+ {\r
+ projectId: 100, project: 'House',\r
+ taskId: 112, description: 'Paint',\r
+ estimate: 6, rate: 150,\r
+ due:'06/24/2007'\r
+ },\r
+ ...\r
+ ],\r
+\r
+ summaryData: {\r
+ 'House': {\r
+ description: 14, estimate: 9,\r
+ rate: 99, due: new Date(2009, 6, 29),\r
+ cost: 999\r
+ }\r
+ }\r
+}\r
+ * </code></pre>\r
+ *\r
+ */\r
+Ext.ux.grid.HybridSummary = Ext.extend(Ext.ux.grid.GroupSummary, {\r
+ /**\r
+ * @private\r
+ * @param {Object} rs\r
+ * @param {Object} cs\r
+ */\r
+ calculate : function(rs, cs){\r
+ var gcol = this.view.getGroupField();\r
+ var gvalue = rs[0].data[gcol];\r
+ var gdata = this.getSummaryData(gvalue);\r
+ return gdata || Ext.ux.grid.HybridSummary.superclass.calculate.call(this, rs, cs);\r
+ },\r
+\r
+ /**\r
+ * <pre><code>\r
+grid.on('afteredit', function(){\r
+ var groupValue = 'Ext Forms: Field Anchoring';\r
+ summary.showSummaryMsg(groupValue, 'Updating Summary...');\r
+ setTimeout(function(){ // simulate server call\r
+ // HybridSummary class implements updateSummaryData\r
+ summary.updateSummaryData(groupValue,\r
+ // create data object based on configured dataIndex\r
+ {description: 22, estimate: 888, rate: 888, due: new Date(), cost: 8});\r
+ }, 2000);\r
+});\r
+ * </code></pre>\r
+ * @param {String} groupValue\r
+ * @param {Object} data data object\r
+ * @param {Boolean} skipRefresh (Optional) Defaults to false\r
+ */\r
+ updateSummaryData : function(groupValue, data, skipRefresh){\r
+ var json = this.grid.store.reader.jsonData;\r
+ if(!json.summaryData){\r
+ json.summaryData = {};\r
+ }\r
+ json.summaryData[groupValue] = data;\r
+ if(!skipRefresh){\r
+ this.refreshSummary(groupValue);\r
+ }\r
+ },\r
+\r
+ /**\r
+ * Returns the summaryData for the specified groupValue or null.\r
+ * @param {String} groupValue\r
+ * @return {Object} summaryData\r
+ */\r
+ getSummaryData : function(groupValue){\r
+ var json = this.grid.store.reader.jsonData;\r
+ if(json && json.summaryData){\r
+ return json.summaryData[groupValue];\r
+ }\r
+ return null;\r
+ }\r
+});\r
+\r
+//backwards compat\r
+Ext.grid.HybridSummary = Ext.ux.grid.HybridSummary;\r
+Ext.ux.GroupTab = Ext.extend(Ext.Container, {\r
+ mainItem: 0,\r
+ \r
+ expanded: true,\r
+ \r
+ deferredRender: true,\r
+ \r
+ activeTab: null,\r
+ \r
+ idDelimiter: '__',\r
+ \r
+ headerAsText: false,\r
+ \r
+ frame: false,\r
+ \r
+ hideBorders: true,\r
+ \r
+ initComponent: function(config){\r
+ Ext.apply(this, config);\r
+ this.frame = false;\r
+ \r
+ Ext.ux.GroupTab.superclass.initComponent.call(this);\r
+ \r
+ this.addEvents('activate', 'deactivate', 'changemainitem', 'beforetabchange', 'tabchange');\r
+ \r
+ this.setLayout(new Ext.layout.CardLayout({\r
+ deferredRender: this.deferredRender\r
+ }));\r
+ \r
+ if (!this.stack) {\r
+ this.stack = Ext.TabPanel.AccessStack();\r
+ }\r
+ \r
+ this.initItems();\r
+ \r
+ this.on('beforerender', function(){\r
+ this.groupEl = this.ownerCt.getGroupEl(this);\r
+ }, this);\r
+ \r
+ this.on('add', this.onAdd, this, {\r
+ target: this\r
+ });\r
+ this.on('remove', this.onRemove, this, {\r
+ target: this\r
+ });\r
+ \r
+ if (this.mainItem !== undefined) {\r
+ var item = (typeof this.mainItem == 'object') ? this.mainItem : this.items.get(this.mainItem);\r
+ delete this.mainItem;\r
+ this.setMainItem(item);\r
+ }\r
+ },\r
+ \r
+ /**\r
+ * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which\r
+ * can return false to cancel the tab change.\r
+ * @param {String/Panel} tab The id or tab Panel to activate\r
+ */\r
+ setActiveTab : function(item){\r
+ item = this.getComponent(item);\r
+ if(!item || this.fireEvent('beforetabchange', this, item, this.activeTab) === false){\r
+ return;\r
+ }\r
+ if(!this.rendered){\r
+ this.activeTab = item;\r
+ return;\r
+ }\r
+ if(this.activeTab != item){\r
+ if(this.activeTab && this.activeTab != this.mainItem){\r
+ var oldEl = this.getTabEl(this.activeTab);\r
+ if(oldEl){\r
+ Ext.fly(oldEl).removeClass('x-grouptabs-strip-active');\r
+ }\r
+ this.activeTab.fireEvent('deactivate', this.activeTab);\r
+ }\r
+ var el = this.getTabEl(item);\r
+ Ext.fly(el).addClass('x-grouptabs-strip-active');\r
+ this.activeTab = item;\r
+ this.stack.add(item);\r
+\r
+ this.layout.setActiveItem(item);\r
+ if(this.layoutOnTabChange && item.doLayout){\r
+ item.doLayout();\r
+ }\r
+ if(this.scrolling){\r
+ this.scrollToTab(item, this.animScroll);\r
+ }\r
+\r
+ item.fireEvent('activate', item);\r
+ this.fireEvent('tabchange', this, item);\r
+ }\r
+ },\r
+ \r
+ getTabEl: function(item){\r
+ if (item == this.mainItem) {\r
+ return this.groupEl;\r
+ }\r
+ return Ext.TabPanel.prototype.getTabEl.call(this, item);\r
+ },\r
+ \r
+ onRender: function(ct, position){\r
+ Ext.ux.GroupTab.superclass.onRender.call(this, ct, position);\r
+ \r
+ this.strip = Ext.fly(this.groupEl).createChild({\r
+ tag: 'ul',\r
+ cls: 'x-grouptabs-sub'\r
+ });\r
+\r
+ this.tooltip = new Ext.ToolTip({\r
+ target: this.groupEl,\r
+ delegate: 'a.x-grouptabs-text',\r
+ trackMouse: true,\r
+ renderTo: document.body,\r
+ listeners: {\r
+ beforeshow: function(tip) {\r
+ var item = (tip.triggerElement.parentNode === this.mainItem.tabEl)\r
+ ? this.mainItem\r
+ : this.findById(tip.triggerElement.parentNode.id.split(this.idDelimiter)[1]);\r
+\r
+ if(!item.tabTip) {\r
+ return false;\r
+ }\r
+ tip.body.dom.innerHTML = item.tabTip;\r
+ },\r
+ scope: this\r
+ }\r
+ });\r
+ \r
+ if (!this.itemTpl) {\r
+ var tt = new Ext.Template('<li class="{cls}" id="{id}">', '<a onclick="return false;" class="x-grouptabs-text {iconCls}">{text}</a>', '</li>');\r
+ tt.disableFormats = true;\r
+ tt.compile();\r
+ Ext.ux.GroupTab.prototype.itemTpl = tt;\r
+ }\r
+ \r
+ this.items.each(this.initTab, this);\r
+ },\r
+ \r
+ afterRender: function(){\r
+ Ext.ux.GroupTab.superclass.afterRender.call(this);\r
+ \r
+ if (this.activeTab !== undefined) {\r
+ var item = (typeof this.activeTab == 'object') ? this.activeTab : this.items.get(this.activeTab);\r
+ delete this.activeTab;\r
+ this.setActiveTab(item);\r
+ }\r
+ },\r
+ \r
+ // private\r
+ initTab: function(item, index){\r
+ var before = this.strip.dom.childNodes[index];\r
+ var p = Ext.TabPanel.prototype.getTemplateArgs.call(this, item);\r
+ \r
+ if (item === this.mainItem) {\r
+ item.tabEl = this.groupEl;\r
+ p.cls += ' x-grouptabs-main-item';\r
+ }\r
+ \r
+ var el = before ? this.itemTpl.insertBefore(before, p) : this.itemTpl.append(this.strip, p);\r
+ \r
+ item.tabEl = item.tabEl || el;\r
+ \r
+ item.on('disable', this.onItemDisabled, this);\r
+ item.on('enable', this.onItemEnabled, this);\r
+ item.on('titlechange', this.onItemTitleChanged, this);\r
+ item.on('iconchange', this.onItemIconChanged, this);\r
+ item.on('beforeshow', this.onBeforeShowItem, this);\r
+ },\r
+ \r
+ setMainItem: function(item){\r
+ item = this.getComponent(item);\r
+ if (!item || this.fireEvent('changemainitem', this, item, this.mainItem) === false) {\r
+ return;\r
+ }\r
+ \r
+ this.mainItem = item;\r
+ },\r
+ \r
+ getMainItem: function(){\r
+ return this.mainItem || null;\r
+ },\r
+ \r
+ // private\r
+ onBeforeShowItem: function(item){\r
+ if (item != this.activeTab) {\r
+ this.setActiveTab(item);\r
+ return false;\r
+ }\r
+ },\r
+ \r
+ // private\r
+ onAdd: function(gt, item, index){\r
+ if (this.rendered) {\r
+ this.initTab.call(this, item, index);\r
+ }\r
+ },\r
+ \r
+ // private\r
+ onRemove: function(tp, item){\r
+ Ext.destroy(Ext.get(this.getTabEl(item)));\r
+ this.stack.remove(item);\r
+ item.un('disable', this.onItemDisabled, this);\r
+ item.un('enable', this.onItemEnabled, this);\r
+ item.un('titlechange', this.onItemTitleChanged, this);\r
+ item.un('iconchange', this.onItemIconChanged, this);\r
+ item.un('beforeshow', this.onBeforeShowItem, this);\r
+ if (item == this.activeTab) {\r
+ var next = this.stack.next();\r
+ if (next) {\r
+ this.setActiveTab(next);\r
+ }\r
+ else if (this.items.getCount() > 0) {\r
+ this.setActiveTab(0);\r
+ }\r
+ else {\r
+ this.activeTab = null;\r
+ }\r
+ }\r
+ },\r
+ \r
+ // private\r
+ onBeforeAdd: function(item){\r
+ var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item);\r
+ if (existing) {\r
+ this.setActiveTab(item);\r
+ return false;\r
+ }\r
+ Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments);\r
+ var es = item.elements;\r
+ item.elements = es ? es.replace(',header', '') : es;\r
+ item.border = (item.border === true);\r
+ },\r
+ \r
+ // private\r
+ onItemDisabled: Ext.TabPanel.prototype.onItemDisabled,\r
+ onItemEnabled: Ext.TabPanel.prototype.onItemEnabled,\r
+ \r
+ // private\r
+ onItemTitleChanged: function(item){\r
+ var el = this.getTabEl(item);\r
+ if (el) {\r
+ Ext.fly(el).child('a.x-grouptabs-text', true).innerHTML = item.title;\r
+ }\r
+ },\r
+ \r
+ //private\r
+ onItemIconChanged: function(item, iconCls, oldCls){\r
+ var el = this.getTabEl(item);\r
+ if (el) {\r
+ Ext.fly(el).child('a.x-grouptabs-text').replaceClass(oldCls, iconCls);\r
+ }\r
+ },\r
+ \r
+ beforeDestroy: function(){\r
+ Ext.TabPanel.prototype.beforeDestroy.call(this);\r
+ this.tooltip.destroy();\r
+ }\r
+});\r
+\r
+Ext.reg('grouptab', Ext.ux.GroupTab);\r
+Ext.ns('Ext.ux');\r
+\r
+Ext.ux.GroupTabPanel = Ext.extend(Ext.TabPanel, {\r
+ tabPosition: 'left',\r
+ \r
+ alternateColor: false,\r
+ \r
+ alternateCls: 'x-grouptabs-panel-alt',\r
+ \r
+ defaultType: 'grouptab',\r
+ \r
+ deferredRender: false,\r
+ \r
+ activeGroup : null,\r
+ \r
+ initComponent: function(){\r
+ Ext.ux.GroupTabPanel.superclass.initComponent.call(this);\r
+ \r
+ this.addEvents(\r
+ 'beforegroupchange',\r
+ 'groupchange'\r
+ );\r
+ this.elements = 'body,header';\r
+ this.stripTarget = 'header';\r
+ \r
+ this.tabPosition = this.tabPosition == 'right' ? 'right' : 'left';\r
+ \r
+ this.addClass('x-grouptabs-panel');\r
+ \r
+ if (this.tabStyle && this.tabStyle != '') {\r
+ this.addClass('x-grouptabs-panel-' + this.tabStyle);\r
+ }\r
+ \r
+ if (this.alternateColor) {\r
+ this.addClass(this.alternateCls);\r
+ }\r
+ \r
+ this.on('beforeadd', function(gtp, item, index){\r
+ this.initGroup(item, index);\r
+ }); \r
+ },\r
+ \r
+ initEvents : function() {\r
+ this.mon(this.strip, 'mousedown', this.onStripMouseDown, this);\r
+ },\r
+ \r
+ onRender: function(ct, position){\r
+ Ext.TabPanel.superclass.onRender.call(this, ct, position);\r
+\r
+ if(this.plain){\r
+ var pos = this.tabPosition == 'top' ? 'header' : 'footer';\r
+ this[pos].addClass('x-tab-panel-'+pos+'-plain');\r
+ }\r
+\r
+ var st = this[this.stripTarget];\r
+\r
+ this.stripWrap = st.createChild({cls:'x-tab-strip-wrap ', cn:{\r
+ tag:'ul', cls:'x-grouptabs-strip x-grouptabs-tab-strip-'+this.tabPosition}});\r
+\r
+ var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null);\r
+ this.strip = new Ext.Element(this.stripWrap.dom.firstChild);\r
+\r
+ this.header.addClass('x-grouptabs-panel-header');\r
+ this.bwrap.addClass('x-grouptabs-bwrap');\r
+ this.body.addClass('x-tab-panel-body-'+this.tabPosition + ' x-grouptabs-panel-body');\r
+\r
+ if (!this.itemTpl) {\r
+ var tt = new Ext.Template(\r
+ '<li class="{cls}" id="{id}">', \r
+ '<a class="x-grouptabs-expand" onclick="return false;"></a>', \r
+ '<a class="x-grouptabs-text {iconCls}" href="#" onclick="return false;">',\r
+ '<span>{text}</span></a>', \r
+ '</li>'\r
+ );\r
+ tt.disableFormats = true;\r
+ tt.compile();\r
+ Ext.ux.GroupTabPanel.prototype.itemTpl = tt;\r
+ }\r
+\r
+ this.items.each(this.initGroup, this);\r
+ },\r
+ \r
+ afterRender: function(){\r
+ Ext.ux.GroupTabPanel.superclass.afterRender.call(this);\r
+ \r
+ this.tabJoint = Ext.fly(this.body.dom.parentNode).createChild({\r
+ cls: 'x-tab-joint'\r
+ });\r
+ \r
+ this.addClass('x-tab-panel-' + this.tabPosition);\r
+ this.header.setWidth(this.tabWidth);\r
+ \r
+ if (this.activeGroup !== undefined) {\r
+ var group = (typeof this.activeGroup == 'object') ? this.activeGroup : this.items.get(this.activeGroup);\r
+ delete this.activeGroup;\r
+ this.setActiveGroup(group);\r
+ group.setActiveTab(group.getMainItem());\r
+ }\r
+ },\r
+\r
+ getGroupEl : Ext.TabPanel.prototype.getTabEl,\r
+ \r
+ // private\r
+ findTargets: function(e){\r
+ var item = null;\r
+ var itemEl = e.getTarget('li', this.strip);\r
+ if (itemEl) {\r
+ item = this.findById(itemEl.id.split(this.idDelimiter)[1]);\r
+ if (item.disabled) {\r
+ return {\r
+ expand: null,\r
+ item: null,\r
+ el: null\r
+ };\r
+ }\r
+ }\r
+ return {\r
+ expand: e.getTarget('.x-grouptabs-expand', this.strip),\r
+ isGroup: !e.getTarget('ul.x-grouptabs-sub', this.strip),\r
+ item: item,\r
+ el: itemEl\r
+ };\r
+ },\r
+ \r
+ // private\r
+ onStripMouseDown: function(e){\r
+ if (e.button != 0) {\r
+ return;\r
+ }\r
+ e.preventDefault();\r
+ var t = this.findTargets(e);\r
+ if (t.expand) {\r
+ this.toggleGroup(t.el);\r
+ }\r
+ else if (t.item) {\r
+ if(t.isGroup) {\r
+ t.item.setActiveTab(t.item.getMainItem());\r
+ }\r
+ else {\r
+ t.item.ownerCt.setActiveTab(t.item);\r
+ }\r
+ }\r
+ },\r
+ \r
+ expandGroup: function(groupEl){\r
+ if(groupEl.isXType) {\r
+ groupEl = this.getGroupEl(groupEl);\r
+ }\r
+ Ext.fly(groupEl).addClass('x-grouptabs-expanded');\r
+ },\r
+ \r
+ toggleGroup: function(groupEl){\r
+ if(groupEl.isXType) {\r
+ groupEl = this.getGroupEl(groupEl);\r
+ } \r
+ Ext.fly(groupEl).toggleClass('x-grouptabs-expanded');\r
+ this.syncTabJoint();\r
+ }, \r
+ \r
+ syncTabJoint: function(groupEl){\r
+ if (!this.tabJoint) {\r
+ return;\r
+ }\r
+ \r
+ groupEl = groupEl || this.getGroupEl(this.activeGroup);\r
+ if(groupEl) {\r
+ this.tabJoint.setHeight(Ext.fly(groupEl).getHeight() - 2); \r
+ \r
+ var y = Ext.isGecko2 ? 0 : 1;\r
+ if (this.tabPosition == 'left'){\r
+ this.tabJoint.alignTo(groupEl, 'tl-tr', [-2,y]);\r
+ }\r
+ else {\r
+ this.tabJoint.alignTo(groupEl, 'tr-tl', [1,y]);\r
+ } \r
+ }\r
+ else {\r
+ this.tabJoint.hide();\r
+ }\r
+ },\r
+ \r
+ getActiveTab : function() {\r
+ if(!this.activeGroup) return null;\r
+ return this.activeGroup.getTabEl(this.activeGroup.activeTab) || null; \r
+ },\r
+ \r
+ onResize: function(){\r
+ Ext.ux.GroupTabPanel.superclass.onResize.apply(this, arguments);\r
+ this.syncTabJoint();\r
+ },\r
+ \r
+ createCorner: function(el, pos){\r
+ return Ext.fly(el).createChild({\r
+ cls: 'x-grouptabs-corner x-grouptabs-corner-' + pos\r
+ });\r
+ },\r
+ \r
+ initGroup: function(group, index){\r
+ var before = this.strip.dom.childNodes[index]; \r
+ var p = this.getTemplateArgs(group);\r
+ if (index === 0) {\r
+ p.cls += ' x-tab-first';\r
+ }\r
+ p.cls += ' x-grouptabs-main';\r
+ p.text = group.getMainItem().title;\r
+ \r
+ var el = before ? this.itemTpl.insertBefore(before, p) : this.itemTpl.append(this.strip, p);\r
+ \r
+ var tl = this.createCorner(el, 'top-' + this.tabPosition);\r
+ var bl = this.createCorner(el, 'bottom-' + this.tabPosition);\r
+\r
+ if (group.expanded) {\r
+ this.expandGroup(el);\r
+ }\r
+\r
+ if (Ext.isIE6 || (Ext.isIE && !Ext.isStrict)){\r
+ bl.setLeft('-10px');\r
+ bl.setBottom('-5px');\r
+ tl.setLeft('-10px');\r
+ tl.setTop('-5px');\r
+ }\r
+\r
+ this.mon(group, 'changemainitem', this.onGroupChangeMainItem, this);\r
+ this.mon(group, 'beforetabchange', this.onGroupBeforeTabChange, this);\r
+ },\r
+ \r
+ setActiveGroup : function(group) {\r
+ group = this.getComponent(group);\r
+ if(!group || this.fireEvent('beforegroupchange', this, group, this.activeGroup) === false){\r
+ return;\r
+ }\r
+ if(!this.rendered){\r
+ this.activeGroup = group;\r
+ return;\r
+ }\r
+ if(this.activeGroup != group){\r
+ if(this.activeGroup){\r
+ var oldEl = this.getGroupEl(this.activeGroup);\r
+ if(oldEl){\r
+ Ext.fly(oldEl).removeClass('x-grouptabs-strip-active');\r
+ }\r
+ this.activeGroup.fireEvent('deactivate', this.activeTab);\r
+ }\r
+\r
+ var groupEl = this.getGroupEl(group);\r
+ Ext.fly(groupEl).addClass('x-grouptabs-strip-active');\r
+ \r
+ this.activeGroup = group;\r
+ this.stack.add(group);\r
+\r
+ this.layout.setActiveItem(group);\r
+ this.syncTabJoint(groupEl);\r
+\r
+ group.fireEvent('activate', group);\r
+ this.fireEvent('groupchange', this, group);\r
+ } \r
+ },\r
+ \r
+ onGroupBeforeTabChange: function(group, newTab, oldTab){\r
+ if(group !== this.activeGroup || newTab !== oldTab) {\r
+ this.strip.select('.x-grouptabs-sub > li.x-grouptabs-strip-active', true).removeClass('x-grouptabs-strip-active');\r
+ } \r
+ \r
+ this.expandGroup(this.getGroupEl(group));\r
+ this.setActiveGroup(group);\r
+ },\r
+ \r
+ getFrameHeight: function(){\r
+ var h = this.el.getFrameWidth('tb');\r
+ h += (this.tbar ? this.tbar.getHeight() : 0) +\r
+ (this.bbar ? this.bbar.getHeight() : 0);\r
+ \r
+ return h;\r
+ },\r
+ \r
+ adjustBodyWidth: function(w){\r
+ return w - this.tabWidth;\r
+ }\r
+});\r
+\r
+Ext.reg('grouptabpanel', Ext.ux.GroupTabPanel);/*\r
+ * Note that this control will most likely remain as an example, and not as a core Ext form\r
+ * control. However, the API will be changing in a future release and so should not yet be\r
+ * treated as a final, stable API at this time.\r
+ */\r
+\r
+/**\r
+ * @class Ext.ux.form.ItemSelector\r
+ * @extends Ext.form.Field\r
+ * A control that allows selection of between two Ext.ux.form.MultiSelect controls.\r
+ *\r
+ * @history\r
+ * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams)\r
+ *\r
+ * @constructor\r
+ * Create a new ItemSelector\r
+ * @param {Object} config Configuration options\r
+ * @xtype itemselector \r
+ */\r
+Ext.ux.form.ItemSelector = Ext.extend(Ext.form.Field, {\r
+ hideNavIcons:false,\r
+ imagePath:"",\r
+ iconUp:"up2.gif",\r
+ iconDown:"down2.gif",\r
+ iconLeft:"left2.gif",\r
+ iconRight:"right2.gif",\r
+ iconTop:"top2.gif",\r
+ iconBottom:"bottom2.gif",\r
+ drawUpIcon:true,\r
+ drawDownIcon:true,\r
+ drawLeftIcon:true,\r
+ drawRightIcon:true,\r
+ drawTopIcon:true,\r
+ drawBotIcon:true,\r
+ delimiter:',',\r
+ bodyStyle:null,\r
+ border:false,\r
+ defaultAutoCreate:{tag: "div"},\r
+ /**\r
+ * @cfg {Array} multiselects An array of {@link Ext.ux.form.MultiSelect} config objects, with at least all required parameters (e.g., store)\r
+ */\r
+ multiselects:null,\r
+\r
+ initComponent: function(){\r
+ Ext.ux.form.ItemSelector.superclass.initComponent.call(this);\r
+ this.addEvents({\r
+ 'rowdblclick' : true,\r
+ 'change' : true\r
+ });\r
+ },\r
+\r
+ onRender: function(ct, position){\r
+ Ext.ux.form.ItemSelector.superclass.onRender.call(this, ct, position);\r
+\r
+ // Internal default configuration for both multiselects\r
+ var msConfig = [{\r
+ legend: 'Available',\r
+ draggable: true,\r
+ droppable: true,\r
+ width: 100,\r
+ height: 100\r
+ },{\r
+ legend: 'Selected',\r
+ droppable: true,\r
+ draggable: true,\r
+ width: 100,\r
+ height: 100\r
+ }];\r
+\r
+ this.fromMultiselect = new Ext.ux.form.MultiSelect(Ext.applyIf(this.multiselects[0], msConfig[0]));\r
+ this.fromMultiselect.on('dblclick', this.onRowDblClick, this);\r
+\r
+ this.toMultiselect = new Ext.ux.form.MultiSelect(Ext.applyIf(this.multiselects[1], msConfig[1]));\r
+ this.toMultiselect.on('dblclick', this.onRowDblClick, this);\r
+\r
+ var p = new Ext.Panel({\r
+ bodyStyle:this.bodyStyle,\r
+ border:this.border,\r
+ layout:"table",\r
+ layoutConfig:{columns:3}\r
+ });\r
+\r
+ p.add(this.fromMultiselect);\r
+ var icons = new Ext.Panel({header:false});\r
+ p.add(icons);\r
+ p.add(this.toMultiselect);\r
+ p.render(this.el);\r
+ icons.el.down('.'+icons.bwrapCls).remove();\r
+\r
+ // ICON HELL!!!\r
+ if (this.imagePath!="" && this.imagePath.charAt(this.imagePath.length-1)!="/")\r
+ this.imagePath+="/";\r
+ this.iconUp = this.imagePath + (this.iconUp || 'up2.gif');\r
+ this.iconDown = this.imagePath + (this.iconDown || 'down2.gif');\r
+ this.iconLeft = this.imagePath + (this.iconLeft || 'left2.gif');\r
+ this.iconRight = this.imagePath + (this.iconRight || 'right2.gif');\r
+ this.iconTop = this.imagePath + (this.iconTop || 'top2.gif');\r
+ this.iconBottom = this.imagePath + (this.iconBottom || 'bottom2.gif');\r
+ var el=icons.getEl();\r
+ this.toTopIcon = el.createChild({tag:'img', src:this.iconTop, style:{cursor:'pointer', margin:'2px'}});\r
+ el.createChild({tag: 'br'});\r
+ this.upIcon = el.createChild({tag:'img', src:this.iconUp, style:{cursor:'pointer', margin:'2px'}});\r
+ el.createChild({tag: 'br'});\r
+ this.addIcon = el.createChild({tag:'img', src:this.iconRight, style:{cursor:'pointer', margin:'2px'}});\r
+ el.createChild({tag: 'br'});\r
+ this.removeIcon = el.createChild({tag:'img', src:this.iconLeft, style:{cursor:'pointer', margin:'2px'}});\r
+ el.createChild({tag: 'br'});\r
+ this.downIcon = el.createChild({tag:'img', src:this.iconDown, style:{cursor:'pointer', margin:'2px'}});\r
+ el.createChild({tag: 'br'});\r
+ this.toBottomIcon = el.createChild({tag:'img', src:this.iconBottom, style:{cursor:'pointer', margin:'2px'}});\r
+ this.toTopIcon.on('click', this.toTop, this);\r
+ this.upIcon.on('click', this.up, this);\r
+ this.downIcon.on('click', this.down, this);\r
+ this.toBottomIcon.on('click', this.toBottom, this);\r
+ this.addIcon.on('click', this.fromTo, this);\r
+ this.removeIcon.on('click', this.toFrom, this);\r
+ if (!this.drawUpIcon || this.hideNavIcons) { this.upIcon.dom.style.display='none'; }\r
+ if (!this.drawDownIcon || this.hideNavIcons) { this.downIcon.dom.style.display='none'; }\r
+ if (!this.drawLeftIcon || this.hideNavIcons) { this.addIcon.dom.style.display='none'; }\r
+ if (!this.drawRightIcon || this.hideNavIcons) { this.removeIcon.dom.style.display='none'; }\r
+ if (!this.drawTopIcon || this.hideNavIcons) { this.toTopIcon.dom.style.display='none'; }\r
+ if (!this.drawBotIcon || this.hideNavIcons) { this.toBottomIcon.dom.style.display='none'; }\r
+\r
+ var tb = p.body.first();\r
+ this.el.setWidth(p.body.first().getWidth());\r
+ p.body.removeClass();\r
+\r
+ this.hiddenName = this.name;\r
+ var hiddenTag = {tag: "input", type: "hidden", value: "", name: this.name};\r
+ this.hiddenField = this.el.createChild(hiddenTag);\r
+ },\r
+ \r
+ doLayout: function(){\r
+ if(this.rendered){\r
+ this.fromMultiselect.fs.doLayout();\r
+ this.toMultiselect.fs.doLayout();\r
+ }\r
+ },\r
+\r
+ afterRender: function(){\r
+ Ext.ux.form.ItemSelector.superclass.afterRender.call(this);\r
+\r
+ this.toStore = this.toMultiselect.store;\r
+ this.toStore.on('add', this.valueChanged, this);\r
+ this.toStore.on('remove', this.valueChanged, this);\r
+ this.toStore.on('load', this.valueChanged, this);\r
+ this.valueChanged(this.toStore);\r
+ },\r
+\r
+ toTop : function() {\r
+ var selectionsArray = this.toMultiselect.view.getSelectedIndexes();\r
+ var records = [];\r
+ if (selectionsArray.length > 0) {\r
+ selectionsArray.sort();\r
+ for (var i=0; i<selectionsArray.length; i++) {\r
+ record = this.toMultiselect.view.store.getAt(selectionsArray[i]);\r
+ records.push(record);\r
+ }\r
+ selectionsArray = [];\r
+ for (var i=records.length-1; i>-1; i--) {\r
+ record = records[i];\r
+ this.toMultiselect.view.store.remove(record);\r
+ this.toMultiselect.view.store.insert(0, record);\r
+ selectionsArray.push(((records.length - 1) - i));\r
+ }\r
+ }\r
+ this.toMultiselect.view.refresh();\r
+ this.toMultiselect.view.select(selectionsArray);\r
+ },\r
+\r
+ toBottom : function() {\r
+ var selectionsArray = this.toMultiselect.view.getSelectedIndexes();\r
+ var records = [];\r
+ if (selectionsArray.length > 0) {\r
+ selectionsArray.sort();\r
+ for (var i=0; i<selectionsArray.length; i++) {\r
+ record = this.toMultiselect.view.store.getAt(selectionsArray[i]);\r
+ records.push(record);\r
+ }\r
+ selectionsArray = [];\r
+ for (var i=0; i<records.length; i++) {\r
+ record = records[i];\r
+ this.toMultiselect.view.store.remove(record);\r
+ this.toMultiselect.view.store.add(record);\r
+ selectionsArray.push((this.toMultiselect.view.store.getCount()) - (records.length - i));\r
+ }\r
+ }\r
+ this.toMultiselect.view.refresh();\r
+ this.toMultiselect.view.select(selectionsArray);\r
+ },\r
+\r
+ up : function() {\r
+ var record = null;\r
+ var selectionsArray = this.toMultiselect.view.getSelectedIndexes();\r
+ selectionsArray.sort();\r
+ var newSelectionsArray = [];\r
+ if (selectionsArray.length > 0) {\r
+ for (var i=0; i<selectionsArray.length; i++) {\r
+ record = this.toMultiselect.view.store.getAt(selectionsArray[i]);\r
+ if ((selectionsArray[i] - 1) >= 0) {\r
+ this.toMultiselect.view.store.remove(record);\r
+ this.toMultiselect.view.store.insert(selectionsArray[i] - 1, record);\r
+ newSelectionsArray.push(selectionsArray[i] - 1);\r
+ }\r
+ }\r
+ this.toMultiselect.view.refresh();\r
+ this.toMultiselect.view.select(newSelectionsArray);\r
+ }\r
+ },\r
+\r
+ down : function() {\r
+ var record = null;\r
+ var selectionsArray = this.toMultiselect.view.getSelectedIndexes();\r
+ selectionsArray.sort();\r
+ selectionsArray.reverse();\r
+ var newSelectionsArray = [];\r
+ if (selectionsArray.length > 0) {\r
+ for (var i=0; i<selectionsArray.length; i++) {\r
+ record = this.toMultiselect.view.store.getAt(selectionsArray[i]);\r
+ if ((selectionsArray[i] + 1) < this.toMultiselect.view.store.getCount()) {\r
+ this.toMultiselect.view.store.remove(record);\r
+ this.toMultiselect.view.store.insert(selectionsArray[i] + 1, record);\r
+ newSelectionsArray.push(selectionsArray[i] + 1);\r
+ }\r
+ }\r
+ this.toMultiselect.view.refresh();\r
+ this.toMultiselect.view.select(newSelectionsArray);\r
+ }\r
+ },\r
+\r
+ fromTo : function() {\r
+ var selectionsArray = this.fromMultiselect.view.getSelectedIndexes();\r
+ var records = [];\r
+ if (selectionsArray.length > 0) {\r
+ for (var i=0; i<selectionsArray.length; i++) {\r
+ record = this.fromMultiselect.view.store.getAt(selectionsArray[i]);\r
+ records.push(record);\r
+ }\r
+ if(!this.allowDup)selectionsArray = [];\r
+ for (var i=0; i<records.length; i++) {\r
+ record = records[i];\r
+ if(this.allowDup){\r
+ var x=new Ext.data.Record();\r
+ record.id=x.id;\r
+ delete x;\r
+ this.toMultiselect.view.store.add(record);\r
+ }else{\r
+ this.fromMultiselect.view.store.remove(record);\r
+ this.toMultiselect.view.store.add(record);\r
+ selectionsArray.push((this.toMultiselect.view.store.getCount() - 1));\r
+ }\r
+ }\r
+ }\r
+ this.toMultiselect.view.refresh();\r
+ this.fromMultiselect.view.refresh();\r
+ var si = this.toMultiselect.store.sortInfo;\r
+ if(si){\r
+ this.toMultiselect.store.sort(si.field, si.direction);\r
+ }\r
+ this.toMultiselect.view.select(selectionsArray);\r
+ },\r
+\r
+ toFrom : function() {\r
+ var selectionsArray = this.toMultiselect.view.getSelectedIndexes();\r
+ var records = [];\r
+ if (selectionsArray.length > 0) {\r
+ for (var i=0; i<selectionsArray.length; i++) {\r
+ record = this.toMultiselect.view.store.getAt(selectionsArray[i]);\r
+ records.push(record);\r
+ }\r
+ selectionsArray = [];\r
+ for (var i=0; i<records.length; i++) {\r
+ record = records[i];\r
+ this.toMultiselect.view.store.remove(record);\r
+ if(!this.allowDup){\r
+ this.fromMultiselect.view.store.add(record);\r
+ selectionsArray.push((this.fromMultiselect.view.store.getCount() - 1));\r
+ }\r
+ }\r
+ }\r
+ this.fromMultiselect.view.refresh();\r
+ this.toMultiselect.view.refresh();\r
+ var si = this.fromMultiselect.store.sortInfo;\r
+ if (si){\r
+ this.fromMultiselect.store.sort(si.field, si.direction);\r
+ }\r
+ this.fromMultiselect.view.select(selectionsArray);\r
+ },\r
+\r
+ valueChanged: function(store) {\r
+ var record = null;\r
+ var values = [];\r
+ for (var i=0; i<store.getCount(); i++) {\r
+ record = store.getAt(i);\r
+ values.push(record.get(this.toMultiselect.valueField));\r
+ }\r
+ this.hiddenField.dom.value = values.join(this.delimiter);\r
+ this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value);\r
+ },\r
+\r
+ getValue : function() {\r
+ return this.hiddenField.dom.value;\r
+ },\r
+\r
+ onRowDblClick : function(vw, index, node, e) {\r
+ if (vw == this.toMultiselect.view){\r
+ this.toFrom();\r
+ } else if (vw == this.fromMultiselect.view) {\r
+ this.fromTo();\r
+ }\r
+ return this.fireEvent('rowdblclick', vw, index, node, e);\r
+ },\r
+\r
+ reset: function(){\r
+ range = this.toMultiselect.store.getRange();\r
+ this.toMultiselect.store.removeAll();\r
+ this.fromMultiselect.store.add(range);\r
+ var si = this.fromMultiselect.store.sortInfo;\r
+ if (si){\r
+ this.fromMultiselect.store.sort(si.field, si.direction);\r
+ }\r
+ this.valueChanged(this.toMultiselect.store);\r
+ }\r
+});\r
+\r
+Ext.reg('itemselector', Ext.ux.form.ItemSelector);\r
+\r
+//backwards compat\r
+Ext.ux.ItemSelector = Ext.ux.form.ItemSelector;\r
+Ext.ns('Ext.ux.form');\r
+\r
+/**\r
+ * @class Ext.ux.form.MultiSelect\r
+ * @extends Ext.form.Field\r
+ * A control that allows selection and form submission of multiple list items.\r
+ *\r
+ * @history\r
+ * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams)\r
+ * 2008-06-19 bpm Docs and demo code clean up\r
+ *\r
+ * @constructor\r
+ * Create a new MultiSelect\r
+ * @param {Object} config Configuration options\r
+ * @xtype multiselect \r
+ */\r
+Ext.ux.form.MultiSelect = Ext.extend(Ext.form.Field, {\r
+ /**\r
+ * @cfg {String} legend Wraps the object with a fieldset and specified legend.\r
+ */\r
+ /**\r
+ * @cfg {Ext.ListView} view The {@link Ext.ListView} used to render the multiselect list.\r
+ */\r
+ /**\r
+ * @cfg {String/Array} dragGroup The ddgroup name(s) for the MultiSelect DragZone (defaults to undefined).\r
+ */\r
+ /**\r
+ * @cfg {String/Array} dropGroup The ddgroup name(s) for the MultiSelect DropZone (defaults to undefined).\r
+ */\r
+ /**\r
+ * @cfg {Boolean} ddReorder Whether the items in the MultiSelect list are drag/drop reorderable (defaults to false).\r
+ */\r
+ ddReorder:false,\r
+ /**\r
+ * @cfg {Object/Array} tbar The top toolbar of the control. This can be a {@link Ext.Toolbar} object, a\r
+ * toolbar config, or an array of buttons/button configs to be added to the toolbar.\r
+ */\r
+ /**\r
+ * @cfg {String} appendOnly True if the list should only allow append drops when drag/drop is enabled\r
+ * (use for lists which are sorted, defaults to false).\r
+ */\r
+ appendOnly:false,\r
+ /**\r
+ * @cfg {Number} width Width in pixels of the control (defaults to 100).\r
+ */\r
+ width:100,\r
+ /**\r
+ * @cfg {Number} height Height in pixels of the control (defaults to 100).\r
+ */\r
+ height:100,\r
+ /**\r
+ * @cfg {String/Number} displayField Name/Index of the desired display field in the dataset (defaults to 0).\r
+ */\r
+ displayField:0,\r
+ /**\r
+ * @cfg {String/Number} valueField Name/Index of the desired value field in the dataset (defaults to 1).\r
+ */\r
+ valueField:1,\r
+ /**\r
+ * @cfg {Boolean} allowBlank False to require at least one item in the list to be selected, true to allow no\r
+ * selection (defaults to true).\r
+ */\r
+ allowBlank:true,\r
+ /**\r
+ * @cfg {Number} minSelections Minimum number of selections allowed (defaults to 0).\r
+ */\r
+ minSelections:0,\r
+ /**\r
+ * @cfg {Number} maxSelections Maximum number of selections allowed (defaults to Number.MAX_VALUE).\r
+ */\r
+ maxSelections:Number.MAX_VALUE,\r
+ /**\r
+ * @cfg {String} blankText Default text displayed when the control contains no items (defaults to the same value as\r
+ * {@link Ext.form.TextField#blankText}.\r
+ */\r
+ blankText:Ext.form.TextField.prototype.blankText,\r
+ /**\r
+ * @cfg {String} minSelectionsText Validation message displayed when {@link #minSelections} is not met (defaults to 'Minimum {0}\r
+ * item(s) required'). The {0} token will be replaced by the value of {@link #minSelections}.\r
+ */\r
+ minSelectionsText:'Minimum {0} item(s) required',\r
+ /**\r
+ * @cfg {String} maxSelectionsText Validation message displayed when {@link #maxSelections} is not met (defaults to 'Maximum {0}\r
+ * item(s) allowed'). The {0} token will be replaced by the value of {@link #maxSelections}.\r
+ */\r
+ maxSelectionsText:'Maximum {0} item(s) allowed',\r
+ /**\r
+ * @cfg {String} delimiter The string used to delimit between items when set or returned as a string of values\r
+ * (defaults to ',').\r
+ */\r
+ delimiter:',',\r
+ /**\r
+ * @cfg {Ext.data.Store/Array} store The data source to which this MultiSelect is bound (defaults to <tt>undefined</tt>).\r
+ * Acceptable values for this property are:\r
+ * <div class="mdetail-params"><ul>\r
+ * <li><b>any {@link Ext.data.Store Store} subclass</b></li>\r
+ * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally.\r
+ * <div class="mdetail-params"><ul>\r
+ * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">\r
+ * A 1-dimensional array will automatically be expanded (each array item will be the combo\r
+ * {@link #valueField value} and {@link #displayField text})</div></li>\r
+ * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">\r
+ * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo\r
+ * {@link #valueField value}, while the value at index 1 is assumed to be the combo {@link #displayField text}.\r
+ * </div></li></ul></div></li></ul></div>\r
+ */\r
+\r
+ // private\r
+ defaultAutoCreate : {tag: "div"},\r
+\r
+ // private\r
+ initComponent: function(){\r
+ Ext.ux.form.MultiSelect.superclass.initComponent.call(this);\r
+\r
+ if(Ext.isArray(this.store)){\r
+ if (Ext.isArray(this.store[0])){\r
+ this.store = new Ext.data.ArrayStore({\r
+ fields: ['value','text'],\r
+ data: this.store\r
+ });\r
+ this.valueField = 'value';\r
+ }else{\r
+ this.store = new Ext.data.ArrayStore({\r
+ fields: ['text'],\r
+ data: this.store,\r
+ expandData: true\r
+ });\r
+ this.valueField = 'text';\r
+ }\r
+ this.displayField = 'text';\r
+ } else {\r
+ this.store = Ext.StoreMgr.lookup(this.store);\r
+ }\r
+\r
+ this.addEvents({\r
+ 'dblclick' : true,\r
+ 'click' : true,\r
+ 'change' : true,\r
+ 'drop' : true\r
+ });\r
+ },\r
+\r
+ // private\r
+ onRender: function(ct, position){\r
+ Ext.ux.form.MultiSelect.superclass.onRender.call(this, ct, position);\r
+\r
+ var fs = this.fs = new Ext.form.FieldSet({\r
+ renderTo: this.el,\r
+ title: this.legend,\r
+ height: this.height,\r
+ width: this.width,\r
+ style: "padding:0;",\r
+ tbar: this.tbar,\r
+ bodyStyle: 'overflow: auto;'\r
+ });\r
+\r
+ this.view = new Ext.ListView({\r
+ multiSelect: true,\r
+ store: this.store,\r
+ columns: [{ header: 'Value', width: 1, dataIndex: this.displayField }],\r
+ hideHeaders: true\r
+ });\r
+\r
+ fs.add(this.view);\r
+\r
+ this.view.on('click', this.onViewClick, this);\r
+ this.view.on('beforeclick', this.onViewBeforeClick, this);\r
+ this.view.on('dblclick', this.onViewDblClick, this);\r
+\r
+ this.hiddenName = this.name || Ext.id();\r
+ var hiddenTag = { tag: "input", type: "hidden", value: "", name: this.hiddenName };\r
+ this.hiddenField = this.el.createChild(hiddenTag);\r
+ this.hiddenField.dom.disabled = this.hiddenName != this.name;\r
+ fs.doLayout();\r
+ },\r
+\r
+ // private\r
+ afterRender: function(){\r
+ Ext.ux.form.MultiSelect.superclass.afterRender.call(this);\r
+\r
+ if (this.ddReorder && !this.dragGroup && !this.dropGroup){\r
+ this.dragGroup = this.dropGroup = 'MultiselectDD-' + Ext.id();\r
+ }\r
+\r
+ if (this.draggable || this.dragGroup){\r
+ this.dragZone = new Ext.ux.form.MultiSelect.DragZone(this, {\r
+ ddGroup: this.dragGroup\r
+ });\r
+ }\r
+ if (this.droppable || this.dropGroup){\r
+ this.dropZone = new Ext.ux.form.MultiSelect.DropZone(this, {\r
+ ddGroup: this.dropGroup\r
+ });\r
+ }\r
+ },\r
+\r
+ // private\r
+ onViewClick: function(vw, index, node, e) {\r
+ this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value);\r
+ this.hiddenField.dom.value = this.getValue();\r
+ this.fireEvent('click', this, e);\r
+ this.validate();\r
+ },\r
+\r
+ // private\r
+ onViewBeforeClick: function(vw, index, node, e) {\r
+ if (this.disabled) {return false;}\r
+ },\r
+\r
+ // private\r
+ onViewDblClick : function(vw, index, node, e) {\r
+ return this.fireEvent('dblclick', vw, index, node, e);\r
+ },\r
+\r
+ /**\r
+ * Returns an array of data values for the selected items in the list. The values will be separated\r
+ * by {@link #delimiter}.\r
+ * @return {Array} value An array of string data values\r
+ */\r
+ getValue: function(valueField){\r
+ var returnArray = [];\r
+ var selectionsArray = this.view.getSelectedIndexes();\r
+ if (selectionsArray.length == 0) {return '';}\r
+ for (var i=0; i<selectionsArray.length; i++) {\r
+ returnArray.push(this.store.getAt(selectionsArray[i]).get((valueField != null) ? valueField : this.valueField));\r
+ }\r
+ return returnArray.join(this.delimiter);\r
+ },\r
+\r
+ /**\r
+ * Sets a delimited string (using {@link #delimiter}) or array of data values into the list.\r
+ * @param {String/Array} values The values to set\r
+ */\r
+ setValue: function(values) {\r
+ var index;\r
+ var selections = [];\r
+ this.view.clearSelections();\r
+ this.hiddenField.dom.value = '';\r
+\r
+ if (!values || (values == '')) { return; }\r
+\r
+ if (!Ext.isArray(values)) { values = values.split(this.delimiter); }\r
+ for (var i=0; i<values.length; i++) {\r
+ index = this.view.store.indexOf(this.view.store.query(this.valueField,\r
+ new RegExp('^' + values[i] + '$', "i")).itemAt(0));\r
+ selections.push(index);\r
+ }\r
+ this.view.select(selections);\r
+ this.hiddenField.dom.value = this.getValue();\r
+ this.validate();\r
+ },\r
+\r
+ // inherit docs\r
+ reset : function() {\r
+ this.setValue('');\r
+ },\r
+\r
+ // inherit docs\r
+ getRawValue: function(valueField) {\r
+ var tmp = this.getValue(valueField);\r
+ if (tmp.length) {\r
+ tmp = tmp.split(this.delimiter);\r
+ }\r
+ else {\r
+ tmp = [];\r
+ }\r
+ return tmp;\r
+ },\r
+\r
+ // inherit docs\r
+ setRawValue: function(values){\r
+ setValue(values);\r
+ },\r
+\r
+ // inherit docs\r
+ validateValue : function(value){\r
+ if (value.length < 1) { // if it has no value\r
+ if (this.allowBlank) {\r
+ this.clearInvalid();\r
+ return true;\r
+ } else {\r
+ this.markInvalid(this.blankText);\r
+ return false;\r
+ }\r
+ }\r
+ if (value.length < this.minSelections) {\r
+ this.markInvalid(String.format(this.minSelectionsText, this.minSelections));\r
+ return false;\r
+ }\r
+ if (value.length > this.maxSelections) {\r
+ this.markInvalid(String.format(this.maxSelectionsText, this.maxSelections));\r
+ return false;\r
+ }\r
+ return true;\r
+ },\r
+\r
+ // inherit docs\r
+ disable: function(){\r
+ this.disabled = true;\r
+ this.hiddenField.dom.disabled = true;\r
+ this.fs.disable();\r
+ },\r
+\r
+ // inherit docs\r
+ enable: function(){\r
+ this.disabled = false;\r
+ this.hiddenField.dom.disabled = false;\r
+ this.fs.enable();\r
+ },\r
+\r
+ // inherit docs\r
+ destroy: function(){\r
+ Ext.destroy(this.fs, this.dragZone, this.dropZone);\r
+ Ext.ux.form.MultiSelect.superclass.destroy.call(this);\r
+ }\r
+});\r
+\r
+\r
+Ext.reg('multiselect', Ext.ux.form.MultiSelect);\r
+\r
+//backwards compat\r
+Ext.ux.Multiselect = Ext.ux.form.MultiSelect;\r
+\r
+\r
+Ext.ux.form.MultiSelect.DragZone = function(ms, config){\r
+ this.ms = ms;\r
+ this.view = ms.view;\r
+ var ddGroup = config.ddGroup || 'MultiselectDD';\r
+ var dd;\r
+ if (Ext.isArray(ddGroup)){\r
+ dd = ddGroup.shift();\r
+ } else {\r
+ dd = ddGroup;\r
+ ddGroup = null;\r
+ }\r
+ Ext.ux.form.MultiSelect.DragZone.superclass.constructor.call(this, this.ms.fs.body, { containerScroll: true, ddGroup: dd });\r
+ this.setDraggable(ddGroup);\r
+};\r
+\r
+Ext.extend(Ext.ux.form.MultiSelect.DragZone, Ext.dd.DragZone, {\r
+ onInitDrag : function(x, y){\r
+ var el = Ext.get(this.dragData.ddel.cloneNode(true));\r
+ this.proxy.update(el.dom);\r
+ el.setWidth(el.child('em').getWidth());\r
+ this.onStartDrag(x, y);\r
+ return true;\r
+ },\r
+ \r
+ // private\r
+ collectSelection: function(data) {\r
+ data.repairXY = Ext.fly(this.view.getSelectedNodes()[0]).getXY();\r
+ var i = 0;\r
+ this.view.store.each(function(rec){\r
+ if (this.view.isSelected(i)) {\r
+ var n = this.view.getNode(i);\r
+ var dragNode = n.cloneNode(true);\r
+ dragNode.id = Ext.id();\r
+ data.ddel.appendChild(dragNode);\r
+ data.records.push(this.view.store.getAt(i));\r
+ data.viewNodes.push(n);\r
+ }\r
+ i++;\r
+ }, this);\r
+ },\r
+\r
+ // override\r
+ onEndDrag: function(data, e) {\r
+ var d = Ext.get(this.dragData.ddel);\r
+ if (d && d.hasClass("multi-proxy")) {\r
+ d.remove();\r
+ }\r
+ },\r
+\r
+ // override\r
+ getDragData: function(e){\r
+ var target = this.view.findItemFromChild(e.getTarget());\r
+ if(target) {\r
+ if (!this.view.isSelected(target) && !e.ctrlKey && !e.shiftKey) {\r
+ this.view.select(target);\r
+ this.ms.setValue(this.ms.getValue());\r
+ }\r
+ if (this.view.getSelectionCount() == 0 || e.ctrlKey || e.shiftKey) return false;\r
+ var dragData = {\r
+ sourceView: this.view,\r
+ viewNodes: [],\r
+ records: []\r
+ };\r
+ if (this.view.getSelectionCount() == 1) {\r
+ var i = this.view.getSelectedIndexes()[0];\r
+ var n = this.view.getNode(i);\r
+ dragData.viewNodes.push(dragData.ddel = n);\r
+ dragData.records.push(this.view.store.getAt(i));\r
+ dragData.repairXY = Ext.fly(n).getXY();\r
+ } else {\r
+ dragData.ddel = document.createElement('div');\r
+ dragData.ddel.className = 'multi-proxy';\r
+ this.collectSelection(dragData);\r
+ }\r
+ return dragData;\r
+ }\r
+ return false;\r
+ },\r
+\r
+ // override the default repairXY.\r
+ getRepairXY : function(e){\r
+ return this.dragData.repairXY;\r
+ },\r
+\r
+ // private\r
+ setDraggable: function(ddGroup){\r
+ if (!ddGroup) return;\r
+ if (Ext.isArray(ddGroup)) {\r
+ Ext.each(ddGroup, this.setDraggable, this);\r
+ return;\r
+ }\r
+ this.addToGroup(ddGroup);\r
+ }\r
+});\r
+\r
+Ext.ux.form.MultiSelect.DropZone = function(ms, config){\r
+ this.ms = ms;\r
+ this.view = ms.view;\r
+ var ddGroup = config.ddGroup || 'MultiselectDD';\r
+ var dd;\r
+ if (Ext.isArray(ddGroup)){\r
+ dd = ddGroup.shift();\r
+ } else {\r
+ dd = ddGroup;\r
+ ddGroup = null;\r
+ }\r
+ Ext.ux.form.MultiSelect.DropZone.superclass.constructor.call(this, this.ms.fs.body, { containerScroll: true, ddGroup: dd });\r
+ this.setDroppable(ddGroup);\r
+};\r
+\r
+Ext.extend(Ext.ux.form.MultiSelect.DropZone, Ext.dd.DropZone, {\r
+ /**\r
+ * Part of the Ext.dd.DropZone interface. If no target node is found, the\r
+ * whole Element becomes the target, and this causes the drop gesture to append.\r
+ */\r
+ getTargetFromEvent : function(e) {\r
+ var target = e.getTarget();\r
+ return target;\r
+ },\r
+\r
+ // private\r
+ getDropPoint : function(e, n, dd){\r
+ if (n == this.ms.fs.body.dom) { return "below"; }\r
+ var t = Ext.lib.Dom.getY(n), b = t + n.offsetHeight;\r
+ var c = t + (b - t) / 2;\r
+ var y = Ext.lib.Event.getPageY(e);\r
+ if(y <= c) {\r
+ return "above";\r
+ }else{\r
+ return "below";\r
+ }\r
+ },\r
+\r
+ // private\r
+ isValidDropPoint: function(pt, n, data) {\r
+ if (!data.viewNodes || (data.viewNodes.length != 1)) {\r
+ return true;\r
+ }\r
+ var d = data.viewNodes[0];\r
+ if (d == n) {\r
+ return false;\r
+ }\r
+ if ((pt == "below") && (n.nextSibling == d)) {\r
+ return false;\r
+ }\r
+ if ((pt == "above") && (n.previousSibling == d)) {\r
+ return false;\r
+ }\r
+ return true;\r
+ },\r
+\r
+ // override\r
+ onNodeEnter : function(n, dd, e, data){\r
+ return false;\r
+ },\r
+\r
+ // override\r
+ onNodeOver : function(n, dd, e, data){\r
+ var dragElClass = this.dropNotAllowed;\r
+ var pt = this.getDropPoint(e, n, dd);\r
+ if (this.isValidDropPoint(pt, n, data)) {\r
+ if (this.ms.appendOnly) {\r
+ return "x-tree-drop-ok-below";\r
+ }\r
+\r
+ // set the insert point style on the target node\r
+ if (pt) {\r
+ var targetElClass;\r
+ if (pt == "above"){\r
+ dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";\r
+ targetElClass = "x-view-drag-insert-above";\r
+ } else {\r
+ dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";\r
+ targetElClass = "x-view-drag-insert-below";\r
+ }\r
+ if (this.lastInsertClass != targetElClass){\r
+ Ext.fly(n).replaceClass(this.lastInsertClass, targetElClass);\r
+ this.lastInsertClass = targetElClass;\r
+ }\r
+ }\r
+ }\r
+ return dragElClass;\r
+ },\r
+\r
+ // private\r
+ onNodeOut : function(n, dd, e, data){\r
+ this.removeDropIndicators(n);\r
+ },\r
+\r
+ // private\r
+ onNodeDrop : function(n, dd, e, data){\r
+ if (this.ms.fireEvent("drop", this, n, dd, e, data) === false) {\r
+ return false;\r
+ }\r
+ var pt = this.getDropPoint(e, n, dd);\r
+ if (n != this.ms.fs.body.dom)\r
+ n = this.view.findItemFromChild(n);\r
+ var insertAt = (this.ms.appendOnly || (n == this.ms.fs.body.dom)) ? this.view.store.getCount() : this.view.indexOf(n);\r
+ if (pt == "below") {\r
+ insertAt++;\r
+ }\r
+\r
+ var dir = false;\r
+\r
+ // Validate if dragging within the same MultiSelect\r
+ if (data.sourceView == this.view) {\r
+ // If the first element to be inserted below is the target node, remove it\r
+ if (pt == "below") {\r
+ if (data.viewNodes[0] == n) {\r
+ data.viewNodes.shift();\r
+ }\r
+ } else { // If the last element to be inserted above is the target node, remove it\r
+ if (data.viewNodes[data.viewNodes.length - 1] == n) {\r
+ data.viewNodes.pop();\r
+ }\r
+ }\r
+\r
+ // Nothing to drop...\r
+ if (!data.viewNodes.length) {\r
+ return false;\r
+ }\r
+\r
+ // If we are moving DOWN, then because a store.remove() takes place first,\r
+ // the insertAt must be decremented.\r
+ if (insertAt > this.view.store.indexOf(data.records[0])) {\r
+ dir = 'down';\r
+ insertAt--;\r
+ }\r
+ }\r
+\r
+ for (var i = 0; i < data.records.length; i++) {\r
+ var r = data.records[i];\r
+ if (data.sourceView) {\r
+ data.sourceView.store.remove(r);\r
+ }\r
+ this.view.store.insert(dir == 'down' ? insertAt : insertAt++, r);\r
+ var si = this.view.store.sortInfo;\r
+ if(si){\r
+ this.view.store.sort(si.field, si.direction);\r
+ }\r
+ }\r
+ return true;\r
+ },\r
+\r
+ // private\r
+ removeDropIndicators : function(n){\r
+ if(n){\r
+ Ext.fly(n).removeClass([\r
+ "x-view-drag-insert-above",\r
+ "x-view-drag-insert-left",\r
+ "x-view-drag-insert-right",\r
+ "x-view-drag-insert-below"]);\r
+ this.lastInsertClass = "_noclass";\r
+ }\r
+ },\r
+\r
+ // private\r
+ setDroppable: function(ddGroup){\r
+ if (!ddGroup) return;\r
+ if (Ext.isArray(ddGroup)) {\r
+ Ext.each(ddGroup, this.setDroppable, this);\r
+ return;\r
+ }\r
+ this.addToGroup(ddGroup);\r
+ }\r
+});\r
+
+/* Fix for Opera, which does not seem to include the map function on Array's */
+if (!Array.prototype.map) {
+ Array.prototype.map = function(fun){
+ var len = this.length;
+ if (typeof fun != 'function') {
+ throw new TypeError();
+ }
+ var res = new Array(len);
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++) {
+ if (i in this) {
+ res[i] = fun.call(thisp, this[i], i, this);
+ }
+ }
+ return res;
+ };
+}
+
+Ext.ns('Ext.ux.data');
+
+/**
+ * @class Ext.ux.data.PagingMemoryProxy
+ * @extends Ext.data.MemoryProxy
+ * <p>Paging Memory Proxy, allows to use paging grid with in memory dataset</p>
+ */
+Ext.ux.data.PagingMemoryProxy = Ext.extend(Ext.data.MemoryProxy, {
+ constructor : function(data){
+ Ext.ux.data.PagingMemoryProxy.superclass.constructor.call(this);
+ this.data = data;
+ },
+ doRequest : function(action, rs, params, reader, callback, scope, options){
+ params = params ||
+ {};
+ var result;
+ try {
+ result = reader.readRecords(this.data);
+ }
+ catch (e) {
+ this.fireEvent('loadexception', this, options, null, e);
+ callback.call(scope, null, options, false);
+ return;
+ }
+
+ // filtering
+ if (params.filter !== undefined) {
+ result.records = result.records.filter(function(el){
+ if (typeof(el) == 'object') {
+ var att = params.filterCol || 0;
+ return String(el.data[att]).match(params.filter) ? true : false;
+ }
+ else {
+ return String(el).match(params.filter) ? true : false;
+ }
+ });
+ result.totalRecords = result.records.length;
+ }
+
+ // sorting
+ if (params.sort !== undefined) {
+ // use integer as params.sort to specify column, since arrays are not named
+ // params.sort=0; would also match a array without columns
+ var dir = String(params.dir).toUpperCase() == 'DESC' ? -1 : 1;
+ var fn = function(r1, r2){
+ return r1 < r2;
+ };
+ result.records.sort(function(a, b){
+ var v = 0;
+ if (typeof(a) == 'object') {
+ v = fn(a.data[params.sort], b.data[params.sort]) * dir;
+ }
+ else {
+ v = fn(a, b) * dir;
+ }
+ if (v == 0) {
+ v = (a.index < b.index ? -1 : 1);
+ }
+ return v;
+ });
+ }
+ // paging (use undefined cause start can also be 0 (thus false))
+ if (params.start !== undefined && params.limit !== undefined) {
+ result.records = result.records.slice(params.start, params.start + params.limit);
+ }
+ callback.call(scope, result, options, true);
+ }
+});
+
+//backwards compat.
+Ext.data.PagingMemoryProxy = Ext.ux.data.PagingMemoryProxy;
+Ext.ux.PanelResizer = Ext.extend(Ext.util.Observable, {\r
+ minHeight: 0,\r
+ maxHeight:10000000,\r
+\r
+ constructor: function(config){\r
+ Ext.apply(this, config);\r
+ this.events = {};\r
+ Ext.ux.PanelResizer.superclass.constructor.call(this, config);\r
+ },\r
+\r
+ init : function(p){\r
+ this.panel = p;\r
+\r
+ if(this.panel.elements.indexOf('footer')==-1){\r
+ p.elements += ',footer';\r
+ }\r
+ p.on('render', this.onRender, this);\r
+ },\r
+\r
+ onRender : function(p){\r
+ this.handle = p.footer.createChild({cls:'x-panel-resize'});\r
+\r
+ this.tracker = new Ext.dd.DragTracker({\r
+ onStart: this.onDragStart.createDelegate(this),\r
+ onDrag: this.onDrag.createDelegate(this),\r
+ onEnd: this.onDragEnd.createDelegate(this),\r
+ tolerance: 3,\r
+ autoStart: 300\r
+ });\r
+ this.tracker.initEl(this.handle);\r
+ p.on('beforedestroy', this.tracker.destroy, this.tracker);\r
+ },\r
+\r
+ // private\r
+ onDragStart: function(e){\r
+ this.dragging = true;\r
+ this.startHeight = this.panel.el.getHeight();\r
+ this.fireEvent('dragstart', this, e);\r
+ },\r
+\r
+ // private\r
+ onDrag: function(e){\r
+ this.panel.setHeight((this.startHeight-this.tracker.getOffset()[1]).constrain(this.minHeight, this.maxHeight));\r
+ this.fireEvent('drag', this, e);\r
+ },\r
+\r
+ // private\r
+ onDragEnd: function(e){\r
+ this.dragging = false;\r
+ this.fireEvent('dragend', this, e);\r
+ }\r
+});\r
+Ext.preg('panelresizer', Ext.ux.PanelResizer);Ext.ux.Portal = Ext.extend(Ext.Panel, {\r
+ layout : 'column',\r
+ autoScroll : true,\r
+ cls : 'x-portal',\r
+ defaultType : 'portalcolumn',\r
+ \r
+ initComponent : function(){\r
+ Ext.ux.Portal.superclass.initComponent.call(this);\r
+ this.addEvents({\r
+ validatedrop:true,\r
+ beforedragover:true,\r
+ dragover:true,\r
+ beforedrop:true,\r
+ drop:true\r
+ });\r
+ },\r
+\r
+ initEvents : function(){\r
+ Ext.ux.Portal.superclass.initEvents.call(this);\r
+ this.dd = new Ext.ux.Portal.DropZone(this, this.dropConfig);\r
+ },\r
+ \r
+ beforeDestroy : function() {\r
+ if(this.dd){\r
+ this.dd.unreg();\r
+ }\r
+ Ext.ux.Portal.superclass.beforeDestroy.call(this);\r
+ }\r
+});\r
+\r
+Ext.reg('portal', Ext.ux.Portal);\r
+\r
+\r
+Ext.ux.Portal.DropZone = function(portal, cfg){\r
+ this.portal = portal;\r
+ Ext.dd.ScrollManager.register(portal.body);\r
+ Ext.ux.Portal.DropZone.superclass.constructor.call(this, portal.bwrap.dom, cfg);\r
+ portal.body.ddScrollConfig = this.ddScrollConfig;\r
+};\r
+\r
+Ext.extend(Ext.ux.Portal.DropZone, Ext.dd.DropTarget, {\r
+ ddScrollConfig : {\r
+ vthresh: 50,\r
+ hthresh: -1,\r
+ animate: true,\r
+ increment: 200\r
+ },\r
+\r
+ createEvent : function(dd, e, data, col, c, pos){\r
+ return {\r
+ portal: this.portal,\r
+ panel: data.panel,\r
+ columnIndex: col,\r
+ column: c,\r
+ position: pos,\r
+ data: data,\r
+ source: dd,\r
+ rawEvent: e,\r
+ status: this.dropAllowed\r
+ };\r
+ },\r
+\r
+ notifyOver : function(dd, e, data){\r
+ var xy = e.getXY(), portal = this.portal, px = dd.proxy;\r
+\r
+ // case column widths\r
+ if(!this.grid){\r
+ this.grid = this.getGrid();\r
+ }\r
+\r
+ // handle case scroll where scrollbars appear during drag\r
+ var cw = portal.body.dom.clientWidth;\r
+ if(!this.lastCW){\r
+ this.lastCW = cw;\r
+ }else if(this.lastCW != cw){\r
+ this.lastCW = cw;\r
+ portal.doLayout();\r
+ this.grid = this.getGrid();\r
+ }\r
+\r
+ // determine column\r
+ var col = 0, xs = this.grid.columnX, cmatch = false;\r
+ for(var len = xs.length; col < len; col++){\r
+ if(xy[0] < (xs[col].x + xs[col].w)){\r
+ cmatch = true;\r
+ break;\r
+ }\r
+ }\r
+ // no match, fix last index\r
+ if(!cmatch){\r
+ col--;\r
+ }\r
+\r
+ // find insert position\r
+ var p, match = false, pos = 0,\r
+ c = portal.items.itemAt(col),\r
+ items = c.items.items, overSelf = false;\r
+\r
+ for(var len = items.length; pos < len; pos++){\r
+ p = items[pos];\r
+ var h = p.el.getHeight();\r
+ if(h === 0){\r
+ overSelf = true;\r
+ }\r
+ else if((p.el.getY()+(h/2)) > xy[1]){\r
+ match = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ pos = (match && p ? pos : c.items.getCount()) + (overSelf ? -1 : 0);\r
+ var overEvent = this.createEvent(dd, e, data, col, c, pos);\r
+\r
+ if(portal.fireEvent('validatedrop', overEvent) !== false &&\r
+ portal.fireEvent('beforedragover', overEvent) !== false){\r
+\r
+ // make sure proxy width is fluid\r
+ px.getProxy().setWidth('auto');\r
+\r
+ if(p){\r
+ px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null);\r
+ }else{\r
+ px.moveProxy(c.el.dom, null);\r
+ }\r
+\r
+ this.lastPos = {c: c, col: col, p: overSelf || (match && p) ? pos : false};\r
+ this.scrollPos = portal.body.getScroll();\r
+\r
+ portal.fireEvent('dragover', overEvent);\r
+\r
+ return overEvent.status;\r
+ }else{\r
+ return overEvent.status;\r
+ }\r
+\r
+ },\r
+\r
+ notifyOut : function(){\r
+ delete this.grid;\r
+ },\r
+\r
+ notifyDrop : function(dd, e, data){\r
+ delete this.grid;\r
+ if(!this.lastPos){\r
+ return;\r
+ }\r
+ var c = this.lastPos.c, col = this.lastPos.col, pos = this.lastPos.p;\r
+\r
+ var dropEvent = this.createEvent(dd, e, data, col, c,\r
+ pos !== false ? pos : c.items.getCount());\r
+\r
+ if(this.portal.fireEvent('validatedrop', dropEvent) !== false &&\r
+ this.portal.fireEvent('beforedrop', dropEvent) !== false){\r
+\r
+ dd.proxy.getProxy().remove();\r
+ dd.panel.el.dom.parentNode.removeChild(dd.panel.el.dom);\r
+ \r
+ if(pos !== false){\r
+ if(c == dd.panel.ownerCt && (c.items.items.indexOf(dd.panel) <= pos)){\r
+ pos++;\r
+ }\r
+ c.insert(pos, dd.panel);\r
+ }else{\r
+ c.add(dd.panel);\r
+ }\r
+ \r
+ c.doLayout();\r
+\r
+ this.portal.fireEvent('drop', dropEvent);\r
+\r
+ // scroll position is lost on drop, fix it\r
+ var st = this.scrollPos.top;\r
+ if(st){\r
+ var d = this.portal.body.dom;\r
+ setTimeout(function(){\r
+ d.scrollTop = st;\r
+ }, 10);\r
+ }\r
+\r
+ }\r
+ delete this.lastPos;\r
+ },\r
+\r
+ // internal cache of body and column coords\r
+ getGrid : function(){\r
+ var box = this.portal.bwrap.getBox();\r
+ box.columnX = [];\r
+ this.portal.items.each(function(c){\r
+ box.columnX.push({x: c.el.getX(), w: c.el.getWidth()});\r
+ });\r
+ return box;\r
+ },\r
+\r
+ // unregister the dropzone from ScrollManager\r
+ unreg: function() {\r
+ //Ext.dd.ScrollManager.unregister(this.portal.body);\r
+ Ext.ux.Portal.DropZone.superclass.unreg.call(this);\r
+ }\r
+});\r
+Ext.ux.PortalColumn = Ext.extend(Ext.Container, {\r
+ layout : 'anchor',\r
+ //autoEl : 'div',//already defined by Ext.Component\r
+ defaultType : 'portlet',\r
+ cls : 'x-portal-column'\r
+});\r
+\r
+Ext.reg('portalcolumn', Ext.ux.PortalColumn);\r
+Ext.ux.Portlet = Ext.extend(Ext.Panel, {\r
+ anchor : '100%',\r
+ frame : true,\r
+ collapsible : true,\r
+ draggable : true,\r
+ cls : 'x-portlet'\r
+});\r
+\r
+Ext.reg('portlet', Ext.ux.Portlet);\r
+/**
+* @class Ext.ux.ProgressBarPager
+* @extends Object
+* Plugin (ptype = 'tabclosemenu') for displaying a progressbar inside of a paging toolbar instead of plain text
+*
+* @ptype progressbarpager
+* @constructor
+* Create a new ItemSelector
+* @param {Object} config Configuration options
+* @xtype itemselector
+*/
+Ext.ux.ProgressBarPager = Ext.extend(Object, {
+ /**
+ * @cfg {Integer} progBarWidth
+ * <p>The default progress bar width. Default is 225.</p>
+ */
+ progBarWidth : 225,
+ /**
+ * @cfg {String} defaultText
+ * <p>The text to display while the store is loading. Default is 'Loading...'</p>
+ */
+ defaultText : 'Loading...',
+ /**
+ * @cfg {Object} defaultAnimCfg
+ * <p>A {@link Ext.Fx Ext.Fx} configuration object. Default is { duration : 1, easing : 'bounceOut' }.</p>
+ */
+ defaultAnimCfg : {
+ duration : 1,
+ easing : 'bounceOut'
+ },
+ constructor : function(config) {
+ if (config) {
+ Ext.apply(this, config);
+ }
+ },
+ //public
+ init : function (parent) {
+
+ if(parent.displayInfo){
+ this.parent = parent;
+ var ind = parent.items.indexOf(parent.displayItem);
+ parent.remove(parent.displayItem, true);
+ this.progressBar = new Ext.ProgressBar({
+ text : this.defaultText,
+ width : this.progBarWidth,
+ animate : this.defaultAnimCfg
+ });
+
+ parent.displayItem = this.progressBar;
+
+ parent.add(parent.displayItem);
+ parent.doLayout();
+ Ext.apply(parent, this.parentOverrides);
+
+ this.progressBar.on('render', function(pb) {
+ pb.el.applyStyles('cursor:pointer');
+
+ pb.el.on('click', this.handleProgressBarClick, this);
+ }, this);
+
+
+ // Remove the click handler from the
+ this.progressBar.on({
+ scope : this,
+ beforeDestroy : function() {
+ this.progressBar.el.un('click', this.handleProgressBarClick, this);
+ }
+ });
+
+ }
+
+ },
+ // private
+ // This method handles the click for the progress bar
+ handleProgressBarClick : function(e){
+ var parent = this.parent;
+ var displayItem = parent.displayItem;
+
+ var box = this.progressBar.getBox();
+ var xy = e.getXY();
+ var position = xy[0]-box.x;
+ var pages = Math.ceil(parent.store.getTotalCount()/parent.pageSize);
+
+ var newpage = Math.ceil(position/(displayItem.width/pages));
+ parent.changePage(newpage);
+ },
+
+ // private, overriddes
+ parentOverrides : {
+ // private
+ // This method updates the information via the progress bar.
+ updateInfo : function(){
+ if(this.displayItem){
+ var count = this.store.getCount();
+ var pgData = this.getPageData();
+ var pageNum = this.readPage(pgData);
+
+ var msg = count == 0 ?
+ this.emptyMsg :
+ String.format(
+ this.displayMsg,
+ this.cursor+1, this.cursor+count, this.store.getTotalCount()
+ );
+
+ pageNum = pgData.activePage; ;
+
+ var pct = pageNum / pgData.pages;
+
+ this.displayItem.updateProgress(pct, msg, this.animate || this.defaultAnimConfig);
+ }
+ }
+ }
+});
+Ext.preg('progressbarpager', Ext.ux.ProgressBarPager);
+
+Ext.ns('Ext.ux.grid');
+
+/**
+ * @class Ext.ux.grid.RowEditor
+ * @extends Ext.Panel
+ * Plugin (ptype = 'roweditor') that adds the ability to rapidly edit full rows in a grid.
+ * A validation mode may be enabled which uses AnchorTips to notify the user of all
+ * validation errors at once.
+ *
+ * @ptype roweditor
+ */
+Ext.ux.grid.RowEditor = Ext.extend(Ext.Panel, {
+ floating: true,
+ shadow: false,
+ layout: 'hbox',
+ cls: 'x-small-editor',
+ buttonAlign: 'center',
+ baseCls: 'x-row-editor',
+ elements: 'header,footer,body',
+ frameWidth: 5,
+ buttonPad: 3,
+ clicksToEdit: 'auto',
+ monitorValid: true,
+ focusDelay: 250,
+ errorSummary: true,
+
+ defaults: {
+ normalWidth: true
+ },
+
+ initComponent: function(){
+ Ext.ux.grid.RowEditor.superclass.initComponent.call(this);
+ this.addEvents(
+ /**
+ * @event beforeedit
+ * Fired before the row editor is activated.
+ * If the listener returns <tt>false</tt> the editor will not be activated.
+ * @param {Ext.ux.grid.RowEditor} roweditor This object
+ * @param {Number} rowIndex The rowIndex of the row just edited
+ */
+ 'beforeedit',
+ /**
+ * @event validateedit
+ * Fired after a row is edited and passes validation.
+ * If the listener returns <tt>false</tt> changes to the record will not be set.
+ * @param {Ext.ux.grid.RowEditor} roweditor This object
+ * @param {Object} changes Object with changes made to the record.
+ * @param {Ext.data.Record} r The Record that was edited.
+ * @param {Number} rowIndex The rowIndex of the row just edited
+ */
+ 'validateedit',
+ /**
+ * @event afteredit
+ * Fired after a row is edited and passes validation. This event is fired
+ * after the store's update event is fired with this edit.
+ * @param {Ext.ux.grid.RowEditor} roweditor This object
+ * @param {Object} changes Object with changes made to the record.
+ * @param {Ext.data.Record} r The Record that was edited.
+ * @param {Number} rowIndex The rowIndex of the row just edited
+ */
+ 'afteredit'
+ );
+ },
+
+ init: function(grid){
+ this.grid = grid;
+ this.ownerCt = grid;
+ if(this.clicksToEdit === 2){
+ grid.on('rowdblclick', this.onRowDblClick, this);
+ }else{
+ grid.on('rowclick', this.onRowClick, this);
+ if(Ext.isIE){
+ grid.on('rowdblclick', this.onRowDblClick, this);
+ }
+ }
+
+ // stopEditing without saving when a record is removed from Store.
+ grid.getStore().on('remove', function() {
+ this.stopEditing(false);
+ },this);
+
+ grid.on({
+ scope: this,
+ keydown: this.onGridKey,
+ columnresize: this.verifyLayout,
+ columnmove: this.refreshFields,
+ reconfigure: this.refreshFields,
+ destroy : this.destroy,
+ bodyscroll: {
+ buffer: 250,
+ fn: this.positionButtons
+ }
+ });
+ grid.getColumnModel().on('hiddenchange', this.verifyLayout, this, {delay:1});
+ grid.getView().on('refresh', this.stopEditing.createDelegate(this, []));
+ },
+
+ refreshFields: function(){
+ this.initFields();
+ this.verifyLayout();
+ },
+
+ isDirty: function(){
+ var dirty;
+ this.items.each(function(f){
+ if(String(this.values[f.id]) !== String(f.getValue())){
+ dirty = true;
+ return false;
+ }
+ }, this);
+ return dirty;
+ },
+
+ startEditing: function(rowIndex, doFocus){
+ if(this.editing && this.isDirty()){
+ this.showTooltip('You need to commit or cancel your changes');
+ return;
+ }
+ this.editing = true;
+ if(typeof rowIndex == 'object'){
+ rowIndex = this.grid.getStore().indexOf(rowIndex);
+ }
+ if(this.fireEvent('beforeedit', this, rowIndex) !== false){
+ var g = this.grid, view = g.getView();
+ var row = view.getRow(rowIndex);
+ var record = g.store.getAt(rowIndex);
+ this.record = record;
+ this.rowIndex = rowIndex;
+ this.values = {};
+ if(!this.rendered){
+ this.render(view.getEditorParent());
+ }
+ var w = Ext.fly(row).getWidth();
+ this.setSize(w);
+ if(!this.initialized){
+ this.initFields();
+ }
+ var cm = g.getColumnModel(), fields = this.items.items, f, val;
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++){
+ val = this.preEditValue(record, cm.getDataIndex(i));
+ f = fields[i];
+ f.setValue(val);
+ this.values[f.id] = val || '';
+ }
+ this.verifyLayout(true);
+ if(!this.isVisible()){
+ this.setPagePosition(Ext.fly(row).getXY());
+ } else{
+ this.el.setXY(Ext.fly(row).getXY(), {duration:0.15});
+ }
+ if(!this.isVisible()){
+ this.show().doLayout();
+ }
+ if(doFocus !== false){
+ this.doFocus.defer(this.focusDelay, this);
+ }
+ }
+ },
+
+ stopEditing : function(saveChanges){
+ this.editing = false;
+ if(!this.isVisible()){
+ return;
+ }
+ if(saveChanges === false || !this.isValid()){
+ this.hide();
+ return;
+ }
+ var changes = {}, r = this.record, hasChange = false;
+ var cm = this.grid.colModel, fields = this.items.items;
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++){
+ if(!cm.isHidden(i)){
+ var dindex = cm.getDataIndex(i);
+ if(!Ext.isEmpty(dindex)){
+ var oldValue = r.data[dindex];
+ var value = this.postEditValue(fields[i].getValue(), oldValue, r, dindex);
+ if(String(oldValue) !== String(value)){
+ changes[dindex] = value;
+ hasChange = true;
+ }
+ }
+ }
+ }
+ if(hasChange && this.fireEvent('validateedit', this, changes, r, this.rowIndex) !== false){
+ r.beginEdit();
+ for(var k in changes){
+ if(changes.hasOwnProperty(k)){
+ r.set(k, changes[k]);
+ }
+ }
+ r.endEdit();
+ this.fireEvent('afteredit', this, changes, r, this.rowIndex);
+ }
+ this.hide();
+ },
+
+ verifyLayout: function(force){
+ if(this.el && (this.isVisible() || force === true)){
+ var row = this.grid.getView().getRow(this.rowIndex);
+ this.setSize(Ext.fly(row).getWidth(), Ext.isIE ? Ext.fly(row).getHeight() + (Ext.isBorderBox ? 9 : 0) : undefined);
+ var cm = this.grid.colModel, fields = this.items.items;
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++){
+ if(!cm.isHidden(i)){
+ var adjust = 0;
+ if(i === 0){
+ adjust += 0; // outer padding
+ }
+ if(i === (len - 1)){
+ adjust += 3; // outer padding
+ } else{
+ adjust += 1;
+ }
+ fields[i].show();
+ fields[i].setWidth(cm.getColumnWidth(i) - adjust);
+ } else{
+ fields[i].hide();
+ }
+ }
+ this.doLayout();
+ this.positionButtons();
+ }
+ },
+
+ slideHide : function(){
+ this.hide();
+ },
+
+ initFields: function(){
+ var cm = this.grid.getColumnModel(), pm = Ext.layout.ContainerLayout.prototype.parseMargins;
+ this.removeAll(false);
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++){
+ var c = cm.getColumnAt(i);
+ var ed = c.getEditor();
+ if(!ed){
+ ed = c.displayEditor || new Ext.form.DisplayField();
+ }
+ if(i == 0){
+ ed.margins = pm('0 1 2 1');
+ } else if(i == len - 1){
+ ed.margins = pm('0 0 2 1');
+ } else{
+ ed.margins = pm('0 1 2');
+ }
+ ed.setWidth(cm.getColumnWidth(i));
+ ed.column = c;
+ if(ed.ownerCt !== this){
+ ed.on('focus', this.ensureVisible, this);
+ ed.on('specialkey', this.onKey, this);
+ }
+ this.insert(i, ed);
+ }
+ this.initialized = true;
+ },
+
+ onKey: function(f, e){
+ if(e.getKey() === e.ENTER){
+ this.stopEditing(true);
+ e.stopPropagation();
+ }
+ },
+
+ onGridKey: function(e){
+ if(e.getKey() === e.ENTER && !this.isVisible()){
+ var r = this.grid.getSelectionModel().getSelected();
+ if(r){
+ var index = this.grid.store.indexOf(r);
+ this.startEditing(index);
+ e.stopPropagation();
+ }
+ }
+ },
+
+ ensureVisible: function(editor){
+ if(this.isVisible()){
+ this.grid.getView().ensureVisible(this.rowIndex, this.grid.colModel.getIndexById(editor.column.id), true);
+ }
+ },
+
+ onRowClick: function(g, rowIndex, e){
+ if(this.clicksToEdit == 'auto'){
+ var li = this.lastClickIndex;
+ this.lastClickIndex = rowIndex;
+ if(li != rowIndex && !this.isVisible()){
+ return;
+ }
+ }
+ this.startEditing(rowIndex, false);
+ this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
+ },
+
+ onRowDblClick: function(g, rowIndex, e){
+ this.startEditing(rowIndex, false);
+ this.doFocus.defer(this.focusDelay, this, [e.getPoint()]);
+ },
+
+ onRender: function(){
+ Ext.ux.grid.RowEditor.superclass.onRender.apply(this, arguments);
+ this.el.swallowEvent(['keydown', 'keyup', 'keypress']);
+ this.btns = new Ext.Panel({
+ baseCls: 'x-plain',
+ cls: 'x-btns',
+ elements:'body',
+ layout: 'table',
+ width: (this.minButtonWidth * 2) + (this.frameWidth * 2) + (this.buttonPad * 4), // width must be specified for IE
+ items: [{
+ ref: 'saveBtn',
+ itemId: 'saveBtn',
+ xtype: 'button',
+ text: this.saveText || 'Save',
+ width: this.minButtonWidth,
+ handler: this.stopEditing.createDelegate(this, [true])
+ }, {
+ xtype: 'button',
+ text: this.cancelText || 'Cancel',
+ width: this.minButtonWidth,
+ handler: this.stopEditing.createDelegate(this, [false])
+ }]
+ });
+ this.btns.render(this.bwrap);
+ },
+
+ afterRender: function(){
+ Ext.ux.grid.RowEditor.superclass.afterRender.apply(this, arguments);
+ this.positionButtons();
+ if(this.monitorValid){
+ this.startMonitoring();
+ }
+ },
+
+ onShow: function(){
+ if(this.monitorValid){
+ this.startMonitoring();
+ }
+ Ext.ux.grid.RowEditor.superclass.onShow.apply(this, arguments);
+ },
+
+ onHide: function(){
+ Ext.ux.grid.RowEditor.superclass.onHide.apply(this, arguments);
+ this.stopMonitoring();
+ this.grid.getView().focusRow(this.rowIndex);
+ },
+
+ positionButtons: function(){
+ if(this.btns){
+ var h = this.el.dom.clientHeight;
+ var view = this.grid.getView();
+ var scroll = view.scroller.dom.scrollLeft;
+ var width = view.mainBody.getWidth();
+ var bw = this.btns.getWidth();
+ this.btns.el.shift({left: (width/2)-(bw/2)+scroll, top: h - 2, stopFx: true, duration:0.2});
+ }
+ },
+
+ // private
+ preEditValue : function(r, field){
+ var value = r.data[field];
+ return this.autoEncode && typeof value === 'string' ? Ext.util.Format.htmlDecode(value) : value;
+ },
+
+ // private
+ postEditValue : function(value, originalValue, r, field){
+ return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlEncode(value) : value;
+ },
+
+ doFocus: function(pt){
+ if(this.isVisible()){
+ var index = 0;
+ if(pt){
+ index = this.getTargetColumnIndex(pt);
+ }
+ var cm = this.grid.getColumnModel();
+ for(var i = index||0, len = cm.getColumnCount(); i < len; i++){
+ var c = cm.getColumnAt(i);
+ if(!c.hidden && c.getEditor()){
+ c.getEditor().focus();
+ break;
+ }
+ }
+ }
+ },
+
+ getTargetColumnIndex: function(pt){
+ var grid = this.grid, v = grid.view;
+ var x = pt.left;
+ var cms = grid.colModel.config;
+ var i = 0, match = false;
+ for(var len = cms.length, c; c = cms[i]; i++){
+ if(!c.hidden){
+ if(Ext.fly(v.getHeaderCell(i)).getRegion().right >= x){
+ match = i;
+ break;
+ }
+ }
+ }
+ return match;
+ },
+
+ startMonitoring : function(){
+ if(!this.bound && this.monitorValid){
+ this.bound = true;
+ Ext.TaskMgr.start({
+ run : this.bindHandler,
+ interval : this.monitorPoll || 200,
+ scope: this
+ });
+ }
+ },
+
+ stopMonitoring : function(){
+ this.bound = false;
+ if(this.tooltip){
+ this.tooltip.hide();
+ }
+ },
+
+ isValid: function(){
+ var valid = true;
+ this.items.each(function(f){
+ if(!f.isValid(true)){
+ valid = false;
+ return false;
+ }
+ });
+ return valid;
+ },
+
+ // private
+ bindHandler : function(){
+ if(!this.bound){
+ return false; // stops binding
+ }
+ var valid = this.isValid();
+ if(!valid && this.errorSummary){
+ this.showTooltip(this.getErrorText().join(''));
+ }
+ this.btns.saveBtn.setDisabled(!valid);
+ this.fireEvent('validation', this, valid);
+ },
+
+ showTooltip: function(msg){
+ var t = this.tooltip;
+ if(!t){
+ t = this.tooltip = new Ext.ToolTip({
+ maxWidth: 600,
+ cls: 'errorTip',
+ width: 300,
+ title: 'Errors',
+ autoHide: false,
+ anchor: 'left',
+ anchorToTarget: true,
+ mouseOffset: [40,0]
+ });
+ }
+ t.initTarget(this.items.last().getEl());
+ if(!t.rendered){
+ t.show();
+ t.hide();
+ }
+ t.body.update(msg);
+ t.doAutoWidth();
+ t.show();
+ },
+
+ getErrorText: function(){
+ var data = ['<ul>'];
+ this.items.each(function(f){
+ if(!f.isValid(true)){
+ data.push('<li>', f.activeError, '</li>');
+ }
+ });
+ data.push('</ul>');
+ return data;
+ }
+});
+Ext.preg('roweditor', Ext.ux.grid.RowEditor);
+
+Ext.override(Ext.form.Field, {
+ markInvalid : function(msg){
+ if(!this.rendered || this.preventMark){ // not rendered
+ return;
+ }
+ msg = msg || this.invalidText;
+
+ var mt = this.getMessageHandler();
+ if(mt){
+ mt.mark(this, msg);
+ }else if(this.msgTarget){
+ this.el.addClass(this.invalidClass);
+ var t = Ext.getDom(this.msgTarget);
+ if(t){
+ t.innerHTML = msg;
+ t.style.display = this.msgDisplay;
+ }
+ }
+ this.activeError = msg;
+ this.fireEvent('invalid', this, msg);
+ }
+});
+
+Ext.override(Ext.ToolTip, {
+ doAutoWidth : function(){
+ var bw = this.body.getTextWidth();
+ if(this.title){
+ bw = Math.max(bw, this.header.child('span').getTextWidth(this.title));
+ }
+ bw += this.getFrameWidth() + (this.closable ? 20 : 0) + this.body.getPadding("lr") + 20;
+ this.setWidth(bw.constrain(this.minWidth, this.maxWidth));
+
+ // IE7 repaint bug on initial show
+ if(Ext.isIE7 && !this.repainted){
+ this.el.repaint();
+ this.repainted = true;
+ }
+ }
+});
+Ext.ns('Ext.ux.grid');\r
+\r
+/**\r
+ * @class Ext.ux.grid.RowExpander\r
+ * @extends Ext.util.Observable\r
+ * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables\r
+ * a second row body which expands/contracts. The expand/contract behavior is configurable to react\r
+ * on clicking of the column, double click of the row, and/or hitting enter while a row is selected.\r
+ *\r
+ * @ptype rowexpander\r
+ */\r
+Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, {\r
+ /**\r
+ * @cfg {Boolean} expandOnEnter\r
+ * <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter\r
+ * key is pressed (defaults to <tt>true</tt>).\r
+ */\r
+ expandOnEnter : true,\r
+ /**\r
+ * @cfg {Boolean} expandOnDblClick\r
+ * <tt>true</tt> to toggle a row between expanded/collapsed when double clicked\r
+ * (defaults to <tt>true</tt>).\r
+ */\r
+ expandOnDblClick : true,\r
+\r
+ header : '',\r
+ width : 20,\r
+ sortable : false,\r
+ fixed : true,\r
+ menuDisabled : true,\r
+ dataIndex : '',\r
+ id : 'expander',\r
+ lazyRender : true,\r
+ enableCaching : true,\r
+\r
+ constructor: function(config){\r
+ Ext.apply(this, config);\r
+\r
+ this.addEvents({\r
+ /**\r
+ * @event beforeexpand\r
+ * Fires before the row expands. Have the listener return false to prevent the row from expanding.\r
+ * @param {Object} this RowExpander object.\r
+ * @param {Object} Ext.data.Record Record for the selected row.\r
+ * @param {Object} body body element for the secondary row.\r
+ * @param {Number} rowIndex The current row index.\r
+ */\r
+ beforeexpand: true,\r
+ /**\r
+ * @event expand\r
+ * Fires after the row expands.\r
+ * @param {Object} this RowExpander object.\r
+ * @param {Object} Ext.data.Record Record for the selected row.\r
+ * @param {Object} body body element for the secondary row.\r
+ * @param {Number} rowIndex The current row index.\r
+ */\r
+ expand: true,\r
+ /**\r
+ * @event beforecollapse\r
+ * Fires before the row collapses. Have the listener return false to prevent the row from collapsing.\r
+ * @param {Object} this RowExpander object.\r
+ * @param {Object} Ext.data.Record Record for the selected row.\r
+ * @param {Object} body body element for the secondary row.\r
+ * @param {Number} rowIndex The current row index.\r
+ */\r
+ beforecollapse: true,\r
+ /**\r
+ * @event collapse\r
+ * Fires after the row collapses.\r
+ * @param {Object} this RowExpander object.\r
+ * @param {Object} Ext.data.Record Record for the selected row.\r
+ * @param {Object} body body element for the secondary row.\r
+ * @param {Number} rowIndex The current row index.\r
+ */\r
+ collapse: true\r
+ });\r
+\r
+ Ext.ux.grid.RowExpander.superclass.constructor.call(this);\r
+\r
+ if(this.tpl){\r
+ if(typeof this.tpl == 'string'){\r
+ this.tpl = new Ext.Template(this.tpl);\r
+ }\r
+ this.tpl.compile();\r
+ }\r
+\r
+ this.state = {};\r
+ this.bodyContent = {};\r
+ },\r
+\r
+ getRowClass : function(record, rowIndex, p, ds){\r
+ p.cols = p.cols-1;\r
+ var content = this.bodyContent[record.id];\r
+ if(!content && !this.lazyRender){\r
+ content = this.getBodyContent(record, rowIndex);\r
+ }\r
+ if(content){\r
+ p.body = content;\r
+ }\r
+ return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';\r
+ },\r
+\r
+ init : function(grid){\r
+ this.grid = grid;\r
+\r
+ var view = grid.getView();\r
+ view.getRowClass = this.getRowClass.createDelegate(this);\r
+\r
+ view.enableRowBody = true;\r
+\r
+\r
+ grid.on('render', this.onRender, this);\r
+ grid.on('destroy', this.onDestroy, this);\r
+ },\r
+\r
+ // @private\r
+ onRender: function() {\r
+ var grid = this.grid;\r
+ var mainBody = grid.getView().mainBody;\r
+ mainBody.on('mousedown', this.onMouseDown, this, {delegate: '.x-grid3-row-expander'});\r
+ if (this.expandOnEnter) {\r
+ this.keyNav = new Ext.KeyNav(this.grid.getGridEl(), {\r
+ 'enter' : this.onEnter,\r
+ scope: this\r
+ });\r
+ }\r
+ if (this.expandOnDblClick) {\r
+ grid.on('rowdblclick', this.onRowDblClick, this);\r
+ }\r
+ },\r
+ \r
+ // @private \r
+ onDestroy: function() {\r
+ this.keyNav.disable();\r
+ delete this.keyNav;\r
+ var mainBody = this.grid.getView().mainBody;\r
+ mainBody.un('mousedown', this.onMouseDown, this);\r
+ },\r
+ // @private\r
+ onRowDblClick: function(grid, rowIdx, e) {\r
+ this.toggleRow(rowIdx);\r
+ },\r
+\r
+ onEnter: function(e) {\r
+ var g = this.grid;\r
+ var sm = g.getSelectionModel();\r
+ var sels = sm.getSelections();\r
+ for (var i = 0, len = sels.length; i < len; i++) {\r
+ var rowIdx = g.getStore().indexOf(sels[i]);\r
+ this.toggleRow(rowIdx);\r
+ }\r
+ },\r
+\r
+ getBodyContent : function(record, index){\r
+ if(!this.enableCaching){\r
+ return this.tpl.apply(record.data);\r
+ }\r
+ var content = this.bodyContent[record.id];\r
+ if(!content){\r
+ content = this.tpl.apply(record.data);\r
+ this.bodyContent[record.id] = content;\r
+ }\r
+ return content;\r
+ },\r
+\r
+ onMouseDown : function(e, t){\r
+ e.stopEvent();\r
+ var row = e.getTarget('.x-grid3-row');\r
+ this.toggleRow(row);\r
+ },\r
+\r
+ renderer : function(v, p, record){\r
+ p.cellAttr = 'rowspan="2"';\r
+ return '<div class="x-grid3-row-expander"> </div>';\r
+ },\r
+\r
+ beforeExpand : function(record, body, rowIndex){\r
+ if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){\r
+ if(this.tpl && this.lazyRender){\r
+ body.innerHTML = this.getBodyContent(record, rowIndex);\r
+ }\r
+ return true;\r
+ }else{\r
+ return false;\r
+ }\r
+ },\r
+\r
+ toggleRow : function(row){\r
+ if(typeof row == 'number'){\r
+ row = this.grid.view.getRow(row);\r
+ }\r
+ this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);\r
+ },\r
+\r
+ expandRow : function(row){\r
+ if(typeof row == 'number'){\r
+ row = this.grid.view.getRow(row);\r
+ }\r
+ var record = this.grid.store.getAt(row.rowIndex);\r
+ var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);\r
+ if(this.beforeExpand(record, body, row.rowIndex)){\r
+ this.state[record.id] = true;\r
+ Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');\r
+ this.fireEvent('expand', this, record, body, row.rowIndex);\r
+ }\r
+ },\r
+\r
+ collapseRow : function(row){\r
+ if(typeof row == 'number'){\r
+ row = this.grid.view.getRow(row);\r
+ }\r
+ var record = this.grid.store.getAt(row.rowIndex);\r
+ var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);\r
+ if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){\r
+ this.state[record.id] = false;\r
+ Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');\r
+ this.fireEvent('collapse', this, record, body, row.rowIndex);\r
+ }\r
+ }\r
+});\r
+\r
+Ext.preg('rowexpander', Ext.ux.grid.RowExpander);\r
+\r
+//backwards compat\r
+Ext.grid.RowExpander = Ext.ux.grid.RowExpander;// We are adding these custom layouts to a namespace that does not
+// exist by default in Ext, so we have to add the namespace first:
+Ext.ns('Ext.ux.layout');
+
+/**
+ * @class Ext.ux.layout.RowLayout
+ * @extends Ext.layout.ContainerLayout
+ * <p>This is the layout style of choice for creating structural layouts in a multi-row format where the height of
+ * each row can be specified as a percentage or fixed height. Row widths can also be fixed, percentage or auto.
+ * This class is intended to be extended or created via the layout:'ux.row' {@link Ext.Container#layout} config,
+ * and should generally not need to be created directly via the new keyword.</p>
+ * <p>RowLayout does not have any direct config options (other than inherited ones), but it does support a
+ * specific config property of <b><tt>rowHeight</tt></b> that can be included in the config of any panel added to it. The
+ * layout will use the rowHeight (if present) or height of each panel during layout to determine how to size each panel.
+ * If height or rowHeight is not specified for a given panel, its height will default to the panel's height (or auto).</p>
+ * <p>The height property is always evaluated as pixels, and must be a number greater than or equal to 1.
+ * The rowHeight property is always evaluated as a percentage, and must be a decimal value greater than 0 and
+ * less than 1 (e.g., .25).</p>
+ * <p>The basic rules for specifying row heights are pretty simple. The logic makes two passes through the
+ * set of contained panels. During the first layout pass, all panels that either have a fixed height or none
+ * specified (auto) are skipped, but their heights are subtracted from the overall container height. During the second
+ * pass, all panels with rowHeights are assigned pixel heights in proportion to their percentages based on
+ * the total <b>remaining</b> container height. In other words, percentage height panels are designed to fill the space
+ * left over by all the fixed-height and/or auto-height panels. Because of this, while you can specify any number of rows
+ * with different percentages, the rowHeights must always add up to 1 (or 100%) when added together, otherwise your
+ * layout may not render as expected. Example usage:</p>
+ * <pre><code>
+// All rows are percentages -- they must add up to 1
+var p = new Ext.Panel({
+ title: 'Row Layout - Percentage Only',
+ layout:'ux.row',
+ items: [{
+ title: 'Row 1',
+ rowHeight: .25
+ },{
+ title: 'Row 2',
+ rowHeight: .6
+ },{
+ title: 'Row 3',
+ rowHeight: .15
+ }]
+});
+
+// Mix of height and rowHeight -- all rowHeight values must add
+// up to 1. The first row will take up exactly 120px, and the last two
+// rows will fill the remaining container height.
+var p = new Ext.Panel({
+ title: 'Row Layout - Mixed',
+ layout:'ux.row',
+ items: [{
+ title: 'Row 1',
+ height: 120,
+ // standard panel widths are still supported too:
+ width: '50%' // or 200
+ },{
+ title: 'Row 2',
+ rowHeight: .8,
+ width: 300
+ },{
+ title: 'Row 3',
+ rowHeight: .2
+ }]
+});
+</code></pre>
+ */
+Ext.ux.layout.RowLayout = Ext.extend(Ext.layout.ContainerLayout, {
+ // private
+ monitorResize:true,
+
+ // private
+ isValidParent : function(c, target){
+ return c.getEl().dom.parentNode == this.innerCt.dom;
+ },
+
+ // private
+ onLayout : function(ct, target){
+ var rs = ct.items.items, len = rs.length, r, i;
+
+ if(!this.innerCt){
+ target.addClass('ux-row-layout-ct');
+ this.innerCt = target.createChild({cls:'x-row-inner'});
+ }
+ this.renderAll(ct, this.innerCt);
+
+ var size = target.getViewSize();
+
+ if(size.width < 1 && size.height < 1){ // display none?
+ return;
+ }
+
+ var h = size.height - target.getPadding('tb'),
+ ph = h;
+
+ this.innerCt.setSize({height:h});
+
+ // some rows can be percentages while others are fixed
+ // so we need to make 2 passes
+
+ for(i = 0; i < len; i++){
+ r = rs[i];
+ if(!r.rowHeight){
+ ph -= (r.getSize().height + r.getEl().getMargins('tb'));
+ }
+ }
+
+ ph = ph < 0 ? 0 : ph;
+
+ for(i = 0; i < len; i++){
+ r = rs[i];
+ if(r.rowHeight){
+ r.setSize({height: Math.floor(r.rowHeight*ph) - r.getEl().getMargins('tb')});
+ }
+ }
+ }
+
+ /**
+ * @property activeItem
+ * @hide
+ */
+});
+
+Ext.Container.LAYOUTS['ux.row'] = Ext.ux.layout.RowLayout;
+Ext.ns('Ext.ux.form');\r
+\r
+Ext.ux.form.SearchField = Ext.extend(Ext.form.TwinTriggerField, {\r
+ initComponent : function(){\r
+ Ext.ux.form.SearchField.superclass.initComponent.call(this);\r
+ this.on('specialkey', function(f, e){\r
+ if(e.getKey() == e.ENTER){\r
+ this.onTrigger2Click();\r
+ }\r
+ }, this);\r
+ },\r
+\r
+ validationEvent:false,\r
+ validateOnBlur:false,\r
+ trigger1Class:'x-form-clear-trigger',\r
+ trigger2Class:'x-form-search-trigger',\r
+ hideTrigger1:true,\r
+ width:180,\r
+ hasSearch : false,\r
+ paramName : 'query',\r
+\r
+ onTrigger1Click : function(){\r
+ if(this.hasSearch){\r
+ this.el.dom.value = '';\r
+ var o = {start: 0};\r
+ this.store.baseParams = this.store.baseParams || {};\r
+ this.store.baseParams[this.paramName] = '';\r
+ this.store.reload({params:o});\r
+ this.triggers[0].hide();\r
+ this.hasSearch = false;\r
+ }\r
+ },\r
+\r
+ onTrigger2Click : function(){\r
+ var v = this.getRawValue();\r
+ if(v.length < 1){\r
+ this.onTrigger1Click();\r
+ return;\r
+ }\r
+ var o = {start: 0};\r
+ this.store.baseParams = this.store.baseParams || {};\r
+ this.store.baseParams[this.paramName] = v;\r
+ this.store.reload({params:o});\r
+ this.hasSearch = true;\r
+ this.triggers[0].show();\r
+ }\r
+});Ext.ns('Ext.ux.form');\r
+\r
+/**\r
+ * @class Ext.ux.form.SelectBox\r
+ * @extends Ext.form.ComboBox\r
+ * <p>Makes a ComboBox more closely mimic an HTML SELECT. Supports clicking and dragging\r
+ * through the list, with item selection occurring when the mouse button is released.\r
+ * When used will automatically set {@link #editable} to false and call {@link Ext.Element#unselectable}\r
+ * on inner elements. Re-enabling editable after calling this will NOT work.</p>\r
+ * @author Corey Gilmore http://extjs.com/forum/showthread.php?t=6392\r
+ * @history 2007-07-08 jvs\r
+ * Slight mods for Ext 2.0\r
+ * @xtype selectbox\r
+ */\r
+Ext.ux.form.SelectBox = Ext.extend(Ext.form.ComboBox, {\r
+ constructor: function(config){\r
+ this.searchResetDelay = 1000;\r
+ config = config || {};\r
+ config = Ext.apply(config || {}, {\r
+ editable: false,\r
+ forceSelection: true,\r
+ rowHeight: false,\r
+ lastSearchTerm: false,\r
+ triggerAction: 'all',\r
+ mode: 'local'\r
+ });\r
+\r
+ Ext.ux.form.SelectBox.superclass.constructor.apply(this, arguments);\r
+\r
+ this.lastSelectedIndex = this.selectedIndex || 0;\r
+ },\r
+\r
+ initEvents : function(){\r
+ Ext.ux.form.SelectBox.superclass.initEvents.apply(this, arguments);\r
+ // you need to use keypress to capture upper/lower case and shift+key, but it doesn't work in IE\r
+ this.el.on('keydown', this.keySearch, this, true);\r
+ this.cshTask = new Ext.util.DelayedTask(this.clearSearchHistory, this);\r
+ },\r
+\r
+ keySearch : function(e, target, options) {\r
+ var raw = e.getKey();\r
+ var key = String.fromCharCode(raw);\r
+ var startIndex = 0;\r
+\r
+ if( !this.store.getCount() ) {\r
+ return;\r
+ }\r
+\r
+ switch(raw) {\r
+ case Ext.EventObject.HOME:\r
+ e.stopEvent();\r
+ this.selectFirst();\r
+ return;\r
+\r
+ case Ext.EventObject.END:\r
+ e.stopEvent();\r
+ this.selectLast();\r
+ return;\r
+\r
+ case Ext.EventObject.PAGEDOWN:\r
+ this.selectNextPage();\r
+ e.stopEvent();\r
+ return;\r
+\r
+ case Ext.EventObject.PAGEUP:\r
+ this.selectPrevPage();\r
+ e.stopEvent();\r
+ return;\r
+ }\r
+\r
+ // skip special keys other than the shift key\r
+ if( (e.hasModifier() && !e.shiftKey) || e.isNavKeyPress() || e.isSpecialKey() ) {\r
+ return;\r
+ }\r
+ if( this.lastSearchTerm == key ) {\r
+ startIndex = this.lastSelectedIndex;\r
+ }\r
+ this.search(this.displayField, key, startIndex);\r
+ this.cshTask.delay(this.searchResetDelay);\r
+ },\r
+\r
+ onRender : function(ct, position) {\r
+ this.store.on('load', this.calcRowsPerPage, this);\r
+ Ext.ux.form.SelectBox.superclass.onRender.apply(this, arguments);\r
+ if( this.mode == 'local' ) {\r
+ this.calcRowsPerPage();\r
+ }\r
+ },\r
+\r
+ onSelect : function(record, index, skipCollapse){\r
+ if(this.fireEvent('beforeselect', this, record, index) !== false){\r
+ this.setValue(record.data[this.valueField || this.displayField]);\r
+ if( !skipCollapse ) {\r
+ this.collapse();\r
+ }\r
+ this.lastSelectedIndex = index + 1;\r
+ this.fireEvent('select', this, record, index);\r
+ }\r
+ },\r
+\r
+ render : function(ct) {\r
+ Ext.ux.form.SelectBox.superclass.render.apply(this, arguments);\r
+ if( Ext.isSafari ) {\r
+ this.el.swallowEvent('mousedown', true);\r
+ }\r
+ this.el.unselectable();\r
+ this.innerList.unselectable();\r
+ this.trigger.unselectable();\r
+ this.innerList.on('mouseup', function(e, target, options) {\r
+ if( target.id && target.id == this.innerList.id ) {\r
+ return;\r
+ }\r
+ this.onViewClick();\r
+ }, this);\r
+\r
+ this.innerList.on('mouseover', function(e, target, options) {\r
+ if( target.id && target.id == this.innerList.id ) {\r
+ return;\r
+ }\r
+ this.lastSelectedIndex = this.view.getSelectedIndexes()[0] + 1;\r
+ this.cshTask.delay(this.searchResetDelay);\r
+ }, this);\r
+\r
+ this.trigger.un('click', this.onTriggerClick, this);\r
+ this.trigger.on('mousedown', function(e, target, options) {\r
+ e.preventDefault();\r
+ this.onTriggerClick();\r
+ }, this);\r
+\r
+ this.on('collapse', function(e, target, options) {\r
+ Ext.getDoc().un('mouseup', this.collapseIf, this);\r
+ }, this, true);\r
+\r
+ this.on('expand', function(e, target, options) {\r
+ Ext.getDoc().on('mouseup', this.collapseIf, this);\r
+ }, this, true);\r
+ },\r
+\r
+ clearSearchHistory : function() {\r
+ this.lastSelectedIndex = 0;\r
+ this.lastSearchTerm = false;\r
+ },\r
+\r
+ selectFirst : function() {\r
+ this.focusAndSelect(this.store.data.first());\r
+ },\r
+\r
+ selectLast : function() {\r
+ this.focusAndSelect(this.store.data.last());\r
+ },\r
+\r
+ selectPrevPage : function() {\r
+ if( !this.rowHeight ) {\r
+ return;\r
+ }\r
+ var index = Math.max(this.selectedIndex-this.rowsPerPage, 0);\r
+ this.focusAndSelect(this.store.getAt(index));\r
+ },\r
+\r
+ selectNextPage : function() {\r
+ if( !this.rowHeight ) {\r
+ return;\r
+ }\r
+ var index = Math.min(this.selectedIndex+this.rowsPerPage, this.store.getCount() - 1);\r
+ this.focusAndSelect(this.store.getAt(index));\r
+ },\r
+\r
+ search : function(field, value, startIndex) {\r
+ field = field || this.displayField;\r
+ this.lastSearchTerm = value;\r
+ var index = this.store.find.apply(this.store, arguments);\r
+ if( index !== -1 ) {\r
+ this.focusAndSelect(index);\r
+ }\r
+ },\r
+\r
+ focusAndSelect : function(record) {\r
+ var index = typeof record === 'number' ? record : this.store.indexOf(record);\r
+ this.select(index, this.isExpanded());\r
+ this.onSelect(this.store.getAt(record), index, this.isExpanded());\r
+ },\r
+\r
+ calcRowsPerPage : function() {\r
+ if( this.store.getCount() ) {\r
+ this.rowHeight = Ext.fly(this.view.getNode(0)).getHeight();\r
+ this.rowsPerPage = this.maxHeight / this.rowHeight;\r
+ } else {\r
+ this.rowHeight = false;\r
+ }\r
+ }\r
+\r
+});\r
+\r
+Ext.reg('selectbox', Ext.ux.form.SelectBox);\r
+\r
+//backwards compat\r
+Ext.ux.SelectBox = Ext.ux.form.SelectBox;\r
+/**\r
+ * @class Ext.ux.SliderTip\r
+ * @extends Ext.Tip\r
+ * Simple plugin for using an Ext.Tip with a slider to show the slider value\r
+ */\r
+Ext.ux.SliderTip = Ext.extend(Ext.Tip, {\r
+ minWidth: 10,\r
+ offsets : [0, -10],\r
+ init : function(slider){\r
+ slider.on('dragstart', this.onSlide, this);\r
+ slider.on('drag', this.onSlide, this);\r
+ slider.on('dragend', this.hide, this);\r
+ slider.on('destroy', this.destroy, this);\r
+ },\r
+\r
+ onSlide : function(slider){\r
+ this.show();\r
+ this.body.update(this.getText(slider));\r
+ this.doAutoWidth();\r
+ this.el.alignTo(slider.thumb, 'b-t?', this.offsets);\r
+ },\r
+\r
+ getText : function(slider){\r
+ return String(slider.getValue());\r
+ }\r
+});\r
+Ext.ux.SlidingPager = Ext.extend(Object, {\r
+ init : function(pbar){\r
+ Ext.each(pbar.items.getRange(2,6), function(c){\r
+ c.hide();\r
+ });\r
+ var slider = new Ext.Slider({\r
+ width: 114,\r
+ minValue: 1,\r
+ maxValue: 1,\r
+ plugins: new Ext.ux.SliderTip({\r
+ getText : function(s){\r
+ return String.format('Page <b>{0}</b> of <b>{1}</b>', s.value, s.maxValue);\r
+ }\r
+ }),\r
+ listeners: {\r
+ changecomplete: function(s, v){\r
+ pbar.changePage(v);\r
+ }\r
+ }\r
+ });\r
+ pbar.insert(5, slider);\r
+ pbar.on({\r
+ change: function(pb, data){\r
+ slider.maxValue = data.pages;\r
+ slider.setValue(data.activePage);\r
+ },\r
+ beforedestroy: function(){\r
+ slider.destroy();\r
+ }\r
+ });\r
+ }\r
+});Ext.ns('Ext.ux.form');\r
+\r
+/**\r
+ * @class Ext.ux.form.SpinnerField\r
+ * @extends Ext.form.NumberField\r
+ * Creates a field utilizing Ext.ux.Spinner\r
+ * @xtype spinnerfield\r
+ */\r
+Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, {\r
+ deferHeight: true,\r
+ autoSize: Ext.emptyFn,\r
+ onBlur: Ext.emptyFn,\r
+ adjustSize: Ext.BoxComponent.prototype.adjustSize,\r
+\r
+ constructor: function(config) {\r
+ var spinnerConfig = Ext.copyTo({}, config, 'incrementValue,alternateIncrementValue,accelerate,defaultValue,triggerClass,splitterClass');\r
+\r
+ var spl = this.spinner = new Ext.ux.Spinner(spinnerConfig);\r
+\r
+ var plugins = config.plugins\r
+ ? (Ext.isArray(config.plugins)\r
+ ? config.plugins.push(spl)\r
+ : [config.plugins, spl])\r
+ : spl;\r
+\r
+ Ext.ux.form.SpinnerField.superclass.constructor.call(this, Ext.apply(config, {plugins: plugins}));\r
+ },\r
+\r
+ onShow: function(){\r
+ if (this.wrap) {\r
+ this.wrap.dom.style.display = '';\r
+ this.wrap.dom.style.visibility = 'visible';\r
+ }\r
+ },\r
+\r
+ onHide: function(){\r
+ this.wrap.dom.style.display = 'none';\r
+ },\r
+\r
+ // private\r
+ getResizeEl: function(){\r
+ return this.wrap;\r
+ },\r
+\r
+ // private\r
+ getPositionEl: function(){\r
+ return this.wrap;\r
+ },\r
+\r
+ // private\r
+ alignErrorIcon: function(){\r
+ if (this.wrap) {\r
+ this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);\r
+ }\r
+ },\r
+\r
+ validateBlur: function(){\r
+ return true;\r
+ }\r
+});\r
+\r
+Ext.reg('spinnerfield', Ext.ux.form.SpinnerField);\r
+\r
+//backwards compat\r
+Ext.form.SpinnerField = Ext.ux.form.SpinnerField;\r
+/**\r
+ * @class Ext.ux.Spinner\r
+ * @extends Ext.util.Observable\r
+ * Creates a Spinner control utilized by Ext.ux.form.SpinnerField\r
+ */\r
+Ext.ux.Spinner = Ext.extend(Ext.util.Observable, {\r
+ incrementValue: 1,\r
+ alternateIncrementValue: 5,\r
+ triggerClass: 'x-form-spinner-trigger',\r
+ splitterClass: 'x-form-spinner-splitter',\r
+ alternateKey: Ext.EventObject.shiftKey,\r
+ defaultValue: 0,\r
+ accelerate: false,\r
+\r
+ constructor: function(config){\r
+ Ext.ux.Spinner.superclass.constructor.call(this, config);\r
+ Ext.apply(this, config);\r
+ this.mimicing = false;\r
+ },\r
+\r
+ init: function(field){\r
+ this.field = field;\r
+\r
+ field.afterMethod('onRender', this.doRender, this);\r
+ field.afterMethod('onEnable', this.doEnable, this);\r
+ field.afterMethod('onDisable', this.doDisable, this);\r
+ field.afterMethod('afterRender', this.doAfterRender, this);\r
+ field.afterMethod('onResize', this.doResize, this);\r
+ field.afterMethod('onFocus', this.doFocus, this);\r
+ field.beforeMethod('onDestroy', this.doDestroy, this);\r
+ },\r
+\r
+ doRender: function(ct, position){\r
+ var el = this.el = this.field.getEl();\r
+ var f = this.field;\r
+\r
+ if (!f.wrap) {\r
+ f.wrap = this.wrap = el.wrap({\r
+ cls: "x-form-field-wrap"\r
+ });\r
+ }\r
+ else {\r
+ this.wrap = f.wrap.addClass('x-form-field-wrap');\r
+ }\r
+\r
+ this.trigger = this.wrap.createChild({\r
+ tag: "img",\r
+ src: Ext.BLANK_IMAGE_URL,\r
+ cls: "x-form-trigger " + this.triggerClass\r
+ });\r
+\r
+ if (!f.width) {\r
+ this.wrap.setWidth(el.getWidth() + this.trigger.getWidth());\r
+ }\r
+\r
+ this.splitter = this.wrap.createChild({\r
+ tag: 'div',\r
+ cls: this.splitterClass,\r
+ style: 'width:13px; height:2px;'\r
+ });\r
+ this.splitter.setRight((Ext.isIE) ? 1 : 2).setTop(10).show();\r
+\r
+ this.proxy = this.trigger.createProxy('', this.splitter, true);\r
+ this.proxy.addClass("x-form-spinner-proxy");\r
+ this.proxy.setStyle('left', '0px');\r
+ this.proxy.setSize(14, 1);\r
+ this.proxy.hide();\r
+ this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", {\r
+ dragElId: this.proxy.id\r
+ });\r
+\r
+ this.initTrigger();\r
+ this.initSpinner();\r
+ },\r
+\r
+ doAfterRender: function(){\r
+ var y;\r
+ if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) {\r
+ this.el.position();\r
+ this.el.setY(y);\r
+ }\r
+ },\r
+\r
+ doEnable: function(){\r
+ if (this.wrap) {\r
+ this.wrap.removeClass(this.field.disabledClass);\r
+ }\r
+ },\r
+\r
+ doDisable: function(){\r
+ if (this.wrap) {\r
+ this.wrap.addClass(this.field.disabledClass);\r
+ this.el.removeClass(this.field.disabledClass);\r
+ }\r
+ },\r
+\r
+ doResize: function(w, h){\r
+ if (typeof w == 'number') {\r
+ this.el.setWidth(this.field.adjustWidth('input', w - this.trigger.getWidth()));\r
+ }\r
+ this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth());\r
+ },\r
+\r
+ doFocus: function(){\r
+ if (!this.mimicing) {\r
+ this.wrap.addClass('x-trigger-wrap-focus');\r
+ this.mimicing = true;\r
+ Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, {\r
+ delay: 10\r
+ });\r
+ this.el.on('keydown', this.checkTab, this);\r
+ }\r
+ },\r
+\r
+ // private\r
+ checkTab: function(e){\r
+ if (e.getKey() == e.TAB) {\r
+ this.triggerBlur();\r
+ }\r
+ },\r
+\r
+ // private\r
+ mimicBlur: function(e){\r
+ if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) {\r
+ this.triggerBlur();\r
+ }\r
+ },\r
+\r
+ // private\r
+ triggerBlur: function(){\r
+ this.mimicing = false;\r
+ Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);\r
+ this.el.un("keydown", this.checkTab, this);\r
+ this.field.beforeBlur();\r
+ this.wrap.removeClass('x-trigger-wrap-focus');\r
+ this.field.onBlur.call(this.field);\r
+ },\r
+\r
+ initTrigger: function(){\r
+ this.trigger.addClassOnOver('x-form-trigger-over');\r
+ this.trigger.addClassOnClick('x-form-trigger-click');\r
+ },\r
+\r
+ initSpinner: function(){\r
+ this.field.addEvents({\r
+ 'spin': true,\r
+ 'spinup': true,\r
+ 'spindown': true\r
+ });\r
+\r
+ this.keyNav = new Ext.KeyNav(this.el, {\r
+ "up": function(e){\r
+ e.preventDefault();\r
+ this.onSpinUp();\r
+ },\r
+\r
+ "down": function(e){\r
+ e.preventDefault();\r
+ this.onSpinDown();\r
+ },\r
+\r
+ "pageUp": function(e){\r
+ e.preventDefault();\r
+ this.onSpinUpAlternate();\r
+ },\r
+\r
+ "pageDown": function(e){\r
+ e.preventDefault();\r
+ this.onSpinDownAlternate();\r
+ },\r
+\r
+ scope: this\r
+ });\r
+\r
+ this.repeater = new Ext.util.ClickRepeater(this.trigger, {\r
+ accelerate: this.accelerate\r
+ });\r
+ this.field.mon(this.repeater, "click", this.onTriggerClick, this, {\r
+ preventDefault: true\r
+ });\r
+\r
+ this.field.mon(this.trigger, {\r
+ mouseover: this.onMouseOver,\r
+ mouseout: this.onMouseOut,\r
+ mousemove: this.onMouseMove,\r
+ mousedown: this.onMouseDown,\r
+ mouseup: this.onMouseUp,\r
+ scope: this,\r
+ preventDefault: true\r
+ });\r
+\r
+ this.field.mon(this.wrap, "mousewheel", this.handleMouseWheel, this);\r
+\r
+ this.dd.setXConstraint(0, 0, 10)\r
+ this.dd.setYConstraint(1500, 1500, 10);\r
+ this.dd.endDrag = this.endDrag.createDelegate(this);\r
+ this.dd.startDrag = this.startDrag.createDelegate(this);\r
+ this.dd.onDrag = this.onDrag.createDelegate(this);\r
+ },\r
+\r
+ onMouseOver: function(){\r
+ if (this.disabled) {\r
+ return;\r
+ }\r
+ var middle = this.getMiddle();\r
+ this.tmpHoverClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown';\r
+ this.trigger.addClass(this.tmpHoverClass);\r
+ },\r
+\r
+ //private\r
+ onMouseOut: function(){\r
+ this.trigger.removeClass(this.tmpHoverClass);\r
+ },\r
+\r
+ //private\r
+ onMouseMove: function(){\r
+ if (this.disabled) {\r
+ return;\r
+ }\r
+ var middle = this.getMiddle();\r
+ if (((Ext.EventObject.getPageY() > middle) && this.tmpHoverClass == "x-form-spinner-overup") ||\r
+ ((Ext.EventObject.getPageY() < middle) && this.tmpHoverClass == "x-form-spinner-overdown")) {\r
+ }\r
+ },\r
+\r
+ //private\r
+ onMouseDown: function(){\r
+ if (this.disabled) {\r
+ return;\r
+ }\r
+ var middle = this.getMiddle();\r
+ this.tmpClickClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown';\r
+ this.trigger.addClass(this.tmpClickClass);\r
+ },\r
+\r
+ //private\r
+ onMouseUp: function(){\r
+ this.trigger.removeClass(this.tmpClickClass);\r
+ },\r
+\r
+ //private\r
+ onTriggerClick: function(){\r
+ if (this.disabled || this.el.dom.readOnly) {\r
+ return;\r
+ }\r
+ var middle = this.getMiddle();\r
+ var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down';\r
+ this['onSpin' + ud]();\r
+ },\r
+\r
+ //private\r
+ getMiddle: function(){\r
+ var t = this.trigger.getTop();\r
+ var h = this.trigger.getHeight();\r
+ var middle = t + (h / 2);\r
+ return middle;\r
+ },\r
+\r
+ //private\r
+ //checks if control is allowed to spin\r
+ isSpinnable: function(){\r
+ if (this.disabled || this.el.dom.readOnly) {\r
+ Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly\r
+ return false;\r
+ }\r
+ return true;\r
+ },\r
+\r
+ handleMouseWheel: function(e){\r
+ //disable scrolling when not focused\r
+ if (this.wrap.hasClass('x-trigger-wrap-focus') == false) {\r
+ return;\r
+ }\r
+\r
+ var delta = e.getWheelDelta();\r
+ if (delta > 0) {\r
+ this.onSpinUp();\r
+ e.stopEvent();\r
+ }\r
+ else\r
+ if (delta < 0) {\r
+ this.onSpinDown();\r
+ e.stopEvent();\r
+ }\r
+ },\r
+\r
+ //private\r
+ startDrag: function(){\r
+ this.proxy.show();\r
+ this._previousY = Ext.fly(this.dd.getDragEl()).getTop();\r
+ },\r
+\r
+ //private\r
+ endDrag: function(){\r
+ this.proxy.hide();\r
+ },\r
+\r
+ //private\r
+ onDrag: function(){\r
+ if (this.disabled) {\r
+ return;\r
+ }\r
+ var y = Ext.fly(this.dd.getDragEl()).getTop();\r
+ var ud = '';\r
+\r
+ if (this._previousY > y) {\r
+ ud = 'Up';\r
+ } //up\r
+ if (this._previousY < y) {\r
+ ud = 'Down';\r
+ } //down\r
+ if (ud != '') {\r
+ this['onSpin' + ud]();\r
+ }\r
+\r
+ this._previousY = y;\r
+ },\r
+\r
+ //private\r
+ onSpinUp: function(){\r
+ if (this.isSpinnable() == false) {\r
+ return;\r
+ }\r
+ if (Ext.EventObject.shiftKey == true) {\r
+ this.onSpinUpAlternate();\r
+ return;\r
+ }\r
+ else {\r
+ this.spin(false, false);\r
+ }\r
+ this.field.fireEvent("spin", this);\r
+ this.field.fireEvent("spinup", this);\r
+ },\r
+\r
+ //private\r
+ onSpinDown: function(){\r
+ if (this.isSpinnable() == false) {\r
+ return;\r
+ }\r
+ if (Ext.EventObject.shiftKey == true) {\r
+ this.onSpinDownAlternate();\r
+ return;\r
+ }\r
+ else {\r
+ this.spin(true, false);\r
+ }\r
+ this.field.fireEvent("spin", this);\r
+ this.field.fireEvent("spindown", this);\r
+ },\r
+\r
+ //private\r
+ onSpinUpAlternate: function(){\r
+ if (this.isSpinnable() == false) {\r
+ return;\r
+ }\r
+ this.spin(false, true);\r
+ this.field.fireEvent("spin", this);\r
+ this.field.fireEvent("spinup", this);\r
+ },\r
+\r
+ //private\r
+ onSpinDownAlternate: function(){\r
+ if (this.isSpinnable() == false) {\r
+ return;\r
+ }\r
+ this.spin(true, true);\r
+ this.field.fireEvent("spin", this);\r
+ this.field.fireEvent("spindown", this);\r
+ },\r
+\r
+ spin: function(down, alternate){\r
+ var v = parseFloat(this.field.getValue());\r
+ var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue;\r
+ (down == true) ? v -= incr : v += incr;\r
+\r
+ v = (isNaN(v)) ? this.defaultValue : v;\r
+ v = this.fixBoundries(v);\r
+ this.field.setRawValue(v);\r
+ },\r
+\r
+ fixBoundries: function(value){\r
+ var v = value;\r
+\r
+ if (this.field.minValue != undefined && v < this.field.minValue) {\r
+ v = this.field.minValue;\r
+ }\r
+ if (this.field.maxValue != undefined && v > this.field.maxValue) {\r
+ v = this.field.maxValue;\r
+ }\r
+\r
+ return this.fixPrecision(v);\r
+ },\r
+\r
+ // private\r
+ fixPrecision: function(value){\r
+ var nan = isNaN(value);\r
+ if (!this.field.allowDecimals || this.field.decimalPrecision == -1 || nan || !value) {\r
+ return nan ? '' : value;\r
+ }\r
+ return parseFloat(parseFloat(value).toFixed(this.field.decimalPrecision));\r
+ },\r
+\r
+ doDestroy: function(){\r
+ if (this.trigger) {\r
+ this.trigger.remove();\r
+ }\r
+ if (this.wrap) {\r
+ this.wrap.remove();\r
+ delete this.field.wrap;\r
+ }\r
+\r
+ if (this.splitter) {\r
+ this.splitter.remove();\r
+ }\r
+\r
+ if (this.dd) {\r
+ this.dd.unreg();\r
+ this.dd = null;\r
+ }\r
+\r
+ if (this.proxy) {\r
+ this.proxy.remove();\r
+ }\r
+\r
+ if (this.repeater) {\r
+ this.repeater.purgeListeners();\r
+ }\r
+ }\r
+});\r
+\r
+//backwards compat\r
+Ext.form.Spinner = Ext.ux.Spinner;Ext.ux.Spotlight = function(config){\r
+ Ext.apply(this, config);\r
+}\r
+Ext.ux.Spotlight.prototype = {\r
+ active : false,\r
+ animate : true,\r
+ duration: .25,\r
+ easing:'easeNone',\r
+\r
+ // private\r
+ animated : false,\r
+\r
+ createElements : function(){\r
+ var bd = Ext.getBody();\r
+\r
+ this.right = bd.createChild({cls:'x-spotlight'});\r
+ this.left = bd.createChild({cls:'x-spotlight'});\r
+ this.top = bd.createChild({cls:'x-spotlight'});\r
+ this.bottom = bd.createChild({cls:'x-spotlight'});\r
+\r
+ this.all = new Ext.CompositeElement([this.right, this.left, this.top, this.bottom]);\r
+ },\r
+\r
+ show : function(el, callback, scope){\r
+ if(this.animated){\r
+ this.show.defer(50, this, [el, callback, scope]);\r
+ return;\r
+ }\r
+ this.el = Ext.get(el);\r
+ if(!this.right){\r
+ this.createElements();\r
+ }\r
+ if(!this.active){\r
+ this.all.setDisplayed('');\r
+ this.applyBounds(true, false);\r
+ this.active = true;\r
+ Ext.EventManager.onWindowResize(this.syncSize, this);\r
+ this.applyBounds(false, this.animate, false, callback, scope);\r
+ }else{\r
+ this.applyBounds(false, false, false, callback, scope); // all these booleans look hideous\r
+ }\r
+ },\r
+\r
+ hide : function(callback, scope){\r
+ if(this.animated){\r
+ this.hide.defer(50, this, [callback, scope]);\r
+ return;\r
+ }\r
+ Ext.EventManager.removeResizeListener(this.syncSize, this);\r
+ this.applyBounds(true, this.animate, true, callback, scope);\r
+ },\r
+\r
+ doHide : function(){\r
+ this.active = false;\r
+ this.all.setDisplayed(false);\r
+ },\r
+\r
+ syncSize : function(){\r
+ this.applyBounds(false, false);\r
+ },\r
+\r
+ applyBounds : function(basePts, anim, doHide, callback, scope){\r
+\r
+ var rg = this.el.getRegion();\r
+\r
+ var dw = Ext.lib.Dom.getViewWidth(true);\r
+ var dh = Ext.lib.Dom.getViewHeight(true);\r
+\r
+ var c = 0, cb = false;\r
+ if(anim){\r
+ cb = {\r
+ callback: function(){\r
+ c++;\r
+ if(c == 4){\r
+ this.animated = false;\r
+ if(doHide){\r
+ this.doHide();\r
+ }\r
+ Ext.callback(callback, scope, [this]);\r
+ }\r
+ },\r
+ scope: this,\r
+ duration: this.duration,\r
+ easing: this.easing\r
+ };\r
+ this.animated = true;\r
+ }\r
+\r
+ this.right.setBounds(\r
+ rg.right,\r
+ basePts ? dh : rg.top,\r
+ dw - rg.right,\r
+ basePts ? 0 : (dh - rg.top),\r
+ cb);\r
+\r
+ this.left.setBounds(\r
+ 0,\r
+ 0,\r
+ rg.left,\r
+ basePts ? 0 : rg.bottom,\r
+ cb);\r
+\r
+ this.top.setBounds(\r
+ basePts ? dw : rg.left,\r
+ 0,\r
+ basePts ? 0 : dw - rg.left,\r
+ rg.top,\r
+ cb);\r
+\r
+ this.bottom.setBounds(\r
+ 0,\r
+ rg.bottom,\r
+ basePts ? 0 : rg.right,\r
+ dh - rg.bottom,\r
+ cb);\r
+\r
+ if(!anim){\r
+ if(doHide){\r
+ this.doHide();\r
+ }\r
+ if(callback){\r
+ Ext.callback(callback, scope, [this]);\r
+ }\r
+ }\r
+ },\r
+\r
+ destroy : function(){\r
+ this.doHide();\r
+ Ext.destroy(\r
+ this.right,\r
+ this.left,\r
+ this.top,\r
+ this.bottom);\r
+ delete this.el;\r
+ delete this.all;\r
+ }\r
+};\r
+\r
+//backwards compat\r
+Ext.Spotlight = Ext.ux.Spotlight;/**\r
+ * @class Ext.ux.TabCloseMenu\r
+ * @extends Object \r
+ * Plugin (ptype = 'tabclosemenu') for adding a close context menu to tabs.\r
+ * \r
+ * @ptype tabclosemenu\r
+ */\r
+Ext.ux.TabCloseMenu = function(){\r
+ var tabs, menu, ctxItem;\r
+ this.init = function(tp){\r
+ tabs = tp;\r
+ tabs.on('contextmenu', onContextMenu);\r
+ };\r
+\r
+ function onContextMenu(ts, item, e){\r
+ if(!menu){ // create context menu on first right click\r
+ menu = new Ext.menu.Menu({ \r
+ items: [{\r
+ id: tabs.id + '-close',\r
+ text: 'Close Tab',\r
+ handler : function(){\r
+ tabs.remove(ctxItem);\r
+ }\r
+ },{\r
+ id: tabs.id + '-close-others',\r
+ text: 'Close Other Tabs',\r
+ handler : function(){\r
+ tabs.items.each(function(item){\r
+ if(item.closable && item != ctxItem){\r
+ tabs.remove(item);\r
+ }\r
+ });\r
+ }\r
+ }]});\r
+ }\r
+ ctxItem = item;\r
+ var items = menu.items;\r
+ items.get(tabs.id + '-close').setDisabled(!item.closable);\r
+ var disableOthers = true;\r
+ tabs.items.each(function(){\r
+ if(this != item && this.closable){\r
+ disableOthers = false;\r
+ return false;\r
+ }\r
+ });\r
+ items.get(tabs.id + '-close-others').setDisabled(disableOthers);\r
+ e.stopEvent();\r
+ menu.showAt(e.getPoint());\r
+ }\r
+};\r
+\r
+Ext.preg('tabclosemenu', Ext.ux.TabCloseMenu);\r
+Ext.ns('Ext.ux.grid');
+
+/**
+ * @class Ext.ux.grid.TableGrid
+ * @extends Ext.grid.GridPanel
+ * A Grid which creates itself from an existing HTML table element.
+ * @history
+ * 2007-03-01 Original version by Nige "Animal" White
+ * 2007-03-10 jvs Slightly refactored to reuse existing classes * @constructor
+ * @param {String/HTMLElement/Ext.Element} table The table element from which this grid will be created -
+ * The table MUST have some type of size defined for the grid to fill. The container will be
+ * automatically set to position relative if it isn't already.
+ * @param {Object} config A config object that sets properties on this grid and has two additional (optional)
+ * properties: fields and columns which allow for customizing data fields and columns for this grid.
+ */
+Ext.ux.grid.TableGrid = function(table, config){
+ config = config ||
+ {};
+ Ext.apply(this, config);
+ var cf = config.fields || [], ch = config.columns || [];
+ table = Ext.get(table);
+
+ var ct = table.insertSibling();
+
+ var fields = [], cols = [];
+ var headers = table.query("thead th");
+ for (var i = 0, h; h = headers[i]; i++) {
+ var text = h.innerHTML;
+ var name = 'tcol-' + i;
+
+ fields.push(Ext.applyIf(cf[i] ||
+ {}, {
+ name: name,
+ mapping: 'td:nth(' + (i + 1) + ')/@innerHTML'
+ }));
+
+ cols.push(Ext.applyIf(ch[i] ||
+ {}, {
+ 'header': text,
+ 'dataIndex': name,
+ 'width': h.offsetWidth,
+ 'tooltip': h.title,
+ 'sortable': true
+ }));
+ }
+
+ var ds = new Ext.data.Store({
+ reader: new Ext.data.XmlReader({
+ record: 'tbody tr'
+ }, fields)
+ });
+
+ ds.loadData(table.dom);
+
+ var cm = new Ext.grid.ColumnModel(cols);
+
+ if (config.width || config.height) {
+ ct.setSize(config.width || 'auto', config.height || 'auto');
+ }
+ else {
+ ct.setWidth(table.getWidth());
+ }
+
+ if (config.remove !== false) {
+ table.remove();
+ }
+
+ Ext.applyIf(this, {
+ 'ds': ds,
+ 'cm': cm,
+ 'sm': new Ext.grid.RowSelectionModel(),
+ autoHeight: true,
+ autoWidth: false
+ });
+ Ext.ux.grid.TableGrid.superclass.constructor.call(this, ct, {});
+};
+
+Ext.extend(Ext.ux.grid.TableGrid, Ext.grid.GridPanel);
+
+//backwards compat
+Ext.grid.TableGrid = Ext.ux.grid.TableGrid;
+
+
+Ext.ux.TabScrollerMenu = Ext.extend(Object, {
+ pageSize : 10,
+ maxText : 15,
+ menuPrefixText : 'Items',
+ constructor : function(config) {
+ config = config || {};
+ Ext.apply(this, config);
+ },
+ init : function(tabPanel) {
+ Ext.apply(tabPanel, this.tabPanelMethods);
+
+ tabPanel.tabScrollerMenu = this;
+ var thisRef = this;
+
+ tabPanel.on({
+ render : {
+ scope : tabPanel,
+ single : true,
+ fn : function() {
+ var newFn = tabPanel.createScrollers.createSequence(thisRef.createPanelsMenu, this);
+ tabPanel.createScrollers = newFn;
+ }
+ }
+ });
+ },
+ // private && sequeneced
+ createPanelsMenu : function() {
+ var h = this.stripWrap.dom.offsetHeight;
+
+ //move the right menu item to the left 18px
+ var rtScrBtn = this.header.dom.firstChild;
+ Ext.fly(rtScrBtn).applyStyles({
+ right : '18px'
+ });
+
+ var stripWrap = Ext.get(this.strip.dom.parentNode);
+ stripWrap.applyStyles({
+ 'margin-right' : '36px'
+ });
+
+ // Add the new righthand menu
+ var scrollMenu = this.header.insertFirst({
+ cls:'x-tab-tabmenu-right'
+ });
+ scrollMenu.setHeight(h);
+ scrollMenu.addClassOnOver('x-tab-tabmenu-over');
+ scrollMenu.on('click', this.showTabsMenu, this);
+
+ this.scrollLeft.show = this.scrollLeft.show.createSequence(function() {
+ scrollMenu.show();
+ });
+
+ this.scrollLeft.hide = this.scrollLeft.hide.createSequence(function() {
+ scrollMenu.hide();
+ });
+
+ },
+ // public
+ getPageSize : function() {
+ return this.pageSize;
+ },
+ // public
+ setPageSize : function(pageSize) {
+ this.pageSize = pageSize;
+ },
+ // public
+ getMaxText : function() {
+ return this.maxText;
+ },
+ // public
+ setMaxText : function(t) {
+ this.maxText = t;
+ },
+ getMenuPrefixText : function() {
+ return this.menuPrefixText;
+ },
+ setMenuPrefixText : function(t) {
+ this.menuPrefixText = t;
+ },
+ // private && applied to the tab panel itself.
+ tabPanelMethods : {
+ // all execute within the scope of the tab panel
+ // private
+ showTabsMenu : function(e) {
+ if (! this.tabsMenu) {
+ this.tabsMenu = new Ext.menu.Menu();
+ this.on('beforedestroy', this.tabsMenu.destroy, this.tabsMenu);
+ }
+
+ this.tabsMenu.removeAll();
+
+ this.generateTabMenuItems();
+
+ var target = Ext.get(e.getTarget());
+ var xy = target.getXY();
+
+ //Y param + 24 pixels
+ xy[1] += 24;
+
+ this.tabsMenu.showAt(xy);
+ },
+ // private
+ generateTabMenuItems : function() {
+ var curActive = this.getActiveTab();
+ var totalItems = this.items.getCount();
+ var pageSize = this.tabScrollerMenu.getPageSize();
+
+
+ if (totalItems > pageSize) {
+ var numSubMenus = Math.floor(totalItems / pageSize);
+ var remainder = totalItems % pageSize;
+
+ // Loop through all of the items and create submenus in chunks of 10
+ for (var i = 0 ; i < numSubMenus; i++) {
+ var curPage = (i + 1) * pageSize;
+ var menuItems = [];
+
+
+ for (var x = 0; x < pageSize; x++) {
+ index = x + curPage - pageSize;
+ var item = this.items.get(index);
+ menuItems.push(this.autoGenMenuItem(item));
+ }
+
+ this.tabsMenu.add({
+ text : this.tabScrollerMenu.getMenuPrefixText() + ' ' + (curPage - pageSize + 1) + ' - ' + curPage,
+ menu : menuItems
+ });
+
+ }
+ // remaining items
+ if (remainder > 0) {
+ var start = numSubMenus * pageSize;
+ menuItems = [];
+ for (var i = start ; i < totalItems; i ++ ) {
+ var item = this.items.get(i);
+ menuItems.push(this.autoGenMenuItem(item));
+ }
+
+
+ this.tabsMenu.add({
+ text : this.tabScrollerMenu.menuPrefixText + ' ' + (start + 1) + ' - ' + (start + menuItems.length),
+ menu : menuItems
+ });
+
+
+ }
+ }
+ else {
+ this.items.each(function(item) {
+ if (item.id != curActive.id && ! item.hidden) {
+ menuItems.push(this.autoGenMenuItem(item));
+ }
+ }, this);
+ }
+ },
+ // private
+ autoGenMenuItem : function(item) {
+ var maxText = this.tabScrollerMenu.getMaxText();
+ var text = Ext.util.Format.ellipsis(item.title, maxText);
+
+ return {
+ text : text,
+ handler : this.showTabFromMenu,
+ scope : this,
+ disabled : item.disabled,
+ tabToShow : item,
+ iconCls : item.iconCls
+ }
+
+ },
+ // private
+ showTabFromMenu : function(menuItem) {
+ this.setActiveTab(menuItem.tabToShow);
+ }
+ }
+});
+Ext.ns('Ext.ux.tree');
+
+/**
+ * @class Ext.ux.tree.XmlTreeLoader
+ * @extends Ext.tree.TreeLoader
+ * <p>A TreeLoader that can convert an XML document into a hierarchy of {@link Ext.tree.TreeNode}s.
+ * Any text value included as a text node in the XML will be added to the parent node as an attribute
+ * called <tt>innerText</tt>. Also, the tag name of each XML node will be added to the tree node as
+ * an attribute called <tt>tagName</tt>.</p>
+ * <p>By default, this class expects that your source XML will provide the necessary attributes on each
+ * node as expected by the {@link Ext.tree.TreePanel} to display and load properly. However, you can
+ * provide your own custom processing of node attributes by overriding the {@link #processNode} method
+ * and modifying the attributes as needed before they are used to create the associated TreeNode.</p>
+ * @constructor
+ * Creates a new XmlTreeloader.
+ * @param {Object} config A config object containing config properties.
+ */
+Ext.ux.tree.XmlTreeLoader = Ext.extend(Ext.tree.TreeLoader, {
+ /**
+ * @property XML_NODE_ELEMENT
+ * XML element node (value 1, read-only)
+ * @type Number
+ */
+ XML_NODE_ELEMENT : 1,
+ /**
+ * @property XML_NODE_TEXT
+ * XML text node (value 3, read-only)
+ * @type Number
+ */
+ XML_NODE_TEXT : 3,
+
+ // private override
+ processResponse : function(response, node, callback){
+ var xmlData = response.responseXML;
+ var root = xmlData.documentElement || xmlData;
+
+ try{
+ node.beginUpdate();
+ node.appendChild(this.parseXml(root));
+ node.endUpdate();
+
+ if(typeof callback == "function"){
+ callback(this, node);
+ }
+ }catch(e){
+ this.handleFailure(response);
+ }
+ },
+
+ // private
+ parseXml : function(node) {
+ var nodes = [];
+ Ext.each(node.childNodes, function(n){
+ if(n.nodeType == this.XML_NODE_ELEMENT){
+ var treeNode = this.createNode(n);
+ if(n.childNodes.length > 0){
+ var child = this.parseXml(n);
+ if(typeof child == 'string'){
+ treeNode.attributes.innerText = child;
+ }else{
+ treeNode.appendChild(child);
+ }
+ }
+ nodes.push(treeNode);
+ }
+ else if(n.nodeType == this.XML_NODE_TEXT){
+ var text = n.nodeValue.trim();
+ if(text.length > 0){
+ return nodes = text;
+ }
+ }
+ }, this);
+
+ return nodes;
+ },
+
+ // private override
+ createNode : function(node){
+ var attr = {
+ tagName: node.tagName
+ };
+
+ Ext.each(node.attributes, function(a){
+ attr[a.nodeName] = a.nodeValue;
+ });
+
+ this.processAttributes(attr);
+
+ return Ext.ux.tree.XmlTreeLoader.superclass.createNode.call(this, attr);
+ },
+
+ /*
+ * Template method intended to be overridden by subclasses that need to provide
+ * custom attribute processing prior to the creation of each TreeNode. This method
+ * will be passed a config object containing existing TreeNode attribute name/value
+ * pairs which can be modified as needed directly (no need to return the object).
+ */
+ processAttributes: Ext.emptyFn
+});
+
+//backwards compat
+Ext.ux.XmlTreeLoader = Ext.ux.tree.XmlTreeLoader;