MDL-35616 import YUI 3.7.2
[moodle.git] / lib / yuilib / 3.7.2 / build / dom-create / dom-create.js
blob2a88c29d5e9789656573e2c8ff3ddaf086f98cad
1 /*
2 YUI 3.7.2 (build 5639)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('dom-create', function(Y) {
9 var re_tag = /<([a-z]+)/i,
11     Y_DOM = Y.DOM,
13     addFeature = Y.Features.add,
14     testFeature = Y.Features.test,
16     creators = {},
18     createFromDIV = function(html, tag) {
19         var div = Y.config.doc.createElement('div'),
20             ret = true;
22         div.innerHTML = html;
23         if (!div.firstChild || div.firstChild.tagName !== tag.toUpperCase()) {
24             ret = false;
25         }
27         return ret;
28     },
30     re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
32     TABLE_OPEN = '<table>',
33     TABLE_CLOSE = '</table>';
35 Y.mix(Y.DOM, {
36     _fragClones: {},
38     _create: function(html, doc, tag) {
39         tag = tag || 'div';
41         var frag = Y_DOM._fragClones[tag];
42         if (frag) {
43             frag = frag.cloneNode(false);
44         } else {
45             frag = Y_DOM._fragClones[tag] = doc.createElement(tag);
46         }
47         frag.innerHTML = html;
48         return frag;
49     },
51     /**
52      * Creates a new dom node using the provided markup string. 
53      * @method create
54      * @param {String} html The markup used to create the element
55      * @param {HTMLDocument} doc An optional document context 
56      * @return {HTMLElement|DocumentFragment} returns a single HTMLElement 
57      * when creating one node, and a documentFragment when creating
58      * multiple nodes.
59      */
60     create: function(html, doc) {
61         if (typeof html === 'string') {
62             html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
64         }
66         doc = doc || Y.config.doc;
67         var m = re_tag.exec(html),
68             create = Y_DOM._create,
69             custom = creators,
70             ret = null,
71             creator,
72             tag, nodes;
74         if (html != undefined) { // not undefined or null
75             if (m && m[1]) {
76                 creator = custom[m[1].toLowerCase()];
77                 if (typeof creator === 'function') {
78                     create = creator; 
79                 } else {
80                     tag = creator;
81                 }
82             }
84             nodes = create(html, doc, tag).childNodes;
86             if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
87                 ret = nodes[0].parentNode.removeChild(nodes[0]);
88             } else if (nodes[0] && nodes[0].className === 'yui3-big-dummy') { // using dummy node to preserve some attributes (e.g. OPTION not selected)
89                 if (nodes.length === 2) {
90                     ret = nodes[0].nextSibling;
91                 } else {
92                     nodes[0].parentNode.removeChild(nodes[0]); 
93                      ret = Y_DOM._nl2frag(nodes, doc);
94                 }
95             } else { // return multiple nodes as a fragment
96                  ret = Y_DOM._nl2frag(nodes, doc);
97             }
98         }
100         return ret;
101     },
103     _nl2frag: function(nodes, doc) {
104         var ret = null,
105             i, len;
107         if (nodes && (nodes.push || nodes.item) && nodes[0]) {
108             doc = doc || nodes[0].ownerDocument; 
109             ret = doc.createDocumentFragment();
111             if (nodes.item) { // convert live list to static array
112                 nodes = Y.Array(nodes, 0, true);
113             }
115             for (i = 0, len = nodes.length; i < len; i++) {
116                 ret.appendChild(nodes[i]); 
117             }
118         } // else inline with log for minification
119         return ret;
120     },
122     /**
123      * Inserts content in a node at the given location 
124      * @method addHTML
125      * @param {HTMLElement} node The node to insert into
126      * @param {HTMLElement | Array | HTMLCollection} content The content to be inserted 
127      * @param {HTMLElement} where Where to insert the content
128      * If no "where" is given, content is appended to the node
129      * Possible values for "where"
130      * <dl>
131      * <dt>HTMLElement</dt>
132      * <dd>The element to insert before</dd>
133      * <dt>"replace"</dt>
134      * <dd>Replaces the existing HTML</dd>
135      * <dt>"before"</dt>
136      * <dd>Inserts before the existing HTML</dd>
137      * <dt>"before"</dt>
138      * <dd>Inserts content before the node</dd>
139      * <dt>"after"</dt>
140      * <dd>Inserts content after the node</dd>
141      * </dl>
142      */
143     addHTML: function(node, content, where) {
144         var nodeParent = node.parentNode,
145             i = 0,
146             item,
147             ret = content,
148             newNode;
149             
151         if (content != undefined) { // not null or undefined (maybe 0)
152             if (content.nodeType) { // DOM node, just add it
153                 newNode = content;
154             } else if (typeof content == 'string' || typeof content == 'number') {
155                 ret = newNode = Y_DOM.create(content);
156             } else if (content[0] && content[0].nodeType) { // array or collection 
157                 newNode = Y.config.doc.createDocumentFragment();
158                 while ((item = content[i++])) {
159                     newNode.appendChild(item); // append to fragment for insertion
160                 }
161             }
162         }
164         if (where) {
165             if (where.nodeType) { // insert regardless of relationship to node
166                 where.parentNode.insertBefore(newNode, where);
167             } else {
168                 switch (where) {
169                     case 'replace':
170                         while (node.firstChild) {
171                             node.removeChild(node.firstChild);
172                         }
173                         if (newNode) { // allow empty content to clear node
174                             node.appendChild(newNode);
175                         }
176                         break;
177                     case 'before':
178                         nodeParent.insertBefore(newNode, node);
179                         break;
180                     case 'after':
181                         if (node.nextSibling) { // IE errors if refNode is null
182                             nodeParent.insertBefore(newNode, node.nextSibling);
183                         } else {
184                             nodeParent.appendChild(newNode);
185                         }
186                         break;
187                     default:
188                         node.appendChild(newNode);
189                 }
190             }
191         } else if (newNode) {
192             node.appendChild(newNode);
193         }
195         return ret;
196     }
199 addFeature('innerhtml', 'table', {
200     test: function() {
201         var node = Y.config.doc.createElement('table');
202         try {
203             node.innerHTML = '<tbody></tbody>';
204         } catch(e) {
205             return false;
206         }
207         return (node.firstChild && node.firstChild.nodeName === 'TBODY');
208     }
211 addFeature('innerhtml-div', 'tr', {
212     test: function() {
213         return createFromDIV('<tr></tr>', 'tr');
214     }
217 addFeature('innerhtml-div', 'script', {
218     test: function() {
219         return createFromDIV('<script></script>', 'script');
220     }
223 if (!testFeature('innerhtml', 'table')) {
224     // TODO: thead/tfoot with nested tbody
225         // IE adds TBODY when creating TABLE elements (which may share this impl)
226     creators.tbody = function(html, doc) {
227         var frag = Y_DOM.create(TABLE_OPEN + html + TABLE_CLOSE, doc),
228             tb = frag.children.tags('tbody')[0];
230         if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
231             tb.parentNode.removeChild(tb); // strip extraneous tbody
232         }
233         return frag;
234     };
237 if (!testFeature('innerhtml-div', 'script')) {
238     creators.script = function(html, doc) {
239         var frag = doc.createElement('div');
241         frag.innerHTML = '-' + html;
242         frag.removeChild(frag.firstChild);
243         return frag;
244     }
246     creators.link = creators.style = creators.script;
249 if (!testFeature('innerhtml-div', 'tr')) {
250     Y.mix(creators, {
251         option: function(html, doc) {
252             return Y_DOM.create('<select><option class="yui3-big-dummy" selected></option>' + html + '</select>', doc);
253         },
255         tr: function(html, doc) {
256             return Y_DOM.create('<tbody>' + html + '</tbody>', doc);
257         },
259         td: function(html, doc) {
260             return Y_DOM.create('<tr>' + html + '</tr>', doc);
261         }, 
263         col: function(html, doc) {
264             return Y_DOM.create('<colgroup>' + html + '</colgroup>', doc);
265         }, 
267         tbody: 'table'
268     });
270     Y.mix(creators, {
271         legend: 'fieldset',
272         th: creators.td,
273         thead: creators.tbody,
274         tfoot: creators.tbody,
275         caption: creators.tbody,
276         colgroup: creators.tbody,
277         optgroup: creators.option
278     });
281 Y_DOM.creators = creators;
284 }, '3.7.2' ,{requires:['dom-core']});