jsonml.js
Version:
JsonML-related tools for losslessly converting between XML/HTML and JSON, including mixed-mode XML. http://jsonml.org
238 lines (194 loc) • 5.09 kB
JavaScript
/*
jsonml-utils.js
JsonML manipulation methods
Created: 2006-11-09-0116
Copyright (c)2006-2012 Stephen M. McKamey
Distributed under The MIT License: http://jsonml.org/license
// tests if a given object is a valid JsonML element
bool JsonML.isElement(jml);
// gets the name of a JsonML element
string JsonML.getTagName(jml);
// tests if a given object is a JsonML attributes collection
bool JsonML.isAttributes(jml);
// tests if a JsonML element has a JsonML attributes collection
bool JsonML.hasAttributes(jml);
// gets the attributes collection for a JsonML element
object JsonML.getAttributes(jml);
// sets multiple attributes for a JsonML element
void JsonML.addAttributes(jml, attr);
// gets a single attribute for a JsonML element
object JsonML.getAttribute(jml, key);
// sets a single attribute for a JsonML element
void JsonML.setAttribute(jml, key, value);
// appends a JsonML child node to a parent JsonML element
void JsonML.appendChild(parent, child);
// gets an array of the child nodes of a JsonML element
array JsonML.getChildren(jml);
*/
;
/* Utility Methods -------------------------*/
/**
* Determines if the value is an Array
*
* @private
* @param {*} val the object being tested
* @return {boolean}
*/
var isArray = Array.isArray || function(val) {
return (val instanceof Array);
};
/**
* @param {*} jml
* @return {boolean}
*/
exports.isFragment = function(jml) {
return isArray(jml) && (jml[0] === '');
};
/**
* @param {*} jml
* @return {string}
*/
exports.getTagName = function(jml) {
return jml[0] || '';
};
/**
* @param {*} jml
* @return {boolean}
*/
var isElement = exports.isElement = function(jml) {
return isArray(jml) && ('string' === typeof jml[0]);
};
/**
* @param {*} jml
* @return {boolean}
*/
var isAttributes = exports.isAttributes = function(jml) {
return !!jml && ('object' === typeof jml) && !isArray(jml);
};
/**
* @param {*} jml
* @return {boolean}
*/
var hasAttributes = exports.hasAttributes = function(jml) {
if (!isElement(jml)) {
throw new SyntaxError('invalid JsonML');
}
return isAttributes(jml[1]);
};
/**
* @param {*} jml
* @param {boolean} addIfMissing
* @return {object}
*/
var getAttributes = exports.getAttributes = function(jml, addIfMissing) {
if (hasAttributes(jml)) {
return jml[1];
}
if (!addIfMissing) {
return undefined;
}
// need to add an attribute object
var name = jml.shift();
var attr = {};
jml.unshift(attr);
jml.unshift(name||'');
return attr;
};
/**
* @param {*} jml
* @param {object} attr
*/
var addAttributes = exports.addAttributes = function(jml, attr) {
if (!isElement(jml) || !isAttributes(attr)) {
throw new SyntaxError('invalid JsonML');
}
if (!isAttributes(jml[1])) {
// just insert attributes
var name = jml.shift();
jml.unshift(attr);
jml.unshift(name||'');
return;
}
// merge attribute objects
var old = jml[1];
for (var key in attr) {
if (attr.hasOwnProperty(key)) {
old[key] = attr[key];
}
}
};
/**
* @param {*} jml
* @param {string} key
* @return {string|number|boolean}
*/
exports.getAttribute = function(jml, key) {
if (!hasAttributes(jml)) {
return undefined;
}
return jml[1][key];
};
/**
* @param {*} jml
* @param {string} key
* @param {string|number|boolean} value
*/
exports.setAttribute = function(jml, key, value) {
getAttributes(jml, true)[key] = value;
};
/**
* @param {*} jml
* @param {array|object|string} child
*/
var appendChild = exports.appendChild = function(parent, child) {
if (!isArray(parent)) {
throw new SyntaxError('invalid JsonML');
}
if (isArray(child) && child[0] === '') {
// result was multiple JsonML sub-trees (i.e. documentFragment)
child.shift();// remove fragment ident
// directly append children
while (child.length) {
appendChild(parent, child.shift(), arguments[2]);
}
} else if (child && 'object' === typeof child) {
if (isArray(child)) {
if (!isElement(child)) {
throw new SyntaxError('invalid JsonML');
}
if (typeof arguments[2] === 'function') {
// onAppend callback for JBST use
(arguments[2])(parent, child);
}
// result was a JsonML node
parent.push(child);
} else if (exports.isRaw(child)) {
// result was a JsonML node
parent.push(child);
} else {
// result was JsonML attributes
addAttributes(parent, child);
}
} else if ('undefined' !== typeof child && child !== null) {
// must convert to string or JsonML will discard
child = String(child);
// skip processing empty string literals
if (child && parent.length > 1 && 'string' === typeof parent[parent.length-1]) {
// combine strings
parent[parent.length-1] += child;
} else if (child || !parent.length) {
// append
parent.push(child);
}
}
};
/**
* @param {*} jml
* @return {array}
*/
exports.getChildren = function(jml) {
if (hasAttributes(jml)) {
return jml.slice(2);
}
return jml.slice(1);
};