migrated knockout asset to bower
[openemr.git] / public / assets / knockout-2-2-1 / src / virtualElements.js
blob104b3131ec9e40c734b1e8d27d1dcca4dcec7771
1 (function() {
2     // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
3     // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
4     // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
5     // of that virtual hierarchy
6     //
7     // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
8     // without having to scatter special cases all over the binding and templating code.
10     // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
11     // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
12     // So, use node.text where available, and node.nodeValue elsewhere
13     var commentNodesHaveTextProperty = document.createComment("test").text === "<!--test-->";
15     var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*-->$/ : /^\s*ko(?:\s+(.+\s*\:[\s\S]*))?\s*$/;
16     var endCommentRegex =   commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
17     var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };
19     function isStartComment(node) {
20         return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
21     }
23     function isEndComment(node) {
24         return (node.nodeType == 8) && (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(endCommentRegex);
25     }
27     function getVirtualChildren(startComment, allowUnbalanced) {
28         var currentNode = startComment;
29         var depth = 1;
30         var children = [];
31         while (currentNode = currentNode.nextSibling) {
32             if (isEndComment(currentNode)) {
33                 depth--;
34                 if (depth === 0)
35                     return children;
36             }
38             children.push(currentNode);
40             if (isStartComment(currentNode))
41                 depth++;
42         }
43         if (!allowUnbalanced)
44             throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
45         return null;
46     }
48     function getMatchingEndComment(startComment, allowUnbalanced) {
49         var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
50         if (allVirtualChildren) {
51             if (allVirtualChildren.length > 0)
52                 return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
53             return startComment.nextSibling;
54         } else
55             return null; // Must have no matching end comment, and allowUnbalanced is true
56     }
58     function getUnbalancedChildTags(node) {
59         // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
60         //       from <div>OK</div><!-- /ko --><!-- /ko -->,             returns: <!-- /ko --><!-- /ko -->
61         var childNode = node.firstChild, captureRemaining = null;
62         if (childNode) {
63             do {
64                 if (captureRemaining)                   // We already hit an unbalanced node and are now just scooping up all subsequent nodes
65                     captureRemaining.push(childNode);
66                 else if (isStartComment(childNode)) {
67                     var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
68                     if (matchingEndComment)             // It's a balanced tag, so skip immediately to the end of this virtual set
69                         childNode = matchingEndComment;
70                     else
71                         captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
72                 } else if (isEndComment(childNode)) {
73                     captureRemaining = [childNode];     // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
74                 }
75             } while (childNode = childNode.nextSibling);
76         }
77         return captureRemaining;
78     }
80     ko.virtualElements = {
81         allowedBindings: {},
83         childNodes: function(node) {
84             return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
85         },
87         emptyNode: function(node) {
88             if (!isStartComment(node))
89                 ko.utils.emptyDomNode(node);
90             else {
91                 var virtualChildren = ko.virtualElements.childNodes(node);
92                 for (var i = 0, j = virtualChildren.length; i < j; i++)
93                     ko.removeNode(virtualChildren[i]);
94             }
95         },
97         setDomNodeChildren: function(node, childNodes) {
98             if (!isStartComment(node))
99                 ko.utils.setDomNodeChildren(node, childNodes);
100             else {
101                 ko.virtualElements.emptyNode(node);
102                 var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
103                 for (var i = 0, j = childNodes.length; i < j; i++)
104                     endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
105             }
106         },
108         prepend: function(containerNode, nodeToPrepend) {
109             if (!isStartComment(containerNode)) {
110                 if (containerNode.firstChild)
111                     containerNode.insertBefore(nodeToPrepend, containerNode.firstChild);
112                 else
113                     containerNode.appendChild(nodeToPrepend);
114             } else {
115                 // Start comments must always have a parent and at least one following sibling (the end comment)
116                 containerNode.parentNode.insertBefore(nodeToPrepend, containerNode.nextSibling);
117             }
118         },
120         insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {
121             if (!insertAfterNode) {
122                 ko.virtualElements.prepend(containerNode, nodeToInsert);
123             } else if (!isStartComment(containerNode)) {
124                 // Insert after insertion point
125                 if (insertAfterNode.nextSibling)
126                     containerNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
127                 else
128                     containerNode.appendChild(nodeToInsert);
129             } else {
130                 // Children of start comments must always have a parent and at least one following sibling (the end comment)
131                 containerNode.parentNode.insertBefore(nodeToInsert, insertAfterNode.nextSibling);
132             }
133         },
135         firstChild: function(node) {
136             if (!isStartComment(node))
137                 return node.firstChild;
138             if (!node.nextSibling || isEndComment(node.nextSibling))
139                 return null;
140             return node.nextSibling;
141         },
143         nextSibling: function(node) {
144             if (isStartComment(node))
145                 node = getMatchingEndComment(node);
146             if (node.nextSibling && isEndComment(node.nextSibling))
147                 return null;
148             return node.nextSibling;
149         },
151         virtualNodeBindingValue: function(node) {
152             var regexMatch = isStartComment(node);
153             return regexMatch ? regexMatch[1] : null;
154         },
156         normaliseVirtualElementDomStructure: function(elementVerified) {
157             // Workaround for https://github.com/SteveSanderson/knockout/issues/155
158             // (IE <= 8 or IE 9 quirks mode parses your HTML weirdly, treating closing </li> tags as if they don't exist, thereby moving comment nodes
159             // that are direct descendants of <ul> into the preceding <li>)
160             if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
161                 return;
163             // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
164             // must be intended to appear *after* that child, so move them there.
165             var childNode = elementVerified.firstChild;
166             if (childNode) {
167                 do {
168                     if (childNode.nodeType === 1) {
169                         var unbalancedTags = getUnbalancedChildTags(childNode);
170                         if (unbalancedTags) {
171                             // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
172                             var nodeToInsertBefore = childNode.nextSibling;
173                             for (var i = 0; i < unbalancedTags.length; i++) {
174                                 if (nodeToInsertBefore)
175                                     elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
176                                 else
177                                     elementVerified.appendChild(unbalancedTags[i]);
178                             }
179                         }
180                     }
181                 } while (childNode = childNode.nextSibling);
182             }
183         }
184     };
185 })();
186 ko.exportSymbol('virtualElements', ko.virtualElements);
187 ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
188 ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
189 //ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild);     // firstChild is not minified
190 ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
191 //ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling);   // nextSibling is not minified
192 ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
193 ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);