/// <summary> /// Returns a filtered collection of the descendant elements for this document /// or element, in document order. Only elements that have a matching System.Xml.Linq.XName /// are included in the collection. /// </summary> /// <param name="name">The System.Xml.Linq.XName to match.</param> /// <returns> /// An System.Collections.Generic.IEnumerable`1 of System.Xml.Linq.XElement /// containing the descendant elements of the System.Xml.Linq.XContainer that /// match the specified System.Xml.Linq.XName. /// </returns> public IEnumerable <XElement> Descendants(XName name) { //this one is much simpler than Elements since we can call the getElementsByTagName method that does exactly what we want. object jsNodesForElement = CSHTML5.Interop.ExecuteJavaScript("$0.getElementsByTagName($1)", INTERNAL_jsnode, name.ToString()); if (XDocument.IsNullOrUndefined(jsNodesForElement)) //no children. { yield break; } int i = 0; while (true) { object nodeAtI = CSHTML5.Interop.ExecuteJavaScript("$0[$1]", jsNodesForElement, i); if (CSHTML5.Interop.IsUndefined(nodeAtI)) { break; } //note: they have a tagName so they are definitely XElements. yield return((XElement)XDocument.GetXNodeFromJSNode(nodeAtI)); ++i; } yield break; }
/// <summary> /// Returns a collection of the child elements of this element or document, in /// document order. /// </summary> /// <returns> /// An System.Collections.Generic.IEnumerable<XElement> of System.Xml.Linq.XElement /// containing the child elements of this System.Xml.Linq.XContainer, in document /// order. /// </returns> public IEnumerable <XElement> Elements() { //Note on the JS below: the filter is to get rid of the XText elements which have no tagName. //object jsNodesForElement = CSHTML5.Interop.ExecuteJavaScript("Array.from($0.childNodes).filter(node => node.tagName !== undefined)", INTERNAL_jsnode); //todo: check whether the line above is more efficient than the line below and change accordingly (if IE allows it, see todo in Elements(XName)) object jsNodesForElement = CSHTML5.Interop.ExecuteJavaScript("Array.from($0.childNodes).filter(function(node) { return node.tagName !== undefined})", INTERNAL_jsnode); if (XDocument.IsNullOrUndefined(jsNodesForElement)) { yield break; // no children. } int i = 0; while (true) { object nodeAtI = CSHTML5.Interop.ExecuteJavaScript("$0[$1]", jsNodesForElement, i); if (CSHTML5.Interop.IsUndefined(nodeAtI)) { break; } //note: they have a tagName so they are definitely XElements. yield return((XElement)XDocument.GetXNodeFromJSNode(nodeAtI)); ++i; } yield break; }
/// <summary> /// Gets the first (in document order) child element with the specified System.Xml.Linq.XName. /// </summary> /// <param name="name">The System.Xml.Linq.XName to match.</param> /// <returns> /// A System.Xml.Linq.XElement that matches the specified System.Xml.Linq.XName, /// or null. /// </returns> public XElement Element(XName name) { //note: we couldn't use INTERNAL_jsnode.getElementsByTagName(name) because it returns the children of the children nodes as well (we only want the immediate children). //Note about the js below: Array.from is to get the possibility to call the filter method: string nameAsString = name.LocalName; string namespaceAsString = name.NamespaceName; // object jsNodeForElement = CSHTML5.Interop.ExecuteJavaScript("Array.from($0.childNodes).filter(node => node.tagName == $1)[0]", INTERNAL_jsnode, name.ToString()); //todo: check whether the line above is more efficient than the line below and change accordingly (if IE allows it, see todo in Elements(XName)) // Note: below, the test ((!$2 && !node.namespaceURI) || node.namespaceURI == $2) can be read as both are null or empty, or they are the same. This way, the case one is null while the other is empty will still be considered a match. object jsNodeForElement = CSHTML5.Interop.ExecuteJavaScript("Array.from($0.childNodes).filter(function(node) { return (node.localName == $1 && ((!$2 && !node.namespaceURI) || node.namespaceURI == $2))})[0]", INTERNAL_jsnode, nameAsString, namespaceAsString); //} if (XDocument.IsNullOrUndefined(jsNodeForElement)) //not found, we return null. { return(null); } XNode node = XDocument.GetXNodeFromJSNode(jsNodeForElement); if (node is XElement) { return((XElement)node); } return(null); }
/// <summary> /// Returns a filtered collection of the child elements of this element or document, /// in document order. Only elements that have a matching System.Xml.Linq.XName /// are included in the collection. /// </summary> /// <param name="name">The System.Xml.Linq.XName to match.</param> /// <returns> /// An System.Collections.Generic.IEnumerable`1 of System.Xml.Linq.XElement /// containing the children of the System.Xml.Linq.XContainer that have a matching /// System.Xml.Linq.XName, in document order. /// </returns> public IEnumerable <XElement> Elements(XName name) { //note: we couldn't use INTERNAL_jsnode.getElementsByTagName(name) because it returns the children of the children nodes as well (we only want the immediate children). //jsNodesForElement = CSHTML5.Interop.ExecuteJavaScript("Array.from($0.childNodes).filter(node => node.tagName == $1)", INTERNAL_jsnode, name.ToString()); //todo: if IE learns that => isn't a syntax error (which breaks the whole file) and can use it properly, use the line above instead of the following because it seems to be more efficient. object jsNodesForElement = CSHTML5.Interop.ExecuteJavaScript("Array.from($0.childNodes).filter(document.functionToCompareWordForFilter($1))", INTERNAL_jsnode, name.ToString()); #region explanation of the line above //note: normal use of filter: myArray.filter( function(node) { return testOnNode; }) //problem here: testOnNode depends on the name than node and we cannot put $1 in there because JSIL changes it to "this.$name", but "this" does not exist where "testOnNode" is. //solution: document.functionToCompareWordForFilter(name) returns A FUNCTION that takes a node and compares its tagName to name (see in cshtml5.js) // when calling document.functionToCompareWordForFilter, this.$name exists // and the created method that is used by filter can use it normally. //in short: the function created by document.functionToCompareWordForFilter provides the name, filter provides the node. #endregion if (XDocument.IsNullOrUndefined(jsNodesForElement)) //nothing fits the request. { yield break; } int i = 0; while (true) { object nodeAtI = CSHTML5.Interop.ExecuteJavaScript("$0[$1]", jsNodesForElement, i); if (CSHTML5.Interop.IsUndefined(nodeAtI)) { break; } //note: they have a tagName so they are definitely XElements. yield return((XElement)XDocument.GetXNodeFromJSNode(nodeAtI)); ++i; } yield break; }
/// <summary> /// Gets the first (in document order) child element with the specified System.Xml.Linq.XName. /// </summary> /// <param name="name">The System.Xml.Linq.XName to match.</param> /// <returns> /// A System.Xml.Linq.XElement that matches the specified System.Xml.Linq.XName, /// or null. /// </returns> public XElement Element(XName name) { //note: we couldn't use INTERNAL_jsnode.getElementsByTagName(name) because it returns the children of the children nodes as well (we only want the immediate children). //Note about the js below: Array.from is to get the possibility to call the filter method: string nameAsString = name.LocalName; // object jsNodeForElement = CSHTML5.Interop.ExecuteJavaScript("Array.from($0.childNodes).filter(node => node.tagName == $1)[0]", INTERNAL_jsnode, name.ToString()); //todo: check whether the line above is more efficient than the line below and change accordingly (if IE allows it, see todo in Elements(XName)) object jsNodeForElement = CSHTML5.Interop.ExecuteJavaScript("Array.from($0.childNodes).filter(function(node) { return node.tagName == $1})[0]", INTERNAL_jsnode, nameAsString); //} if (XDocument.IsNullOrUndefined(jsNodeForElement)) //not found, we return null. { return(null); } XNode node = XDocument.GetXNodeFromJSNode(jsNodeForElement); if (node is XElement) { return((XElement)node); } return(null); }