Roo/form/ComboBoxArray.js
[roojs1] / Roo / DomHelper.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 // nasty IE9 hack - what a pile of crap that is..
14
15  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
16     Range.prototype.createContextualFragment = function (html) {
17         var doc = window.document;
18         var container = doc.createElement("div");
19         container.innerHTML = html;
20         var frag = doc.createDocumentFragment(), n;
21         while ((n = container.firstChild)) {
22             frag.appendChild(n);
23         }
24         return frag;
25     };
26 }
27
28 /**
29  * @class Roo.DomHelper
30  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
31  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
32  * @singleton
33  */
34 Roo.DomHelper = function(){
35     var tempTableEl = null;
36     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
37     var tableRe = /^table|tbody|tr|td$/i;
38     var xmlns = {};
39     // build as innerHTML where available
40     /** @ignore */
41     var createHtml = function(o){
42         if(typeof o == 'string'){
43             return o;
44         }
45         var b = "";
46         if(!o.tag){
47             o.tag = "div";
48         }
49         b += "<" + o.tag;
50         for(var attr in o){
51             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
52             if(attr == "style"){
53                 var s = o["style"];
54                 if(typeof s == "function"){
55                     s = s.call();
56                 }
57                 if(typeof s == "string"){
58                     b += ' style="' + s + '"';
59                 }else if(typeof s == "object"){
60                     b += ' style="';
61                     for(var key in s){
62                         if(typeof s[key] != "function"){
63                             b += key + ":" + s[key] + ";";
64                         }
65                     }
66                     b += '"';
67                 }
68             }else{
69                 if(attr == "cls"){
70                     b += ' class="' + o["cls"] + '"';
71                 }else if(attr == "htmlFor"){
72                     b += ' for="' + o["htmlFor"] + '"';
73                 }else{
74                     b += " " + attr + '="' + o[attr] + '"';
75                 }
76             }
77         }
78         if(emptyTags.test(o.tag)){
79             b += "/>";
80         }else{
81             b += ">";
82             var cn = o.children || o.cn;
83             if(cn){
84                 //http://bugs.kde.org/show_bug.cgi?id=71506
85                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
86                     for(var i = 0, len = cn.length; i < len; i++) {
87                         b += createHtml(cn[i], b);
88                     }
89                 }else{
90                     b += createHtml(cn, b);
91                 }
92             }
93             if(o.html){
94                 b += o.html;
95             }
96             b += "</" + o.tag + ">";
97         }
98         return b;
99     };
100
101     // build as dom
102     /** @ignore */
103     var createDom = function(o, parentNode){
104          
105         // defininition craeted..
106         var ns = false;
107         if (o.ns && o.ns != 'html') {
108                
109             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
110                 xmlns[o.ns] = o.xmlns;
111                 ns = o.xmlns;
112             }
113             if (typeof(xmlns[o.ns]) == 'undefined') {
114                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
115             }
116             ns = xmlns[o.ns];
117         }
118         
119         
120         if (typeof(o) == 'string') {
121             return parentNode.appendChild(document.createTextNode(o));
122         }
123         o.tag = o.tag || div;
124         if (o.ns && Roo.isIE) {
125             ns = false;
126             o.tag = o.ns + ':' + o.tag;
127             
128         }
129         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
130         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
131         for(var attr in o){
132             
133             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
134                     attr == "style" || typeof o[attr] == "function") continue;
135                     
136             if(attr=="cls" && Roo.isIE){
137                 el.className = o["cls"];
138             }else{
139                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
140                 else el[attr] = o[attr];
141             }
142         }
143         Roo.DomHelper.applyStyles(el, o.style);
144         var cn = o.children || o.cn;
145         if(cn){
146             //http://bugs.kde.org/show_bug.cgi?id=71506
147              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
148                 for(var i = 0, len = cn.length; i < len; i++) {
149                     createDom(cn[i], el);
150                 }
151             }else{
152                 createDom(cn, el);
153             }
154         }
155         if(o.html){
156             el.innerHTML = o.html;
157         }
158         if(parentNode){
159            parentNode.appendChild(el);
160         }
161         return el;
162     };
163
164     var ieTable = function(depth, s, h, e){
165         tempTableEl.innerHTML = [s, h, e].join('');
166         var i = -1, el = tempTableEl;
167         while(++i < depth){
168             el = el.firstChild;
169         }
170         return el;
171     };
172
173     // kill repeat to save bytes
174     var ts = '<table>',
175         te = '</table>',
176         tbs = ts+'<tbody>',
177         tbe = '</tbody>'+te,
178         trs = tbs + '<tr>',
179         tre = '</tr>'+tbe;
180
181     /**
182      * @ignore
183      * Nasty code for IE's broken table implementation
184      */
185     var insertIntoTable = function(tag, where, el, html){
186         if(!tempTableEl){
187             tempTableEl = document.createElement('div');
188         }
189         var node;
190         var before = null;
191         if(tag == 'td'){
192             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
193                 return;
194             }
195             if(where == 'beforebegin'){
196                 before = el;
197                 el = el.parentNode;
198             } else{
199                 before = el.nextSibling;
200                 el = el.parentNode;
201             }
202             node = ieTable(4, trs, html, tre);
203         }
204         else if(tag == 'tr'){
205             if(where == 'beforebegin'){
206                 before = el;
207                 el = el.parentNode;
208                 node = ieTable(3, tbs, html, tbe);
209             } else if(where == 'afterend'){
210                 before = el.nextSibling;
211                 el = el.parentNode;
212                 node = ieTable(3, tbs, html, tbe);
213             } else{ // INTO a TR
214                 if(where == 'afterbegin'){
215                     before = el.firstChild;
216                 }
217                 node = ieTable(4, trs, html, tre);
218             }
219         } else if(tag == 'tbody'){
220             if(where == 'beforebegin'){
221                 before = el;
222                 el = el.parentNode;
223                 node = ieTable(2, ts, html, te);
224             } else if(where == 'afterend'){
225                 before = el.nextSibling;
226                 el = el.parentNode;
227                 node = ieTable(2, ts, html, te);
228             } else{
229                 if(where == 'afterbegin'){
230                     before = el.firstChild;
231                 }
232                 node = ieTable(3, tbs, html, tbe);
233             }
234         } else{ // TABLE
235             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
236                 return;
237             }
238             if(where == 'afterbegin'){
239                 before = el.firstChild;
240             }
241             node = ieTable(2, ts, html, te);
242         }
243         el.insertBefore(node, before);
244         return node;
245     };
246
247     return {
248     /** True to force the use of DOM instead of html fragments @type Boolean */
249     useDom : false,
250
251     /**
252      * Returns the markup for the passed Element(s) config
253      * @param {Object} o The Dom object spec (and children)
254      * @return {String}
255      */
256     markup : function(o){
257         return createHtml(o);
258     },
259
260     /**
261      * Applies a style specification to an element
262      * @param {String/HTMLElement} el The element to apply styles to
263      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
264      * a function which returns such a specification.
265      */
266     applyStyles : function(el, styles){
267         if(styles){
268            el = Roo.fly(el);
269            if(typeof styles == "string"){
270                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
271                var matches;
272                while ((matches = re.exec(styles)) != null){
273                    el.setStyle(matches[1], matches[2]);
274                }
275            }else if (typeof styles == "object"){
276                for (var style in styles){
277                   el.setStyle(style, styles[style]);
278                }
279            }else if (typeof styles == "function"){
280                 Roo.DomHelper.applyStyles(el, styles.call());
281            }
282         }
283     },
284
285     /**
286      * Inserts an HTML fragment into the Dom
287      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
288      * @param {HTMLElement} el The context element
289      * @param {String} html The HTML fragmenet
290      * @return {HTMLElement} The new node
291      */
292     insertHtml : function(where, el, html){
293         where = where.toLowerCase();
294         if(el.insertAdjacentHTML){
295             if(tableRe.test(el.tagName)){
296                 var rs;
297                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
298                     return rs;
299                 }
300             }
301             switch(where){
302                 case "beforebegin":
303                     el.insertAdjacentHTML('BeforeBegin', html);
304                     return el.previousSibling;
305                 case "afterbegin":
306                     el.insertAdjacentHTML('AfterBegin', html);
307                     return el.firstChild;
308                 case "beforeend":
309                     el.insertAdjacentHTML('BeforeEnd', html);
310                     return el.lastChild;
311                 case "afterend":
312                     el.insertAdjacentHTML('AfterEnd', html);
313                     return el.nextSibling;
314             }
315             throw 'Illegal insertion point -> "' + where + '"';
316         }
317         var range = el.ownerDocument.createRange();
318         var frag;
319         switch(where){
320              case "beforebegin":
321                 range.setStartBefore(el);
322                 frag = range.createContextualFragment(html);
323                 el.parentNode.insertBefore(frag, el);
324                 return el.previousSibling;
325              case "afterbegin":
326                 if(el.firstChild){
327                     range.setStartBefore(el.firstChild);
328                     frag = range.createContextualFragment(html);
329                     el.insertBefore(frag, el.firstChild);
330                     return el.firstChild;
331                 }else{
332                     el.innerHTML = html;
333                     return el.firstChild;
334                 }
335             case "beforeend":
336                 if(el.lastChild){
337                     range.setStartAfter(el.lastChild);
338                     frag = range.createContextualFragment(html);
339                     el.appendChild(frag);
340                     return el.lastChild;
341                 }else{
342                     el.innerHTML = html;
343                     return el.lastChild;
344                 }
345             case "afterend":
346                 range.setStartAfter(el);
347                 frag = range.createContextualFragment(html);
348                 el.parentNode.insertBefore(frag, el.nextSibling);
349                 return el.nextSibling;
350             }
351             throw 'Illegal insertion point -> "' + where + '"';
352     },
353
354     /**
355      * Creates new Dom element(s) and inserts them before el
356      * @param {String/HTMLElement/Element} el The context element
357      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
358      * @param {Boolean} returnElement (optional) true to return a Roo.Element
359      * @return {HTMLElement/Roo.Element} The new node
360      */
361     insertBefore : function(el, o, returnElement){
362         return this.doInsert(el, o, returnElement, "beforeBegin");
363     },
364
365     /**
366      * Creates new Dom element(s) and inserts them after el
367      * @param {String/HTMLElement/Element} el The context element
368      * @param {Object} o The Dom object spec (and children)
369      * @param {Boolean} returnElement (optional) true to return a Roo.Element
370      * @return {HTMLElement/Roo.Element} The new node
371      */
372     insertAfter : function(el, o, returnElement){
373         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
374     },
375
376     /**
377      * Creates new Dom element(s) and inserts them as the first child of el
378      * @param {String/HTMLElement/Element} el The context element
379      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
380      * @param {Boolean} returnElement (optional) true to return a Roo.Element
381      * @return {HTMLElement/Roo.Element} The new node
382      */
383     insertFirst : function(el, o, returnElement){
384         return this.doInsert(el, o, returnElement, "afterBegin");
385     },
386
387     // private
388     doInsert : function(el, o, returnElement, pos, sibling){
389         el = Roo.getDom(el);
390         var newNode;
391         if(this.useDom || o.ns){
392             newNode = createDom(o, null);
393             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
394         }else{
395             var html = createHtml(o);
396             newNode = this.insertHtml(pos, el, html);
397         }
398         return returnElement ? Roo.get(newNode, true) : newNode;
399     },
400
401     /**
402      * Creates new Dom element(s) and appends them to el
403      * @param {String/HTMLElement/Element} el The context element
404      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
405      * @param {Boolean} returnElement (optional) true to return a Roo.Element
406      * @return {HTMLElement/Roo.Element} The new node
407      */
408     append : function(el, o, returnElement){
409         el = Roo.getDom(el);
410         var newNode;
411         if(this.useDom || o.ns){
412             newNode = createDom(o, null);
413             el.appendChild(newNode);
414         }else{
415             var html = createHtml(o);
416             newNode = this.insertHtml("beforeEnd", el, html);
417         }
418         return returnElement ? Roo.get(newNode, true) : newNode;
419     },
420
421     /**
422      * Creates new Dom element(s) and overwrites the contents of el with them
423      * @param {String/HTMLElement/Element} el The context element
424      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
425      * @param {Boolean} returnElement (optional) true to return a Roo.Element
426      * @return {HTMLElement/Roo.Element} The new node
427      */
428     overwrite : function(el, o, returnElement){
429         el = Roo.getDom(el);
430         if (o.ns) {
431           
432             while (el.childNodes.length) {
433                 el.removeChild(el.firstChild);
434             }
435             createDom(o, el);
436         } else {
437             el.innerHTML = createHtml(o);   
438         }
439         
440         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
441     },
442
443     /**
444      * Creates a new Roo.DomHelper.Template from the Dom object spec
445      * @param {Object} o The Dom object spec (and children)
446      * @return {Roo.DomHelper.Template} The new template
447      */
448     createTemplate : function(o){
449         var html = createHtml(o);
450         return new Roo.Template(html);
451     }
452     };
453 }();