ux-all-debug.js
资源名称:ext-3.1.0.zip [点击查看]
上传用户:dawnssy
上传日期:2022-08-06
资源大小:9345k
文件大小:337k
源码类别:
JavaScript
开发平台:
JavaScript
- this.scroller.dom.style.position = 'static';
- this.lockedScroller.dom.style.position = 'static';
- }
- }else{
- this.el.setSize(csize.width, csize.height);
- var hdHeight = this.mainHd.getHeight();
- var vh = csize.height - (hdHeight);
- }
- this.updateLockedWidth();
- if(this.forceFit){
- if(this.lastViewWidth != vw){
- this.fitColumns(false, false);
- this.lastViewWidth = vw;
- }
- }else {
- this.autoExpand();
- this.syncHeaderScroll();
- }
- this.onLayout(vw, vh);
- },
- getOffsetWidth : function() {
- return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth() + this.getScrollOffset()) + 'px';
- },
- renderHeaders : function(){
- var cm = this.cm,
- ts = this.templates,
- ct = ts.hcell,
- cb = [], lcb = [],
- p = {},
- len = cm.getColumnCount(),
- last = len - 1;
- for(var i = 0; i < len; i++){
- p.id = cm.getColumnId(i);
- p.value = cm.getColumnHeader(i) || '';
- p.style = this.getColumnStyle(i, true);
- p.tooltip = this.getColumnTooltip(i);
- p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +
- (cm.config[i].headerCls ? ' ' + cm.config[i].headerCls : '');
- if(cm.config[i].align == 'right'){
- p.istyle = 'padding-right:16px';
- } else {
- delete p.istyle;
- }
- if(cm.isLocked(i)){
- lcb[lcb.length] = ct.apply(p);
- }else{
- cb[cb.length] = ct.apply(p);
- }
- }
- return [ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'}),
- ts.header.apply({cells: lcb.join(''), tstyle:'width:'+this.getLockedWidth()+';'})];
- },
- updateHeaders : function(){
- var hd = this.renderHeaders();
- this.innerHd.firstChild.innerHTML = hd[0];
- this.innerHd.firstChild.style.width = this.getOffsetWidth();
- this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
- this.lockedInnerHd.firstChild.innerHTML = hd[1];
- var lw = this.getLockedWidth();
- this.lockedInnerHd.firstChild.style.width = lw;
- this.lockedInnerHd.firstChild.firstChild.style.width = lw;
- },
- getResolvedXY : function(resolved){
- if(!resolved){
- return null;
- }
- var c = resolved.cell, r = resolved.row;
- return c ? Ext.fly(c).getXY() : [this.scroller.getX(), Ext.fly(r).getY()];
- },
- syncFocusEl : function(row, col, hscroll){
- Ext.ux.grid.LockingGridView.superclass.syncFocusEl.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);
- },
- ensureVisible : function(row, col, hscroll){
- return Ext.ux.grid.LockingGridView.superclass.ensureVisible.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);
- },
- insertRows : function(dm, firstRow, lastRow, isUpdate){
- var last = dm.getCount() - 1;
- if(!isUpdate && firstRow === 0 && lastRow >= last){
- this.refresh();
- }else{
- if(!isUpdate){
- this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
- }
- var html = this.renderRows(firstRow, lastRow),
- before = this.getRow(firstRow);
- if(before){
- if(firstRow === 0){
- this.removeRowClass(0, this.firstRowCls);
- }
- Ext.DomHelper.insertHtml('beforeBegin', before, html[0]);
- before = this.getLockedRow(firstRow);
- Ext.DomHelper.insertHtml('beforeBegin', before, html[1]);
- }else{
- this.removeRowClass(last - 1, this.lastRowCls);
- Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html[0]);
- Ext.DomHelper.insertHtml('beforeEnd', this.lockedBody.dom, html[1]);
- }
- if(!isUpdate){
- this.fireEvent('rowsinserted', this, firstRow, lastRow);
- this.processRows(firstRow);
- }else if(firstRow === 0 || firstRow >= last){
- this.addRowClass(firstRow, firstRow === 0 ? this.firstRowCls : this.lastRowCls);
- }
- }
- this.syncFocusEl(firstRow);
- },
- getColumnStyle : function(col, isHeader){
- var style = !isHeader ? this.cm.config[col].cellStyle || this.cm.config[col].css || '' : this.cm.config[col].headerStyle || '';
- style += 'width:'+this.getColumnWidth(col)+';';
- if(this.cm.isHidden(col)){
- style += 'display:none;';
- }
- var align = this.cm.config[col].align;
- if(align){
- style += 'text-align:'+align+';';
- }
- return style;
- },
- getLockedWidth : function() {
- return this.cm.getTotalLockedWidth() + 'px';
- },
- getTotalWidth : function() {
- return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth()) + 'px';
- },
- getColumnData : function(){
- var cs = [], cm = this.cm, colCount = cm.getColumnCount();
- for(var i = 0; i < colCount; i++){
- var name = cm.getDataIndex(i);
- cs[i] = {
- name : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
- renderer : cm.getRenderer(i),
- id : cm.getColumnId(i),
- style : this.getColumnStyle(i),
- locked : cm.isLocked(i)
- };
- }
- return cs;
- },
- renderBody : function(){
- var markup = this.renderRows() || [' ', ' '];
- return [this.templates.body.apply({rows: markup[0]}), this.templates.body.apply({rows: markup[1]})];
- },
- refreshRow : function(record){
- Ext.ux.grid.LockingGridView.superclass.refreshRow.call(this, record);
- var index = Ext.isNumber(record) ? record : this.ds.indexOf(record);
- this.getLockedRow(index).rowIndex = index;
- },
- refresh : function(headersToo){
- this.fireEvent('beforerefresh', this);
- this.grid.stopEditing(true);
- var result = this.renderBody();
- this.mainBody.update(result[0]).setWidth(this.getTotalWidth());
- this.lockedBody.update(result[1]).setWidth(this.getLockedWidth());
- if(headersToo === true){
- this.updateHeaders();
- this.updateHeaderSortState();
- }
- this.processRows(0, true);
- this.layout();
- this.applyEmptyText();
- this.fireEvent('refresh', this);
- },
- onDenyColumnLock : function(){
- },
- initData : function(ds, cm){
- if(this.cm){
- this.cm.un('columnlockchange', this.onColumnLock, this);
- }
- Ext.ux.grid.LockingGridView.superclass.initData.call(this, ds, cm);
- if(this.cm){
- this.cm.on('columnlockchange', this.onColumnLock, this);
- }
- },
- onColumnLock : function(){
- this.refresh(true);
- },
- handleHdMenuClick : function(item){
- var index = this.hdCtxIndex,
- cm = this.cm,
- id = item.getItemId(),
- llen = cm.getLockedCount();
- switch(id){
- case 'lock':
- if(cm.getColumnCount(true) <= llen + 1){
- this.onDenyColumnLock();
- return;
- }
- if(llen != index){
- cm.setLocked(index, true, true);
- cm.moveColumn(index, llen);
- this.grid.fireEvent('columnmove', index, llen);
- }else{
- cm.setLocked(index, true);
- }
- break;
- case 'unlock':
- if(llen - 1 != index){
- cm.setLocked(index, false, true);
- cm.moveColumn(index, llen - 1);
- this.grid.fireEvent('columnmove', index, llen - 1);
- }else{
- cm.setLocked(index, false);
- }
- break;
- default:
- return Ext.ux.grid.LockingGridView.superclass.handleHdMenuClick.call(this, item);
- }
- return true;
- },
- handleHdDown : function(e, t){
- Ext.ux.grid.LockingGridView.superclass.handleHdDown.call(this, e, t);
- if(this.grid.enableColLock !== false){
- if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
- var hd = this.findHeaderCell(t),
- index = this.getCellIndex(hd),
- ms = this.hmenu.items, cm = this.cm;
- ms.get('lock').setDisabled(cm.isLocked(index));
- ms.get('unlock').setDisabled(!cm.isLocked(index));
- }
- }
- },
- syncHeaderHeight: function(){
- this.innerHd.firstChild.firstChild.style.height = 'auto';
- this.lockedInnerHd.firstChild.firstChild.style.height = 'auto';
- var hd = this.innerHd.firstChild.firstChild.offsetHeight,
- lhd = this.lockedInnerHd.firstChild.firstChild.offsetHeight,
- height = (lhd > hd ? lhd : hd) + 'px';
- this.innerHd.firstChild.firstChild.style.height = height;
- this.lockedInnerHd.firstChild.firstChild.style.height = height;
- },
- updateLockedWidth: function(){
- var lw = this.cm.getTotalLockedWidth(),
- tw = this.cm.getTotalWidth() - lw,
- csize = this.grid.getGridEl().getSize(true),
- lp = Ext.isBorderBox ? 0 : this.lockedBorderWidth,
- rp = Ext.isBorderBox ? 0 : this.rowBorderWidth,
- vw = (csize.width - lw - lp - rp) + 'px',
- so = this.getScrollOffset();
- if(!this.grid.autoHeight){
- var vh = (csize.height - this.mainHd.getHeight()) + 'px';
- this.lockedScroller.dom.style.height = vh;
- this.scroller.dom.style.height = vh;
- }
- this.lockedWrap.dom.style.width = (lw + rp) + 'px';
- this.scroller.dom.style.width = vw;
- this.mainWrap.dom.style.left = (lw + lp + rp) + 'px';
- if(this.innerHd){
- this.lockedInnerHd.firstChild.style.width = lw + 'px';
- this.lockedInnerHd.firstChild.firstChild.style.width = lw + 'px';
- this.innerHd.style.width = vw;
- this.innerHd.firstChild.style.width = (tw + rp + so) + 'px';
- this.innerHd.firstChild.firstChild.style.width = tw + 'px';
- }
- if(this.mainBody){
- this.lockedBody.dom.style.width = (lw + rp) + 'px';
- this.mainBody.dom.style.width = (tw + rp) + 'px';
- }
- }
- });
- Ext.ux.grid.LockingColumnModel = Ext.extend(Ext.grid.ColumnModel, {
- isLocked : function(colIndex){
- return this.config[colIndex].locked === true;
- },
- setLocked : function(colIndex, value, suppressEvent){
- if(this.isLocked(colIndex) == value){
- return;
- }
- this.config[colIndex].locked = value;
- if(!suppressEvent){
- this.fireEvent('columnlockchange', this, colIndex, value);
- }
- },
- getTotalLockedWidth : function(){
- var totalWidth = 0;
- for(var i = 0, len = this.config.length; i < len; i++){
- if(this.isLocked(i) && !this.isHidden(i)){
- totalWidth += this.getColumnWidth(i);
- }
- }
- return totalWidth;
- },
- getLockedCount : function(){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(!this.isLocked(i)){
- return i;
- }
- }
- },
- moveColumn : function(oldIndex, newIndex){
- if(oldIndex < newIndex && this.isLocked(oldIndex) && !this.isLocked(newIndex)){
- this.setLocked(oldIndex, false, true);
- }else if(oldIndex > newIndex && !this.isLocked(oldIndex) && this.isLocked(newIndex)){
- this.setLocked(oldIndex, true, true);
- }
- Ext.ux.grid.LockingColumnModel.superclass.moveColumn.apply(this, arguments);
- }
- });
- Ext.ns('Ext.ux.form');
- /**
- * @class Ext.ux.form.MultiSelect
- * @extends Ext.form.Field
- * A control that allows selection and form submission of multiple list items.
- *
- * @history
- * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams)
- * 2008-06-19 bpm Docs and demo code clean up
- *
- * @constructor
- * Create a new MultiSelect
- * @param {Object} config Configuration options
- * @xtype multiselect
- */
- Ext.ux.form.MultiSelect = Ext.extend(Ext.form.Field, {
- /**
- * @cfg {String} legend Wraps the object with a fieldset and specified legend.
- */
- /**
- * @cfg {Ext.ListView} view The {@link Ext.ListView} used to render the multiselect list.
- */
- /**
- * @cfg {String/Array} dragGroup The ddgroup name(s) for the MultiSelect DragZone (defaults to undefined).
- */
- /**
- * @cfg {String/Array} dropGroup The ddgroup name(s) for the MultiSelect DropZone (defaults to undefined).
- */
- /**
- * @cfg {Boolean} ddReorder Whether the items in the MultiSelect list are drag/drop reorderable (defaults to false).
- */
- ddReorder:false,
- /**
- * @cfg {Object/Array} tbar The top toolbar of the control. This can be a {@link Ext.Toolbar} object, a
- * toolbar config, or an array of buttons/button configs to be added to the toolbar.
- */
- /**
- * @cfg {String} appendOnly True if the list should only allow append drops when drag/drop is enabled
- * (use for lists which are sorted, defaults to false).
- */
- appendOnly:false,
- /**
- * @cfg {Number} width Width in pixels of the control (defaults to 100).
- */
- width:100,
- /**
- * @cfg {Number} height Height in pixels of the control (defaults to 100).
- */
- height:100,
- /**
- * @cfg {String/Number} displayField Name/Index of the desired display field in the dataset (defaults to 0).
- */
- displayField:0,
- /**
- * @cfg {String/Number} valueField Name/Index of the desired value field in the dataset (defaults to 1).
- */
- valueField:1,
- /**
- * @cfg {Boolean} allowBlank False to require at least one item in the list to be selected, true to allow no
- * selection (defaults to true).
- */
- allowBlank:true,
- /**
- * @cfg {Number} minSelections Minimum number of selections allowed (defaults to 0).
- */
- minSelections:0,
- /**
- * @cfg {Number} maxSelections Maximum number of selections allowed (defaults to Number.MAX_VALUE).
- */
- maxSelections:Number.MAX_VALUE,
- /**
- * @cfg {String} blankText Default text displayed when the control contains no items (defaults to the same value as
- * {@link Ext.form.TextField#blankText}.
- */
- blankText:Ext.form.TextField.prototype.blankText,
- /**
- * @cfg {String} minSelectionsText Validation message displayed when {@link #minSelections} is not met (defaults to 'Minimum {0}
- * item(s) required'). The {0} token will be replaced by the value of {@link #minSelections}.
- */
- minSelectionsText:'Minimum {0} item(s) required',
- /**
- * @cfg {String} maxSelectionsText Validation message displayed when {@link #maxSelections} is not met (defaults to 'Maximum {0}
- * item(s) allowed'). The {0} token will be replaced by the value of {@link #maxSelections}.
- */
- maxSelectionsText:'Maximum {0} item(s) allowed',
- /**
- * @cfg {String} delimiter The string used to delimit between items when set or returned as a string of values
- * (defaults to ',').
- */
- delimiter:',',
- /**
- * @cfg {Ext.data.Store/Array} store The data source to which this MultiSelect is bound (defaults to <tt>undefined</tt>).
- * Acceptable values for this property are:
- * <div class="mdetail-params"><ul>
- * <li><b>any {@link Ext.data.Store Store} subclass</b></li>
- * <li><b>an Array</b> : Arrays will be converted to a {@link Ext.data.ArrayStore} internally.
- * <div class="mdetail-params"><ul>
- * <li><b>1-dimensional array</b> : (e.g., <tt>['Foo','Bar']</tt>)<div class="sub-desc">
- * A 1-dimensional array will automatically be expanded (each array item will be the combo
- * {@link #valueField value} and {@link #displayField text})</div></li>
- * <li><b>2-dimensional array</b> : (e.g., <tt>[['f','Foo'],['b','Bar']]</tt>)<div class="sub-desc">
- * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo
- * {@link #valueField value}, while the value at index 1 is assumed to be the combo {@link #displayField text}.
- * </div></li></ul></div></li></ul></div>
- */
- // private
- defaultAutoCreate : {tag: "div"},
- // private
- initComponent: function(){
- Ext.ux.form.MultiSelect.superclass.initComponent.call(this);
- if(Ext.isArray(this.store)){
- if (Ext.isArray(this.store[0])){
- this.store = new Ext.data.ArrayStore({
- fields: ['value','text'],
- data: this.store
- });
- this.valueField = 'value';
- }else{
- this.store = new Ext.data.ArrayStore({
- fields: ['text'],
- data: this.store,
- expandData: true
- });
- this.valueField = 'text';
- }
- this.displayField = 'text';
- } else {
- this.store = Ext.StoreMgr.lookup(this.store);
- }
- this.addEvents({
- 'dblclick' : true,
- 'click' : true,
- 'change' : true,
- 'drop' : true
- });
- },
- // private
- onRender: function(ct, position){
- Ext.ux.form.MultiSelect.superclass.onRender.call(this, ct, position);
- var fs = this.fs = new Ext.form.FieldSet({
- renderTo: this.el,
- title: this.legend,
- height: this.height,
- width: this.width,
- style: "padding:0;",
- tbar: this.tbar
- });
- fs.body.addClass('ux-mselect');
- this.view = new Ext.ListView({
- multiSelect: true,
- store: this.store,
- columns: [{ header: 'Value', width: 1, dataIndex: this.displayField }],
- hideHeaders: true
- });
- fs.add(this.view);
- this.view.on('click', this.onViewClick, this);
- this.view.on('beforeclick', this.onViewBeforeClick, this);
- this.view.on('dblclick', this.onViewDblClick, this);
- this.hiddenName = this.name || Ext.id();
- var hiddenTag = { tag: "input", type: "hidden", value: "", name: this.hiddenName };
- this.hiddenField = this.el.createChild(hiddenTag);
- this.hiddenField.dom.disabled = this.hiddenName != this.name;
- fs.doLayout();
- },
- // private
- afterRender: function(){
- Ext.ux.form.MultiSelect.superclass.afterRender.call(this);
- if (this.ddReorder && !this.dragGroup && !this.dropGroup){
- this.dragGroup = this.dropGroup = 'MultiselectDD-' + Ext.id();
- }
- if (this.draggable || this.dragGroup){
- this.dragZone = new Ext.ux.form.MultiSelect.DragZone(this, {
- ddGroup: this.dragGroup
- });
- }
- if (this.droppable || this.dropGroup){
- this.dropZone = new Ext.ux.form.MultiSelect.DropZone(this, {
- ddGroup: this.dropGroup
- });
- }
- },
- // private
- onViewClick: function(vw, index, node, e) {
- this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value);
- this.hiddenField.dom.value = this.getValue();
- this.fireEvent('click', this, e);
- this.validate();
- },
- // private
- onViewBeforeClick: function(vw, index, node, e) {
- if (this.disabled) {return false;}
- },
- // private
- onViewDblClick : function(vw, index, node, e) {
- return this.fireEvent('dblclick', vw, index, node, e);
- },
- /**
- * Returns an array of data values for the selected items in the list. The values will be separated
- * by {@link #delimiter}.
- * @return {Array} value An array of string data values
- */
- getValue: function(valueField){
- var returnArray = [];
- var selectionsArray = this.view.getSelectedIndexes();
- if (selectionsArray.length == 0) {return '';}
- for (var i=0; i<selectionsArray.length; i++) {
- returnArray.push(this.store.getAt(selectionsArray[i]).get((valueField != null) ? valueField : this.valueField));
- }
- return returnArray.join(this.delimiter);
- },
- /**
- * Sets a delimited string (using {@link #delimiter}) or array of data values into the list.
- * @param {String/Array} values The values to set
- */
- setValue: function(values) {
- var index;
- var selections = [];
- this.view.clearSelections();
- this.hiddenField.dom.value = '';
- if (!values || (values == '')) { return; }
- if (!Ext.isArray(values)) { values = values.split(this.delimiter); }
- for (var i=0; i<values.length; i++) {
- index = this.view.store.indexOf(this.view.store.query(this.valueField,
- new RegExp('^' + values[i] + '$', "i")).itemAt(0));
- selections.push(index);
- }
- this.view.select(selections);
- this.hiddenField.dom.value = this.getValue();
- this.validate();
- },
- // inherit docs
- reset : function() {
- this.setValue('');
- },
- // inherit docs
- getRawValue: function(valueField) {
- var tmp = this.getValue(valueField);
- if (tmp.length) {
- tmp = tmp.split(this.delimiter);
- }
- else {
- tmp = [];
- }
- return tmp;
- },
- // inherit docs
- setRawValue: function(values){
- setValue(values);
- },
- // inherit docs
- validateValue : function(value){
- if (value.length < 1) { // if it has no value
- if (this.allowBlank) {
- this.clearInvalid();
- return true;
- } else {
- this.markInvalid(this.blankText);
- return false;
- }
- }
- if (value.length < this.minSelections) {
- this.markInvalid(String.format(this.minSelectionsText, this.minSelections));
- return false;
- }
- if (value.length > this.maxSelections) {
- this.markInvalid(String.format(this.maxSelectionsText, this.maxSelections));
- return false;
- }
- return true;
- },
- // inherit docs
- disable: function(){
- this.disabled = true;
- this.hiddenField.dom.disabled = true;
- this.fs.disable();
- },
- // inherit docs
- enable: function(){
- this.disabled = false;
- this.hiddenField.dom.disabled = false;
- this.fs.enable();
- },
- // inherit docs
- destroy: function(){
- Ext.destroy(this.fs, this.dragZone, this.dropZone);
- Ext.ux.form.MultiSelect.superclass.destroy.call(this);
- }
- });
- Ext.reg('multiselect', Ext.ux.form.MultiSelect);
- //backwards compat
- Ext.ux.Multiselect = Ext.ux.form.MultiSelect;
- Ext.ux.form.MultiSelect.DragZone = function(ms, config){
- this.ms = ms;
- this.view = ms.view;
- var ddGroup = config.ddGroup || 'MultiselectDD';
- var dd;
- if (Ext.isArray(ddGroup)){
- dd = ddGroup.shift();
- } else {
- dd = ddGroup;
- ddGroup = null;
- }
- Ext.ux.form.MultiSelect.DragZone.superclass.constructor.call(this, this.ms.fs.body, { containerScroll: true, ddGroup: dd });
- this.setDraggable(ddGroup);
- };
- Ext.extend(Ext.ux.form.MultiSelect.DragZone, Ext.dd.DragZone, {
- onInitDrag : function(x, y){
- var el = Ext.get(this.dragData.ddel.cloneNode(true));
- this.proxy.update(el.dom);
- el.setWidth(el.child('em').getWidth());
- this.onStartDrag(x, y);
- return true;
- },
- // private
- collectSelection: function(data) {
- data.repairXY = Ext.fly(this.view.getSelectedNodes()[0]).getXY();
- var i = 0;
- this.view.store.each(function(rec){
- if (this.view.isSelected(i)) {
- var n = this.view.getNode(i);
- var dragNode = n.cloneNode(true);
- dragNode.id = Ext.id();
- data.ddel.appendChild(dragNode);
- data.records.push(this.view.store.getAt(i));
- data.viewNodes.push(n);
- }
- i++;
- }, this);
- },
- // override
- onEndDrag: function(data, e) {
- var d = Ext.get(this.dragData.ddel);
- if (d && d.hasClass("multi-proxy")) {
- d.remove();
- }
- },
- // override
- getDragData: function(e){
- var target = this.view.findItemFromChild(e.getTarget());
- if(target) {
- if (!this.view.isSelected(target) && !e.ctrlKey && !e.shiftKey) {
- this.view.select(target);
- this.ms.setValue(this.ms.getValue());
- }
- if (this.view.getSelectionCount() == 0 || e.ctrlKey || e.shiftKey) return false;
- var dragData = {
- sourceView: this.view,
- viewNodes: [],
- records: []
- };
- if (this.view.getSelectionCount() == 1) {
- var i = this.view.getSelectedIndexes()[0];
- var n = this.view.getNode(i);
- dragData.viewNodes.push(dragData.ddel = n);
- dragData.records.push(this.view.store.getAt(i));
- dragData.repairXY = Ext.fly(n).getXY();
- } else {
- dragData.ddel = document.createElement('div');
- dragData.ddel.className = 'multi-proxy';
- this.collectSelection(dragData);
- }
- return dragData;
- }
- return false;
- },
- // override the default repairXY.
- getRepairXY : function(e){
- return this.dragData.repairXY;
- },
- // private
- setDraggable: function(ddGroup){
- if (!ddGroup) return;
- if (Ext.isArray(ddGroup)) {
- Ext.each(ddGroup, this.setDraggable, this);
- return;
- }
- this.addToGroup(ddGroup);
- }
- });
- Ext.ux.form.MultiSelect.DropZone = function(ms, config){
- this.ms = ms;
- this.view = ms.view;
- var ddGroup = config.ddGroup || 'MultiselectDD';
- var dd;
- if (Ext.isArray(ddGroup)){
- dd = ddGroup.shift();
- } else {
- dd = ddGroup;
- ddGroup = null;
- }
- Ext.ux.form.MultiSelect.DropZone.superclass.constructor.call(this, this.ms.fs.body, { containerScroll: true, ddGroup: dd });
- this.setDroppable(ddGroup);
- };
- Ext.extend(Ext.ux.form.MultiSelect.DropZone, Ext.dd.DropZone, {
- /**
- * Part of the Ext.dd.DropZone interface. If no target node is found, the
- * whole Element becomes the target, and this causes the drop gesture to append.
- */
- getTargetFromEvent : function(e) {
- var target = e.getTarget();
- return target;
- },
- // private
- getDropPoint : function(e, n, dd){
- if (n == this.ms.fs.body.dom) { return "below"; }
- var t = Ext.lib.Dom.getY(n), b = t + n.offsetHeight;
- var c = t + (b - t) / 2;
- var y = Ext.lib.Event.getPageY(e);
- if(y <= c) {
- return "above";
- }else{
- return "below";
- }
- },
- // private
- isValidDropPoint: function(pt, n, data) {
- if (!data.viewNodes || (data.viewNodes.length != 1)) {
- return true;
- }
- var d = data.viewNodes[0];
- if (d == n) {
- return false;
- }
- if ((pt == "below") && (n.nextSibling == d)) {
- return false;
- }
- if ((pt == "above") && (n.previousSibling == d)) {
- return false;
- }
- return true;
- },
- // override
- onNodeEnter : function(n, dd, e, data){
- return false;
- },
- // override
- onNodeOver : function(n, dd, e, data){
- var dragElClass = this.dropNotAllowed;
- var pt = this.getDropPoint(e, n, dd);
- if (this.isValidDropPoint(pt, n, data)) {
- if (this.ms.appendOnly) {
- return "x-tree-drop-ok-below";
- }
- // set the insert point style on the target node
- if (pt) {
- var targetElClass;
- if (pt == "above"){
- dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
- targetElClass = "x-view-drag-insert-above";
- } else {
- dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
- targetElClass = "x-view-drag-insert-below";
- }
- if (this.lastInsertClass != targetElClass){
- Ext.fly(n).replaceClass(this.lastInsertClass, targetElClass);
- this.lastInsertClass = targetElClass;
- }
- }
- }
- return dragElClass;
- },
- // private
- onNodeOut : function(n, dd, e, data){
- this.removeDropIndicators(n);
- },
- // private
- onNodeDrop : function(n, dd, e, data){
- if (this.ms.fireEvent("drop", this, n, dd, e, data) === false) {
- return false;
- }
- var pt = this.getDropPoint(e, n, dd);
- if (n != this.ms.fs.body.dom)
- n = this.view.findItemFromChild(n);
- var insertAt = (this.ms.appendOnly || (n == this.ms.fs.body.dom)) ? this.view.store.getCount() : this.view.indexOf(n);
- if (pt == "below") {
- insertAt++;
- }
- var dir = false;
- // Validate if dragging within the same MultiSelect
- if (data.sourceView == this.view) {
- // If the first element to be inserted below is the target node, remove it
- if (pt == "below") {
- if (data.viewNodes[0] == n) {
- data.viewNodes.shift();
- }
- } else { // If the last element to be inserted above is the target node, remove it
- if (data.viewNodes[data.viewNodes.length - 1] == n) {
- data.viewNodes.pop();
- }
- }
- // Nothing to drop...
- if (!data.viewNodes.length) {
- return false;
- }
- // If we are moving DOWN, then because a store.remove() takes place first,
- // the insertAt must be decremented.
- if (insertAt > this.view.store.indexOf(data.records[0])) {
- dir = 'down';
- insertAt--;
- }
- }
- for (var i = 0; i < data.records.length; i++) {
- var r = data.records[i];
- if (data.sourceView) {
- data.sourceView.store.remove(r);
- }
- this.view.store.insert(dir == 'down' ? insertAt : insertAt++, r);
- var si = this.view.store.sortInfo;
- if(si){
- this.view.store.sort(si.field, si.direction);
- }
- }
- return true;
- },
- // private
- removeDropIndicators : function(n){
- if(n){
- Ext.fly(n).removeClass([
- "x-view-drag-insert-above",
- "x-view-drag-insert-left",
- "x-view-drag-insert-right",
- "x-view-drag-insert-below"]);
- this.lastInsertClass = "_noclass";
- }
- },
- // private
- setDroppable: function(ddGroup){
- if (!ddGroup) return;
- if (Ext.isArray(ddGroup)) {
- Ext.each(ddGroup, this.setDroppable, this);
- return;
- }
- this.addToGroup(ddGroup);
- }
- });
- /* 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(v1, v2){ return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); }; 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, {
- minHeight: 0,
- maxHeight:10000000,
- constructor: function(config){
- Ext.apply(this, config);
- this.events = {};
- Ext.ux.PanelResizer.superclass.constructor.call(this, config);
- },
- init : function(p){
- this.panel = p;
- if(this.panel.elements.indexOf('footer')==-1){
- p.elements += ',footer';
- }
- p.on('render', this.onRender, this);
- },
- onRender : function(p){
- this.handle = p.footer.createChild({cls:'x-panel-resize'});
- this.tracker = new Ext.dd.DragTracker({
- onStart: this.onDragStart.createDelegate(this),
- onDrag: this.onDrag.createDelegate(this),
- onEnd: this.onDragEnd.createDelegate(this),
- tolerance: 3,
- autoStart: 300
- });
- this.tracker.initEl(this.handle);
- p.on('beforedestroy', this.tracker.destroy, this.tracker);
- },
- // private
- onDragStart: function(e){
- this.dragging = true;
- this.startHeight = this.panel.el.getHeight();
- this.fireEvent('dragstart', this, e);
- },
- // private
- onDrag: function(e){
- this.panel.setHeight((this.startHeight-this.tracker.getOffset()[1]).constrain(this.minHeight, this.maxHeight));
- this.fireEvent('drag', this, e);
- },
- // private
- onDragEnd: function(e){
- this.dragging = false;
- this.fireEvent('dragend', this, e);
- }
- });
- Ext.preg('panelresizer', Ext.ux.PanelResizer);Ext.ux.Portal = Ext.extend(Ext.Panel, {
- layout : 'column',
- autoScroll : true,
- cls : 'x-portal',
- defaultType : 'portalcolumn',
- initComponent : function(){
- Ext.ux.Portal.superclass.initComponent.call(this);
- this.addEvents({
- validatedrop:true,
- beforedragover:true,
- dragover:true,
- beforedrop:true,
- drop:true
- });
- },
- initEvents : function(){
- Ext.ux.Portal.superclass.initEvents.call(this);
- this.dd = new Ext.ux.Portal.DropZone(this, this.dropConfig);
- },
- beforeDestroy : function() {
- if(this.dd){
- this.dd.unreg();
- }
- Ext.ux.Portal.superclass.beforeDestroy.call(this);
- }
- });
- Ext.reg('portal', Ext.ux.Portal);
- Ext.ux.Portal.DropZone = function(portal, cfg){
- this.portal = portal;
- Ext.dd.ScrollManager.register(portal.body);
- Ext.ux.Portal.DropZone.superclass.constructor.call(this, portal.bwrap.dom, cfg);
- portal.body.ddScrollConfig = this.ddScrollConfig;
- };
- Ext.extend(Ext.ux.Portal.DropZone, Ext.dd.DropTarget, {
- ddScrollConfig : {
- vthresh: 50,
- hthresh: -1,
- animate: true,
- increment: 200
- },
- createEvent : function(dd, e, data, col, c, pos){
- return {
- portal: this.portal,
- panel: data.panel,
- columnIndex: col,
- column: c,
- position: pos,
- data: data,
- source: dd,
- rawEvent: e,
- status: this.dropAllowed
- };
- },
- notifyOver : function(dd, e, data){
- var xy = e.getXY(), portal = this.portal, px = dd.proxy;
- // case column widths
- if(!this.grid){
- this.grid = this.getGrid();
- }
- // handle case scroll where scrollbars appear during drag
- var cw = portal.body.dom.clientWidth;
- if(!this.lastCW){
- this.lastCW = cw;
- }else if(this.lastCW != cw){
- this.lastCW = cw;
- portal.doLayout();
- this.grid = this.getGrid();
- }
- // determine column
- var col = 0, xs = this.grid.columnX, cmatch = false;
- for(var len = xs.length; col < len; col++){
- if(xy[0] < (xs[col].x + xs[col].w)){
- cmatch = true;
- break;
- }
- }
- // no match, fix last index
- if(!cmatch){
- col--;
- }
- // find insert position
- var p, match = false, pos = 0,
- c = portal.items.itemAt(col),
- items = c.items.items, overSelf = false;
- for(var len = items.length; pos < len; pos++){
- p = items[pos];
- var h = p.el.getHeight();
- if(h === 0){
- overSelf = true;
- }
- else if((p.el.getY()+(h/2)) > xy[1]){
- match = true;
- break;
- }
- }
- pos = (match && p ? pos : c.items.getCount()) + (overSelf ? -1 : 0);
- var overEvent = this.createEvent(dd, e, data, col, c, pos);
- if(portal.fireEvent('validatedrop', overEvent) !== false &&
- portal.fireEvent('beforedragover', overEvent) !== false){
- // make sure proxy width is fluid
- px.getProxy().setWidth('auto');
- if(p){
- px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null);
- }else{
- px.moveProxy(c.el.dom, null);
- }
- this.lastPos = {c: c, col: col, p: overSelf || (match && p) ? pos : false};
- this.scrollPos = portal.body.getScroll();
- portal.fireEvent('dragover', overEvent);
- return overEvent.status;
- }else{
- return overEvent.status;
- }
- },
- notifyOut : function(){
- delete this.grid;
- },
- notifyDrop : function(dd, e, data){
- delete this.grid;
- if(!this.lastPos){
- return;
- }
- var c = this.lastPos.c, col = this.lastPos.col, pos = this.lastPos.p;
- var dropEvent = this.createEvent(dd, e, data, col, c,
- pos !== false ? pos : c.items.getCount());
- if(this.portal.fireEvent('validatedrop', dropEvent) !== false &&
- this.portal.fireEvent('beforedrop', dropEvent) !== false){
- dd.proxy.getProxy().remove();
- dd.panel.el.dom.parentNode.removeChild(dd.panel.el.dom);
- if(pos !== false){
- if(c == dd.panel.ownerCt && (c.items.items.indexOf(dd.panel) <= pos)){
- pos++;
- }
- c.insert(pos, dd.panel);
- }else{
- c.add(dd.panel);
- }
- c.doLayout();
- this.portal.fireEvent('drop', dropEvent);
- // scroll position is lost on drop, fix it
- var st = this.scrollPos.top;
- if(st){
- var d = this.portal.body.dom;
- setTimeout(function(){
- d.scrollTop = st;
- }, 10);
- }
- }
- delete this.lastPos;
- },
- // internal cache of body and column coords
- getGrid : function(){
- var box = this.portal.bwrap.getBox();
- box.columnX = [];
- this.portal.items.each(function(c){
- box.columnX.push({x: c.el.getX(), w: c.el.getWidth()});
- });
- return box;
- },
- // unregister the dropzone from ScrollManager
- unreg: function() {
- //Ext.dd.ScrollManager.unregister(this.portal.body);
- Ext.ux.Portal.DropZone.superclass.unreg.call(this);
- }
- });
- Ext.ux.PortalColumn = Ext.extend(Ext.Container, {
- layout : 'anchor',
- //autoEl : 'div',//already defined by Ext.Component
- defaultType : 'portlet',
- cls : 'x-portal-column'
- });
- Ext.reg('portalcolumn', Ext.ux.PortalColumn);
- Ext.ux.Portlet = Ext.extend(Ext.Panel, {
- anchor : '100%',
- frame : true,
- collapsible : true,
- draggable : true,
- cls : 'x-portlet'
- });
- Ext.reg('portlet', Ext.ux.Portlet);
- /** * @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.mon(pb.getEl().applyStyles('cursor:pointer'), 'click', this.handleProgressBarClick, this); }, this, {single: true}); } }, // private // This method handles the click for the progress bar handleProgressBarClick : function(e){ var parent = this.parent, displayItem = parent.displayItem, box = this.progressBar.getBox(), xy = e.getXY(), position = xy[0]-box.x, pages = Math.ceil(parent.store.getTotalCount()/parent.pageSize), 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(), pgData = this.getPageData(), pageNum = this.readPage(pgData), 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, saveText: 'Save', cancelText: 'Cancel', commitChangesText: 'You need to commit or cancel your changes', errorText: 'Errors', 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 canceledit * Fired when the editor is cancelled. * @param {Ext.ux.grid.RowEditor} roweditor This object * @param {Boolean} forced True if the cancel button is pressed, false is the editor was invalid. */ 'canceledit', /** * @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, beforedestroy : this.beforedestroy, 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, [])); }, beforedestroy: function() { this.grid.getStore().un('remove', this.onStoreRemove, this); this.stopEditing(false); Ext.destroy(this.btns); }, 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(this.commitChangesText); return; } if(Ext.isObject(rowIndex)){ rowIndex = this.grid.getStore().indexOf(rowIndex); } if(this.fireEvent('beforeedit', this, rowIndex) !== false){ this.editing = true; var g = this.grid, view = g.getView(), row = view.getRow(rowIndex), 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] = Ext.isEmpty(val) ? '' : 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(); this.fireEvent('canceledit', this, saveChanges === false); return; } var changes = {}, r = this.record, hasChange = false, 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], 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(); Ext.iterate(changes, function(name, value){ r.set(name, value); }); 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.fly(row).getHeight() + 9); 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 === (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), 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, width: this.minButtonWidth, handler: this.stopEditing.createDelegate(this, [true]) }, { xtype: 'button', text: this.cancelText, 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 g = this.grid, h = this.el.dom.clientHeight, view = g.getView(), scroll = view.scroller.dom.scrollLeft, bw = this.btns.getWidth(), width = Math.min(g.getWidth(), g.getColumnModel().getTotalWidth()); 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, cm = this.grid.getColumnModel(), c; if(pt){ index = this.getTargetColumnIndex(pt); } for(var i = index||0, len = cm.getColumnCount(); i < len; i++){ c = cm.getColumnAt(i); if(!c.hidden && c.getEditor()){ c.getEditor().focus(); break; } } } }, getTargetColumnIndex: function(pt){ var grid = this.grid, v = grid.view, x = pt.left, cms = grid.colModel.config, 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: this.errorText, autoHide: false, anchor: 'left', anchorToTarget: true, mouseOffset: [40,0] }); } var v = this.grid.getView(), top = parseInt(this.el.dom.style.top, 10), scroll = v.scroller.dom.scrollTop, h = this.el.getHeight(); if(top + h >= scroll){ t.initTarget(this.items.last().getEl()); if(!t.rendered){ t.show(); t.hide(); } t.body.update(msg); t.doAutoWidth(20); t.show(); }else if(t.rendered){ t.hide(); } }, getErrorText: function(){ var data = ['<ul>']; this.items.each(function(f){ if(!f.isValid(true)){ data.push('<li>', f.getActiveError(), '</li>'); } }); data.push('</ul>'); return data; } }); Ext.preg('roweditor', Ext.ux.grid.RowEditor); Ext.ns('Ext.ux.grid');
- /**
- * @class Ext.ux.grid.RowExpander
- * @extends Ext.util.Observable
- * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables
- * a second row body which expands/contracts. The expand/contract behavior is configurable to react
- * on clicking of the column, double click of the row, and/or hitting enter while a row is selected.
- *
- * @ptype rowexpander
- */
- Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, {
- /**
- * @cfg {Boolean} expandOnEnter
- * <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter
- * key is pressed (defaults to <tt>true</tt>).
- */
- expandOnEnter : true,
- /**
- * @cfg {Boolean} expandOnDblClick
- * <tt>true</tt> to toggle a row between expanded/collapsed when double clicked
- * (defaults to <tt>true</tt>).
- */
- expandOnDblClick : true,
- header : '',
- width : 20,
- sortable : false,
- fixed : true,
- menuDisabled : true,
- dataIndex : '',
- id : 'expander',
- lazyRender : true,
- enableCaching : true,
- constructor: function(config){
- Ext.apply(this, config);
- this.addEvents({
- /**
- * @event beforeexpand
- * Fires before the row expands. Have the listener return false to prevent the row from expanding.
- * @param {Object} this RowExpander object.
- * @param {Object} Ext.data.Record Record for the selected row.
- * @param {Object} body body element for the secondary row.
- * @param {Number} rowIndex The current row index.
- */
- beforeexpand: true,
- /**
- * @event expand
- * Fires after the row expands.
- * @param {Object} this RowExpander object.
- * @param {Object} Ext.data.Record Record for the selected row.
- * @param {Object} body body element for the secondary row.
- * @param {Number} rowIndex The current row index.
- */
- expand: true,
- /**
- * @event beforecollapse
- * Fires before the row collapses. Have the listener return false to prevent the row from collapsing.
- * @param {Object} this RowExpander object.
- * @param {Object} Ext.data.Record Record for the selected row.
- * @param {Object} body body element for the secondary row.
- * @param {Number} rowIndex The current row index.
- */
- beforecollapse: true,
- /**
- * @event collapse
- * Fires after the row collapses.
- * @param {Object} this RowExpander object.
- * @param {Object} Ext.data.Record Record for the selected row.
- * @param {Object} body body element for the secondary row.
- * @param {Number} rowIndex The current row index.
- */
- collapse: true
- });
- Ext.ux.grid.RowExpander.superclass.constructor.call(this);
- if(this.tpl){
- if(typeof this.tpl == 'string'){
- this.tpl = new Ext.Template(this.tpl);
- }
- this.tpl.compile();
- }
- this.state = {};
- this.bodyContent = {};
- },
- getRowClass : function(record, rowIndex, p, ds){
- p.cols = p.cols-1;
- var content = this.bodyContent[record.id];
- if(!content && !this.lazyRender){
- content = this.getBodyContent(record, rowIndex);
- }
- if(content){
- p.body = content;
- }
- return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
- },
- init : function(grid){
- this.grid = grid;
- var view = grid.getView();
- view.getRowClass = this.getRowClass.createDelegate(this);
- view.enableRowBody = true;
- grid.on('render', this.onRender, this);
- grid.on('destroy', this.onDestroy, this);
- },
- // @private
- onRender: function() {
- var grid = this.grid;
- var mainBody = grid.getView().mainBody;
- mainBody.on('mousedown', this.onMouseDown, this, {delegate: '.x-grid3-row-expander'});
- if (this.expandOnEnter) {
- this.keyNav = new Ext.KeyNav(this.grid.getGridEl(), {
- 'enter' : this.onEnter,
- scope: this
- });
- }
- if (this.expandOnDblClick) {
- grid.on('rowdblclick', this.onRowDblClick, this);
- }
- },
- // @private
- onDestroy: function() {
- if(this.keyNav){
- this.keyNav.disable();
- delete this.keyNav;
- }
- /*
- * A majority of the time, the plugin will be destroyed along with the grid,
- * which means the mainBody won't be available. On the off chance that the plugin
- * isn't destroyed with the grid, take care of removing the listener.
- */
- var mainBody = this.grid.getView().mainBody;
- if(mainBody){
- mainBody.un('mousedown', this.onMouseDown, this);
- }
- },
- // @private
- onRowDblClick: function(grid, rowIdx, e) {
- this.toggleRow(rowIdx);
- },
- onEnter: function(e) {
- var g = this.grid;
- var sm = g.getSelectionModel();
- var sels = sm.getSelections();
- for (var i = 0, len = sels.length; i < len; i++) {
- var rowIdx = g.getStore().indexOf(sels[i]);
- this.toggleRow(rowIdx);
- }
- },
- getBodyContent : function(record, index){
- if(!this.enableCaching){
- return this.tpl.apply(record.data);
- }
- var content = this.bodyContent[record.id];
- if(!content){
- content = this.tpl.apply(record.data);
- this.bodyContent[record.id] = content;
- }
- return content;
- },
- onMouseDown : function(e, t){
- e.stopEvent();
- var row = e.getTarget('.x-grid3-row');
- this.toggleRow(row);
- },
- renderer : function(v, p, record){
- p.cellAttr = 'rowspan="2"';
- return '<div class="x-grid3-row-expander"> </div>';
- },
- beforeExpand : function(record, body, rowIndex){
- if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){
- if(this.tpl && this.lazyRender){
- body.innerHTML = this.getBodyContent(record, rowIndex);
- }
- return true;
- }else{
- return false;
- }
- },
- toggleRow : function(row){
- if(typeof row == 'number'){
- row = this.grid.view.getRow(row);
- }
- this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
- },
- expandRow : function(row){
- if(typeof row == 'number'){
- row = this.grid.view.getRow(row);
- }
- var record = this.grid.store.getAt(row.rowIndex);
- var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
- if(this.beforeExpand(record, body, row.rowIndex)){
- this.state[record.id] = true;
- Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
- this.fireEvent('expand', this, record, body, row.rowIndex);
- }
- },
- collapseRow : function(row){
- if(typeof row == 'number'){
- row = this.grid.view.getRow(row);
- }
- var record = this.grid.store.getAt(row.rowIndex);
- var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
- if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){
- this.state[record.id] = false;
- Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
- this.fireEvent('collapse', this, record, body, row.rowIndex);
- }
- }
- });
- Ext.preg('rowexpander', Ext.ux.grid.RowExpander);
- //backwards compat
- 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(true); if(size.width < 1 && size.height < 1){ // display none? return; } var h = size.height, 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');
- Ext.ux.form.SearchField = Ext.extend(Ext.form.TwinTriggerField, {
- initComponent : function(){
- Ext.ux.form.SearchField.superclass.initComponent.call(this);
- this.on('specialkey', function(f, e){
- if(e.getKey() == e.ENTER){
- this.onTrigger2Click();
- }
- }, this);
- },
- validationEvent:false,
- validateOnBlur:false,
- trigger1Class:'x-form-clear-trigger',
- trigger2Class:'x-form-search-trigger',
- hideTrigger1:true,
- width:180,
- hasSearch : false,
- paramName : 'query',
- onTrigger1Click : function(){
- if(this.hasSearch){
- this.el.dom.value = '';
- var o = {start: 0};
- this.store.baseParams = this.store.baseParams || {};
- this.store.baseParams[this.paramName] = '';
- this.store.reload({params:o});
- this.triggers[0].hide();
- this.hasSearch = false;
- }
- },
- onTrigger2Click : function(){
- var v = this.getRawValue();
- if(v.length < 1){
- this.onTrigger1Click();
- return;
- }
- var o = {start: 0};
- this.store.baseParams = this.store.baseParams || {};
- this.store.baseParams[this.paramName] = v;
- this.store.reload({params:o});
- this.hasSearch = true;
- this.triggers[0].show();
- }
- });Ext.ns('Ext.ux.form');
- /**
- * @class Ext.ux.form.SelectBox
- * @extends Ext.form.ComboBox
- * <p>Makes a ComboBox more closely mimic an HTML SELECT. Supports clicking and dragging
- * through the list, with item selection occurring when the mouse button is released.
- * When used will automatically set {@link #editable} to false and call {@link Ext.Element#unselectable}
- * on inner elements. Re-enabling editable after calling this will NOT work.</p>
- * @author Corey Gilmore http://extjs.com/forum/showthread.php?t=6392
- * @history 2007-07-08 jvs
- * Slight mods for Ext 2.0
- * @xtype selectbox
- */
- Ext.ux.form.SelectBox = Ext.extend(Ext.form.ComboBox, {
- constructor: function(config){
- this.searchResetDelay = 1000;
- config = config || {};
- config = Ext.apply(config || {}, {
- editable: false,
- forceSelection: true,
- rowHeight: false,
- lastSearchTerm: false,
- triggerAction: 'all',
- mode: 'local'
- });
- Ext.ux.form.SelectBox.superclass.constructor.apply(this, arguments);
- this.lastSelectedIndex = this.selectedIndex || 0;
- },
- initEvents : function(){
- Ext.ux.form.SelectBox.superclass.initEvents.apply(this, arguments);
- // you need to use keypress to capture upper/lower case and shift+key, but it doesn't work in IE
- this.el.on('keydown', this.keySearch, this, true);
- this.cshTask = new Ext.util.DelayedTask(this.clearSearchHistory, this);
- },
- keySearch : function(e, target, options) {
- var raw = e.getKey();
- var key = String.fromCharCode(raw);
- var startIndex = 0;
- if( !this.store.getCount() ) {
- return;
- }
- switch(raw) {
- case Ext.EventObject.HOME:
- e.stopEvent();
- this.selectFirst();
- return;
- case Ext.EventObject.END:
- e.stopEvent();
- this.selectLast();
- return;
- case Ext.EventObject.PAGEDOWN:
- this.selectNextPage();
- e.stopEvent();
- return;
- case Ext.EventObject.PAGEUP:
- this.selectPrevPage();
- e.stopEvent();
- return;
- }
- // skip special keys other than the shift key
- if( (e.hasModifier() && !e.shiftKey) || e.isNavKeyPress() || e.isSpecialKey() ) {
- return;
- }
- if( this.lastSearchTerm == key ) {
- startIndex = this.lastSelectedIndex;
- }
- this.search(this.displayField, key, startIndex);
- this.cshTask.delay(this.searchResetDelay);
- },
- onRender : function(ct, position) {
- this.store.on('load', this.calcRowsPerPage, this);
- Ext.ux.form.SelectBox.superclass.onRender.apply(this, arguments);
- if( this.mode == 'local' ) {
- this.initList();
- this.calcRowsPerPage();
- }
- },
- onSelect : function(record, index, skipCollapse){
- if(this.fireEvent('beforeselect', this, record, index) !== false){
- this.setValue(record.data[this.valueField || this.displayField]);
- if( !skipCollapse ) {
- this.collapse();
- }
- this.lastSelectedIndex = index + 1;
- this.fireEvent('select', this, record, index);
- }
- },
- afterRender : function() {
- Ext.ux.form.SelectBox.superclass.afterRender.apply(this, arguments);
- if(Ext.isWebKit) {
- this.el.swallowEvent('mousedown', true);
- }
- this.el.unselectable();
- this.innerList.unselectable();
- this.trigger.unselectable();
- this.innerList.on('mouseup', function(e, target, options) {
- if( target.id && target.id == this.innerList.id ) {
- return;
- }
- this.onViewClick();
- }, this);
- this.innerList.on('mouseover', function(e, target, options) {
- if( target.id && target.id == this.innerList.id ) {
- return;
- }
- this.lastSelectedIndex = this.view.getSelectedIndexes()[0] + 1;
- this.cshTask.delay(this.searchResetDelay);
- }, this);
- this.trigger.un('click', this.onTriggerClick, this);
- this.trigger.on('mousedown', function(e, target, options) {
- e.preventDefault();
- this.onTriggerClick();
- }, this);
- this.on('collapse', function(e, target, options) {
- Ext.getDoc().un('mouseup', this.collapseIf, this);
- }, this, true);
- this.on('expand', function(e, target, options) {
- Ext.getDoc().on('mouseup', this.collapseIf, this);
- }, this, true);
- },
- clearSearchHistory : function() {
- this.lastSelectedIndex = 0;
- this.lastSearchTerm = false;
- },
- selectFirst : function() {
- this.focusAndSelect(this.store.data.first());
- },
- selectLast : function() {
- this.focusAndSelect(this.store.data.last());
- },
- selectPrevPage : function() {
- if( !this.rowHeight ) {
- return;
- }
- var index = Math.max(this.selectedIndex-this.rowsPerPage, 0);
- this.focusAndSelect(this.store.getAt(index));
- },
- selectNextPage : function() {
- if( !this.rowHeight ) {
- return;
- }
- var index = Math.min(this.selectedIndex+this.rowsPerPage, this.store.getCount() - 1);
- this.focusAndSelect(this.store.getAt(index));
- },
- search : function(field, value, startIndex) {
- field = field || this.displayField;
- this.lastSearchTerm = value;
- var index = this.store.find.apply(this.store, arguments);
- if( index !== -1 ) {
- this.focusAndSelect(index);
- }
- },
- focusAndSelect : function(record) {
- var index = Ext.isNumber(record) ? record : this.store.indexOf(record);
- this.select(index, this.isExpanded());
- this.onSelect(this.store.getAt(index), index, this.isExpanded());
- },
- calcRowsPerPage : function() {
- if( this.store.getCount() ) {
- this.rowHeight = Ext.fly(this.view.getNode(0)).getHeight();
- this.rowsPerPage = this.maxHeight / this.rowHeight;
- } else {
- this.rowHeight = false;
- }
- }
- });
- Ext.reg('selectbox', Ext.ux.form.SelectBox);
- //backwards compat
- Ext.ux.SelectBox = Ext.ux.form.SelectBox;
- /**
- * @class Ext.ux.SliderTip
- * @extends Ext.Tip
- * Simple plugin for using an Ext.Tip with a slider to show the slider value
- */
- Ext.ux.SliderTip = Ext.extend(Ext.Tip, {
- minWidth: 10,
- offsets : [0, -10],
- init : function(slider){
- slider.on('dragstart', this.onSlide, this);
- slider.on('drag', this.onSlide, this);
- slider.on('dragend', this.hide, this);
- slider.on('destroy', this.destroy, this);
- },
- onSlide : function(slider){
- this.show();
- this.body.update(this.getText(slider));
- this.doAutoWidth();
- this.el.alignTo(slider.thumb, 'b-t?', this.offsets);
- },
- getText : function(slider){
- return String(slider.getValue());
- }
- });
- Ext.ux.SlidingPager = Ext.extend(Object, {
- init : function(pbar){
- Ext.each(pbar.items.getRange(2,6), function(c){
- c.hide();
- });
- var slider = new Ext.Slider({
- width: 114,
- minValue: 1,
- maxValue: 1,
- plugins: new Ext.ux.SliderTip({
- getText : function(s){
- return String.format('Page <b>{0}</b> of <b>{1}</b>', s.value, s.maxValue);
- }
- }),
- listeners: {
- changecomplete: function(s, v){
- pbar.changePage(v);
- }
- }
- });
- pbar.insert(5, slider);
- pbar.on({
- change: function(pb, data){
- slider.maxValue = data.pages;
- slider.setValue(data.activePage);
- },
- beforedestroy: function(){
- slider.destroy();
- }
- });
- }
- });Ext.ns('Ext.ux.form');
- /**
- * @class Ext.ux.form.SpinnerField
- * @extends Ext.form.NumberField
- * Creates a field utilizing Ext.ux.Spinner
- * @xtype spinnerfield
- */
- Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, {
- actionMode: 'wrap',
- deferHeight: true,
- autoSize: Ext.emptyFn,
- onBlur: Ext.emptyFn,
- adjustSize: Ext.BoxComponent.prototype.adjustSize,
- constructor: function(config) {
- var spinnerConfig = Ext.copyTo({}, config, 'incrementValue,alternateIncrementValue,accelerate,defaultValue,triggerClass,splitterClass');
- var spl = this.spinner = new Ext.ux.Spinner(spinnerConfig);
- var plugins = config.plugins
- ? (Ext.isArray(config.plugins)
- ? config.plugins.push(spl)
- : [config.plugins, spl])
- : spl;
- Ext.ux.form.SpinnerField.superclass.constructor.call(this, Ext.apply(config, {plugins: plugins}));
- },
- // private
- getResizeEl: function(){
- return this.wrap;
- },
- // private
- getPositionEl: function(){
- return this.wrap;
- },
- // private
- alignErrorIcon: function(){
- if (this.wrap) {
- this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
- }
- },
- validateBlur: function(){
- return true;
- }
- });
- Ext.reg('spinnerfield', Ext.ux.form.SpinnerField);
- //backwards compat
- Ext.form.SpinnerField = Ext.ux.form.SpinnerField;
- /**
- * @class Ext.ux.Spinner
- * @extends Ext.util.Observable
- * Creates a Spinner control utilized by Ext.ux.form.SpinnerField
- */
- Ext.ux.Spinner = Ext.extend(Ext.util.Observable, {
- incrementValue: 1,
- alternateIncrementValue: 5,
- triggerClass: 'x-form-spinner-trigger',
- splitterClass: 'x-form-spinner-splitter',
- alternateKey: Ext.EventObject.shiftKey,
- defaultValue: 0,
- accelerate: false,
- constructor: function(config){
- Ext.ux.Spinner.superclass.constructor.call(this, config);
- Ext.apply(this, config);
- this.mimicing = false;
- },
- init: function(field){
- this.field = field;
- field.afterMethod('onRender', this.doRender, this);
- field.afterMethod('onEnable', this.doEnable, this);
- field.afterMethod('onDisable', this.doDisable, this);
- field.afterMethod('afterRender', this.doAfterRender, this);
- field.afterMethod('onResize', this.doResize, this);
- field.afterMethod('onFocus', this.doFocus, this);
- field.beforeMethod('onDestroy', this.doDestroy, this);
- },
- doRender: function(ct, position){
- var el = this.el = this.field.getEl();
- var f = this.field;
- if (!f.wrap) {
- f.wrap = this.wrap = el.wrap({
- cls: "x-form-field-wrap"
- });
- }
- else {
- this.wrap = f.wrap.addClass('x-form-field-wrap');
- }
- this.trigger = this.wrap.createChild({
- tag: "img",
- src: Ext.BLANK_IMAGE_URL,
- cls: "x-form-trigger " + this.triggerClass
- });
- if (!f.width) {
- this.wrap.setWidth(el.getWidth() + this.trigger.getWidth());
- }
- this.splitter = this.wrap.createChild({
- tag: 'div',
- cls: this.splitterClass,
- style: 'width:13px; height:2px;'
- });
- this.splitter.setRight((Ext.isIE) ? 1 : 2).setTop(10).show();
- this.proxy = this.trigger.createProxy('', this.splitter, true);
- this.proxy.addClass("x-form-spinner-proxy");
- this.proxy.setStyle('left', '0px');
- this.proxy.setSize(14, 1);
- this.proxy.hide();
- this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", {
- dragElId: this.proxy.id
- });
- this.initTrigger();
- this.initSpinner();
- },
- doAfterRender: function(){
- var y;
- if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) {
- this.el.position();
- this.el.setY(y);
- }
- },
- doEnable: function(){
- if (this.wrap) {
- this.wrap.removeClass(this.field.disabledClass);
- }
- },
- doDisable: function(){
- if (this.wrap) {
- this.wrap.addClass(this.field.disabledClass);
- this.el.removeClass(this.field.disabledClass);
- }
- },
- doResize: function(w, h){
- if (typeof w == 'number') {
- this.el.setWidth(w - this.trigger.getWidth());
- }
- this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth());
- },
- doFocus: function(){
- if (!this.mimicing) {
- this.wrap.addClass('x-trigger-wrap-focus');
- this.mimicing = true;
- Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, {
- delay: 10
- });
- this.el.on('keydown', this.checkTab, this);
- }
- },
- // private
- checkTab: function(e){
- if (e.getKey() == e.TAB) {
- this.triggerBlur();
- }
- },
- // private
- mimicBlur: function(e){
- if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) {
- this.triggerBlur();
- }
- },
- // private
- triggerBlur: function(){
- this.mimicing = false;
- Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this);
- this.el.un("keydown", this.checkTab, this);
- this.field.beforeBlur();
- this.wrap.removeClass('x-trigger-wrap-focus');
- this.field.onBlur.call(this.field);
- },
- initTrigger: function(){
- this.trigger.addClassOnOver('x-form-trigger-over');
- this.trigger.addClassOnClick('x-form-trigger-click');
- },
- initSpinner: function(){
- this.field.addEvents({
- 'spin': true,
- 'spinup': true,
- 'spindown': true
- });
- this.keyNav = new Ext.KeyNav(this.el, {
- "up": function(e){
- e.preventDefault();
- this.onSpinUp();
- },
- "down": function(e){
- e.preventDefault();
- this.onSpinDown();
- },
- "pageUp": function(e){
- e.preventDefault();
- this.onSpinUpAlternate();
- },
- "pageDown": function(e){
- e.preventDefault();
- this.onSpinDownAlternate();
- },
- scope: this
- });
- this.repeater = new Ext.util.ClickRepeater(this.trigger, {
- accelerate: this.accelerate
- });
- this.field.mon(this.repeater, "click", this.onTriggerClick, this, {
- preventDefault: true
- });
- this.field.mon(this.trigger, {
- mouseover: this.onMouseOver,
- mouseout: this.onMouseOut,
- mousemove: this.onMouseMove,
- mousedown: this.onMouseDown,
- mouseup: this.onMouseUp,
- scope: this,
- preventDefault: true
- });
- this.field.mon(this.wrap, "mousewheel", this.handleMouseWheel, this);
- this.dd.setXConstraint(0, 0, 10)
- this.dd.setYConstraint(1500, 1500, 10);
- this.dd.endDrag = this.endDrag.createDelegate(this);
- this.dd.startDrag = this.startDrag.createDelegate(this);
- this.dd.onDrag = this.onDrag.createDelegate(this);
- },
- onMouseOver: function(){
- if (this.disabled) {
- return;
- }
- var middle = this.getMiddle();
- this.tmpHoverClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown';
- this.trigger.addClass(this.tmpHoverClass);
- },
- //private
- onMouseOut: function(){
- this.trigger.removeClass(this.tmpHoverClass);
- },
- //private
- onMouseMove: function(){
- if (this.disabled) {
- return;
- }
- var middle = this.getMiddle();
- if (((Ext.EventObject.getPageY() > middle) && this.tmpHoverClass == "x-form-spinner-overup") ||
- ((Ext.EventObject.getPageY() < middle) && this.tmpHoverClass == "x-form-spinner-overdown")) {
- }
- },
- //private
- onMouseDown: function(){
- if (this.disabled) {
- return;
- }
- var middle = this.getMiddle();
- this.tmpClickClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown';
- this.trigger.addClass(this.tmpClickClass);
- },
- //private
- onMouseUp: function(){
- this.trigger.removeClass(this.tmpClickClass);
- },
- //private
- onTriggerClick: function(){
- if (this.disabled || this.el.dom.readOnly) {
- return;
- }
- var middle = this.getMiddle();
- var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down';
- this['onSpin' + ud]();
- },
- //private
- getMiddle: function(){
- var t = this.trigger.getTop();
- var h = this.trigger.getHeight();
- var middle = t + (h / 2);
- return middle;
- },
- //private
- //checks if control is allowed to spin
- isSpinnable: function(){
- if (this.disabled || this.el.dom.readOnly) {
- Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly
- return false;
- }
- return true;
- },
- handleMouseWheel: function(e){
- //disable scrolling when not focused
- if (this.wrap.hasClass('x-trigger-wrap-focus') == false) {
- return;
- }
- var delta = e.getWheelDelta();
- if (delta > 0) {
- this.onSpinUp();
- e.stopEvent();
- }
- else
- if (delta < 0) {
- this.onSpinDown();
- e.stopEvent();
- }
- },
- //private
- startDrag: function(){
- this.proxy.show();
- this._previousY = Ext.fly(this.dd.getDragEl()).getTop();
- },
- //private
- endDrag: function(){
- this.proxy.hide();
- },
- //private
- onDrag: function(){
- if (this.disabled) {
- return;
- }
- var y = Ext.fly(this.dd.getDragEl()).getTop();
- var ud = '';
- if (this._previousY > y) {
- ud = 'Up';
- } //up
- if (this._previousY < y) {
- ud = 'Down';
- } //down
- if (ud != '') {
- this['onSpin' + ud]();
- }
- this._previousY = y;
- },
- //private
- onSpinUp: function(){
- if (this.isSpinnable() == false) {
- return;
- }
- if (Ext.EventObject.shiftKey == true) {
- this.onSpinUpAlternate();
- return;
- }
- else {
- this.spin(false, false);
- }
- this.field.fireEvent("spin", this);
- this.field.fireEvent("spinup", this);
- },
- //private
- onSpinDown: function(){
- if (this.isSpinnable() == false) {
- return;
- }
- if (Ext.EventObject.shiftKey == true) {
- this.onSpinDownAlternate();
- return;
- }
- else {
- this.spin(true, false);
- }
- this.field.fireEvent("spin", this);
- this.field.fireEvent("spindown", this);
- },
- //private
- onSpinUpAlternate: function(){
- if (this.isSpinnable() == false) {
- return;
- }
- this.spin(false, true);
- this.field.fireEvent("spin", this);
- this.field.fireEvent("spinup", this);
- },
- //private
- onSpinDownAlternate: function(){
- if (this.isSpinnable() == false) {
- return;
- }
- this.spin(true, true);
- this.field.fireEvent("spin", this);
- this.field.fireEvent("spindown", this);
- },
- spin: function(down, alternate){
- var v = parseFloat(this.field.getValue());
- var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue;
- (down == true) ? v -= incr : v += incr;
- v = (isNaN(v)) ? this.defaultValue : v;
- v = this.fixBoundries(v);
- this.field.setRawValue(v);
- },
- fixBoundries: function(value){
- var v = value;
- if (this.field.minValue != undefined && v < this.field.minValue) {
- v = this.field.minValue;
- }
- if (this.field.maxValue != undefined && v > this.field.maxValue) {
- v = this.field.maxValue;
- }
- return this.fixPrecision(v);
- },
- // private
- fixPrecision: function(value){
- var nan = isNaN(value);
- if (!this.field.allowDecimals || this.field.decimalPrecision == -1 || nan || !value) {
- return nan ? '' : value;
- }
- return parseFloat(parseFloat(value).toFixed(this.field.decimalPrecision));
- },
- doDestroy: function(){
- if (this.trigger) {
- this.trigger.remove();
- }
- if (this.wrap) {
- this.wrap.remove();
- delete this.field.wrap;
- }
- if (this.splitter) {
- this.splitter.remove();
- }
- if (this.dd) {
- this.dd.unreg();
- this.dd = null;
- }
- if (this.proxy) {
- this.proxy.remove();
- }
- if (this.repeater) {
- this.repeater.purgeListeners();
- }
- }
- });
- //backwards compat
- Ext.form.Spinner = Ext.ux.Spinner;Ext.ux.Spotlight = function(config){
- Ext.apply(this, config);
- }
- Ext.ux.Spotlight.prototype = {
- active : false,
- animate : true,
- duration: .25,
- easing:'easeNone',
- // private
- animated : false,
- createElements : function(){
- var bd = Ext.getBody();
- this.right = bd.createChild({cls:'x-spotlight'});
- this.left = bd.createChild({cls:'x-spotlight'});
- this.top = bd.createChild({cls:'x-spotlight'});
- this.bottom = bd.createChild({cls:'x-spotlight'});
- this.all = new Ext.CompositeElement([this.right, this.left, this.top, this.bottom]);
- },
- show : function(el, callback, scope){
- if(this.animated){
- this.show.defer(50, this, [el, callback, scope]);
- return;
- }
- this.el = Ext.get(el);
- if(!this.right){
- this.createElements();
- }
- if(!this.active){
- this.all.setDisplayed('');
- this.applyBounds(true, false);
- this.active = true;
- Ext.EventManager.onWindowResize(this.syncSize, this);
- this.applyBounds(false, this.animate, false, callback, scope);
- }else{
- this.applyBounds(false, false, false, callback, scope); // all these booleans look hideous
- }
- },
- hide : function(callback, scope){
- if(this.animated){
- this.hide.defer(50, this, [callback, scope]);
- return;
- }
- Ext.EventManager.removeResizeListener(this.syncSize, this);
- this.applyBounds(true, this.animate, true, callback, scope);
- },
- doHide : function(){
- this.active = false;
- this.all.setDisplayed(false);
- },
- syncSize : function(){
- this.applyBounds(false, false);
- },
- applyBounds : function(basePts, anim, doHide, callback, scope){
- var rg = this.el.getRegion();
- var dw = Ext.lib.Dom.getViewWidth(true);
- var dh = Ext.lib.Dom.getViewHeight(true);
- var c = 0, cb = false;
- if(anim){
- cb = {
- callback: function(){
- c++;
- if(c == 4){
- this.animated = false;
- if(doHide){
- this.doHide();
- }
- Ext.callback(callback, scope, [this]);
- }
- },
- scope: this,
- duration: this.duration,
- easing: this.easing
- };
- this.animated = true;
- }
- this.right.setBounds(
- rg.right,
- basePts ? dh : rg.top,
- dw - rg.right,
- basePts ? 0 : (dh - rg.top),
- cb);
- this.left.setBounds(
- 0,
- 0,
- rg.left,
- basePts ? 0 : rg.bottom,
- cb);
- this.top.setBounds(
- basePts ? dw : rg.left,
- 0,
- basePts ? 0 : dw - rg.left,
- rg.top,
- cb);
- this.bottom.setBounds(
- 0,
- rg.bottom,
- basePts ? 0 : rg.right,
- dh - rg.bottom,
- cb);
- if(!anim){
- if(doHide){
- this.doHide();
- }
- if(callback){
- Ext.callback(callback, scope, [this]);
- }
- }
- },
- destroy : function(){
- this.doHide();
- Ext.destroy(
- this.right,
- this.left,
- this.top,
- this.bottom);
- delete this.el;
- delete this.all;
- }
- };
- //backwards compat
- Ext.Spotlight = Ext.ux.Spotlight;/** * @class Ext.ux.StatusBar * <p>Basic status bar component that can be used as the bottom toolbar of any {@link Ext.Panel}. In addition to * supporting the standard {@link Ext.Toolbar} interface for adding buttons, menus and other items, the StatusBar * provides a greedy status element that can be aligned to either side and has convenient methods for setting the * status text and icon. You can also indicate that something is processing using the {@link #showBusy} method.</p> * <pre><code> new Ext.Panel({ title: 'StatusBar', // etc. bbar: new Ext.ux.StatusBar({ id: 'my-status', // defaults to use when the status is cleared: defaultText: 'Default status text', defaultIconCls: 'default-icon', // values to set initially: text: 'Ready', iconCls: 'ready-icon', // any standard Toolbar items: items: [{ text: 'A Button' }, '-', 'Plain Text'] }) }); // Update the status bar later in code: var sb = Ext.getCmp('my-status'); sb.setStatus({ text: 'OK', iconCls: 'ok-icon', clear: true // auto-clear after a set interval }); // Set the status bar to show that something is processing: sb.showBusy(); // processing.... sb.clearStatus(); // once completeed </code></pre> * @extends Ext.Toolbar * @constructor * Creates a new StatusBar * @param {Object/Array} config A config object */ Ext.ux.StatusBar = Ext.extend(Ext.Toolbar, { /** * @cfg {String} statusAlign * The alignment of the status element within the overall StatusBar layout. When the StatusBar is rendered, * it creates an internal div containing the status text and icon. Any additional Toolbar items added in the * StatusBar's {@link #items} config, or added via {@link #add} or any of the supported add* methods, will be * rendered, in added order, to the opposite side. The status element is greedy, so it will automatically * expand to take up all sapce left over by any other items. Example usage: * <pre><code> // Create a left-aligned status bar containing a button, // separator and text item that will be right-aligned (default): new Ext.Panel({ title: 'StatusBar', // etc. bbar: new Ext.ux.StatusBar({ defaultText: 'Default status text', id: 'status-id', items: [{ text: 'A Button' }, '-', 'Plain Text'] }) }); // By adding the statusAlign config, this will create the // exact same toolbar, except the status and toolbar item // layout will be reversed from the previous example: new Ext.Panel({ title: 'StatusBar', // etc. bbar: new Ext.ux.StatusBar({ defaultText: 'Default status text', id: 'status-id', statusAlign: 'right', items: [{ text: 'A Button' }, '-', 'Plain Text'] }) }); </code></pre> */ /** * @cfg {String} defaultText * The default {@link #text} value. This will be used anytime the status bar is cleared with the * <tt>useDefaults:true</tt> option (defaults to ''). */ /** * @cfg {String} defaultIconCls * The default {@link #iconCls} value (see the iconCls docs for additional details about customizing the icon). * This will be used anytime the status bar is cleared with the <tt>useDefaults:true</tt> option (defaults to ''). */ /** * @cfg {String} text * A string that will be <b>initially</b> set as the status message. This string * will be set as innerHTML (html tags are accepted) for the toolbar item. * If not specified, the value set for <code>{@link #defaultText}</code> * will be used. */ /** * @cfg {String} iconCls * A CSS class that will be <b>initially</b> set as the status bar icon and is * expected to provide a background image (defaults to ''). * Example usage:<pre><code> // Example CSS rule: .x-statusbar .x-status-custom { padding-left: 25px; background: transparent url(images/custom-icon.gif) no-repeat 3px 2px; } // Setting a default icon: var sb = new Ext.ux.StatusBar({ defaultIconCls: 'x-status-custom' }); // Changing the icon: sb.setStatus({ text: 'New status', iconCls: 'x-status-custom' }); </code></pre> */ /** * @cfg {String} cls * The base class applied to the containing element for this component on render (defaults to 'x-statusbar') */ cls : 'x-statusbar', /** * @cfg {String} busyIconCls * The default <code>{@link #iconCls}</code> applied when calling * <code>{@link #showBusy}</code> (defaults to <tt>'x-status-busy'</tt>). * It can be overridden at any time by passing the <code>iconCls</code> * argument into <code>{@link #showBusy}</code>. */ busyIconCls : 'x-status-busy', /** * @cfg {String} busyText * The default <code>{@link #text}</code> applied when calling * <code>{@link #showBusy}</code> (defaults to <tt>'Loading...'</tt>). * It can be overridden at any time by passing the <code>text</code> * argument into <code>{@link #showBusy}</code>. */ busyText : 'Loading...', /** * @cfg {Number} autoClear * The number of milliseconds to wait after setting the status via * <code>{@link #setStatus}</code> before automatically clearing the status * text and icon (defaults to <tt>5000</tt>). Note that this only applies * when passing the <tt>clear</tt> argument to <code>{@link #setStatus}</code> * since that is the only way to defer clearing the status. This can * be overridden by specifying a different <tt>wait</tt> value in * <code>{@link #setStatus}</code>. Calls to <code>{@link #clearStatus}</code> * always clear the status bar immediately and ignore this value. */ autoClear : 5000, /** * @cfg {String} emptyText * The text string to use if no text has been set. Defaults to * <tt>' '</tt>). If there are no other items in the toolbar using * an empty string (<tt>''</tt>) for this value would end up in the toolbar * height collapsing since the empty string will not maintain the toolbar * height. Use <tt>''</tt> if the toolbar should collapse in height * vertically when no text is specified and there are no other items in * the toolbar. */ emptyText : ' ', // private activeThreadId : 0, // private initComponent : function(){ if(this.statusAlign=='right'){ this.cls += ' x-status-right'; } Ext.ux.StatusBar.superclass.initComponent.call(this); }, // private afterRender : function(){ Ext.ux.StatusBar.superclass.afterRender.call(this); var right = this.statusAlign == 'right'; this.currIconCls = this.iconCls || this.defaultIconCls; this.statusEl = new Ext.Toolbar.TextItem({ cls: 'x-status-text ' + (this.currIconCls || ''), text: this.text || this.defaultText || '' }); if(right){ this.add('->'); this.add(this.statusEl); }else{ this.insert(0, this.statusEl); this.insert(1, '->'); } // this.statusEl = td.createChild({ // cls: 'x-status-text ' + (this.iconCls || this.defaultIconCls || ''), // html: this.text || this.defaultText || '' // }); // this.statusEl.unselectable(); // this.spacerEl = td.insertSibling({ // tag: 'td', // style: 'width:100%', // cn: [{cls:'ytb-spacer'}] // }, right ? 'before' : 'after'); }, /** * Sets the status {@link #text} and/or {@link #iconCls}. Also supports automatically clearing the * status that was set after a specified interval. * @param {Object/String} config A config object specifying what status to set, or a string assumed * to be the status text (and all other options are defaulted as explained below). A config * object containing any or all of the following properties can be passed:<ul> * <li><tt>text</tt> {String} : (optional) The status text to display. If not specified, any current * status text will remain unchanged.</li> * <li><tt>iconCls</tt> {String} : (optional) The CSS class used to customize the status icon (see * {@link #iconCls} for details). If not specified, any current iconCls will remain unchanged.</li> * <li><tt>clear</tt> {Boolean/Number/Object} : (optional) Allows you to set an internal callback that will * automatically clear the status text and iconCls after a specified amount of time has passed. If clear is not * specified, the new status will not be auto-cleared and will stay until updated again or cleared using * {@link #clearStatus}. If <tt>true</tt> is passed, the status will be cleared using {@link #autoClear}, * {@link #defaultText} and {@link #defaultIconCls} via a fade out animation. If a numeric value is passed, * it will be used as the callback interval (in milliseconds), overriding the {@link #autoClear} value. * All other options will be defaulted as with the boolean option. To customize any other options, * you can pass an object in the format:<ul> * <li><tt>wait</tt> {Number} : (optional) The number of milliseconds to wait before clearing * (defaults to {@link #autoClear}).</li> * <li><tt>anim</tt> {Number} : (optional) False to clear the status immediately once the callback * executes (defaults to true which fades the status out).</li> * <li><tt>useDefaults</tt> {Number} : (optional) False to completely clear the status text and iconCls * (defaults to true which uses {@link #defaultText} and {@link #defaultIconCls}).</li> * </ul></li></ul> * Example usage:<pre><code> // Simple call to update the text statusBar.setStatus('New status'); // Set the status and icon, auto-clearing with default options: statusBar.setStatus({ text: 'New status', iconCls: 'x-status-custom', clear: true }); // Auto-clear with custom options: statusBar.setStatus({ text: 'New status', iconCls: 'x-status-custom', clear: { wait: 8000, anim: false, useDefaults: false } }); </code></pre> * @return {Ext.ux.StatusBar} this */ setStatus : function(o){ o = o || {}; if(typeof o == 'string'){ o = {text:o}; } if(o.text !== undefined){ this.setText(o.text); } if(o.iconCls !== undefined){ this.setIcon(o.iconCls); } if(o.clear){ var c = o.clear, wait = this.autoClear, defaults = {useDefaults: true, anim: true}; if(typeof c == 'object'){ c = Ext.applyIf(c, defaults); if(c.wait){ wait = c.wait; } }else if(typeof c == 'number'){ wait = c; c = defaults; }else if(typeof c == 'boolean'){ c = defaults; } c.threadId = this.activeThreadId; this.clearStatus.defer(wait, this, [c]); } return this; }, /** * Clears the status {@link #text} and {@link #iconCls}. Also supports clearing via an optional fade out animation. * @param {Object} config (optional) A config object containing any or all of the following properties. If this * object is not specified the status will be cleared using the defaults below:<ul> * <li><tt>anim</tt> {Boolean} : (optional) True to clear the status by fading out the status element (defaults * to false which clears immediately).</li> * <li><tt>useDefaults</tt> {Boolean} : (optional) True to reset the text and icon using {@link #defaultText} and * {@link #defaultIconCls} (defaults to false which sets the text to '' and removes any existing icon class).</li> * </ul> * @return {Ext.ux.StatusBar} this */ clearStatus : function(o){ o = o || {}; if(o.threadId && o.threadId !== this.activeThreadId){ // this means the current call was made internally, but a newer // thread has set a message since this call was deferred. Since // we don't want to overwrite a newer message just ignore. return this; } var text = o.useDefaults ? this.defaultText : this.emptyText, iconCls = o.useDefaults ? (this.defaultIconCls ? this.defaultIconCls : '') : ''; if(o.anim){ // animate the statusEl Ext.Element this.statusEl.el.fadeOut({ remove: false, useDisplay: true, scope: this, callback: function(){ this.setStatus({ text: text, iconCls: iconCls }); this.statusEl.el.show(); } }); }else{ // hide/show the el to avoid jumpy text or icon this.statusEl.hide(); this.setStatus({ text: text, iconCls: iconCls }); this.statusEl.show(); } return this; }, /** * Convenience method for setting the status text directly. For more flexible options see {@link #setStatus}. * @param {String} text (optional) The text to set (defaults to '') * @return {Ext.ux.StatusBar} this */ setText : function(text){ this.activeThreadId++; this.text = text || ''; if(this.rendered){ this.statusEl.setText(this.text); } return this; }, /** * Returns the current status text. * @return {String} The status text */ getText : function(){ return this.text; }, /** * Convenience method for setting the status icon directly. For more flexible options see {@link #setStatus}. * See {@link #iconCls} for complete details about customizing the icon. * @param {String} iconCls (optional) The icon class to set (defaults to '', and any current icon class is removed) * @return {Ext.ux.StatusBar} this */ setIcon : function(cls){ this.activeThreadId++; cls = cls || ''; if(this.rendered){ if(this.currIconCls){ this.statusEl.removeClass(this.currIconCls); this.currIconCls = null; } if(cls.length > 0){ this.statusEl.addClass(cls); this.currIconCls = cls; } }else{ this.currIconCls = cls; } return this; }, /** * Convenience method for setting the status text and icon to special values that are pre-configured to indicate * a "busy" state, usually for loading or processing activities. * @param {Object/String} config (optional) A config object in the same format supported by {@link #setStatus}, or a * string to use as the status text (in which case all other options for setStatus will be defaulted). Use the * <tt>text</tt> and/or <tt>iconCls</tt> properties on the config to override the default {@link #busyText} * and {@link #busyIconCls} settings. If the config argument is not specified, {@link #busyText} and * {@link #busyIconCls} will be used in conjunction with all of the default options for {@link #setStatus}. * @return {Ext.ux.StatusBar} this */ showBusy : function(o){ if(typeof o == 'string'){ o = {text:o}; } o = Ext.applyIf(o || {}, { text: this.busyText, iconCls: this.busyIconCls }); return this.setStatus(o); } }); Ext.reg('statusbar', Ext.ux.StatusBar); /**
- * @class Ext.ux.TabCloseMenu
- * @extends Object
- * Plugin (ptype = 'tabclosemenu') for adding a close context menu to tabs.
- *
- * @ptype tabclosemenu
- */
- Ext.ux.TabCloseMenu = function(){
- var tabs, menu, ctxItem;
- this.init = function(tp){
- tabs = tp;
- tabs.on('contextmenu', onContextMenu);
- };
- function onContextMenu(ts, item, e){
- if(!menu){ // create context menu on first right click
- menu = new Ext.menu.Menu({
- items: [{
- id: tabs.id + '-close',
- text: 'Close Tab',
- handler : function(){
- tabs.remove(ctxItem);
- }
- },{
- id: tabs.id + '-close-others',
- text: 'Close Other Tabs',
- handler : function(){
- tabs.items.each(function(item){
- if(item.closable && item != ctxItem){
- tabs.remove(item);
- }
- });
- }
- }]});
- }
- ctxItem = item;
- var items = menu.items;
- items.get(tabs.id + '-close').setDisabled(!item.closable);
- var disableOthers = true;
- tabs.items.each(function(){
- if(this != item && this.closable){
- disableOthers = false;
- return false;
- }
- });
- items.get(tabs.id + '-close-others').setDisabled(disableOthers);
- e.stopEvent();
- menu.showAt(e.getPoint());
- }
- };
- Ext.preg('tabclosemenu', Ext.ux.TabCloseMenu);
- 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.ns('Ext.ux'); /** * @class Ext.ux.TabScrollerMenu * @extends Object * Plugin (ptype = 'tabscrollermenu') for adding a tab scroller menu to tabs. * @constructor * @param {Object} config Configuration options * @ptype tabscrollermenu */ Ext.ux.TabScrollerMenu = Ext.extend(Object, { /** * @cfg {Number} pageSize How many items to allow per submenu. */ pageSize : 10, /** * @cfg {Number} maxText How long should the title of each {@link Ext.menu.Item} be. */ maxText : 15, /** * @cfg {String} menuPrefixText Text to prefix the submenus. */ menuPrefixText : 'Items', constructor : function(config) { config = config || {}; Ext.apply(this, config); }, //private init : function(tabPanel) { Ext.apply(tabPanel, this.parentOverrides); 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(); }); }, /** * Returns an the current page size (this.pageSize); * @return {Number} this.pageSize The current page size. */ getPageSize : function() { return this.pageSize; }, /** * Sets the number of menu items per submenu "page size". * @param {Number} pageSize The page size */ setPageSize : function(pageSize) { this.pageSize = pageSize; }, /** * Returns the current maxText length; * @return {Number} this.maxText The current max text length. */ getMaxText : function() { return this.maxText; }, /** * Sets the maximum text size for each menu item. * @param {Number} t The max text per each menu item. */ setMaxText : function(t) { this.maxText = t; }, /** * Returns the current menu prefix text String.; * @return {String} this.menuPrefixText The current menu prefix text. */ getMenuPrefixText : function() { return this.menuPrefixText; }, /** * Sets the menu prefix text String. * @param {String} t The menu prefix text. */ setMenuPrefixText : function(t) { this.menuPrefixText = t; }, // private && applied to the tab panel itself. parentOverrides : { // all execute within the scope of the tab panel // private showTabsMenu : function(e) { if (this.tabsMenu) { this.tabsMenu.destroy(); this.un('destroy', this.tabsMenu.destroy, this.tabsMenu); this.tabsMenu = null; } this.tabsMenu = new Ext.menu.Menu(); this.on('destroy', this.tabsMenu.destroy, this.tabsMenu); 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.reg('tabscrollermenu', Ext.ux.TabScrollerMenu); 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; /** * @class Ext.ux.ValidationStatus * A {@link Ext.StatusBar} plugin that provides automatic error notification when the * associated form contains validation errors. * @extends Ext.Component * @constructor * Creates a new ValiationStatus plugin * @param {Object} config A config object */ Ext.ux.ValidationStatus = Ext.extend(Ext.Component, { /** * @cfg {String} errorIconCls * The {@link #iconCls} value to be applied to the status message when there is a * validation error. Defaults to <tt>'x-status-error'</tt>. */ errorIconCls : 'x-status-error', /** * @cfg {String} errorListCls * The css class to be used for the error list when there are validation errors. * Defaults to <tt>'x-status-error-list'</tt>. */ errorListCls : 'x-status-error-list', /** * @cfg {String} validIconCls * The {@link #iconCls} value to be applied to the status message when the form * validates. Defaults to <tt>'x-status-valid'</tt>. */ validIconCls : 'x-status-valid', /** * @cfg {String} showText * The {@link #text} value to be applied when there is a form validation error. * Defaults to <tt>'The form has errors (click for details...)'</tt>. */ showText : 'The form has errors (click for details...)', /** * @cfg {String} showText * The {@link #text} value to display when the error list is displayed. * Defaults to <tt>'Click again to hide the error list'</tt>. */ hideText : 'Click again to hide the error list', /** * @cfg {String} submitText * The {@link #text} value to be applied when the form is being submitted. * Defaults to <tt>'Saving...'</tt>. */ submitText : 'Saving...', // private init : function(sb){ sb.on('render', function(){ this.statusBar = sb; this.monitor = true; this.errors = new Ext.util.MixedCollection(); this.listAlign = (sb.statusAlign=='right' ? 'br-tr?' : 'bl-tl?'); if(this.form){ this.form = Ext.getCmp(this.form).getForm(); this.startMonitoring(); this.form.on('beforeaction', function(f, action){ if(action.type == 'submit'){ // Ignore monitoring while submitting otherwise the field validation // events cause the status message to reset too early this.monitor = false; } }, this); var startMonitor = function(){ this.monitor = true; }; this.form.on('actioncomplete', startMonitor, this); this.form.on('actionfailed', startMonitor, this); } }, this, {single:true}); sb.on({ scope: this, afterlayout:{ single: true, fn: function(){ // Grab the statusEl after the first layout. sb.statusEl.getEl().on('click', this.onStatusClick, this, {buffer:200}); } }, beforedestroy:{ single: true, fn: this.onDestroy } }); }, // private startMonitoring : function(){ this.form.items.each(function(f){ f.on('invalid', this.onFieldValidation, this); f.on('valid', this.onFieldValidation, this); }, this); }, // private stopMonitoring : function(){ this.form.items.each(function(f){ f.un('invalid', this.onFieldValidation, this); f.un('valid', this.onFieldValidation, this); }, this); }, // private onDestroy : function(){ this.stopMonitoring(); this.statusBar.statusEl.un('click', this.onStatusClick, this); Ext.ux.ValidationStatus.superclass.onDestroy.call(this); }, // private onFieldValidation : function(f, msg){ if(!this.monitor){ return false; } if(msg){ this.errors.add(f.id, {field:f, msg:msg}); }else{ this.errors.removeKey(f.id); } this.updateErrorList(); if(this.errors.getCount() > 0){ if(this.statusBar.getText() != this.showText){ this.statusBar.setStatus({text:this.showText, iconCls:this.errorIconCls}); } }else{ this.statusBar.clearStatus().setIcon(this.validIconCls); } }, // private updateErrorList : function(){ if(this.errors.getCount() > 0){ var msg = '<ul>'; this.errors.each(function(err){ msg += ('<li id="x-err-'+ err.field.id +'"><a href="#">' + err.msg + '</a></li>'); }, this); this.getMsgEl().update(msg+'</ul>'); }else{ this.getMsgEl().update(''); } }, // private getMsgEl : function(){ if(!this.msgEl){ this.msgEl = Ext.DomHelper.append(Ext.getBody(), { cls: this.errorListCls+' x-hide-offsets' }, true); this.msgEl.on('click', function(e){ var t = e.getTarget('li', 10, true); if(t){ Ext.getCmp(t.id.split('x-err-')[1]).focus(); this.hideErrors(); } }, this, {stopEvent:true}); // prevent anchor click navigation } return this.msgEl; }, // private showErrors : function(){ this.updateErrorList(); this.getMsgEl().alignTo(this.statusBar.getEl(), this.listAlign).slideIn('b', {duration:0.3, easing:'easeOut'}); this.statusBar.setText(this.hideText); this.form.getEl().on('click', this.hideErrors, this, {single:true}); // hide if the user clicks directly into the form }, // private hideErrors : function(){ var el = this.getMsgEl(); if(el.isVisible()){ el.slideOut('b', {duration:0.2, easing:'easeIn'}); this.statusBar.setText(this.showText); } this.form.getEl().un('click', this.hideErrors, this); }, // private onStatusClick : function(){ if(this.getMsgEl().isVisible()){ this.hideErrors(); }else if(this.errors.getCount() > 0){ this.showErrors(); } } });(function() { Ext.override(Ext.list.Column, { init : function() { if(!this.type){ this.type = "auto"; } var st = Ext.data.SortTypes; // named sortTypes are supported, here we look them up if(typeof this.sortType == "string"){ this.sortType = st[this.sortType]; } // set default sortType for strings and dates if(!this.sortType){ switch(this.type){ case "string": this.sortType = st.asUCString; break; case "date": this.sortType = st.asDate; break; default: this.sortType = st.none; } } } }); Ext.tree.Column = Ext.extend(Ext.list.Column, {}); Ext.tree.NumberColumn = Ext.extend(Ext.list.NumberColumn, {}); Ext.tree.DateColumn = Ext.extend(Ext.list.DateColumn, {}); Ext.tree.BooleanColumn = Ext.extend(Ext.list.BooleanColumn, {}); Ext.reg('tgcolumn', Ext.tree.Column); Ext.reg('tgnumbercolumn', Ext.tree.NumberColumn); Ext.reg('tgdatecolumn', Ext.tree.DateColumn); Ext.reg('tgbooleancolumn', Ext.tree.BooleanColumn); })(); /** * @class Ext.ux.tree.TreeGridNodeUI * @extends Ext.tree.TreeNodeUI */ Ext.ux.tree.TreeGridNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { isTreeGridNodeUI: true, renderElements : function(n, a, targetNode, bulkRender){ var t = n.getOwnerTree(), cols = t.columns, c = cols[0], i, buf, len; this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : ''; buf = [ '<tbody class="x-tree-node">', '<tr ext:tree-node-id="', n.id ,'" class="x-tree-node-el ', a.cls, '">', '<td class="x-treegrid-col">', '<span class="x-tree-node-indent">', this.indentMarkup, "</span>", '<img src="', this.emptyIcon, '" class="x-tree-ec-icon x-tree-elbow">', '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon', (a.icon ? " x-tree-node-inline-icon" : ""), (a.iconCls ? " "+a.iconCls : ""), '" unselectable="on">', '<a hidefocus="on" class="x-tree-node-anchor" href="', a.href ? a.href : '#', '" tabIndex="1" ', a.hrefTarget ? ' target="'+a.hrefTarget+'"' : '', '>', '<span unselectable="on">', (c.tpl ? c.tpl.apply(a) : a[c.dataIndex] || c.text), '</span></a>', '</td>' ]; for(i = 1, len = cols.length; i < len; i++){ c = cols[i]; buf.push( '<td class="x-treegrid-col ', (c.cls ? c.cls : ''), '">', '<div unselectable="on" class="x-treegrid-text"', (c.align ? ' style="text-align: ' + c.align + ';"' : ''), '>', (c.tpl ? c.tpl.apply(a) : a[c.dataIndex]), '</div>', '</td>' ); } buf.push( '</tr><tr class="x-tree-node-ct"><td colspan="', cols.length, '">', '<table class="x-treegrid-node-ct-table" cellpadding="0" cellspacing="0" style="table-layout: fixed; display: none; width: ', t.innerCt.getWidth() ,'px;"><colgroup>' ); for(i = 0, len = cols.length; i<len; i++) { buf.push('<col style="width: ', (cols[i].hidden ? 0 : cols[i].width) ,'px;" />'); } buf.push('</colgroup></table></td></tr></tbody>'); if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){ this.wrap = Ext.DomHelper.insertHtml("beforeBegin", n.nextSibling.ui.getEl(), buf.join('')); }else{ this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join('')); } this.elNode = this.wrap.childNodes[0]; this.ctNode = this.wrap.childNodes[1].firstChild.firstChild; var cs = this.elNode.firstChild.childNodes; this.indentNode = cs[0]; this.ecNode = cs[1]; this.iconNode = cs[2]; this.anchor = cs[3]; this.textNode = cs[3].firstChild; }, // private animExpand : function(cb){ this.ctNode.style.display = ""; Ext.ux.tree.TreeGridNodeUI.superclass.animExpand.call(this, cb); } }); Ext.ux.tree.TreeGridRootNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { isTreeGridNodeUI: true, // private render : function(){ if(!this.rendered){ this.wrap = this.ctNode = this.node.ownerTree.innerCt.dom; this.node.expanded = true; } if(Ext.isWebKit) { // weird table-layout: fixed issue in webkit var ct = this.ctNode; ct.style.tableLayout = null; (function() { ct.style.tableLayout = 'fixed'; }).defer(1); } }, destroy : function(){ if(this.elNode){ Ext.dd.Registry.unregister(this.elNode.id); } delete this.node; }, collapse : Ext.emptyFn, expand : Ext.emptyFn });/** * @class Ext.tree.ColumnResizer * @extends Ext.util.Observable */ Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { /** * @cfg {Number} minWidth The minimum width the column can be dragged to. * Defaults to <tt>14</tt>. */ minWidth: 14, constructor: function(config){ Ext.apply(this, config); Ext.tree.ColumnResizer.superclass.constructor.call(this); }, init : function(tree){ this.tree = tree; tree.on('render', this.initEvents, this); }, initEvents : function(tree){ tree.mon(tree.innerHd, 'mousemove', this.handleHdMove, this); this.tracker = new Ext.dd.DragTracker({ onBeforeStart: this.onBeforeStart.createDelegate(this), onStart: this.onStart.createDelegate(this), onDrag: this.onDrag.createDelegate(this), onEnd: this.onEnd.createDelegate(this), tolerance: 3, autoStart: 300 }); this.tracker.initEl(tree.innerHd); tree.on('beforedestroy', this.tracker.destroy, this.tracker); }, handleHdMove : function(e, t){ var hw = 5, x = e.getPageX(), hd = e.getTarget('.x-treegrid-hd', 3, true); if(hd){ var r = hd.getRegion(), ss = hd.dom.style, pn = hd.dom.parentNode; if(x - r.left <= hw && hd.dom !== pn.firstChild) { var ps = hd.dom.previousSibling; while(ps && Ext.fly(ps).hasClass('x-treegrid-hd-hidden')) { ps = ps.previousSibling; } if(ps) { this.activeHd = Ext.get(ps); ss.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize'; } } else if(r.right - x <= hw) { var ns = hd.dom; while(ns && Ext.fly(ns).hasClass('x-treegrid-hd-hidden')) { ns = ns.previousSibling; } if(ns) { this.activeHd = Ext.get(ns); ss.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize'; } } else{ delete this.activeHd; ss.cursor = ''; } } }, onBeforeStart : function(e){ this.dragHd = this.activeHd; return !!this.dragHd; }, onStart : function(e){ this.tree.headersDisabled = true; this.proxy = this.tree.body.createChild({cls:'x-treegrid-resizer'}); this.proxy.setHeight(this.tree.body.getHeight()); var x = this.tracker.getXY()[0]; this.hdX = this.dragHd.getX(); this.hdIndex = this.tree.findHeaderIndex(this.dragHd); this.proxy.setX(this.hdX); this.proxy.setWidth(x-this.hdX); this.maxWidth = this.tree.outerCt.getWidth() - this.tree.innerBody.translatePoints(this.hdX).left; }, onDrag : function(e){ var cursorX = this.tracker.getXY()[0]; this.proxy.setWidth((cursorX-this.hdX).constrain(this.minWidth, this.maxWidth)); }, onEnd : function(e){ var nw = this.proxy.getWidth(), tree = this.tree; this.proxy.remove(); delete this.dragHd; tree.columns[this.hdIndex].width = nw; tree.updateColumnWidths(); setTimeout(function(){ tree.headersDisabled = false; }, 100); } });Ext.ns('Ext.ux.tree'); /** * @class Ext.ux.tree.TreeGridSorter * @extends Ext.tree.TreeSorter */ Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { /** * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to <tt>['sort-asc', 'sort-desc']</tt>) */ sortClasses : ['sort-asc', 'sort-desc'], /** * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to <tt>'Sort Ascending'</tt>) */ sortAscText : 'Sort Ascending', /** * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to <tt>'Sort Descending'</tt>) */ sortDescText : 'Sort Descending', constructor : function(tree, config) { if(!Ext.isObject(config)) { config = { property: tree.columns[0].dataIndex || 'text', folderSort: true } } Ext.ux.tree.TreeGridSorter.superclass.constructor.apply(this, arguments); this.tree = tree; tree.on('headerclick', this.onHeaderClick, this); tree.ddAppendOnly = true; me = this; this.defaultSortFn = function(n1, n2){ var dsc = me.dir && me.dir.toLowerCase() == 'desc'; var p = me.property || 'text'; var sortType = me.sortType; var fs = me.folderSort; var cs = me.caseSensitive === true; var leafAttr = me.leafAttr || 'leaf'; if(fs){ if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){ return 1; } if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){ return -1; } } var v1 = sortType ? sortType(n1.attributes[p]) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase()); var v2 = sortType ? sortType(n2.attributes[p]) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase()); if(v1 < v2){ return dsc ? +1 : -1; }else if(v1 > v2){ return dsc ? -1 : +1; }else{ return 0; } }; tree.on('afterrender', this.onAfterTreeRender, this, {single: true}); tree.on('headermenuclick', this.onHeaderMenuClick, this); }, onAfterTreeRender : function() { var hmenu = this.tree.hmenu; hmenu.insert(0, {itemId:'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'}, {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'} ); this.updateSortIcon(0, 'asc'); }, onHeaderMenuClick : function(c, id, index) { if(id === 'asc' || id === 'desc') { this.onHeaderClick(c, null, index); return false; } }, onHeaderClick : function(c, el, i) { if(c && !this.tree.headersDisabled){ var me = this; me.property = c.dataIndex; me.dir = c.dir = (c.dir === 'desc' ? 'asc' : 'desc'); me.sortType = c.sortType; me.caseSensitive === Ext.isBoolean(c.caseSensitive) ? c.caseSensitive : this.caseSensitive; me.sortFn = c.sortFn || this.defaultSortFn; this.tree.root.cascade(function(n) { if(!n.isLeaf()) { me.updateSort(me.tree, n); } }); this.updateSortIcon(i, c.dir); } }, // private updateSortIcon : function(col, dir){ var sc = this.sortClasses; var hds = this.tree.innerHd.select('td').removeClass(sc); hds.item(col).addClass(sc[dir == 'desc' ? 1 : 0]); } });/** * @class Ext.ux.tree.TreeGridLoader * @extends Ext.tree.TreeLoader */ Ext.ux.tree.TreeGridLoader = Ext.extend(Ext.tree.TreeLoader, { createNode : function(attr) { if (!attr.uiProvider) { attr.uiProvider = Ext.ux.tree.TreeGridNodeUI; } return Ext.tree.TreeLoader.prototype.createNode.call(this, attr); } });/** * @class Ext.ux.tree.TreeGrid * @extends Ext.tree.TreePanel * * @xtype treegrid */ Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { rootVisible : false, useArrows : true, lines : false, borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell cls : 'x-treegrid', columnResize : true, enableSort : true, reserveScrollOffset : true, enableHdMenu : true, columnsText : 'Columns', initComponent : function() { if(!this.root) { this.root = new Ext.tree.AsyncTreeNode({text: 'Root'}); } // initialize the loader var l = this.loader; if(!l){ l = new Ext.ux.tree.TreeGridLoader({ dataUrl: this.dataUrl, requestMethod: this.requestMethod, store: this.store }); }else if(Ext.isObject(l) && !l.load){ l = new Ext.ux.tree.TreeGridLoader(l); } else if(l) { l.createNode = function(attr) { if (!attr.uiProvider) { attr.uiProvider = Ext.ux.tree.TreeGridNodeUI; } return Ext.tree.TreeLoader.prototype.createNode.call(this, attr); } } this.loader = l; Ext.ux.tree.TreeGrid.superclass.initComponent.call(this); this.initColumns(); if(this.enableSort) { this.treeGridSorter = new Ext.ux.tree.TreeGridSorter(this, this.enableSort); } if(this.columnResize){ this.colResizer = new Ext.tree.ColumnResizer(this.columnResize); this.colResizer.init(this); } var c = this.columns; if(!this.internalTpl){ this.internalTpl = new Ext.XTemplate( '<div class="x-grid3-header">', '<div class="x-treegrid-header-inner">', '<div class="x-grid3-header-offset">', '<table cellspacing="0" cellpadding="0" border="0"><colgroup><tpl for="columns"><col /></tpl></colgroup>', '<thead><tr class="x-grid3-hd-row">', '<tpl for="columns">', '<td class="x-grid3-hd x-grid3-cell x-treegrid-hd" style="text-align: {align};" id="', this.id, '-xlhd-{#}">', '<div class="x-grid3-hd-inner x-treegrid-hd-inner" unselectable="on">', this.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '', '{header}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />', '</div>', '</td></tpl>', '</tr></thead>', '</div></table>', '</div></div>', '</div>', '<div class="x-treegrid-root-node">', '<table class="x-treegrid-root-table" cellpadding="0" cellspacing="0" style="table-layout: fixed;"></table>', '</div>' ); } if(!this.colgroupTpl) { this.colgroupTpl = new Ext.XTemplate( '<colgroup><tpl for="columns"><col style="width: {width}px"/></tpl></colgroup>' ); } }, initColumns : function() { var cs = this.columns, len = cs.length, columns = [], i, c; for(i = 0; i < len; i++){ c = cs[i]; if(!c.isColumn) { c.xtype = c.xtype ? (/^tg/.test(c.xtype) ? c.xtype : 'tg' + c.xtype) : 'tgcolumn'; c = Ext.create(c); } c.init(this); columns.push(c); if(this.enableSort !== false && c.sortable !== false) { c.sortable = true; this.enableSort = true; } } this.columns = columns; }, onRender : function(){ Ext.tree.TreePanel.superclass.onRender.apply(this, arguments); this.el.addClass('x-treegrid'); this.outerCt = this.body.createChild({ cls:'x-tree-root-ct x-treegrid-ct ' + (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines') }); this.internalTpl.overwrite(this.outerCt, {columns: this.columns}); this.mainHd = Ext.get(this.outerCt.dom.firstChild); this.innerHd = Ext.get(this.mainHd.dom.firstChild); this.innerBody = Ext.get(this.outerCt.dom.lastChild); this.innerCt = Ext.get(this.innerBody.dom.firstChild); this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns}); if(this.hideHeaders){ this.header.dom.style.display = 'none'; } else if(this.enableHdMenu !== false){ this.hmenu = new Ext.menu.Menu({id: this.id + '-hctx'}); if(this.enableColumnHide !== false){ this.colMenu = new Ext.menu.Menu({id: this.id + '-hcols-menu'}); this.colMenu.on({ scope: this, beforeshow: this.beforeColMenuShow, itemclick: this.handleHdMenuClick }); this.hmenu.add({ itemId:'columns', hideOnClick: false, text: this.columnsText, menu: this.colMenu, iconCls: 'x-cols-icon' }); } this.hmenu.on('itemclick', this.handleHdMenuClick, this); } }, setRootNode : function(node){ node.attributes.uiProvider = Ext.ux.tree.TreeGridRootNodeUI; node = Ext.ux.tree.TreeGrid.superclass.setRootNode.call(this, node); if(this.innerCt) { this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns}); } return node; }, initEvents : function() { Ext.ux.tree.TreeGrid.superclass.initEvents.apply(this, arguments); this.mon(this.innerBody, 'scroll', this.syncScroll, this); this.mon(this.innerHd, 'click', this.handleHdDown, this); this.mon(this.mainHd, { scope: this, mouseover: this.handleHdOver, mouseout: this.handleHdOut }); }, onResize : function(w, h) { Ext.ux.tree.TreeGrid.superclass.onResize.apply(this, arguments); var bd = this.innerBody.dom; var hd = this.innerHd.dom; if(!bd){ return; } if(Ext.isNumber(h)){ bd.style.height = this.body.getHeight(true) - hd.offsetHeight + 'px'; } if(Ext.isNumber(w)){ var sw = Ext.num(this.scrollOffset, Ext.getScrollBarWidth()); if(this.reserveScrollOffset || ((bd.offsetWidth - bd.clientWidth) > 10)){ this.setScrollOffset(sw); }else{ var me = this; setTimeout(function(){ me.setScrollOffset(bd.offsetWidth - bd.clientWidth > 10 ? sw : 0); }, 10); } } }, updateColumnWidths : function() { var cols = this.columns, colCount = cols.length, groups = this.outerCt.query('colgroup'), groupCount = groups.length, c, g, i, j; for(i = 0; i<colCount; i++) { c = cols[i]; for(j = 0; j<groupCount; j++) { g = groups[j]; g.childNodes[i].style.width = (c.hidden ? 0 : c.width) + 'px'; } } for(i = 0, groups = this.innerHd.query('td'), len = groups.length; i<len; i++) { c = Ext.fly(groups[i]); if(cols[i] && cols[i].hidden) { c.addClass('x-treegrid-hd-hidden'); } else { c.removeClass('x-treegrid-hd-hidden'); } } var tcw = this.getTotalColumnWidth(); Ext.fly(this.innerHd.dom.firstChild).setWidth(tcw + (this.scrollOffset || 0)); this.outerCt.select('table').setWidth(tcw); this.syncHeaderScroll(); }, getVisibleColumns : function() { var columns = [], cs = this.columns, len = cs.length, i; for(i = 0; i<len; i++) { if(!cs[i].hidden) { columns.push(cs[i]); } } return columns; }, getTotalColumnWidth : function() { var total = 0; for(var i = 0, cs = this.getVisibleColumns(), len = cs.length; i<len; i++) { total += cs[i].width; } return total; }, setScrollOffset : function(scrollOffset) { this.scrollOffset = scrollOffset; this.updateColumnWidths(); }, // private handleHdDown : function(e, t){ var hd = e.getTarget('.x-treegrid-hd'); if(hd && Ext.fly(t).hasClass('x-grid3-hd-btn')){ var ms = this.hmenu.items, cs = this.columns, index = this.findHeaderIndex(hd), c = cs[index], sort = c.sortable; e.stopEvent(); Ext.fly(hd).addClass('x-grid3-hd-menu-open'); this.hdCtxIndex = index; this.fireEvent('headerbuttonclick', ms, c, hd, index); this.hmenu.on('hide', function(){ Ext.fly(hd).removeClass('x-grid3-hd-menu-open'); }, this, {single:true}); this.hmenu.show(t, 'tl-bl?'); } else if(hd) { var index = this.findHeaderIndex(hd); this.fireEvent('headerclick', this.columns[index], hd, index); } }, // private handleHdOver : function(e, t){ var hd = e.getTarget('.x-treegrid-hd'); if(hd && !this.headersDisabled){ index = this.findHeaderIndex(hd); this.activeHdRef = t; this.activeHdIndex = index; var el = Ext.get(hd); this.activeHdRegion = el.getRegion(); el.addClass('x-grid3-hd-over'); this.activeHdBtn = el.child('.x-grid3-hd-btn'); if(this.activeHdBtn){ this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight-1)+'px'; } } }, // private handleHdOut : function(e, t){ var hd = e.getTarget('.x-treegrid-hd'); if(hd && (!Ext.isIE || !e.within(hd, true))){ this.activeHdRef = null; Ext.fly(hd).removeClass('x-grid3-hd-over'); hd.style.cursor = ''; } }, findHeaderIndex : function(hd){ hd = hd.dom || hd; var cs = hd.parentNode.childNodes; for(var i = 0, c; c = cs[i]; i++){ if(c == hd){ return i; } } return -1; }, // private beforeColMenuShow : function(){ var cols = this.columns, colCount = cols.length, i, c; this.colMenu.removeAll(); for(i = 1; i < colCount; i++){ c = cols[i]; if(c.hideable !== false){ this.colMenu.add(new Ext.menu.CheckItem({ itemId: 'col-' + i, text: c.header, checked: !c.hidden, hideOnClick:false, disabled: c.hideable === false })); } } }, // private handleHdMenuClick : function(item){ var index = this.hdCtxIndex, id = item.getItemId(); if(this.fireEvent('headermenuclick', this.columns[index], id, index) !== false) { index = id.substr(4); if(index > 0 && this.columns[index]) { this.setColumnVisible(index, !item.checked); } } return true; }, setColumnVisible : function(index, visible) { this.columns[index].hidden = !visible; this.updateColumnWidths(); }, /** * Scrolls the grid to the top */ scrollToTop : function(){ this.innerBody.dom.scrollTop = 0; this.innerBody.dom.scrollLeft = 0; }, // private syncScroll : function(){ this.syncHeaderScroll(); var mb = this.innerBody.dom; this.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop); }, // private syncHeaderScroll : function(){ var mb = this.innerBody.dom; this.innerHd.dom.scrollLeft = mb.scrollLeft; this.innerHd.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore) }, registerNode : function(n) { Ext.ux.tree.TreeGrid.superclass.registerNode.call(this, n); if(!n.uiProvider && !n.isRoot && !n.ui.isTreeGridNodeUI) { n.ui = new Ext.ux.tree.TreeGridNodeUI(n); } } }); Ext.reg('treegrid', Ext.ux.tree.TreeGrid);