/**
* A specialized panel intended for use as an application window. Windows are floated, {@link #resizable}, and
* {@link #cfg-draggable} by default. Windows can be {@link #maximizable maximized} to fill the viewport, restored to
* their prior size, and can be {@link #method-minimize}d.
*
* Windows can also be linked to a {@link Ext.ZIndexManager} or managed by the {@link Ext.WindowManager} to provide
* grouping, activation, to front, to back and other application-specific behavior.
*
* By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element specify
* {@link Ext.Component#renderTo renderTo}.
*
* **As with all {@link Ext.container.Container Container}s, it is important to consider how you want the Window to size
* and arrange any child Components. Choose an appropriate {@link #layout} configuration which lays out child Components
* in the required manner.**
*
* @example
* Ext.create('Ext.window.Window', {
* title: 'Hello',
* height: 200,
* width: 400,
* layout: 'fit',
* items: { // Let's put an empty grid in just to illustrate fit layout
* xtype: 'grid',
* border: false,
* columns: [{header: 'World'}], // One header just for show. There's no data,
* store: Ext.create('Ext.data.ArrayStore', {}) // A dummy empty data store
* }
* }).show();
*/
Ext.define('Ext.window.Window', {
extend: 'Ext.panel.Panel',
alternateClassName: 'Ext.Window',
requires: [
'Ext.util.ComponentDragger',
'Ext.util.Region'
],
alias: 'widget.window',
/**
* @cfg {Number} x
* The X position of the left edge of the window on initial showing. Defaults to centering the Window within the
* width of the Window's container {@link Ext.dom.Element Element} (The Element that the Window is rendered to).
*/
/**
* @cfg {Number} y
* The Y position of the top edge of the window on initial showing. Defaults to centering the Window within the
* height of the Window's container {@link Ext.dom.Element Element} (The Element that the Window is rendered to).
*/
/**
* @cfg {String/Ext.dom.Element/Ext.Component/Boolean} [animateTarget=null]
* Id, Component element, or Component from which the window should animate when
* shown or hidden.
*
* You may also pass true to have the Window animate when maximizing and restoring
* using the maximize / restore tools created via the {@link #maximizable} config.
*
* var btn, win;
*
* btn = Ext.create({
* xtype: 'button',
* renderTo: Ext.getBody(),
* text: 'Show Window',
* handler: function() {
* win.show();
* }
* });
*
* win = Ext.create({
* xtype: 'window',
* title: 'Animate from the Show Window Button',
* height: 300,
* width: 400,
* modal: true,
* closeAction: 'hide',
* animateTarget: btn
* // or btn.getId()
* // or btn.getEl()
* // or true (when maximizable is true)
* });
*/
/**
* @cfg {Boolean/Function} ghost
* Set to false to disable the ghost panel during dragging the window.
* Do note that you should not set this to true, by default it is a function.
*/
/**
* @cfg {String/Number/Ext.Component} defaultFocus
* Specifies a Component to receive focus when this Window is focused.
*
* If a String is provided, the Component will be resolved using the {@link #down} method which uses {@link Ext.ComponentQuery}.
* If the string begins with an alphanumeric value, it will first attempt to find the Component based on the {@link Ext.Component#id} or {@link Ext.Component#itemId}.
* If a matching component is not found via id, then an attempt to do a query to find a matching component.
*
* An example of finding the Component with an id/itemId:
*
* Ext.create('Ext.window.Window', {
* autoShow : true,
* width : 300,
* title : 'Login',
* defaultFocus : 'username',
* items : [
* {
* xtype : 'textfield',
* fieldLabel : 'Username',
* itemId : 'username',
* name : 'username'
* },
* {
* xtype : 'textfield',
* inputType : 'password',
* fieldLabel : 'Password',
* itemId : 'password',
* name : 'password'
* }
* ]
* });
*
* If a Number is provided, this will resolve an {@link Ext.button.Button} at that index. This is very useful if
* the window has buttons in the {@link #buttons} config and you want to provide default focus to one of them.
*
* An example of this would be:
*
* Ext.create('Ext.window.Window', {
* autoShow : true,
* width : 300,
* title : 'Login',
* defaultFocus : 1,
* items : [
* {
* xtype : 'textfield',
* fieldLabel : 'Username',
* name : 'username'
* },
* {
* xtype : 'textfield',
* inputType : 'password',
* fieldLabel : 'Password',
* name : 'password'
* }
* ],
* buttons : [
* {
* text : 'Cancel'
* },
* {
* text : 'Login'
* }
* ]
* });
*
* In summary, defaultFocus may be one of:
*
* - The index of a footer Button.
* - The id or {@link Ext.Component#itemId} of a descendant Component.
* - A {@link Ext.ComponentQuery query} to find a {@link Ext.Component}.
* - A descendant {@link Ext.Component}.
*/
/**
* @cfg {Function} onEsc
* Allows override of the built-in processing for the escape key. Default action is to close the Window (performing
* whatever action is specified in {@link #closeAction}. To prevent the Window closing when the escape key is
* pressed, specify this as {@link Ext#emptyFn Ext.emptyFn}.
*/
/**
* @cfg {Boolean} [collapsed=false]
* True to render the window collapsed, false to render it expanded. Note that if {@link #expandOnShow}
* is true (the default) it will override the `collapsed` config and the window will always be
* expanded when shown.
*/
/**
* @cfg {Boolean} [maximized=false]
* True to initially display the window in a maximized state.
*/
/**
* @cfg {Boolean} [hideShadowOnDeactivate=false]
* True to hide this Window's shadow when another floating item in the same z-index stack is activated.
*/
/**
* @cfg {String} [baseCls='x-window']
* The base CSS class to apply to this panel's element.
*/
baseCls: Ext.baseCSSPrefix + 'window',
/**
* @cfg {Boolean/Object} resizable
* Specify as `true` to allow user resizing at each edge and corner of the window, false to disable resizing.
*
* This may also be specified as a config object to Ext.resizer.Resizer
*/
resizable: true,
/**
* @cfg {Boolean} draggable
* True to allow the window to be dragged by the header bar, false to disable dragging. Note that
* by default the window will be centered in the viewport, so if dragging is disabled the window may need to be
* positioned programmatically after render (e.g., `myWindow.setPosition(100, 100);`).
*/
draggable: true,
/**
* @cfg {Boolean} constrain
* True to constrain the window within its containing element, false to allow it to fall outside of its containing
* element. By default the window will be rendered to `document.body`. To render and constrain the window within
* another element specify {@link #renderTo}. Optionally the header only can be constrained
* using {@link #constrainHeader}.
*/
constrain: false,
* @cfg {Boolean} constrainHeader
* True to constrain the window header within its containing element (allowing the window body to fall outside of
* its containing element) or false to allow the header to fall outside its containing element.
* Optionally the entire window can be constrained using {@link #constrain}.
*/
constrainHeader: false,
/**
* @cfg simpleDrag
* @hide
*/
/**
* @cfg {Boolean} plain
* True to render the window body with a transparent background so that it will blend into the framing elements,
* false to add a lighter background color to visually highlight the body element and separate it more distinctly
* from the surrounding frame.
*/
plain: false,
/**
* @cfg {Boolean} minimizable
* True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button
* and disallow minimizing the window. Note that this button provides no implementation -- the
* behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a custom
* minimize behavior implemented for this option to be useful.
*/
minimizable: false,
/**
* @cfg {Boolean} maximizable
* True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button
* and disallow maximizing the window. Note that when a window is maximized, the tool button
* will automatically change to a 'restore' button with the appropriate behavior already built-in that will restore
* the window to its previous size.
*/
maximizable: false,
minHeight: 50,
minWidth: 50,
/**
* @cfg {Boolean} expandOnShow
* True to always expand the window when it is displayed, false to keep it in its current state (which may be
* {@link #collapsed}) when displayed.
*/
expandOnShow: true,
collapsible: false,
/**
* @cfg {Boolean} closable
* True to display the 'close' tool button and allow the user to close the window, false to hide the button and
* disallow closing the window.
*
* By default, when close is requested by either clicking the close button in the header or pressing ESC when the
* Window has focus, the {@link #method-close} method will be called. This will _{@link Ext.Component#method-destroy destroy}_ the
* Window and its content meaning that it may not be reused.
*
* To make closing a Window _hide_ the Window so that it may be reused, set {@link #closeAction} to 'hide'.
*/
closable: true,
/**
* @cfg {Boolean} monitorResize
* `true` to listen to the viewport resize event and perform any layout updating if necessary.
* This is useful if using sizes as percentages for the window.
*/
/**
* @cfg {Boolean} hidden
* Render this Window hidden. If `true`, the {@link #method-hide} method will be called internally.
*/
hidden: true,
/**
* @cfg {Boolean}
* @inheritdoc
* Windows render to the body on first show.
*/
autoRender: true,
/**
* @cfg {String}
* @inheritdoc
* Windows hide using offsets in order to preserve the scroll positions of their descendants. You may review
* other configuration options here: {@link Ext.Component#hideMode}.
*/
hideMode: 'offsets',
/**
* @cfg {Boolean} [floating=true]
* @inheritdoc Ext.Component
*/
floating: true,
alignOnScroll: false,
/**
* @cfg stateEvents
* @inheritdoc Ext.state.Stateful#cfg-stateEvents
* @localdoc By default the following stateEvents are added:
*
* - {@link #event-resize} - _(added by Ext.Component)_
* - {@link #event-collapse} - _(added by Ext.panel.Panel)_
* - {@link #event-expand} - _(added by Ext.panel.Panel)_
* - {@link #event-maximize}
* - {@link #event-restore}
* - {@link #event-resize}
* - {@link #event-dragend}
*/
itemCls: Ext.baseCSSPrefix + 'window-item',
// Flag to Renderable to always look up the framing styles for this Component
alwaysFramed: true,
// Buffer this so we don't recreate the same object
isRootCfg: {
isRoot: true
},
/**
* @property {Boolean} isWindow
* `true` in this class to identify an object as an instantiated Window, or subclass thereof.
*/
isWindow: true,
ariaRole: 'dialog',
focusable: true,
tabGuard: true,
//<locale>
closeToolText: 'Close dialog',
//</locale>
keyMap: {
scope: 'this',
ESC: 'onEsc'
},
/**
* @cfg {String} [maskClickAction=focus]
* The method to call when the window's modal mask is clicked or tapped:
*
* - **`'{@link #method-focus}'`** :
*
* The default. Focus the window, which will then pass focus into its {@link #cfg-defaultFocus} delegate.
*
* - **`'{@link #method-destroy}'`** :
*
* Remove the window from the DOM and {@link Ext.Component#method-destroy destroy} it and all descendant
* Components. The window will **not** be available to be redisplayed via the {@link #method-show} method.
*
* - **`'{@link #method-hide}'`** :
*
* {@link #method-hide} the window by setting visibility to hidden and applying negative offsets. The window will be
* available to be redisplayed via the {@link #method-show} method.
* @since 6.2.0
*/
maskClickAction: 'focus',
/**
* @event activate
* Fires after the window has been visually activated via {@link #setActive}.
* @param {Ext.window.Window} this
*/
/**
* @event deactivate
* Fires after the window has been visually deactivated via {@link #setActive}.
* @param {Ext.window.Window} this
*/
/**
* @event maskclick
* Fires when this Window's modal mask is clicked or tapped. Returning `false` from
* a handler will veto the subsequent preocessing of the {@link #cfg-maskClickAction}..
* @param {Ext.window.Window} this
*/
/**
* @event resize
* Fires after the window has been resized.
* @param {Ext.window.Window} this
* @param {Number} width The window's new width
* @param {Number} height The window's new height
*/
/**
* @event maximize
* Fires after the window has been maximized.
* @param {Ext.window.Window} this
*/
/**
* @event minimize
* Fires after the window has been minimized.
* @param {Ext.window.Window} this
*/
/**
* @event restore
* Fires after the window has been restored to its original size after being maximized.
* @param {Ext.window.Window} this
*/
disableCloseToolFocus: true,
/**
* @private
*/
initComponent: function() {
var me = this;
// Explicitly set frame to false, since alwaysFramed is
// true, we only want to lookup framing in a specific instance
me.frame = false;
me.callParent();
if (me.plain) {
me.addClsWithUI('plain');
}
me.addStateEvents(['maximize', 'restore', 'resize', 'dragend']);
},
getElConfig: function () {
var me = this,
elConfig;
elConfig = me.callParent();
elConfig.tabIndex = -1;
return elConfig;
},
/**
* @protected
* Returns the focus holder element associated with this Window.
* By default, this is the Window's element; this can be overridden
* by setting {@link #defaultFocus} property.
*
* @return {Ext.dom.Element/Ext.Component} the focus holding element or Component.
*/
getFocusEl: function() {
return this.getDefaultFocus() || this.el;
},
// State Management
/**
* @private
*/
getState: function() {
var me = this,
state = me.callParent() || {},
maximized = !!me.maximized,
ghostBox = me.ghostBox,
pos;
state.maximized = maximized;
if (maximized) {
pos = me.restorePos;
} else if (ghostBox) {
// If we're animating a show, it will be from offscreen, so
// grab the position from the final box
pos = [ghostBox.x, ghostBox.y];
} else {
pos = me.getPosition(true);
}
Ext.apply(state, {
size: maximized ? me.restoreSize : me.getSize(),
pos: pos
});
return state;
},
applyState: function(state){
var me = this;
if (state) {
me.maximized = state.maximized;
if (me.maximized) {
me.hasSavedRestore = true;
me.restoreSize = state.size;
me.restorePos = state.pos;
} else {
Ext.apply(me, {
width: state.size.width,
height: state.size.height,
x: state.pos[0],
y: state.pos[1]
});
}
}
},
onRender: function(ct, position) {
var me = this;
me.callParent(arguments);
// Single clicking a header will focus the defaultFocus child
if (me.header) {
me.header.on({
scope: me,
click: me.onHeaderClick
});
}
// Double clicking a header will toggleMaximize
if (me.maximizable) {
me.header.on({
scope: me,
dblclick: me.toggleMaximize
});
}
},
afterRender: function() {
var me = this,
header = me.header;
// Initialize
if (me.maximized) {
me.maximized = false;
me.maximize(null, true);
if (header) {
header.removeCls(header.indicateDragCls);
}
}
me.callParent();
me.initTabGuards();
},
/**
* @private
*/
onEsc: function(e) {
e.stopEvent();
this.close();
},
doDestroy: function() {
var me = this;
if (me.rendered) {
Ext.un('resize', me.onWindowResize, me);
delete me.animateTarget;
me.hide();
}
me.callParent();
},
/**
* @private
* Contribute class-specific tools to the header.
*
* Called by Panel's initTools at initialization time.
*
* Implementations should jst add new tool config objects to `this.tools`
*/
addTools: function() {
var me = this,
tools = [];
// Call Panel's addTools
me.callParent();
if (me.minimizable) {
tools.push({
type: 'minimize',
handler: 'minimize',
scope: me
});
}
if (me.maximizable) {
tools.push({
type: 'maximize',
handler: 'toggleMaximize',
scope: me
});
}
if (tools.length) {
me.addTool(tools);
}
},
addTool: function(tools) {
var me = this;
me.callParent([tools]);
if (me.rendered && me.tabGuard) {
me.initTabGuards();
}
},
add: function() {
var me = this,
ret;
ret = me.callParent(arguments);
if (me.rendered && me.tabGuard) {
me.initTabGuards();
}
return ret;
},
remove: function() {
var me = this,
ret;
ret = me.callParent(arguments);
if (me.rendered && me.tabGuard) {
me.initTabGuards();
}
return ret;
},
addDocked: function() {
var me = this,
ret;
ret = me.callParent(arguments);
if (me.rendered && me.tabGuard) {
me.initTabGuards();
}
return ret;
},
removeDocked: function() {
var me = this,
ret;
ret = me.callParent(arguments);
if (me.rendered && me.tabGuard) {
me.initTabGuards();
}
return ret;
},
onShow: function() {
var me = this;
me.callParent(arguments);
if (me.expandOnShow) {
me.expand(false);
}
me.syncMonitorWindowResize();
if (me.rendered && me.tabGuard) {
me.initTabGuards();
}
},
/**
* @private
*/
doClose: function() {
var me = this;
// Being called as callback after going through the hide call below
if (me.hidden) {
me.fireEvent('close', me);
// This method can be called from hide() which in turn can be called
// from destroy()
if (me.closeAction === 'destroy' && !me.destroying && !me.destroyed) {
me.destroy();
}
} else {
// close after hiding
me.hide(me.animateTarget, me.doClose, me);
}
},
/**
* @private
*/
afterHide: function() {
var me = this;
// No longer subscribe to resizing now that we're hidden
me.syncMonitorWindowResize();
// Perform superclass's afterHide tasks.
me.callParent(arguments);
if (me.rendered && me.tabGuard) {
me.initTabGuards();
}
},
/**
* @private
*/
onWindowResize: function() {
var me = this,
sizeModel;
// This is called on a timer. Window may have been destroyed in the interval.
if (!me.destroyed) {
if (me.maximized) {
me.fitContainer();
} else {
sizeModel = me.getSizeModel();
if (sizeModel.width.natural || sizeModel.height.natural) {
me.updateLayout();
}
me.doConstrain();
}
}
},
/**
* Placeholder method for minimizing the window. By default, this method simply fires the {@link #event-minimize} event
* since the behavior of minimizing a window is application-specific. To implement custom minimize behavior, either
* the minimize event can be handled or this method can be overridden.
* @return {Ext.window.Window} this
*/
minimize: function() {
this.fireEvent('minimize', this);
return this;
},
this.header.resumeLayouts(changed ? this.isRootCfg : null);
},
afterCollapse: function() {
var me = this,
header = me.header,
tools = me.tools;
if (header && me.maximizable) {
header.suspendLayouts();
tools.maximize.hide();
this.resumeHeaderLayout(true);
}
if (me.resizer) {
me.resizer.disable();
}
me.callParent(arguments);
},
afterExpand: function() {
var me = this,
header = me.header,
tools = me.tools,
changed;
if (header) {
header.suspendLayouts();
if (me.maximizable) {
tools.maximize.show();
changed = true;
}
this.resumeHeaderLayout(changed);
}
if (me.resizer) {
me.resizer.enable();
}
me.callParent(arguments);
},
/**
* Fits the window within its current container and automatically replaces the {@link #maximizable 'maximize' tool
* button} with the 'restore' tool button. Also see {@link #toggleMaximize}.
* @param {Boolean} [animate=false] Pass `true` to animate this Window to full size.
* @return {Ext.window.Window} this
*/
maximize: function(animate, /* private */ initial) {
var me = this,
header = me.header,
tools = me.tools,
width = me.width,
height = me.height,
restore, changed;
if (!me.maximized && !me.maximizing) {
me.maximizing = true;
me.expand(false);
if (!me.hasSavedRestore) {
restore = me.restoreSize = {
width: width ? width : null,
height: height ? height : null
};
// If we're not positioned yet, default back to 0,0
if (initial) {
me.restorePos = [me.x || 0, me.y || 0];
} else {
me.restorePos = me.getPosition();
}
}
// Manipulate visibility of header tools if there is a header
if (header) {
header.suspendLayouts();
if (tools.maximize) {
tools.maximize.setType('restore');
}
if (me.collapseTool) {
me.collapseTool.hide();
changed = true;
}
me.resumeHeaderLayout(changed);
}
me.el.disableShadow();
if (me.dd) {
me.dd.disable();
if (header) {
header.removeCls(header.indicateDragCls);
}
}
if (me.resizer) {
me.resizer.disable();
}
me.el.addCls(Ext.baseCSSPrefix + 'window-maximized');
me.container.addCls(Ext.baseCSSPrefix + 'window-maximized-ct');
me.syncMonitorWindowResize();
me.fitContainer(animate = (animate || !!me.animateTarget) ? {
callback: function() {
me.maximizing = false;
me.maximized = true;
if (!initial) {
me.fireEvent('maximize', me);
}
}
} : null);
if (!animate) {
me.maximizing = false;
me.maximized = true;
if (!initial) {
me.fireEvent('maximize', me);
}
}
}
return me;
},
/**
* Restores a {@link #maximizable maximized} window back to its original size and position prior to being maximized
* and also replaces the 'restore' tool button with the 'maximize' tool button. Also see {@link #toggleMaximize}.
* @param {Boolean} [animate=false] Pass `true` to animate the restore.
* @return {Ext.window.Window} this
*/
restore: function(animate) {
var me = this,
tools = me.tools,
header = me.header,
newBox = me.restoreSize,
changed;
if (me.maximized) {
me.hasSavedRestore = null;
me.removeCls(Ext.baseCSSPrefix + 'window-maximized');
// Manipulate visibility of header tools if there is a header
if (header) {
header.suspendLayouts();
if (tools.maximize) {
tools.maximize.setType('maximize');
}
if (me.collapseTool) {
me.collapseTool.show();
changed = true;
}
me.resumeHeaderLayout(changed);
}
// Restore the position/sizing
newBox.x = me.restorePos[0];
newBox.y = me.restorePos[1];
me.setBox(newBox, animate = (animate || !!me.animateTarget) ? {
callback: function() {
me.el.enableShadow(null, true);
me.maximized = false;
me.fireEvent('restore', me);
}
} : null);
// Unset old position/sizing
me.restorePos = me.restoreSize = null;
// Allow users to drag and drop again
if (me.dd) {
me.dd.enable();
if (header) {
header.addCls(header.indicateDragCls);
}
}
if (me.resizer) {
me.resizer.enable();
}
me.container.removeCls(Ext.baseCSSPrefix + 'window-maximized-ct');
me.syncMonitorWindowResize();
if (!animate) {
me.el.enableShadow(null, true);
me.maximized = false;
me.fireEvent('restore', me);
}
}
return me;
},
/**
* Synchronizes the presence of our listener for window resize events. This method
* should be called whenever this status might change.
* @private
*/
syncMonitorWindowResize: function () {
var me = this,
currentlyMonitoring = me._monitoringResize,
// all the states where we should be listening to window resize:
yes = me.monitorResize || me.constrain || me.constrainHeader || me.maximized,
// all the states where we veto this:
veto = me.hidden || me.destroying || me.destroyed;
if (yes && !veto) {
// we should be listening...
if (!currentlyMonitoring) {
// but we aren't, so set it up.
// Delay so that we jump over any Viewport resize activity
Ext.on('resize', me.onWindowResize, me, {buffer: 1});
me._monitoringResize = true;
}
} else if (currentlyMonitoring) {
// we should not be listening, but we are, so tear it down
Ext.un('resize', me.onWindowResize, me);
me._monitoringResize = false;
}
},
/**
* A shortcut method for toggling between {@link #method-maximize} and {@link #method-restore} based on the current maximized
* state of the window.
* @return {Ext.window.Window} this
*/
toggleMaximize: function() {
return this[this.maximized ? 'restore': 'maximize']();
},
createGhost: function() {
var ghost = this.callParent(arguments);
ghost.xtype = 'window';
ghost.focusOnToFront = false;
return ghost;
},
/**
* Gets the configured default focus item. If a {@link #defaultFocus} is set, it will
* receive focus when the Window's `focus` method is called, otherwise the
* Window itself will receive focus.
*/
getDefaultFocus: function() {
var me = this,
result,
defaultComp = me.defaultFocus,
selector;
if (defaultComp !== undefined) {
// Number is index of Button
if (Ext.isNumber(defaultComp)) {
result = me.query('button')[defaultComp];
}
// String is ID or CQ selector
else if (Ext.isString(defaultComp)) {
selector = defaultComp;
// Try id/itemId match if selector begins with alphanumeric
// and is not compound xtype/id selector with # in the middle
// (https://sencha.jira.com/browse/EXTJS-14925)
if (Ext.validIdRe.test(selector)) {
result = me.down(Ext.makeIdSelector(selector));
}
// If not found, use as selector
if (!result) {
result = me.down(selector);
}
}
// Otherwise, if it's got a focus method, use it
else if (defaultComp.focus) {
result = defaultComp;
}
}
return result;
},
privates: {
// Override. Windows are always simple draggable, they do not use Ext.Panel.DDs
// The dd property in a Window is always a ComponentDragger
initDraggable: function() {
/**
* @property {Ext.util.ComponentDragger} dd
* If this Window is configured {@link #cfg-draggable}, this property will contain an instance of
* {@link Ext.util.ComponentDragger} (A subclass of {@link Ext.dd.DragTracker DragTracker}) which handles dragging
* the Window's DOM Element, and constraining according to the {@link #constrain} and {@link #constrainHeader} .
*
* This has implementations of `onBeforeStart`, `onDrag` and `onEnd` which perform the dragging action. If
* extra logic is needed at these points, use {@link Ext.Function#createInterceptor createInterceptor} or
* {@link Ext.Function#createSequence createSequence} to augment the existing implementations.
*/
this.initSimpleDraggable();
},
onHeaderClick: function(header, e) {
var delegate;
if (header.el.contains(e.getTarget())) {
delegate = this.getDefaultFocus();
if (delegate) {
delegate.focus();
}
}
},
initResizable: function(resizable) {
var me = this;
me.callParent([resizable]);
if (me.maximized || me.maximizing) {
me.resizer.disable();
}
},
initSimpleDraggable: function() {
var me = this,
dd;
me.callParent();
dd = me.dd;
if (dd && me.maximized || me.maximizing) {
dd.disable();
}
},
onTabGuardFocusEnter: function(e, target) {
var me = this,
el = me.el,
beforeGuard = me.tabGuardBeforeEl,
afterGuard = me.tabGuardAfterEl,
from = e.relatedTarget,
nodes, forward, nextFocus;
nodes = el.findTabbableElements({
skipSelf: true
});
// Tabbables might include two tab guards, so remove them
if (nodes[0] === beforeGuard.dom) {
nodes.shift();
}
if (nodes[nodes.length - 1] === afterGuard.dom) {
nodes.pop();
}
// Totally possible not to have anything tabbable within the window
// but we have to do something so focus back the window el. At least
// in that case the user will be able to press Escape key to close it.
if (nodes.length === 0) {
nextFocus = el;
}
// The window itself was focused, possibly by clicking or programmatically;
// but this time we do have something tabbable to choose from.
else if (from === el.dom) {
forward = target === beforeGuard.dom;
}
// Focus was within the window and is trying to escape;
// for topmost guard we need to bounce focus back to the last tabbable
// element in the window, and vice versa for the bottom guard.
else if (el.contains(from)) {
forward = !!e.forwardTab;
}
// It is entirely possible that focus was outside the window and
// the user tabbed into the window. In that case we forward the focus
// to the next available element in the natural tab order, i.e. the element
// after the topmost guard, or the element before the bottom guard.
else {
forward = target === beforeGuard.dom;
}
nextFocus = nextFocus || (forward ? nodes[0] : nodes[nodes.length - 1]);
if (nextFocus) {
nextFocus.focus();
}
}
}
});