Xml.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /**
  2. * IXmlAttribute is just the standard Attr
  3. */
  4. export type IXmlAttribute = Attr;
  5. /**
  6. * Just a wrapper for an XML Element object.
  7. * It facilitates handling of XML elements by OSMD
  8. */
  9. export class IXmlElement {
  10. public name: string;
  11. public value: string;
  12. public hasAttributes: boolean = false;
  13. public firstAttribute: IXmlAttribute;
  14. public hasElements: boolean;
  15. private attrs: IXmlAttribute[];
  16. private elem: Element;
  17. /**
  18. * Wraps 'elem' Element in a IXmlElement
  19. * @param elem
  20. */
  21. constructor(elem: Element) {
  22. if (!elem) {
  23. throw new Error("IXmlElement: expected Element, got undefined");
  24. }
  25. this.elem = elem;
  26. this.name = elem.nodeName.toLowerCase();
  27. if (elem.hasAttributes()) {
  28. this.hasAttributes = true;
  29. this.firstAttribute = elem.attributes[0];
  30. }
  31. this.hasElements = elem.hasChildNodes();
  32. // Look for a value
  33. if (elem.childNodes.length === 1 && elem.childNodes[0].nodeType === Node.TEXT_NODE) {
  34. this.value = elem.childNodes[0].nodeValue;
  35. } else {
  36. this.value = "";
  37. }
  38. }
  39. /**
  40. * Get the attribute with the given name
  41. * @param attributeName
  42. * @returns {Attr}
  43. */
  44. public attribute(attributeName: string): IXmlAttribute {
  45. return this.elem.attributes.getNamedItem(attributeName);
  46. }
  47. /**
  48. * Get all attributes
  49. * @returns {IXmlAttribute[]}
  50. */
  51. public attributes(): IXmlAttribute[] {
  52. if (!this.attrs) {
  53. const attributes: NamedNodeMap = this.elem.attributes;
  54. const attrs: IXmlAttribute[] = [];
  55. for (let i: number = 0; i < attributes.length; i += 1) {
  56. attrs.push(attributes[i]);
  57. }
  58. this.attrs = attrs;
  59. }
  60. return this.attrs;
  61. }
  62. /**
  63. * Get the first child element with the given node name
  64. * @param elementName
  65. * @returns {IXmlElement}
  66. */
  67. public element(elementName: string): IXmlElement {
  68. const nodes: NodeList = this.elem.childNodes;
  69. for (let i: number = 0, length: number = nodes.length; i < length; i += 1) {
  70. const node: Node = nodes[i];
  71. if (node.nodeType === Node.ELEMENT_NODE && node.nodeName.toLowerCase() === elementName) {
  72. return new IXmlElement(node as Element);
  73. }
  74. }
  75. }
  76. /**
  77. * Get the children with the given node name (if given, otherwise all child elements)
  78. * @param nodeName
  79. * @returns {IXmlElement[]}
  80. */
  81. public elements(nodeName?: string): IXmlElement[] {
  82. const nodes: NodeList = this.elem.childNodes;
  83. const ret: IXmlElement[] = [];
  84. const nameUnset: boolean = !nodeName;
  85. if (!nameUnset) {
  86. nodeName = nodeName.toLowerCase();
  87. }
  88. for (let i: number = 0; i < nodes.length; i += 1) {
  89. const node: Node = nodes[i];
  90. if (node.nodeType === Node.ELEMENT_NODE &&
  91. (nameUnset || node.nodeName.toLowerCase() === nodeName)
  92. ) {
  93. ret.push(new IXmlElement(node as Element));
  94. }
  95. }
  96. return ret;
  97. }
  98. /**
  99. * Get the first child element with the given node name
  100. * with all the children of consequent child elements with the same node name.
  101. * for example two <notations> tags will be combined for better processing
  102. * @param elementName
  103. * @returns {IXmlElement}
  104. */
  105. public combinedElement(elementName: string): IXmlElement {
  106. const nodes: NodeList = this.elem.childNodes;
  107. if (nodes.length > 0) {
  108. let firstNode: Node;
  109. for (let i: number = 0, length: number = nodes.length; i < length; i += 1) {
  110. const otherNode: Node = nodes[i];
  111. if (otherNode.nodeType === Node.ELEMENT_NODE && otherNode.nodeName.toLowerCase() === elementName) {
  112. if (firstNode) {
  113. const childNodes: NodeList = otherNode.childNodes;
  114. for (let j: number = 0, numChildNodes: number = childNodes.length; j < numChildNodes; j += 1) {
  115. const childNode: Node = childNodes[j];
  116. firstNode.appendChild(childNode.cloneNode(true));
  117. }
  118. } else {
  119. firstNode = otherNode;
  120. }
  121. }
  122. }
  123. if (firstNode) {
  124. return new IXmlElement(firstNode as Element);
  125. }
  126. }
  127. }
  128. }