/* override default Ext.Window component properties */ // these also apply for Windows that do not extend MODx.Window (like console for ex.) // we use CSS3 box-shadows in 2014, removes clutter from the DOM Ext.Window.prototype.floating = { shadow: false }; /* override default Ext.Window component methods */ Ext.override(Ext.Window, { // prevents ugly slow js animations when opening a window // we cannot do the CSS3 animations stuff in these overrides, as not all windows are animated! // so they just prevent the normal JS animation to take effect animShow: function() { this.afterShow(); // some windows (like migx) don't seem to call onShow // so we have to do a check here after onShow should have finished var win = this; // we need a reference to this for setTimeout // wait for onShow to finish and check if the window is already visible then, if not, try to do that setTimeout(function() { if (!win.el.hasClass('anim-ready')) { win.el.addClass('anim-ready'); setTimeout(function() { if (win.mask !== undefined) { // respect that the mask is not always the same object if (win.mask instanceof Ext.Element) { win.mask.addClass('fade-in'); } else { win.mask.el.addClass('fade-in'); } } win.el.addClass('zoom-in'); }, 250); } }, 300); } ,animHide: function() { //this.el.hide(); // dont hide the window here, we'll do that onHide when the animation is finished! this.afterHide(); } ,onShow: function() { // skip MODx.msg windows, the animations do not work with them as they are always the same element! if (!this.el.hasClass('x-window-dlg')) { // first set the class that scales the window down a bit // this has to be done after the full window is positioned correctly by extjs this.addClass('anim-ready'); // let the scale transformation to 0.7 finish before animating in var win = this; // we need a reference to this for setTimeout setTimeout(function() { if (win.mask !== undefined) { // respect that the mask is not always the same object if (win.mask instanceof Ext.Element) { win.mask.addClass('fade-in'); } else { win.mask.el.addClass('fade-in'); } } win.el.addClass('zoom-in'); }, 250); } else { // we need to handle MODx.msg windows (Ext.Msg singletons, e.g. always the same element, no multiple instances) differently this.mask.addClass('fade-in'); this.el.applyStyles({'opacity': 1}); } } ,onHide: function() { // for some unknown (to me) reason, onHide() get's called when a window is initialized, e.g. before onShow() // so we need to prevent the following routine be applied prematurely if (this.el.hasClass('zoom-in')) { this.el.removeClass('zoom-in'); if (this.mask !== undefined) { // respect that the mask is not always the same object if (this.mask instanceof Ext.Element) { this.mask.removeClass('fade-in'); } else { this.mask.el.removeClass('fade-in'); } } this.addClass('zoom-out'); // let the CSS animation finish before hiding the window var win = this; // we need a reference to this for setTimeout setTimeout(function() { // we have an unsolved problem with windows that are destroyed on hide // the zoom-out animation cannot be applied for such windows, as they // get destroyed too early, if someone knows a solution, please tell =) if (!win.isDestroyed) { win.el.hide(); // and remove the CSS3 animation classes win.el.removeClass('zoom-out'); win.el.removeClass('anim-ready'); } }, 250); } else if (this.el.hasClass('x-window-dlg')) { // we need to handle MODx.msg windows (Ext.Msg singletons, e.g. always the same element, no multiple instances) differently this.el.applyStyles({'opacity': 0}); if (this.mask !== undefined) { // respect that the mask is not always the same object if (this.mask instanceof Ext.Element) { this.mask.removeClass('fade-in'); } else { this.mask.el.removeClass('fade-in'); } } } } }); /** * Abstract class for Ext.Window creation in MODx * * @class MODx.Window * @extends Ext.Window * @constructor * @param {Object} config An object of options. * @xtype modx-window */ MODx.Window = function(config) { config = config || {}; Ext.applyIf(config,{ modal: false ,layout: 'auto' ,closeAction: 'hide' ,shadow: true ,resizable: true ,collapsible: true ,maximizable: true // ,autoHeight: true // this messes up many windows on smaller screens (e.g. too much height), ex. macbook air 11" ,autoHeight: false ,autoScroll: true ,allowDrop: true ,width: 400 ,constrain: true ,constrainHeader: true ,cls: 'modx-window' ,buttons: [{ text: config.cancelBtnText || _('cancel') ,scope: this ,handler: function() { config.closeAction !== 'close' ? this.hide() : this.close(); } },{ text: config.saveBtnText || _('save') ,cls: 'primary-button' ,scope: this ,handler: this.submit }] ,record: {} ,keys: [{ key: Ext.EventObject.ENTER ,fn: function(keyCode, event) { var elem = event.getTarget(); var component = Ext.getCmp(elem.id); if (component instanceof Ext.form.TextArea) { return component.append("\n"); } else { this.submit(); } } ,scope: this }] }); MODx.Window.superclass.constructor.call(this,config); this.options = config; this.config = config; this.addEvents({ success: true ,failure: true ,beforeSubmit: true }); this._loadForm(); this.on('show',function() { if (this.config.blankValues) { this.fp.getForm().reset(); } if (this.config.allowDrop) { this.loadDropZones(); } this.syncSize(); this.focusFirstField(); },this); this.on('afterrender', function() { this.originalHeight = this.el.getHeight(); this.toolsHeight = this.originalHeight - this.body.getHeight() + 50; this.resizeWindow(); }); Ext.EventManager.onWindowResize(this.resizeWindow, this); }; Ext.extend(MODx.Window,Ext.Window,{ _loadForm: function() { if (this.checkIfLoaded(this.config.record || null)) { return false; } var r = this.config.record; /* set values here, since setValue after render seems to be broken */ if (this.config.fields) { var l = this.config.fields.length; for (var i=0;i 0) { var fld = this.findFirstTextField(); if (fld) { fld.focus(false,200); } } } ,findFirstTextField: function(i) { i = i || 0; var fld = this.fp.getForm().items.itemAt(i); if (!fld) return false; if (fld.isXType('combo') || fld.isXType('checkbox') || fld.isXType('radio') || fld.isXType('displayfield') || fld.isXType('statictextfield') || fld.isXType('hidden')) { i = i+1; fld = this.findFirstTextField(i); } return fld; } ,submit: function(close) { close = close === false ? false : true; var f = this.fp.getForm(); if (f.isValid() && this.fireEvent('beforeSubmit',f.getValues())) { f.submit({ waitMsg: this.config.waitMsg || _('saving') ,submitEmptyText: this.config.submitEmptyText !== false ,scope: this ,failure: function(frm,a) { if (this.fireEvent('failure',{f:frm,a:a})) { MODx.form.Handler.errorExt(a.result,frm); } this.doLayout(); } ,success: function(frm,a) { if (this.config.success) { Ext.callback(this.config.success,this.config.scope || this,[frm,a]); } this.fireEvent('success',{f:frm,a:a}); if (close) { this.config.closeAction !== 'close' ? this.hide() : this.close(); } this.doLayout(); } }); } } ,createForm: function(config) { Ext.applyIf(this.config,{ formFrame: true ,border: false ,bodyBorder: false ,autoHeight: true }); config = config || {}; Ext.applyIf(config,{ labelAlign: this.config.labelAlign || 'top' ,labelWidth: this.config.labelWidth || 100 ,labelSeparator: this.config.labelSeparator || '' ,frame: this.config.formFrame ,border: this.config.border ,bodyBorder: this.config.bodyBorder ,autoHeight: this.config.autoHeight ,anchor: '100% 100%' ,errorReader: MODx.util.JSONReader ,defaults: this.config.formDefaults || { msgTarget: this.config.msgTarget || 'under' } ,url: this.config.url ,baseParams: this.config.baseParams || {} ,fileUpload: this.config.fileUpload || false }); return new Ext.FormPanel(config); } ,renderForm: function() { this.fp.on('destroy', function() { Ext.EventManager.removeResizeListener(this.resizeWindow, this); }, this); this.add(this.fp); } ,checkIfLoaded: function(r) { r = r || {}; if (this.fp && this.fp.getForm()) { /* so as not to duplicate form */ this.fp.getForm().reset(); this.fp.getForm().setValues(r); return true; } return false; } ,setValues: function(r) { if (r === null) { return false; } this.fp.getForm().setValues(r); } ,reset: function() { this.fp.getForm().reset(); } ,hideField: function(f) { f.disable(); f.hide(); var d = f.getEl().up('.x-form-item'); if (d) { d.setDisplayed(false); } } ,showField: function(f) { f.enable(); f.show(); var d = f.getEl().up('.x-form-item'); if (d) { d.setDisplayed(true); } } ,loadDropZones: function() { if (this._dzLoaded) return false; var flds = this.fp.getForm().items; flds.each(function(fld) { if (fld.isFormField && ( fld.isXType('textfield') || fld.isXType('textarea') ) && !fld.isXType('combo')) { new MODx.load({ xtype: 'modx-treedrop' ,target: fld ,targetEl: fld.getEl().dom }); } }); this._dzLoaded = true; } ,resizeWindow: function(){ var viewHeight = Ext.getBody().getViewSize().height; var el = this.fp.getForm().el; if(viewHeight < this.originalHeight){ el.setStyle('overflow-y', 'scroll'); el.setHeight(viewHeight - this.toolsHeight); }else{ el.setStyle('overflow-y', 'auto'); el.setHeight('auto'); } } }); Ext.reg('modx-window',MODx.Window);