Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / LoadMask.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 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.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.LoadMask
17  * <p>A modal, floating Component which may be shown above a specified {@link Ext.core.Element Element}, or a specified
18  * {@link Ext.Component Component} while loading data. When shown, the configured owning Element or Component will
19  * be covered with a modality mask, and the LoadMask's {@link #msg} will be displayed centered, accompanied by a spinner image.</p>
20  * <p>If the {@link #store} config option is specified, the masking will be automatically shown and then hidden synchronized with
21  * the Store's loading process.</p>
22  * <p>Because this is a floating Component, its z-index will be managed by the global {@link Ext.WindowManager ZIndexManager}
23  * object, and upon show, it will place itsef at the top of the hierarchy.</p>
24  * <p>Example usage:</p>
25  * <pre><code>
26 // Basic mask:
27 var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
28 myMask.show();
29 </code></pre>
30
31  */
32
33 Ext.define('Ext.LoadMask', {
34
35     extend: 'Ext.Component',
36
37     alias: 'widget.loadmask',
38
39     /* Begin Definitions */
40
41     mixins: {
42         floating: 'Ext.util.Floating'
43     },
44
45     uses: ['Ext.data.StoreManager'],
46
47     /* End Definitions */
48
49     /**
50      * @cfg {Ext.data.Store} store
51      * Optional Store to which the mask is bound. The mask is displayed when a load request is issued, and
52      * hidden on either load success, or load fail.
53      */
54
55     /**
56      * @cfg {String} msg
57      * The text to display in a centered loading message box.
58      */
59     msg : 'Loading...',
60     /**
61      * @cfg {String} [msgCls="x-mask-loading"]
62      * The CSS class to apply to the loading message element.
63      */
64     msgCls : Ext.baseCSSPrefix + 'mask-loading',
65     
66     /**
67      * @cfg {Boolean} useMsg
68      * Whether or not to use a loading message class or simply mask the bound element.
69      */
70     useMsg: true,
71
72     /**
73      * Read-only. True if the mask is currently disabled so that it will not be displayed
74      * @type Boolean
75      */
76     disabled: false,
77
78     baseCls: Ext.baseCSSPrefix + 'mask-msg',
79
80     renderTpl: '<div style="position:relative" class="{msgCls}"></div>',
81
82     // Private. The whole point is that there's a mask.
83     modal: true,
84
85     // Private. Obviously, it's floating.
86     floating: {
87         shadow: 'frame'
88     },
89
90     // Private. Masks are not focusable
91     focusOnToFront: false,
92
93     /**
94      * Creates new LoadMask.
95      * @param {String/HTMLElement/Ext.Element} el The element, element ID, or DOM node you wish to mask.
96      * <p>Also, may be a {@link Ext.Component Component} who's element you wish to mask. If a Component is specified, then
97      * the mask will be automatically sized upon Component resize, the message box will be kept centered,
98      * and the mask only be visible when the Component is.</p>
99      * @param {Object} [config] The config object
100      */
101     constructor : function(el, config) {
102         var me = this;
103
104         // If a Component passed, bind to it.
105         if (el.isComponent) {
106             me.ownerCt = el;
107             me.bindComponent(el);
108         }
109         // Create a dumy Component encapsulating the specified Element
110         else {
111             me.ownerCt = new Ext.Component({
112                 el: Ext.get(el),
113                 rendered: true,
114                 componentLayoutCounter: 1
115             });
116             me.container = el;
117         }
118         me.callParent([config]);
119
120         if (me.store) {
121             me.bindStore(me.store, true);
122         }
123         me.renderData = {
124             msgCls: me.msgCls
125         };
126         me.renderSelectors = {
127             msgEl: 'div'
128         };
129     },
130
131     bindComponent: function(comp) {
132         this.mon(comp, {
133             resize: this.onComponentResize,
134             scope: this
135         });
136     },
137
138     afterRender: function() {
139         this.callParent(arguments);
140         this.container = this.floatParent.getContentTarget();
141     },
142
143     /**
144      * @private
145      * Called when this LoadMask's Component is resized. The toFront method rebases and resizes the modal mask.
146      */
147     onComponentResize: function() {
148         var me = this;
149         if (me.rendered && me.isVisible()) {
150             me.toFront();
151             me.center();
152         }
153     },
154
155     /**
156      * Changes the data store bound to this LoadMask.
157      * @param {Ext.data.Store} store The store to bind to this LoadMask
158      */
159     bindStore : function(store, initial) {
160         var me = this;
161
162         if (!initial && me.store) {
163             me.mun(me.store, {
164                 scope: me,
165                 beforeload: me.onBeforeLoad,
166                 load: me.onLoad,
167                 exception: me.onLoad
168             });
169             if (!store) {
170                 me.store = null;
171             }
172         }
173         if (store) {
174             store = Ext.data.StoreManager.lookup(store);
175             me.mon(store, {
176                 scope: me,
177                 beforeload: me.onBeforeLoad,
178                 load: me.onLoad,
179                 exception: me.onLoad
180             });
181
182         }
183         me.store = store;
184         if (store && store.isLoading()) {
185             me.onBeforeLoad();
186         }
187     },
188
189     onDisable : function() {
190         this.callParent(arguments);
191         if (this.loading) {
192             this.onLoad();
193         }
194     },
195
196     // private
197     onBeforeLoad : function() {
198         var me = this,
199             owner = me.ownerCt || me.floatParent,
200             origin;
201         if (!this.disabled) {
202             // If the owning Component has not been layed out, defer so that the ZIndexManager
203             // gets to read its layed out size when sizing the modal mask
204             if (owner.componentLayoutCounter) {
205                 Ext.Component.prototype.show.call(me);
206             } else {
207                 // The code below is a 'run-once' interceptor.
208                 origin = owner.afterComponentLayout;
209                 owner.afterComponentLayout = function() {
210                     owner.afterComponentLayout = origin;
211                     origin.apply(owner, arguments);
212                     if(me.loading) {
213                         Ext.Component.prototype.show.call(me);
214                     }
215                 };
216             }
217         }
218     },
219
220     onHide: function(){
221         var me = this;
222         me.callParent(arguments);
223         me.showOnParentShow = true;
224     },
225
226     onShow: function() {
227         var me = this,
228             msgEl = me.msgEl;
229             
230         me.callParent(arguments);
231         me.loading = true;
232         if (me.useMsg) {
233             msgEl.show().update(me.msg);
234         } else {
235             msgEl.parent().hide();
236         }
237     },
238
239     afterShow: function() {
240         this.callParent(arguments);
241         this.center();
242     },
243
244     // private
245     onLoad : function() {
246         this.loading = false;
247         Ext.Component.prototype.hide.call(this);
248     }
249 });