nativescript-xmlobjects
Version:
A NativeScript module that provides handling XML data as objects similar to LINQ to XML.
651 lines • 19.7 kB
JavaScript
// The MIT License (MIT)
//
// Copyright (c) Marcel Joachim Kloubert <marcel.kloubert@gmx.net>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
"use strict";
var observable_1 = require('data/observable');
var observable_array_1 = require('data/observable-array');
var TypeUtils = require('utils/types');
var Xml = require('xml');
/**
* A XML name.
*/
var XName = (function () {
/**
* Initializes a new instance of that class.
*
* @param {string} name The full name.
*
* @throws Name is invalid.
*/
function XName(name) {
if (TypeUtils.isNullOrUndefined(name)) {
throw "Name cannot be (null) or (undefined)!";
}
name = ('' + name).trim();
var ns = null;
var ln = name;
if (ln.indexOf(':') > -1) {
var parts = ln.split(':');
if (2 !== parts.length) {
throw "Cannot have more than one separator!";
}
ns = parts[0].trim();
ln = parts[1].trim();
}
if ('' === ln) {
throw "Local name cannot be empty!";
}
if (null !== ns) {
ns = ns.trim();
if ('' === ns) {
ns = null;
}
}
this._namespace = ns;
this._localName = ln;
}
/**
* Checks if a value is equal to the full name of that instance.
*
* @param any v The value to check.
*
* @return {Boolean} Is equal or not.
*/
XName.prototype.equals = function (v) {
if (TypeUtils.isNullOrUndefined(v)) {
return false;
}
if (v instanceof XName) {
v = v.toString();
}
return this.toString() === ('' + v);
};
Object.defineProperty(XName.prototype, "localName", {
/**
* Gets the local name.
*/
get: function () {
return this._localName;
},
enumerable: true,
configurable: true
});
Object.defineProperty(XName.prototype, "namespace", {
/**
* Gets the namespace.
*/
get: function () {
return this._namespace;
},
enumerable: true,
configurable: true
});
/**
* Returns the full name.
*
* @return {string} The full name.
*/
XName.prototype.toString = function () {
var str = this._localName;
if (null !== this._namespace) {
str = this._namespace + ":" + str;
}
return str;
};
return XName;
}());
exports.XName = XName;
/**
* A XML object.
*/
var XObject = (function (_super) {
__extends(XObject, _super);
function XObject() {
_super.apply(this, arguments);
}
Object.defineProperty(XObject.prototype, "document", {
/**
* Gets the underlying document.
*/
get: function () {
return !TypeUtils.isNullOrUndefined(this.parent) ? this.parent.document : null;
},
enumerable: true,
configurable: true
});
return XObject;
}(observable_1.Observable));
exports.XObject = XObject;
/**
* A XML attribute.
*/
var XAttribute = (function () {
/**
* Initializes a new instance of that class.
*
* @param any name The name of the element.
*
* @throws Name is invalid.
*/
function XAttribute(name) {
if (!(name instanceof XName)) {
name = new XName(name);
}
this._name = name;
}
Object.defineProperty(XAttribute.prototype, "name", {
/**
* Gets the name of the element.
*/
get: function () {
return this._name;
},
enumerable: true,
configurable: true
});
/**
* Gets the string representation of that object.
*
* @return {String} The object as string.
*/
XAttribute.prototype.toString = function () {
var str = this.name.toString();
var v = this.value;
if (!TypeUtils.isNullOrUndefined(v)) {
str += '="' + parseXmlEntities(v) + '"';
}
return str;
};
return XAttribute;
}());
exports.XAttribute = XAttribute;
/**
* A XML node.
*/
var XNode = (function (_super) {
__extends(XNode, _super);
function XNode() {
_super.apply(this, arguments);
}
return XNode;
}(XObject));
exports.XNode = XNode;
/**
* XML text.
*/
var XText = (function (_super) {
__extends(XText, _super);
function XText() {
_super.apply(this, arguments);
}
/** @inheritdoc */
XText.prototype.toString = function () {
return parseXmlEntities(this.value);
};
return XText;
}(XNode));
exports.XText = XText;
/**
* XML CDATA
*/
var XCData = (function (_super) {
__extends(XCData, _super);
function XCData() {
_super.apply(this, arguments);
}
/** @inheritdoc */
XCData.prototype.toString = function () {
return '<![CDATA[' + parseXmlEntities(this.value) + ']]>';
};
return XCData;
}(XText));
exports.XCData = XCData;
/**
* A XML comment.
*/
var XComment = (function (_super) {
__extends(XComment, _super);
function XComment() {
_super.apply(this, arguments);
}
/** @inheritdoc */
XComment.prototype.toString = function () {
return '<!--' + parseXmlEntities(this.value) + '-->';
};
return XComment;
}(XNode));
exports.XComment = XComment;
/**
* A XML container.
*/
var XContainer = (function (_super) {
__extends(XContainer, _super);
function XContainer() {
_super.apply(this, arguments);
this._nodes = new observable_array_1.ObservableArray();
}
/**
* Adds content.
*
* @param any content The content to add.
*/
XContainer.prototype.add = function (content) {
if (TypeUtils.isNullOrUndefined(content)) {
return;
}
if (!(content instanceof XNode)) {
var txt = new XText();
txt.value = '' + content;
content = txt;
}
this._nodes.push(content);
};
/**
* Returns the first element by name.
*
* @param any name The name of the attribute.
*
* @return {XElement} The element or (null) if not found.
*/
XContainer.prototype.element = function (name) {
var elementList = this.elements(name);
return elementList.length > 0 ? elementList[0] : null;
};
/**
* Gets the element of the container.
*
* @param any [name] The custom name filter.
*
* @return {XElement[]} The elements.
*
* @throws Name is invalid.
*/
XContainer.prototype.elements = function (name) {
var validator = this.getElementValidator(name);
var elementList = [];
for (var i = 0; i < this._nodes.length; i++) {
var n = this._nodes.getItem(i);
if (n instanceof XElement) {
if (validator(n)) {
elementList.push(n);
}
}
}
return elementList;
};
/**
* Returns an element validator by name.
*
* @param any name The XML name.
*
* @return {Function} The validator.
*
* @throws Name is invalid.
*/
XContainer.prototype.getElementValidator = function (name) {
if (TypeUtils.isNullOrUndefined(name)) {
return function () { return true; };
}
if (!(name instanceof XName)) {
name = new XName(name);
}
return function (e) { return e.name.equals(name); };
};
/**
* Gets the nodes of the container.
*
* @return {XNode[]} The nodes.
*/
XContainer.prototype.nodes = function () {
var nodeList = [];
for (var i = 0; i < this._nodes.length; i++) {
nodeList.push(this._nodes.getItem(i));
}
return nodeList;
};
return XContainer;
}(XNode));
exports.XContainer = XContainer;
/**
* A XML container with attributes.
*/
var XContainerWithAttributes = (function (_super) {
__extends(XContainerWithAttributes, _super);
function XContainerWithAttributes() {
_super.apply(this, arguments);
this._attributes = [];
}
/** @inheritdoc */
XContainerWithAttributes.prototype.add = function (content) {
if (content instanceof XAttribute) {
this._attributes.push(content);
}
else {
_super.prototype.add.call(this, content);
}
};
/**
* Returns an attribute by name.
*
* @param any name The name of the attribute.
*
* @return {XAttribute} The attribute or (null) if not found.
*/
XContainerWithAttributes.prototype.attribute = function (name) {
var attribList = this.attributes(name);
return attribList.length > 0 ? attribList[0] : null;
};
/**
* Gets the list of attributes.
*
* @param any [name] The custom name filter.
*
* @return {XAttribute[]} The attributes.
*
* @throws Name is invalid.
*/
XContainerWithAttributes.prototype.attributes = function (name) {
var validator = this.getAttributeValidator(name);
var attributeList = [];
for (var i = 0; i < this._attributes.length; i++) {
var a = this._attributes[i];
if (validator(a)) {
attributeList.push(a);
}
}
return attributeList;
};
/**
* Returns an attribute validator by name.
*
* @param any name The XML name.
*
* @return {Function} The validator.
*
* @throws Name is invalid.
*/
XContainerWithAttributes.prototype.getAttributeValidator = function (name) {
if (TypeUtils.isNullOrUndefined(name)) {
return function () { return true; };
}
if (!(name instanceof XName)) {
name = new XName(name);
}
return function (a) { return a.name.equals(name); };
};
return XContainerWithAttributes;
}(XContainer));
exports.XContainerWithAttributes = XContainerWithAttributes;
/**
* A XML element.
*/
var XElement = (function (_super) {
__extends(XElement, _super);
/**
* Initializes a new instance of that class.
*
* @param any name The name of the element.
*
* @throws Name is invalid.
*/
function XElement(name) {
_super.call(this);
this._elements = new observable_array_1.ObservableArray();
if (!(name instanceof XName)) {
name = new XName(name);
}
this._name = name;
}
/** @inheritdoc */
XElement.prototype.add = function (content) {
if (content instanceof XContainer) {
if (!TypeUtils.isNullOrUndefined(content.parent)) {
throw "Parent is already set.";
}
content.parent = this;
}
_super.prototype.add.call(this, content);
};
Object.defineProperty(XElement.prototype, "document", {
/** @inheritdoc */
get: function () {
var p = this.parent;
return !TypeUtils.isNullOrUndefined(p) ? p.document : this._document;
},
set: function (doc) {
if (!TypeUtils.isNullOrUndefined(this.parent)) {
throw "Cannot set document here!";
}
if (!TypeUtils.isNullOrUndefined(this._document)) {
throw "Document already set!";
}
this._document = doc;
},
enumerable: true,
configurable: true
});
Object.defineProperty(XElement.prototype, "name", {
/**
* Gets the name of the element.
*/
get: function () {
return this._name;
},
enumerable: true,
configurable: true
});
/** @inheritdoc */
XElement.prototype.toString = function () {
var str = '<' + this.name.toString();
var attribs = this.attributes();
if (attribs.length > 0) {
for (var i = 0; i < attribs.length; i++) {
var a = attribs[i];
str += " " + a.toString();
}
}
var nodes = this.nodes();
if (nodes.length > 0) {
str += '>';
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
str += n.toString();
}
str += '</' + this.name.toString() + '>';
}
else {
str += ' />';
}
return str;
};
Object.defineProperty(XElement.prototype, "value", {
/**
* Gets the value of that element.
*
* @return {any} The value.
*/
get: function () {
var childNodes = this.nodes();
if (childNodes.length < 1) {
return null;
}
var elementValue = '';
for (var i = 0; i < childNodes.length; i++) {
var node = childNodes[i];
var valueToAdd;
if (!TypeUtils.isNullOrUndefined(node.value)) {
valueToAdd = node.value;
}
else {
valueToAdd = node.toString();
}
if (!TypeUtils.isNullOrUndefined(valueToAdd)) {
elementValue += valueToAdd;
}
}
return elementValue;
},
enumerable: true,
configurable: true
});
return XElement;
}(XContainerWithAttributes));
exports.XElement = XElement;
/**
* A XML document.
*/
var XDocument = (function (_super) {
__extends(XDocument, _super);
function XDocument() {
_super.apply(this, arguments);
this._root = null;
}
/** @inheritdoc */
XDocument.prototype.add = function (content) {
var invokeParent = true;
if (content instanceof XElement) {
if (TypeUtils.isNullOrUndefined(this._root)) {
content.document = this;
this._root = content;
}
else {
throw "A root element is already defined!";
}
}
if (invokeParent) {
_super.prototype.add.call(this, content);
}
};
Object.defineProperty(XDocument.prototype, "root", {
/**
* Gets the root element.
*/
get: function () {
return this._root;
},
enumerable: true,
configurable: true
});
/** @inheritdoc */
XDocument.prototype.toString = function () {
var str = '';
var nodes = this.nodes();
for (var i = 0; i < nodes.length; i++) {
var n = nodes[i];
str += n.toString();
}
return str;
};
return XDocument;
}(XContainer));
exports.XDocument = XDocument;
function getOwnProperties(obj) {
if (TypeUtils.isNullOrUndefined(obj)) {
return obj;
}
var properties = {};
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
properties[p] = obj[p];
}
}
return properties;
}
/**
* Parses an XML string.
*
* @param {String} xml The string to parse.
* @param {Boolean} [processNamespaces] Process namespaces or not.
* @param {Boolean} [angularSyntax] Handle Angular syntax or not.
*
* @return {XDocument} The new document.
*
* @throws Parse error.
*/
function parse(xml, processNamespaces, angularSyntax) {
var doc = new XDocument();
var errors = [];
var elementStack = [];
var currentContainer = function () { return elementStack.length > 0 ? elementStack[elementStack.length - 1] : doc; };
var xParser = new Xml.XmlParser(function (e) {
var c = currentContainer();
if (e.eventType === Xml.ParserEventType.StartElement) {
var newElement = new XElement(e.elementName);
newElement.add(e.data);
var attribs = getOwnProperties(e.attributes);
if (!TypeUtils.isNullOrUndefined(attribs)) {
for (var p in attribs) {
var a = new XAttribute(p);
a.value = attribs[p];
newElement.add(a);
}
}
currentContainer().add(newElement);
elementStack.push(newElement);
}
else if (e.eventType === Xml.ParserEventType.Text) {
var newText = new XText();
newText.value = e.data;
currentContainer().add(newText);
}
else if (e.eventType === Xml.ParserEventType.CDATA) {
var newCData = new XCData();
newCData.value = e.data;
currentContainer().add(newCData);
}
else if (e.eventType === Xml.ParserEventType.Comment) {
var newComment = new XComment();
newComment.value = e.data;
currentContainer().add(newComment);
}
else if (e.eventType === Xml.ParserEventType.EndElement) {
elementStack.pop();
}
}, function (error, position) {
errors.push([error, position]);
}, processNamespaces, angularSyntax);
xParser.parse(xml);
if (errors.length > 0) {
// collect errors and throw
var exceptionMsg = 'XML parse error:';
for (var i = 0; i < errors.length; i++) {
var err = errors[i][0];
var pos = errors[i][1];
exceptionMsg += "\n(" + pos.column + "," + pos.line + "): [" + err.name + "] " + err.message;
}
throw exceptionMsg;
}
return doc;
}
exports.parse = parse;
function parseXmlEntities(v) {
if (TypeUtils.isNullOrUndefined(v)) {
v = '';
}
v = '' + v;
v = v.replace('&', '&');
v = v.replace('"', '"');
v = v.replace("'", ''');
v = v.replace('<', '<');
v = v.replace('>', '>');
return v;
}
//# sourceMappingURL=index.js.map