diff --git a/jsonml-utils.js b/jsonml-utils.js index 76e352a..9e26a2f 100644 --- a/jsonml-utils.js +++ b/jsonml-utils.js @@ -84,9 +84,38 @@ if (typeof module === 'object') { * @return {boolean} */ var isElement = JsonML.isElement = function(jml) { - return isArray(jml) && ('string' === typeof jml[0]); + return isArray(jml) && ('string' === typeof jml[0]) && (jml[0].charAt(0) !== '?'); }; + /** + * @param {*} jml + * @return {boolean} + */ + var isProcessingInstruction = JsonML.isProcessingInstruction = function(jml) { + return isArray(jml) && ('string' === typeof jml[0]) && (jml[0].charAt(0) === '?'); + }; + + /** + * @param {*} jml + * @return {string} + */ + var getTarget = JsonML.getTarget = function(jml) { + if (!isProcessingInstruction(jml)) { + throw new SyntaxError('invalid JsonML'); + } + return jml[0].substring(1); + }; + + /** + * @param {*} jml + * @return {string} + */ + var getData = JsonML.getData = function(jml) { + if (!isProcessingInstruction(jml)) { + throw new SyntaxError('invalid JsonML'); + } + return jml[1]; + }; /** * @param {*} jml * @return {boolean} @@ -196,7 +225,7 @@ if (typeof module === 'object') { } else if (child && 'object' === typeof child) { if (isArray(child)) { - if (!isElement(child)) { + if (!isElement(child) && !isProcessingInstruction(child)) { throw new SyntaxError('invalid JsonML'); } diff --git a/jsonml-xml.js b/jsonml-xml.js index e8d48c2..9c508b1 100644 --- a/jsonml-xml.js +++ b/jsonml-xml.js @@ -62,6 +62,8 @@ if (typeof module === 'object') { } else if (tag.charAt(0) === '!') { return document.createComment(tag === '!' ? '' : tag.substr(1)+' '); + } else if (tag.charAt(0) === '?') { + return document.createProcessingInstruction(tag === '!' ? '' : tag.substr(1), ''); } return document.createElement(tag); @@ -99,7 +101,8 @@ if (typeof module === 'object') { if (child.nodeType === 3) { // text node elem.nodeValue += child.nodeValue; } - + } else if (elem.nodeType === 7) { + elem.data = child.data; } else if (elem.canHaveChildren !== false) { elem.appendChild(child); } @@ -250,6 +253,10 @@ if (typeof module === 'object') { addChildren(elem, filter, jml); + if (jml[0] === '' && jml.length === 2) { + jml = jml[1] + } + // filter result if ('function' === typeof filter) { jml = filter(jml, elem); @@ -264,6 +271,17 @@ if (typeof module === 'object') { // free references elem = null; return str; + case 7: // ProcessingInstruction node + var jml = ['?'+elem.target, elem.data] + + // filter result + if ('function' === typeof filter) { + jml = filter(jml, elem); + } + + // free references + elem = null; + return jml; case 10: // doctype jml = ['!']; @@ -361,7 +379,7 @@ if (typeof module === 'object') { */ JsonML.fromXMLText = function(xmlText, filter) { var elem = parseXML(xmlText); - elem = elem && (elem.ownerDocument || elem).documentElement; + elem = elem && (elem.ownerDocument || elem); return fromXML(elem, filter); }; diff --git a/test/xmlTests.js b/test/xmlTests.js index 9b270bf..8561930 100644 --- a/test/xmlTests.js +++ b/test/xmlTests.js @@ -340,4 +340,21 @@ test('JsonML.fromXMLText/.toXMLText roundtrip, comments', function() { same(actual, expected); }); +test('JsonML.fromXMLText/.toXMLText roundtrip, processing instructions', function() { + + var expected = + '' + + '' + + '' + + ''; + + // JsonML will strip the XML Declaration + var input = '' + expected; + + var jml = JsonML.fromXMLText(input); + var actual = JsonML.toXMLText(jml); + + same(actual, expected); +}); + }catch(ex){alert(ex);}