Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / core / src / dom / Element-more.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.core.Element
17  */
18
19 Ext.core.Element.addMethods({
20
21     /**
22      * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
23      * the mouse was not moved back into the Element within the delay. If the mouse <i>was</i> moved
24      * back in, the function is not called.
25      * @param {Number} delay The delay <b>in milliseconds</b> to wait for possible mouse re-entry before calling the handler function.
26      * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
27      * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to this Element.
28      * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:</pre><code>
29 // Hide the menu if the mouse moves out for 250ms or more
30 this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
31
32 ...
33 // Remove mouseleave monitor on menu destroy
34 this.menuEl.un(this.mouseLeaveMonitor);
35 </code></pre>
36      */
37     monitorMouseLeave: function(delay, handler, scope) {
38         var me = this,
39             timer,
40             listeners = {
41                 mouseleave: function(e) {
42                     timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
43                 },
44                 mouseenter: function() {
45                     clearTimeout(timer);
46                 },
47                 freezeEvent: true
48             };
49
50         me.on(listeners);
51         return listeners;
52     },
53
54     /**
55      * Stops the specified event(s) from bubbling and optionally prevents the default action
56      * @param {String/Array} eventName an event / array of events to stop from bubbling
57      * @param {Boolean} preventDefault (optional) true to prevent the default action too
58      * @return {Ext.core.Element} this
59      */
60     swallowEvent : function(eventName, preventDefault) {
61         var me = this;
62         function fn(e) {
63             e.stopPropagation();
64             if (preventDefault) {
65                 e.preventDefault();
66             }
67         }
68         
69         if (Ext.isArray(eventName)) {
70             Ext.each(eventName, function(e) {
71                  me.on(e, fn);
72             });
73             return me;
74         }
75         me.on(eventName, fn);
76         return me;
77     },
78
79     /**
80      * Create an event handler on this element such that when the event fires and is handled by this element,
81      * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
82      * @param {String} eventName The type of event to relay
83      * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
84      * for firing the relayed event
85      */
86     relayEvent : function(eventName, observable) {
87         this.on(eventName, function(e) {
88             observable.fireEvent(eventName, e);
89         });
90     },
91
92     /**
93      * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
94      * @param {Boolean} forceReclean (optional) By default the element
95      * keeps track if it has been cleaned already so
96      * you can call this over and over. However, if you update the element and
97      * need to force a reclean, you can pass true.
98      */
99     clean : function(forceReclean) {
100         var me  = this,
101             dom = me.dom,
102             n   = dom.firstChild,
103             nx,
104             ni  = -1;
105
106         if (Ext.core.Element.data(dom, 'isCleaned') && forceReclean !== true) {
107             return me;
108         }
109
110         while (n) {
111             nx = n.nextSibling;
112             if (n.nodeType == 3) {
113                 // Remove empty/whitespace text nodes
114                 if (!(/\S/.test(n.nodeValue))) {
115                     dom.removeChild(n);
116                 // Combine adjacent text nodes
117                 } else if (nx && nx.nodeType == 3) {
118                     n.appendData(Ext.String.trim(nx.data));
119                     dom.removeChild(nx);
120                     nx = n.nextSibling;
121                     n.nodeIndex = ++ni;
122                 }
123             } else {
124                 // Recursively clean
125                 Ext.fly(n).clean();
126                 n.nodeIndex = ++ni;
127             }
128             n = nx;
129         }
130
131         Ext.core.Element.data(dom, 'isCleaned', true);
132         return me;
133     },
134
135     /**
136      * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object
137      * parameter as {@link Ext.ElementLoader#load}
138      * @return {Ext.core.Element} this
139      */
140     load : function(options) {
141         this.getLoader().load(options);
142         return this;
143     },
144
145     /**
146     * Gets this element's {@link Ext.ElementLoader ElementLoader}
147     * @return {Ext.ElementLoader} The loader
148     */
149     getLoader : function() {
150         var dom = this.dom,
151             data = Ext.core.Element.data,
152             loader = data(dom, 'loader');
153             
154         if (!loader) {
155             loader = Ext.create('Ext.ElementLoader', {
156                 target: this
157             });
158             data(dom, 'loader', loader);
159         }
160         return loader;
161     },
162
163     /**
164     * Update the innerHTML of this element, optionally searching for and processing scripts
165     * @param {String} html The new HTML
166     * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
167     * @param {Function} callback (optional) For async script loading you can be notified when the update completes
168     * @return {Ext.core.Element} this
169      */
170     update : function(html, loadScripts, callback) {
171         var me = this,
172             id,
173             dom,
174             interval;
175             
176         if (!me.dom) {
177             return me;
178         }
179         html = html || '';
180         dom = me.dom;
181
182         if (loadScripts !== true) {
183             dom.innerHTML = html;
184             Ext.callback(callback, me);
185             return me;
186         }
187
188         id  = Ext.id();
189         html += '<span id="' + id + '"></span>';
190
191         interval = setInterval(function(){
192             if (!document.getElementById(id)) {
193                 return false;    
194             }
195             clearInterval(interval);
196             var DOC    = document,
197                 hd     = DOC.getElementsByTagName("head")[0],
198                 re     = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
199                 srcRe  = /\ssrc=([\'\"])(.*?)\1/i,
200                 typeRe = /\stype=([\'\"])(.*?)\1/i,
201                 match,
202                 attrs,
203                 srcMatch,
204                 typeMatch,
205                 el,
206                 s;
207
208             while ((match = re.exec(html))) {
209                 attrs = match[1];
210                 srcMatch = attrs ? attrs.match(srcRe) : false;
211                 if (srcMatch && srcMatch[2]) {
212                    s = DOC.createElement("script");
213                    s.src = srcMatch[2];
214                    typeMatch = attrs.match(typeRe);
215                    if (typeMatch && typeMatch[2]) {
216                        s.type = typeMatch[2];
217                    }
218                    hd.appendChild(s);
219                 } else if (match[2] && match[2].length > 0) {
220                     if (window.execScript) {
221                        window.execScript(match[2]);
222                     } else {
223                        window.eval(match[2]);
224                     }
225                 }
226             }
227             
228             el = DOC.getElementById(id);
229             if (el) {
230                 Ext.removeNode(el);
231             }
232             Ext.callback(callback, me);
233         }, 20);
234         dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
235         return me;
236     },
237
238     // inherit docs, overridden so we can add removeAnchor
239     removeAllListeners : function() {
240         this.removeAnchor();
241         Ext.EventManager.removeAll(this.dom);
242         return this;
243     },
244
245     /**
246      * Creates a proxy element of this element
247      * @param {String/Object} config The class name of the proxy element or a DomHelper config object
248      * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
249      * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
250      * @return {Ext.core.Element} The new proxy element
251      */
252     createProxy : function(config, renderTo, matchBox) {
253         config = (typeof config == 'object') ? config : {tag : "div", cls: config};
254
255         var me = this,
256             proxy = renderTo ? Ext.core.DomHelper.append(renderTo, config, true) :
257                                Ext.core.DomHelper.insertBefore(me.dom, config, true);
258
259         proxy.setVisibilityMode(Ext.core.Element.DISPLAY);
260         proxy.hide();
261         if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
262            proxy.setBox(me.getBox());
263         }
264         return proxy;
265     }
266 });
267 Ext.core.Element.prototype.clearListeners = Ext.core.Element.prototype.removeAllListeners;
268