/* This file is part of Ext JS 3.4 Copyright (c) 2011-2013 Sencha Inc Contact: http://www.sencha.com/contact GNU General Public License Usage This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html. If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact. Build date: 2013-04-03 15:07:25 */ /** * @class Ext.Window * @extends Ext.Panel *
A specialized panel intended for use as an application window. Windows are floated, {@link #resizable}, and * {@link #draggable} by default. Windows can be {@link #maximizable maximized} to fill the viewport, * restored to their prior size, and can be {@link #minimize}d.
*Windows can also be linked to a {@link Ext.WindowGroup} or managed by the {@link Ext.WindowMgr} 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}.
*Note: By default, the {@link #closable close}
header tool destroys the Window resulting in
* destruction of any child Components. This makes the Window object, and all its descendants unusable. To enable
* re-use of a Window, use {@link #closeAction closeAction: 'hide'}
.
Specifies a Component to receive focus when this Window is focussed.
*This may be one of:
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 (defaults to true).
*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 #close} method will be called. This * will {@link Ext.Component#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 {String} closeAction *
The action to take when the close header tool is clicked: *
'{@link #close}'
: Default'{@link #hide}'
: Note: This setting does not affect the {@link #close} method * which will always {@link Ext.Component#destroy destroy} the window. To * programatically hide a window, call {@link #hide}.
*/ closeAction : 'close', /** * @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}. * (defaults to false). 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 (defaults to * false). Optionally the entire window can be constrained using {@link #constrain}. */ constrainHeader : false, /** * @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 (defaults to false). */ 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 (defaults to false). 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 (defaults to false). 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, /** * @cfg {Number} minHeight * The minimum height in pixels allowed for this window (defaults to 100). Only applies when resizable = true. */ minHeight : 100, /** * @cfg {Number} minWidth * The minimum width in pixels allowed for this window (defaults to 200). Only applies when resizable = true. */ minWidth : 200, /** * @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 (defaults to true). */ expandOnShow : true, /** * @cfg {Number} showAnimDuration The number of seconds that the window show animation takes if enabled. * Defaults to 0.25 */ showAnimDuration: 0.25, /** * @cfg {Number} hideAnimDuration The number of seconds that the window hide animation takes if enabled. * Defaults to 0.25 */ hideAnimDuration: 0.25, // inherited docs, same default collapsible : false, /** * @cfg {Boolean} initHidden * True to hide the window until show() is explicitly called (defaults to true). * @deprecated */ initHidden : undefined, /** * @cfg {Boolean} hidden * Render this component hidden (default is true). If true, the * {@link #hide} method will be called internally. */ hidden : true, // The following configs are set to provide the basic functionality of a window. // Changing them would require additional code to handle correctly and should // usually only be done in subclasses that can provide custom behavior. Changing them // may have unexpected or undesirable results. /** @cfg {String} elements @hide */ elements : 'header,body', /** @cfg {Boolean} frame @hide */ frame : true, /** @cfg {Boolean} floating @hide */ floating : true, // private initComponent : function(){ this.initTools(); Ext.Window.superclass.initComponent.call(this); this.addEvents( /** * @event activate * Fires after the window has been visually activated via {@link #setActive}. * @param {Ext.Window} this */ /** * @event deactivate * Fires after the window has been visually deactivated via {@link #setActive}. * @param {Ext.Window} this */ /** * @event resize * Fires after the window has been resized. * @param {Ext.Window} this * @param {Number} width The window's new width * @param {Number} height The window's new height */ 'resize', /** * @event maximize * Fires after the window has been maximized. * @param {Ext.Window} this */ 'maximize', /** * @event minimize * Fires after the window has been minimized. * @param {Ext.Window} this */ 'minimize', /** * @event restore * Fires after the window has been restored to its original size after being maximized. * @param {Ext.Window} this */ 'restore' ); // for backwards compat, this should be removed at some point if(Ext.isDefined(this.initHidden)){ this.hidden = this.initHidden; } if(this.hidden === false){ this.hidden = true; this.show(); } }, // private getState : function(){ return Ext.apply(Ext.Window.superclass.getState.call(this) || {}, this.getBox(true)); }, // private onRender : function(ct, position){ Ext.Window.superclass.onRender.call(this, ct, position); if(this.plain){ this.el.addClass('x-window-plain'); } // this element allows the Window to be focused for keyboard events this.focusEl = this.el.createChild({ tag: 'a', href:'#', cls:'x-dlg-focus', tabIndex:'-1', html: ' '}); this.focusEl.swallowEvent('click', true); this.proxy = this.el.createProxy('x-window-proxy'); this.proxy.enableDisplayMode('block'); if(this.modal){ this.mask = this.container.createChild({cls:'ext-el-mask'}, this.el.dom); this.mask.enableDisplayMode('block'); this.mask.hide(); this.mon(this.mask, 'click', this.focus, this); } if(this.maximizable){ this.mon(this.header, 'dblclick', this.toggleMaximize, this); } }, // private initEvents : function(){ Ext.Window.superclass.initEvents.call(this); if(this.animateTarget){ this.setAnimateTarget(this.animateTarget); } if(this.resizable){ this.resizer = new Ext.Resizable(this.el, { minWidth: this.minWidth, minHeight:this.minHeight, handles: this.resizeHandles || 'all', pinned: true, resizeElement : this.resizerAction, handleCls: 'x-window-handle' }); this.resizer.window = this; this.mon(this.resizer, 'beforeresize', this.beforeResize, this); } if(this.draggable){ this.header.addClass('x-window-draggable'); } this.mon(this.el, 'mousedown', this.toFront, this); this.manager = this.manager || Ext.WindowMgr; this.manager.register(this); if(this.maximized){ this.maximized = false; this.maximize(); } if(this.closable){ var km = this.getKeyMap(); km.on(27, this.onEsc, this); km.disable(); } }, initDraggable : function(){ /** *If this Window is configured {@link #draggable}, this property will contain * an instance of {@link Ext.dd.DD} which handles dragging the Window's DOM Element.
*This has implementations of startDrag
, onDrag
and endDrag
* which perform the dragging action. If extra logic is needed at these points, use
* {@link Function#createInterceptor createInterceptor} or {@link Function#createSequence createSequence} to
* augment the existing implementations.
this
reference) in which the callback is executed. Defaults to this Window.
* @return {Ext.Window} this
*/
show : function(animateTarget, cb, scope){
if(!this.rendered){
this.render(Ext.getBody());
}
if(this.hidden === false){
this.toFront();
return this;
}
if(this.fireEvent('beforeshow', this) === false){
return this;
}
if(cb){
this.on('show', cb, scope, {single:true});
}
this.hidden = false;
if(Ext.isDefined(animateTarget)){
this.setAnimateTarget(animateTarget);
}
this.beforeShow();
if(this.animateTarget){
this.animShow();
}else{
this.afterShow();
}
return this;
},
// private
afterShow : function(isAnim){
if (this.isDestroyed){
return false;
}
this.proxy.hide();
this.el.setStyle('display', 'block');
this.el.show();
if(this.maximized){
this.fitContainer();
}
if(Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
this.cascade(this.setAutoScroll);
}
if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
Ext.EventManager.onWindowResize(this.onWindowResize, this);
}
this.doConstrain();
this.doLayout();
if(this.keyMap){
this.keyMap.enable();
}
this.toFront();
this.updateHandles();
if(isAnim && (Ext.isIE || Ext.isWebKit)){
var sz = this.getSize();
this.onResize(sz.width, sz.height);
}
this.onShow();
this.fireEvent('show', this);
},
// private
animShow : function(){
this.proxy.show();
this.proxy.setBox(this.animateTarget.getBox());
this.proxy.setOpacity(0);
var b = this.getBox();
this.el.setStyle('display', 'none');
this.proxy.shift(Ext.apply(b, {
callback: this.afterShow.createDelegate(this, [true], false),
scope: this,
easing: 'easeNone',
duration: this.showAnimDuration,
opacity: 0.5
}));
},
/**
* Hides the window, setting it to invisible and applying negative offsets.
* @param {String/Element} animateTarget (optional) The target element or id to which the window should
* animate while hiding (defaults to null with no animation)
* @param {Function} callback (optional) A callback function to call after the window is hidden
* @param {Object} scope (optional) The scope (this
reference) in which the callback is executed. Defaults to this Window.
* @return {Ext.Window} this
*/
hide : function(animateTarget, cb, scope){
if(this.hidden || this.fireEvent('beforehide', this) === false){
return this;
}
if(cb){
this.on('hide', cb, scope, {single:true});
}
this.hidden = true;
if(animateTarget !== undefined){
this.setAnimateTarget(animateTarget);
}
if(this.modal){
this.mask.hide();
Ext.getBody().removeClass('x-body-masked');
}
if(this.animateTarget){
this.animHide();
}else{
this.el.hide();
this.afterHide();
}
return this;
},
// private
afterHide : function(){
this.proxy.hide();
if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){
Ext.EventManager.removeResizeListener(this.onWindowResize, this);
}
if(this.keyMap){
this.keyMap.disable();
}
this.onHide();
this.fireEvent('hide', this);
},
// private
animHide : function(){
this.proxy.setOpacity(0.5);
this.proxy.show();
var tb = this.getBox(false);
this.proxy.setBox(tb);
this.el.hide();
this.proxy.shift(Ext.apply(this.animateTarget.getBox(), {
callback: this.afterHide,
scope: this,
duration: this.hideAnimDuration,
easing: 'easeNone',
opacity: 0
}));
},
/**
* Method that is called immediately before the show
event is fired.
* Defaults to Ext.emptyFn
.
*/
onShow : Ext.emptyFn,
/**
* Method that is called immediately before the hide
event is fired.
* Defaults to Ext.emptyFn
.
*/
onHide : Ext.emptyFn,
// private
onWindowResize : function(){
if(this.maximized){
this.fitContainer();
}
if(this.modal){
this.mask.setSize('100%', '100%');
var force = this.mask.dom.offsetHeight;
this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
}
this.doConstrain();
},
// private
doConstrain : function(){
if(this.constrain || this.constrainHeader){
var offsets;
if(this.constrain){
offsets = {
right:this.el.shadowOffset,
left:this.el.shadowOffset,
bottom:this.el.shadowOffset
};
}else {
var s = this.getSize();
offsets = {
right:-(s.width - 100),
bottom:-(s.height - 25 + this.el.getConstrainOffset())
};
}
var xy = this.el.getConstrainToXY(this.container, true, offsets);
if(xy){
this.setPosition(xy[0], xy[1]);
}
}
},
// private - used for dragging
ghost : function(cls){
var ghost = this.createGhost(cls);
var box = this.getBox(true);
ghost.setLeftTop(box.x, box.y);
ghost.setWidth(box.width);
this.el.hide();
this.activeGhost = ghost;
return ghost;
},
// private
unghost : function(show, matchPosition){
if(!this.activeGhost) {
return;
}
if(show !== false){
this.el.show();
this.focus.defer(10, this);
if(Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
this.cascade(this.setAutoScroll);
}
}
if(matchPosition !== false){
this.setPosition(this.activeGhost.getLeft(true), this.activeGhost.getTop(true));
}
this.activeGhost.hide();
this.activeGhost.remove();
delete this.activeGhost;
},
/**
* Placeholder method for minimizing the window. By default, this method simply fires the {@link #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} this
*/
minimize : function(){
this.fireEvent('minimize', this);
return this;
},
/**
* Closes the Window, removes it from the DOM, {@link Ext.Component#destroy destroy}s * the Window object and all its descendant Components. The {@link Ext.Panel#beforeclose beforeclose} * event is fired before the close happens and will cancel the close action if it returns false.
*
Note: This method is not affected by the {@link #closeAction} setting which * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}. * To hide the Window without destroying it, call {@link #hide}.
*/ close : function(){ if(this.fireEvent('beforeclose', this) !== false){ if(this.hidden){ this.doClose(); }else{ this.hide(null, this.doClose, this); } } }, // private doClose : function(){ this.fireEvent('close', this); this.destroy(); }, /** * 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}. * @return {Ext.Window} this */ maximize : function(){ if(!this.maximized){ this.expand(false); this.restoreSize = this.getSize(); this.restorePos = this.getPosition(true); if (this.maximizable){ this.tools.maximize.hide(); this.tools.restore.show(); } this.maximized = true; this.el.disableShadow(); if(this.dd){ this.dd.lock(); } if(this.collapsible){ this.tools.toggle.hide(); } this.el.addClass('x-window-maximized'); this.container.addClass('x-window-maximized-ct'); this.setPosition(0, 0); this.fitContainer(); this.fireEvent('maximize', this); } return this; }, /** * 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}. * @return {Ext.Window} this */ restore : function(){ if(this.maximized){ var t = this.tools; this.el.removeClass('x-window-maximized'); if(t.restore){ t.restore.hide(); } if(t.maximize){ t.maximize.show(); } this.setPosition(this.restorePos[0], this.restorePos[1]); this.setSize(this.restoreSize.width, this.restoreSize.height); delete this.restorePos; delete this.restoreSize; this.maximized = false; this.el.enableShadow(true); if(this.dd){ this.dd.unlock(); } if(this.collapsible && t.toggle){ t.toggle.show(); } this.container.removeClass('x-window-maximized-ct'); this.doConstrain(); this.fireEvent('restore', this); } return this; }, /** * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized * state of the window. * @return {Ext.Window} this */ toggleMaximize : function(){ return this[this.maximized ? 'restore' : 'maximize'](); }, // private fitContainer : function(){ var vs = this.container.getViewSize(false); this.setSize(vs.width, vs.height); }, // private // z-index is managed by the WindowManager and may be overwritten at any time setZIndex : function(index){ if(this.modal){ this.mask.setStyle('z-index', index); } this.el.setZIndex(++index); index += 5; if(this.resizer){ this.resizer.proxy.setStyle('z-index', ++index); } this.lastZIndex = index; }, /** * Aligns the window to the specified element * @param {Mixed} element The element to align to. * @param {String} position (optional, defaults to "tl-bl?") The position to align to (see {@link Ext.Element#alignTo} for more details). * @param {Array} offsets (optional) Offset the positioning by [x, y] * @return {Ext.Window} this */ alignTo : function(element, position, offsets){ var xy = this.el.getAlignToXY(element, position, offsets); this.setPagePosition(xy[0], xy[1]); return this; }, /** * Anchors this window to another element and realigns it when the window is resized or scrolled. * @param {Mixed} element The element to align to. * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details) * @param {Array} offsets (optional) Offset the positioning by [x, y] * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter * is a number, it is used as the buffer delay (defaults to 50ms). * @return {Ext.Window} this */ anchorTo : function(el, alignment, offsets, monitorScroll){ this.clearAnchor(); this.anchorTarget = { el: el, alignment: alignment, offsets: offsets }; Ext.EventManager.onWindowResize(this.doAnchor, this); var tm = typeof monitorScroll; if(tm != 'undefined'){ Ext.EventManager.on(window, 'scroll', this.doAnchor, this, {buffer: tm == 'number' ? monitorScroll : 50}); } return this.doAnchor(); }, /** * Performs the anchor, using the saved anchorTarget property. * @return {Ext.Window} this * @private */ doAnchor : function(){ var o = this.anchorTarget; this.alignTo(o.el, o.alignment, o.offsets); return this; }, /** * Removes any existing anchor from this window. See {@link #anchorTo}. * @return {Ext.Window} this */ clearAnchor : function(){ if(this.anchorTarget){ Ext.EventManager.removeResizeListener(this.doAnchor, this); Ext.EventManager.un(window, 'scroll', this.doAnchor, this); delete this.anchorTarget; } return this; }, /** * Brings this window to the front of any other visible windows * @param {Boolean} e (optional) Specify false to prevent the window from being focused. * @return {Ext.Window} this */ toFront : function(e){ if(this.manager.bringToFront(this)){ if(!e || !e.getTarget().focus){ this.focus(); } } return this; }, /** * Makes this the active window by showing its shadow, or deactivates it by hiding its shadow. This method also * fires the {@link #activate} or {@link #deactivate} event depending on which action occurred. This method is * called internally by {@link Ext.WindowMgr}. * @param {Boolean} active True to activate the window, false to deactivate it (defaults to false) */ setActive : function(active){ if(active){ if(!this.maximized){ this.el.enableShadow(true); } this.fireEvent('activate', this); }else{ this.el.disableShadow(); this.fireEvent('deactivate', this); } }, /** * Sends this window to the back of (lower z-index than) any other visible windows * @return {Ext.Window} this */ toBack : function(){ this.manager.sendToBack(this); return this; }, /** * Centers this window in the viewport * @return {Ext.Window} this */ center : function(){ var xy = this.el.getAlignToXY(this.container, 'c-c'); this.setPagePosition(xy[0], xy[1]); return this; } /** * @cfg {Boolean} autoWidth @hide **/ }); Ext.reg('window', Ext.Window); // private - custom Window DD implementation Ext.Window.DD = Ext.extend(Ext.dd.DD, { constructor : function(win){ this.win = win; Ext.Window.DD.superclass.constructor.call(this, win.el.id, 'WindowDD-'+win.id); this.setHandleElId(win.header.id); this.scroll = false; }, moveOnly:true, headerOffsets:[100, 25], startDrag : function(){ var w = this.win; this.proxy = w.ghost(w.initialConfig.cls); if(w.constrain !== false){ var so = w.el.shadowOffset; this.constrainTo(w.container, {right: so, left: so, bottom: so}); }else if(w.constrainHeader !== false){ var s = this.proxy.getSize(); this.constrainTo(w.container, {right: -(s.width-this.headerOffsets[0]), bottom: -(s.height-this.headerOffsets[1])}); } }, b4Drag : Ext.emptyFn, onDrag : function(e){ this.alignElWithMouse(this.proxy, e.getPageX(), e.getPageY()); }, endDrag : function(e){ this.win.unghost(); this.win.saveState(); } }); /** * @class Ext.WindowGroup * An object that manages a group of {@link Ext.Window} instances and provides z-order management * and window activation behavior. * @constructor */ Ext.WindowGroup = function(){ var list = {}; var accessList = []; var front = null; // private var sortWindows = function(d1, d2){ return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1; }; // private var orderWindows = function(){ var a = accessList, len = a.length; if(len > 0){ a.sort(sortWindows); var seed = a[0].manager.zseed; for(var i = 0; i < len; i++){ var win = a[i]; if(win && !win.hidden){ win.setZIndex(seed + (i*10)); } } } activateLast(); }; // private var setActiveWin = function(win){ if(win != front){ if(front){ front.setActive(false); } front = win; if(win){ win.setActive(true); } } }; // private var activateLast = function(){ for(var i = accessList.length-1; i >=0; --i) { if(!accessList[i].hidden){ setActiveWin(accessList[i]); return; } } // none to activate setActiveWin(null); }; return { /** * The starting z-index for windows in this WindowGroup (defaults to 9000) * @type Number The z-index value */ zseed : 9000, /** *Registers a {@link Ext.Window Window} with this WindowManager. This should not * need to be called under normal circumstances. Windows are automatically registered * with a {@link Ext.Window#manager manager} at construction time.
*Where this may be useful is moving Windows between two WindowManagers. For example, * to bring the Ext.MessageBox dialog under the same manager as the Desktop's * WindowManager in the desktop sample app:
var msgWin = Ext.MessageBox.getDialog();
MyDesktop.getDesktop().getManager().register(msgWin);
* @param {Window} win The Window to register.
*/
register : function(win){
if(win.manager){
win.manager.unregister(win);
}
win.manager = this;
list[win.id] = win;
accessList.push(win);
win.on('hide', activateLast);
},
/**
* Unregisters a {@link Ext.Window Window} from this WindowManager. This should not * need to be called. Windows are automatically unregistered upon destruction. * See {@link #register}.
* @param {Window} win The Window to unregister. */ unregister : function(win){ delete win.manager; delete list[win.id]; win.un('hide', activateLast); accessList.remove(win); }, /** * Gets a registered window by id. * @param {String/Object} id The id of the window or a {@link Ext.Window} instance * @return {Ext.Window} */ get : function(id){ return typeof id == "object" ? id : list[id]; }, /** * Brings the specified window to the front of any other active windows in this WindowGroup. * @param {String/Object} win The id of the window or a {@link Ext.Window} instance * @return {Boolean} True if the dialog was brought to the front, else false * if it was already in front */ bringToFront : function(win){ win = this.get(win); if(win != front){ win._lastAccess = new Date().getTime(); orderWindows(); return true; } return false; }, /** * Sends the specified window to the back of other active windows in this WindowGroup. * @param {String/Object} win The id of the window or a {@link Ext.Window} instance * @return {Ext.Window} The window */ sendToBack : function(win){ win = this.get(win); win._lastAccess = -(new Date().getTime()); orderWindows(); return win; }, /** * Hides all windows in this WindowGroup. */ hideAll : function(){ for(var id in list){ if(list[id] && typeof list[id] != "function" && list[id].isVisible()){ list[id].hide(); } } }, /** * Gets the currently-active window in this WindowGroup. * @return {Ext.Window} The active window */ getActive : function(){ return front; }, /** * Returns zero or more windows in this WindowGroup using the custom search function passed to this method. * The function should accept a single {@link Ext.Window} reference as its only argument and should * return true if the window matches the search criteria, otherwise it should return false. * @param {Function} fn The search function * @param {Object} scope (optional) The scope (this
reference) in which the function is executed. Defaults to the Window being tested.
* that gets passed to the function if not specified)
* @return {Array} An array of zero or more matching windows
*/
getBy : function(fn, scope){
var r = [];
for(var i = accessList.length-1; i >=0; --i) {
var win = accessList[i];
if(fn.call(scope||win, win) !== false){
r.push(win);
}
}
return r;
},
/**
* Executes the specified function once for every window in this WindowGroup, passing each
* window as the only parameter. Returning false from the function will stop the iteration.
* @param {Function} fn The function to execute for each item
* @param {Object} scope (optional) The scope (this
reference) in which the function is executed. Defaults to the current Window in the iteration.
*/
each : function(fn, scope){
for(var id in list){
if(list[id] && typeof list[id] != "function"){
if(fn.call(scope || list[id], list[id]) === false){
return;
}
}
}
}
};
};
/**
* @class Ext.WindowMgr
* @extends Ext.WindowGroup
* The default global window group that is available automatically. To have more than one group of windows
* with separate z-order stacks, create additional instances of {@link Ext.WindowGroup} as needed.
* @singleton
*/
Ext.WindowMgr = new Ext.WindowGroup();/**
* @class Ext.MessageBox
* Utility class for generating different styles of message boxes. The alias Ext.Msg can also be used.
*Note that the MessageBox is asynchronous. Unlike a regular JavaScript alert
(which will halt
* browser execution), showing a MessageBox will not cause the code to stop. For this reason, if you have code
* that should only run after some user feedback from the MessageBox, you must use a callback function
* (see the function
parameter for {@link #show} for more details).
Example usage:
*
// Basic alert:
Ext.Msg.alert('Status', 'Changes saved successfully.');
// Prompt for user data and process the result using a callback:
Ext.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
if (btn == 'ok'){
// process text value and close...
}
});
// Show a dialog using config options:
Ext.Msg.show({
title:'Save Changes?',
msg: 'You are closing a tab that has unsaved changes. Would you like to save your changes?',
buttons: Ext.Msg.YESNOCANCEL,
fn: processResult,
animEl: 'elId',
icon: Ext.MessageBox.QUESTION
});
* @singleton
*/
Ext.MessageBox = function(){
var dlg, opt, mask, waitTimer,
bodyEl, msgEl, textboxEl, textareaEl, progressBar, pp, iconEl, spacerEl,
buttons, activeTextEl, bwidth, bufferIcon = '', iconCls = '',
buttonNames = ['ok', 'yes', 'no', 'cancel'];
// private
var handleButton = function(button){
buttons[button].blur();
if(dlg.isVisible()){
dlg.hide();
handleHide();
Ext.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value, opt], 1);
}
};
// private
var handleHide = function(){
if(opt && opt.cls){
dlg.el.removeClass(opt.cls);
}
progressBar.reset();
};
// private
var handleEsc = function(d, k, e){
if(opt && opt.closable !== false){
dlg.hide();
handleHide();
}
if(e){
e.stopEvent();
}
};
// private
var updateButtons = function(b){
var width = 0,
cfg;
if(!b){
Ext.each(buttonNames, function(name){
buttons[name].hide();
});
return width;
}
dlg.footer.dom.style.display = '';
Ext.iterate(buttons, function(name, btn){
cfg = b[name];
if(cfg){
btn.show();
btn.setText(Ext.isString(cfg) ? cfg : Ext.MessageBox.buttonText[name]);
width += btn.getEl().getWidth() + 15;
}else{
btn.hide();
}
});
return width;
};
return {
/**
* Returns a reference to the underlying {@link Ext.Window} element
* @return {Ext.Window} The window
*/
getDialog : function(titleText){
if(!dlg){
var btns = [];
buttons = {};
Ext.each(buttonNames, function(name){
btns.push(buttons[name] = new Ext.Button({
text: this.buttonText[name],
handler: handleButton.createCallback(name),
hideMode: 'offsets'
}));
}, this);
dlg = new Ext.Window({
autoCreate : true,
title:titleText,
resizable:false,
constrain:true,
constrainHeader:true,
minimizable : false,
maximizable : false,
stateful: false,
modal: true,
shim:true,
buttonAlign:"center",
width:400,
height:100,
minHeight: 80,
plain:true,
footer:true,
closable:true,
close : function(){
if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
handleButton("no");
}else{
handleButton("cancel");
}
},
fbar: new Ext.Toolbar({
items: btns,
enableOverflow: false
})
});
dlg.render(document.body);
dlg.getEl().addClass('x-window-dlg');
mask = dlg.mask;
bodyEl = dlg.body.createChild({
html:'Progress and wait dialogs will ignore this option since they do not respond to user * actions and can only be closed programmatically, so any required function should be called * by the same code after it closes the dialog. Parameters passed:
Ext.Msg.show({
title: 'Address',
msg: 'Please enter your address:',
width: 300,
buttons: Ext.MessageBox.OKCANCEL,
multiline: true,
fn: saveAddress,
animEl: 'addAddressBtn',
icon: Ext.MessageBox.INFO
});
* @return {Ext.MessageBox} this
*/
show : function(options){
if(this.isVisible()){
this.hide();
}
opt = options;
var d = this.getDialog(opt.title || " ");
d.setTitle(opt.title || " ");
var allowClose = (opt.closable !== false && opt.progress !== true && opt.wait !== true);
d.tools.close.setDisplayed(allowClose);
activeTextEl = textboxEl;
opt.prompt = opt.prompt || (opt.multiline ? true : false);
if(opt.prompt){
if(opt.multiline){
textboxEl.hide();
textareaEl.show();
textareaEl.setHeight(Ext.isNumber(opt.multiline) ? opt.multiline : this.defaultTextHeight);
activeTextEl = textareaEl;
}else{
textboxEl.show();
textareaEl.hide();
}
}else{
textboxEl.hide();
textareaEl.hide();
}
activeTextEl.dom.value = opt.value || "";
if(opt.prompt){
d.focusEl = activeTextEl;
}else{
var bs = opt.buttons;
var db = null;
if(bs && bs.ok){
db = buttons["ok"];
}else if(bs && bs.yes){
db = buttons["yes"];
}
if (db){
d.focusEl = db;
}
}
if(Ext.isDefined(opt.iconCls)){
d.setIconClass(opt.iconCls);
}
this.setIcon(Ext.isDefined(opt.icon) ? opt.icon : bufferIcon);
bwidth = updateButtons(opt.buttons);
progressBar.setVisible(opt.progress === true || opt.wait === true);
this.updateProgress(0, opt.progressText);
this.updateText(opt.msg);
if(opt.cls){
d.el.addClass(opt.cls);
}
d.proxyDrag = opt.proxyDrag === true;
d.modal = opt.modal !== false;
d.mask = opt.modal !== false ? mask : false;
if(!d.isVisible()){
// force it to the end of the z-index stack so it gets a cursor in FF
document.body.appendChild(dlg.el.dom);
d.setAnimateTarget(opt.animEl);
//workaround for window internally enabling keymap in afterShow
d.on('show', function(){
if(allowClose === true){
d.keyMap.enable();
}else{
d.keyMap.disable();
}
}, this, {single:true});
d.show(opt.animEl);
}
if(opt.wait === true){
progressBar.wait(opt.waitConfig);
}
return this;
},
/**
* Adds the specified icon to the dialog. By default, the class 'ext-mb-icon' is applied for default
* styling, and the class passed in is expected to supply the background image url. Pass in empty string ('')
* to clear any existing icon. This method must be called before the MessageBox is shown.
* The following built-in icon classes are supported, but you can also pass in a custom class name:
* Ext.MessageBox.INFO Ext.MessageBox.WARNING Ext.MessageBox.QUESTION Ext.MessageBox.ERROR ** @param {String} icon A CSS classname specifying the icon's background image url, or empty string to clear the icon * @return {Ext.MessageBox} this */ setIcon : function(icon){ if(!dlg){ bufferIcon = icon; return; } bufferIcon = undefined; if(icon && icon != ''){ iconEl.removeClass('x-hidden'); iconEl.replaceClass(iconCls, icon); bodyEl.addClass('x-dlg-icon'); iconCls = icon; }else{ iconEl.replaceClass(iconCls, 'x-hidden'); bodyEl.removeClass('x-dlg-icon'); iconCls = ''; } return this; }, /** * Displays a message box with a progress bar. This message box has no buttons and is not closeable by * the user. You are responsible for updating the progress bar as needed via {@link Ext.MessageBox#updateProgress} * and closing the message box when the process is complete. * @param {String} title The title bar text * @param {String} msg The message box body text * @param {String} progressText (optional) The text to display inside the progress bar (defaults to '') * @return {Ext.MessageBox} this */ progress : function(title, msg, progressText){ this.show({ title : title, msg : msg, buttons: false, progress:true, closable:false, minWidth: this.minProgressWidth, progressText: progressText }); return this; }, /** * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user * interaction while waiting for a long-running process to complete that does not have defined intervals. * You are responsible for closing the message box when the process is complete. * @param {String} msg The message box body text * @param {String} title (optional) The title bar text * @param {Object} config (optional) A {@link Ext.ProgressBar#waitConfig} object * @return {Ext.MessageBox} this */ wait : function(msg, title, config){ this.show({ title : title, msg : msg, buttons: false, closable:false, wait:true, modal:true, minWidth: this.minProgressWidth, waitConfig: config }); return this; }, /** * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript alert prompt). * If a callback function is passed it will be called after the user clicks the button, and the * id of the button that was clicked will be passed as the only parameter to the callback * (could also be the top-right close button). * @param {String} title The title bar text * @param {String} msg The message box body text * @param {Function} fn (optional) The callback function invoked after the message box is closed * @param {Object} scope (optional) The scope (
this
reference) in which the callback is executed. Defaults to the browser wnidow.
* @return {Ext.MessageBox} this
*/
alert : function(title, msg, fn, scope){
this.show({
title : title,
msg : msg,
buttons: this.OK,
fn: fn,
scope : scope,
minWidth: this.minWidth
});
return this;
},
/**
* Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's confirm).
* If a callback function is passed it will be called after the user clicks either button,
* and the id of the button that was clicked will be passed as the only parameter to the callback
* (could also be the top-right close button).
* @param {String} title The title bar text
* @param {String} msg The message box body text
* @param {Function} fn (optional) The callback function invoked after the message box is closed
* @param {Object} scope (optional) The scope (this
reference) in which the callback is executed. Defaults to the browser wnidow.
* @return {Ext.MessageBox} this
*/
confirm : function(title, msg, fn, scope){
this.show({
title : title,
msg : msg,
buttons: this.YESNO,
fn: fn,
scope : scope,
icon: this.QUESTION,
minWidth: this.minWidth
});
return this;
},
/**
* Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to JavaScript's prompt).
* The prompt can be a single-line or multi-line textbox. If a callback function is passed it will be called after the user
* clicks either button, and the id of the button that was clicked (could also be the top-right
* close button) and the text that was entered will be passed as the two parameters to the callback.
* @param {String} title The title bar text
* @param {String} msg The message box body text
* @param {Function} fn (optional) The callback function invoked after the message box is closed
* @param {Object} scope (optional) The scope (this
reference) in which the callback is executed. Defaults to the browser wnidow.
* @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
* property, or the height in pixels to create the textbox (defaults to false / single-line)
* @param {String} value (optional) Default value of the text input element (defaults to '')
* @return {Ext.MessageBox} this
*/
prompt : function(title, msg, fn, scope, multiline, value){
this.show({
title : title,
msg : msg,
buttons: this.OKCANCEL,
fn: fn,
minWidth: this.minPromptWidth,
scope : scope,
prompt:true,
multiline: multiline,
value: value
});
return this;
},
/**
* Button config that displays a single OK button
* @type Object
*/
OK : {ok:true},
/**
* Button config that displays a single Cancel button
* @type Object
*/
CANCEL : {cancel:true},
/**
* Button config that displays OK and Cancel buttons
* @type Object
*/
OKCANCEL : {ok:true, cancel:true},
/**
* Button config that displays Yes and No buttons
* @type Object
*/
YESNO : {yes:true, no:true},
/**
* Button config that displays Yes, No and Cancel buttons
* @type Object
*/
YESNOCANCEL : {yes:true, no:true, cancel:true},
/**
* The CSS class that provides the INFO icon image
* @type String
*/
INFO : 'ext-mb-info',
/**
* The CSS class that provides the WARNING icon image
* @type String
*/
WARNING : 'ext-mb-warning',
/**
* The CSS class that provides the QUESTION icon image
* @type String
*/
QUESTION : 'ext-mb-question',
/**
* The CSS class that provides the ERROR icon image
* @type String
*/
ERROR : 'ext-mb-error',
/**
* The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
* @type Number
*/
defaultTextHeight : 75,
/**
* The maximum width in pixels of the message box (defaults to 600)
* @type Number
*/
maxWidth : 600,
/**
* The minimum width in pixels of the message box (defaults to 100)
* @type Number
*/
minWidth : 100,
/**
* The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
* for setting a different minimum width than text-only dialogs may need (defaults to 250).
* @type Number
*/
minProgressWidth : 250,
/**
* The minimum width in pixels of the message box if it is a prompt dialog. This is useful
* for setting a different minimum width than text-only dialogs may need (defaults to 250).
* @type Number
*/
minPromptWidth: 250,
/**
* An object containing the default button text strings that can be overriden for localized language support.
* Supported properties are: ok, cancel, yes and no. Generally you should include a locale-specific
* resource file for handling language support across the framework.
* Customize the default text like so: Ext.MessageBox.buttonText.yes = "oui"; //french
* @type Object
*/
buttonText : {
ok : "OK",
cancel : "Cancel",
yes : "Yes",
no : "No"
}
};
}();
/**
* Shorthand for {@link Ext.MessageBox}
*/
Ext.Msg = Ext.MessageBox;/**
* @class Ext.dd.PanelProxy
* A custom drag proxy implementation specific to {@link Ext.Panel}s. This class is primarily used internally
* for the Panel's drag drop implementation, and should never need to be created directly.
* @constructor
* @param panel The {@link Ext.Panel} to proxy for
* @param config Configuration options
*/
Ext.dd.PanelProxy = Ext.extend(Object, {
constructor : function(panel, config){
this.panel = panel;
this.id = this.panel.id +'-ddproxy';
Ext.apply(this, config);
},
/**
* @cfg {Boolean} insertProxy True to insert a placeholder proxy element while dragging the panel,
* false to drag with no proxy (defaults to true).
*/
insertProxy : true,
// private overrides
setStatus : Ext.emptyFn,
reset : Ext.emptyFn,
update : Ext.emptyFn,
stop : Ext.emptyFn,
sync: Ext.emptyFn,
/**
* Gets the proxy's element
* @return {Element} The proxy's element
*/
getEl : function(){
return this.ghost;
},
/**
* Gets the proxy's ghost element
* @return {Element} The proxy's ghost element
*/
getGhost : function(){
return this.ghost;
},
/**
* Gets the proxy's element
* @return {Element} The proxy's element
*/
getProxy : function(){
return this.proxy;
},
/**
* Hides the proxy
*/
hide : function(){
if(this.ghost){
if(this.proxy){
this.proxy.remove();
delete this.proxy;
}
this.panel.el.dom.style.display = '';
this.ghost.remove();
delete this.ghost;
}
},
/**
* Shows the proxy
*/
show : function(){
if(!this.ghost){
this.ghost = this.panel.createGhost(this.panel.initialConfig.cls, undefined, Ext.getBody());
this.ghost.setXY(this.panel.el.getXY());
if(this.insertProxy){
this.proxy = this.panel.el.insertSibling({cls:'x-panel-dd-spacer'});
this.proxy.setSize(this.panel.getSize());
}
this.panel.el.dom.style.display = 'none';
}
},
// private
repair : function(xy, callback, scope){
this.hide();
if(typeof callback == "function"){
callback.call(scope || this);
}
},
/**
* Moves the proxy to a different position in the DOM. This is typically called while dragging the Panel
* to keep the proxy sync'd to the Panel's location.
* @param {HTMLElement} parentNode The proxy's parent DOM node
* @param {HTMLElement} before (optional) The sibling node before which the proxy should be inserted (defaults
* to the parent's last child if not specified)
*/
moveProxy : function(parentNode, before){
if(this.proxy){
parentNode.insertBefore(this.proxy.dom, before);
}
}
});
// private - DD implementation for Panels
Ext.Panel.DD = Ext.extend(Ext.dd.DragSource, {
constructor : function(panel, cfg){
this.panel = panel;
this.dragData = {panel: panel};
this.proxy = new Ext.dd.PanelProxy(panel, cfg);
Ext.Panel.DD.superclass.constructor.call(this, panel.el, cfg);
var h = panel.header,
el = panel.body;
if(h){
this.setHandleElId(h.id);
el = panel.header;
}
el.setStyle('cursor', 'move');
this.scroll = false;
},
showFrame: Ext.emptyFn,
startDrag: Ext.emptyFn,
b4StartDrag: function(x, y) {
this.proxy.show();
},
b4MouseDown: function(e) {
var x = e.getPageX(),
y = e.getPageY();
this.autoOffset(x, y);
},
onInitDrag : function(x, y){
this.onStartDrag(x, y);
return true;
},
createFrame : Ext.emptyFn,
getDragEl : function(e){
return this.proxy.ghost.dom;
},
endDrag : function(e){
this.proxy.hide();
this.panel.saveState();
},
autoOffset : function(x, y) {
x -= this.startPageX;
y -= this.startPageY;
this.setDelta(x, y);
}
});