1 /**
  2  * @fileOverview dom-insertion
  3  * @author yiminghe@gmail.com,lifesinger@gmail.com
  4  */
  5 KISSY.add('dom/insertion', function (S, UA, DOM) {
  6 
  7     var PARENT_NODE = 'parentNode',
  8         rformEls = /^(?:button|input|object|select|textarea)$/i,
  9         getNodeName = DOM.nodeName,
 10         makeArray = S.makeArray,
 11         splice = [].splice,
 12         NEXT_SIBLING = 'nextSibling';
 13 
 14     /**
 15      ie 6,7 lose checked status when append to dom
 16      var c=S.all("<input />");
 17      c.attr("type","radio");
 18      c.attr("checked",true);
 19      S.all("#t").append(c);
 20      alert(c[0].checked);
 21      */
 22     function fixChecked(ret) {
 23         for (var i = 0; i < ret.length; i++) {
 24             var el = ret[i];
 25             if (el.nodeType == DOM.DOCUMENT_FRAGMENT_NODE) {
 26                 fixChecked(el.childNodes);
 27             } else if (getNodeName(el) == "input") {
 28                 fixCheckedInternal(el);
 29             } else if (el.nodeType == DOM.ELEMENT_NODE) {
 30                 var cs = el.getElementsByTagName("input");
 31                 for (var j = 0; j < cs.length; j++) {
 32                     fixChecked(cs[j]);
 33                 }
 34             }
 35         }
 36     }
 37 
 38     function fixCheckedInternal(el) {
 39         if (el.type === "checkbox" || el.type === "radio") {
 40             // after insert , in ie6/7 checked is decided by defaultChecked !
 41             el.defaultChecked = el.checked;
 42         }
 43     }
 44 
 45     var rscriptType = /\/(java|ecma)script/i;
 46 
 47     function isJs(el) {
 48         return !el.type || rscriptType.test(el.type);
 49     }
 50 
 51     // extract script nodes and execute alone later
 52     function filterScripts(nodes, scripts) {
 53         var ret = [], i, el, nodeName;
 54         for (i = 0; nodes[i]; i++) {
 55             el = nodes[i];
 56             nodeName = getNodeName(el);
 57             if (el.nodeType == DOM.DOCUMENT_FRAGMENT_NODE) {
 58                 ret.push.apply(ret, filterScripts(makeArray(el.childNodes), scripts));
 59             } else if (nodeName === "script" && isJs(el)) {
 60                 // remove script to make sure ie9 does not invoke when append
 61                 if (el.parentNode) {
 62                     el.parentNode.removeChild(el)
 63                 }
 64                 if (scripts) {
 65                     scripts.push(el);
 66                 }
 67             } else {
 68                 if (el.nodeType == DOM.ELEMENT_NODE &&
 69                     // ie checkbox getElementsByTagName 后造成 checked 丢失
 70                     !rformEls.test(nodeName)) {
 71                     var tmp = [],
 72                         s,
 73                         j,
 74                         ss = el.getElementsByTagName("script");
 75                     for (j = 0; j < ss.length; j++) {
 76                         s = ss[j];
 77                         if (isJs(s)) {
 78                             tmp.push(s);
 79                         }
 80                     }
 81                     splice.apply(nodes, [i + 1, 0].concat(tmp));
 82                 }
 83                 ret.push(el);
 84             }
 85         }
 86         return ret;
 87     }
 88 
 89     // execute script
 90     function evalScript(el) {
 91         if (el.src) {
 92             S.getScript(el.src);
 93         } else {
 94             var code = S.trim(el.text || el.textContent || el.innerHTML || "");
 95             if (code) {
 96                 S.globalEval(code);
 97             }
 98         }
 99     }
100 
101     // fragment is easier than nodelist
102     function insertion(newNodes, refNodes, fn, scripts) {
103         newNodes = DOM.query(newNodes);
104 
105         if (scripts) {
106             scripts = [];
107         }
108 
109         // filter script nodes ,process script separately if needed
110         newNodes = filterScripts(newNodes, scripts);
111 
112         // Resets defaultChecked for any radios and checkboxes
113         // about to be appended to the DOM in IE 6/7
114         if (UA['ie'] < 8) {
115             fixChecked(newNodes);
116         }
117         refNodes = DOM.query(refNodes);
118         var newNodesLength = newNodes.length,
119             refNodesLength = refNodes.length;
120         if ((!newNodesLength && (!scripts || !scripts.length)) || !refNodesLength) {
121             return;
122         }
123         // fragment 插入速度快点
124         // 而且能够一个操作达到批量插入
125         // refer: http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-B63ED1A3
126         var newNode = DOM.nodeListToFragment(newNodes),
127             clonedNode;
128         //fragment 一旦插入里面就空了,先复制下
129         if (refNodesLength > 1) {
130             clonedNode = DOM.clone(newNode, true);
131             refNodes = S.makeArray(refNodes)
132         }
133         for (var i = 0; i < refNodesLength; i++) {
134             var refNode = refNodes[i];
135             if (newNode) {
136                 //refNodes 超过一个,clone
137                 var node = i > 0 ? DOM.clone(clonedNode, true) : newNode;
138                 fn(node, refNode);
139             }
140             if (scripts && scripts.length) {
141                 S.each(scripts, evalScript);
142             }
143         }
144     }
145 
146     // loadScripts default to false to prevent xss
147     S.mix(DOM,
148         /**
149          * @lends DOM
150          */
151         {
152 
153             /**
154              * Insert every element in the set of newNodes before every element in the set of refNodes.
155              * @param {HTMLElement|HTMLElement[]} newNodes Nodes to be inserted
156              * @param {HTMLElement|HTMLElement[]|String} refNodes Nodes to be referred
157              */
158             insertBefore:function (newNodes, refNodes, loadScripts) {
159                 insertion(newNodes, refNodes, function (newNode, refNode) {
160                     if (refNode[PARENT_NODE]) {
161                         refNode[PARENT_NODE].insertBefore(newNode, refNode);
162                     }
163                 }, loadScripts);
164             },
165 
166             /**
167              * Insert every element in the set of newNodes after every element in the set of refNodes.
168              * @param {HTMLElement|HTMLElement[]} newNodes Nodes to be inserted
169              * @param {HTMLElement|HTMLElement[]|String} refNodes Nodes to be referred
170              */
171             insertAfter:function (newNodes, refNodes, loadScripts) {
172                 insertion(newNodes, refNodes, function (newNode, refNode) {
173                     if (refNode[PARENT_NODE]) {
174                         refNode[PARENT_NODE].insertBefore(newNode, refNode[NEXT_SIBLING]);
175                     }
176                 }, loadScripts);
177             },
178 
179             /**
180              * Insert every element in the set of newNodes to the end of every element in the set of parents.
181              * @param {HTMLElement|HTMLElement[]} newNodes Nodes to be inserted
182              * @param {HTMLElement|HTMLElement[]|String} parents Nodes to be referred as parentNode
183              */
184             appendTo:function (newNodes, parents, loadScripts) {
185                 insertion(newNodes, parents, function (newNode, parent) {
186                     parent.appendChild(newNode);
187                 }, loadScripts);
188             },
189 
190             /**
191              * Insert every element in the set of newNodes to the beginning of every element in the set of parents.
192              * @param {HTMLElement|HTMLElement[]} newNodes Nodes to be inserted
193              * @param {HTMLElement|HTMLElement[]|String} parents Nodes to be referred as parentNode
194              */
195             prependTo:function (newNodes, parents, loadScripts) {
196                 insertion(newNodes, parents, function (newNode, parent) {
197                     parent.insertBefore(newNode, parent.firstChild);
198                 }, loadScripts);
199             },
200 
201             /**
202              * Wrap a node around all elements in the set of matched elements
203              * @param {HTMLElement|HTMLElement[]|String} wrappedNodes set of matched elements
204              * @param {HTMLElement|String} wrapperNode html node or selector to get the node wrapper
205              */
206             wrapAll:function (wrappedNodes, wrapperNode) {
207                 // deep clone
208                 wrapperNode = DOM.clone(DOM.get(wrapperNode), true);
209                 wrappedNodes = DOM.query(wrappedNodes);
210                 if (wrappedNodes[0].parentNode) {
211                     DOM.insertBefore(wrapperNode, wrappedNodes[0]);
212                 }
213                 var c;
214                 while ((c = wrapperNode.firstChild) && c.nodeType == 1) {
215                     wrapperNode = c;
216                 }
217                 DOM.appendTo(wrappedNodes, wrapperNode);
218             },
219 
220             /**
221              * Wrap a node around each element in the set of matched elements
222              * @param {HTMLElement|HTMLElement[]|String} wrappedNodes set of matched elements
223              * @param {HTMLElement|String} wrapperNode html node or selector to get the node wrapper
224              */
225             wrap:function (wrappedNodes, wrapperNode) {
226                 wrappedNodes = DOM.query(wrappedNodes);
227                 wrapperNode = DOM.get(wrapperNode);
228                 S.each(wrappedNodes, function (w) {
229                     DOM.wrapAll(w, wrapperNode);
230                 });
231             },
232 
233             /**
234              * Wrap a node around the childNodes of each element in the set of matched elements.
235              * @param {HTMLElement|HTMLElement[]|String} wrappedNodes set of matched elements
236              * @param {HTMLElement|String} wrapperNode html node or selector to get the node wrapper
237              */
238             wrapInner:function (wrappedNodes, wrapperNode) {
239                 wrappedNodes = DOM.query(wrappedNodes);
240                 wrapperNode = DOM.get(wrapperNode);
241                 S.each(wrappedNodes, function (w) {
242                     var contents = w.childNodes;
243                     if (contents.length) {
244                         DOM.wrapAll(contents, wrapperNode);
245                     } else {
246                         w.appendChild(wrapperNode);
247                     }
248                 });
249             },
250 
251             /**
252              * Remove the parents of the set of matched elements from the DOM,
253              * leaving the matched elements in their place.
254              * @param {HTMLElement|HTMLElement[]|String} wrappedNodes set of matched elements
255              */
256             unwrap:function (wrappedNodes) {
257                 wrappedNodes = DOM.query(wrappedNodes);
258                 S.each(wrappedNodes, function (w) {
259                     var p = w.parentNode;
260                     DOM.replaceWith(p, p.childNodes);
261                 });
262             },
263 
264             /**
265              * Replace each element in the set of matched elements with the provided newNodes.
266              * @param {HTMLElement|HTMLElement[]|String} selector set of matched elements
267              * @param {HTMLElement|HTMLElement[]|String} newNodes new nodes to replace the matched elements
268              */
269             replaceWith:function (selector, newNodes) {
270                 var nodes = DOM.query(selector);
271                 newNodes = DOM.query(newNodes);
272                 DOM.remove(newNodes, true);
273                 DOM.insertBefore(newNodes, nodes);
274                 DOM.remove(nodes);
275             }
276         });
277     var alias = {
278         prepend:"prependTo",
279         append:"appendTo",
280         before:"insertBefore",
281         after:"insertAfter"
282     };
283     for (var a in alias) {
284         DOM[a] = DOM[alias[a]];
285     }
286     return DOM;
287 }, {
288     requires:["ua", "./create"]
289 });
290 
291 /**
292  * 2012-04-05 yiminghe@gmail.com
293  *  - 增加 replaceWith/wrap/wrapAll/wrapInner/unwrap
294  *
295  * 2011-05-25
296  *  - 承玉:参考 jquery 处理多对多的情形 :http://api.jquery.com/append/
297  *      DOM.append(".multi1",".multi2");
298  *
299  */
300