.read .write .replace string elements in jQuery - javascript

I have these 2 functions (serialize and deserialize) in Javascript (below) and I want to change it to jQuery. I am wondering what would the right replacement for read and write in jQuery. Read and write strings are from and to a Textarea. This is part of Openlayers vector formats, getting geometries into / from OL map canvas.
Serialize is outputing the geometries from mapcanvas to textarea.
function serialize(feature) {
var type = document.getElementById("formatType").value;
var pretty = document.getElementById("prettyPrint").checked;
var str = formats['out'][type].write(feature, pretty);
str = str.replace(/,/g, ', ');
document.getElementById('output').value = str;
}
Deserialize is reading string from Textarea into OL mapcanvas.
function deserialize() {
var element = document.getElementById('text');
var type = document.getElementById("formatType").value;
var features = formats['in'][type].read(element.value);
var bounds;
if(features) {
if(features.constructor != Array) {
features = [features];
}
for(var i=0; i<features.length; ++i) {
if (!bounds) {
bounds = features[i].geometry.getBounds();
} else {
bounds.extend(features[i].geometry.getBounds());
}
}
vectors.addFeatures(features);
map.zoomToExtent(bounds);
var plural = (features.length > 1) ? 's' : '';
element.value = features.length + ' feature' + plural + ' added';
} else {
element.value = 'Bad input ' + type;
}
}
Thanks in advance.
Again, I am asking about the read and write function equivalent in jQuery. These 2 lines:
var str = formats['out'][type].write(feature, pretty);
var features = formats['in'][type].read(element.value);

to get the text in a text area
$("#myTextArea").val();
To set it to something
$("#myTextArea").val("Foo bar.");

Related

Javascript - how to render an output by typing three (or more) alphabets into the input?

I am making an html page which is a typer of a foreign script.
my progress: HERE
Here's the entire javascript:
function getReplacedText(latinText) {
if (!latinText) {
return "";
}
var replacedText = "";
for (var i = 0, len = latinText.length; i < len; i++) {
var curLetter = latinText[i];
var pos1Txt = latinText[i + 1];
var pos2Txt = latinText[i + 2];
if (!(curLetter == "")) {
var dualLetter = latreplaced[curLetter + pos1Txt];
if (dualLetter) {
replacedText += dualLetter;
i++;
continue;
}
}
replacedText += latreplaced[curLetter] || curLetter;
}
return replacedText;
}
var latreplaced = {
"u":"う",
"ku":"く",
"tsu":"つ",
};
function onLatinTextChange(txt) {
var replacedTextareaElem = document.getElementById("replaced_textarea");
var div = document.createElement("div");
var replacedHtmlEntities = getReplacedText(txt);
div.innerHTML = replacedHtmlEntities;
replacedTextareaElem.value = div.innerText;
}
The purpose of this project is to create a virtual phonetic keyboard to type certain forign scripts by only using Latin alphabets, without its keyboard setting installed.
Basically, if you enter an alphabet into the input <textarea>, it renders its corresponding foreign alphabet. (For instance, input 'u' > output 'う', input 'ku' > output 'く')
Here is my problem: So far I have enabled rendering an output when one or two alphabet is typed into the input box. But I cannot figure out how to enable the same by entering three alphabets. (For instance, input 'tsu' > output 'つ')
"u":"う", // <- can convert
"ku":"く", // <- can convert
"tsu":"つ", // <- cannot convert!
In the javascript code, there is a var called dualLetter, which goes by the following script:
var dualLetter = latreplaced[curLetter + pos1Txt];
How can I edit this part of code (or the entire javascript) to be able to convert 3 or more input alphabets? Do I need to make var tripleLetter, or create a whole new system? Any alternative ways would also be helpful.
[edit] a solution inspired by your code :
I changed the main function but this definitively works
live demo : https://jsfiddle.net/alias_gui3/wds426mq/12/
source code :
var dictionnary = {
"u":"う",
"ku":"く",
"tsu":"つ",
"test for spaces": "😍"
};
var maxLength = Object.keys(dictionnary)
.reduce((a, b) => a.length > b.length ? a : b) // get the longest word
.length; // and it's length
function translate (text) {
var translated = "";
var cur = 0;
while (cur < text.length) {
var testedPhoneme;
var symbol = undefined;
for (var length = maxLength; length > 0; length --) {
testedPhoneme = text.substr(cur, length);
if (dictionnary[testedPhoneme]) {
symbol = dictionnary[testedPhoneme];
break; // stop the loop
}
}
if (symbol) {
translated += symbol;
cur += testedPhoneme.length;
}
else {
translated += text[cur]
cur++;
}
}
return translated
}
function onLatinTextChange(txt) {
var replacedTextareaElem = document.getElementById("replaced_textarea");
var div = document.createElement("div");
var replacedHtmlEntities = translate(txt);
div.innerHTML = replacedHtmlEntities;
replacedTextareaElem.value = div.innerText;
}
[previous post] a simple solution :
I suggest you split your text using spaces
If i understand well, you want to type u ku tsu to get うくつ, not ukutsu, if this is right then something like that could work :
const dictionnary = {
"u": "う",
"ku": "く",
"tsu": "つ"
var phonemes = text.split(' ') // split text by spaces
var translatedArray = phonemes.map(function (phoneme) {
return dictionnary[phoneme] || phoneme
// will return the latin phoneme if it is not in the dictionnary
})
translatedString = translatedArray.join('')

Google Apps Script - Typewriter Effect

I'm trying to add a sort of 'typewriter effect' on my google apps script for google docs. I want to make it type out text, in this case a wikipedia article, as if a user was typing it, so add a delay. Unfortunately the function appendText(), even if you use Utilities.sleep, it still just types the entire article out as soon as the script finishes. What function would I use to accomplish something like this?
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Start', 'myFunction')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var text = body.editAsText();
var response = UrlFetchApp.fetch('https://en.wikipedia.org/w/api.php?&format=json&action=query&generator=random&grnnamespace=0&prop=title&grnlimit=1');
var json = JSON.parse(response);
for (key in json.query.pages) {
var title = json.query.pages[key].title;
}
var url = 'https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&explaintext=&titles=' + title
var response2 = UrlFetchApp.fetch(url);
var json2 = JSON.parse(response2);
for (key in json2.query.pages) {
var content = json2.query.pages[key].extract;
}
//content = content.replace(/==.*==/, '====')
var all = title + '\n' + content;
text.appendText("Start\n");
Utilities.sleep(1000);
text.appendText(content);
}
You need to flush the document. The DocumentApp API does not have a flush method (like SpreadsheetApp) but you can still flush it by using saveAndClose and then re-opening the document (for example with document=DocumentApp.openById("myid")
saveAndClose is automatically called when a script finishes, but not after every change you make as Google batches those changes for performance.
https://developers.google.com/apps-script/reference/document/document
I have tried #ZigMandel suggestion. It appears to work but the text is being typed from the left out.
function onOpen(e) {
DocumentApp.getUi().createAddonMenu()
.addItem('Start', 'myFunction')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var text = body.editAsText();
var response = UrlFetchApp.fetch('https://en.wikipedia.org/w/api.php?&format=json&action=query&generator=random&grnnamespace=0&prop=title&grnlimit=1');
var json = JSON.parse(response);
for (key in json.query.pages) {
var title = json.query.pages[key].title;
}
var url = 'https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts&explaintext=&titles=' + title
var response2 = UrlFetchApp.fetch(url);
var json2 = JSON.parse(response2);
for (key in json2.query.pages) {
var content = json2.query.pages[key].extract;
}
//format(content);
//var par1 = body.insertParagraph(0, title);
//par1.setAlignment(DocumentApp.HorizontalAlignment.CENTER);
var str = "Sphinx of black quartz, judge my vow."
var split = str.split("");
for (var i = split.length - 1; i >= 0; i--) {
text.appendText(split[i]);
DocumentApp.getActiveDocument().saveAndClose();
body = DocumentApp.getActiveDocument().getBody();
text = body.editAsText();
}
}
function format(txt) {
txt = '\n' + txt;
txt = txt.replace(/\===(.+?)\===/g, "").replace(/\==
(.+?)\==/g,"").replace(/\n+/g, "\n").replace(/\n/g, "\n" + " ");
return txt;
}
To get the script to print out each character with a delay between, you can put your sleep method into a for loop.
for(var i = 0; i < content.length; i++) {
text.appendText(content[i]);
Utilities.sleep(200);
}
I think this gives you the effect you are looking for.

JS - text value of tag - when tag is string

How can I get text value of a tag when tag is stored as string?
var value = "<div>teeext</div>";
tag can be anything, its attributes may contain ">" string as well, regexp can be dangerous - also text value itself can be another tag.
You can make a dummy element:
var value = "<div>teeext</div>";
var div = document.createElement('div');
div.innerHTML = value;
var text = div.innerText || div.textContent;
Using just javascript:
var div = document.createElement('div');
var value = "<div>teeext</div>";
div.innerHTML = value;
var element = div.firstChild
console.log(element.innerHTML); //this the stuff in the tag
Using jquery:
$(value).html();
I guess that you're trying to strip tags.. In that case you can do it like this:
originalString = "<div>teeext</div>";
var value = originalString.replace(/(<([^>]+)>)/ig,"");
Test it out. If there are any exceptions which you need to handle, then just comment here and I'll try to help you further.
Edit for multiple tags:
originalString = "<div>teeext</div>";
outputString = originalString;
while (outputString.indexOf(/(<([^>]+)>)/ig) !== -1){
outputString = outputString.replace(/(<([^>]+)>)/ig,"");
}
value = outputString;
Haven't tested it, but you get the point ;)
Does this help as a starter?
function getText(tagString) {
var firstOpenTag = tagString.indexOf(">"),
lastCloseTag = tagString.lastIndexOf("</"),
substrLength;
if (firstOpenTag == -1 || lastCloseTag == -1) {
return tagString;
}
substrLength = (tagString.length - firstOpenTag) - (tagString.length - lastCloseTag) - 1;
return tagString.substr(firstOpenTag + 1, substrLength);
}
var value = "<div>teeext</div>";
console.log(getText(value));
var moreTags = "<div><ul><li>Text</li></ul></div>",
returnValue = moreTags,
prevReturnValue = "";
while (returnValue !== prevReturnValue) {
prevReturnValue = returnValue;
returnValue = getText(returnValue);
}
console.log(returnValue);

How to parse XML string with Prototype?

I have a string <ul><li e="100" n="50">Foo</li><li e="200" n="150">Bar</li></ul> and on client side I have to convert it to JSON. Something like {data:['Foo','Bar'],params:['100;50','200;150']}
I found a pretty good way to achieve it in here so my code should be something like that
var $input = $(input);
var data = "data:[";
var params = "params:[";
var first = true;
$input.find("li").each(function() {
if (!first) {
data += ",";
params += ",";
} else {
first = false;
}
data += "'" + $(this).text() + "'";
var e = $(this).attr("e");
var n = $(this).attr("n");
params += "'" + e + ';' + n + "'";
});
return "{data + "]," + params + "]}";
But the problem is that I can't use jquery. How can I do the same thing with prototype?
You want to use a DOM parser:
https://developer.mozilla.org/en/DOMParser
Something like this...
var xmlStr = '<ul><li e="100" n="50">Foo</li><li e="200" n="150">Bar</li></ul>';
var parser = new DOMParser();
var doc = parser.parseFromString(xmlStr, "application/xml");
var rootElement = doc.documentElement;
var children = rootElement.childNodes;
var jsonObj = {
data: [],
params: []
};
for (var i = 0; i < children.length; i++) {
// I realize this is not how your implementation is, but this should give
// you an idea of how to work on the DOM element
jsonObj.data.push( children[i].getAttribute('e') );
jsonObj.params.push( children[i].getAttribute('n') );
}
return jsonObj.toJSON();
Also, don't manually build your JSON string. Populate an object, then JSON-encode it.
Edit: Note that you need to test for DOMParser before you can use it. Check here for how you can do that. Sorry for the W3Schools link.
Why you are building an array object with string? Why not
var data = new Array();
var params = new Array();
$$("li").each(function() {
data.push ($(this).text());
params.psuh($(this).attr("e") + ";" + $(this).attr("n"));
});
return {data:data.toString(), params:params.toString()};
or
return {data:data, params:params};

Pretty printing XML with javascript

I have a string that represents a non indented XML that I would like to pretty-print. For example:
<root><node/></root>
should become:
<root>
<node/>
</root>
Syntax highlighting is not a requirement. To tackle the problem I first transform the XML to add carriage returns and white spaces and then use a pre tag to output the XML. To add new lines and white spaces I wrote the following function:
function formatXml(xml) {
var formatted = '';
var reg = /(>)(<)(\/*)/g;
xml = xml.replace(reg, '$1\r\n$2$3');
var pad = 0;
jQuery.each(xml.split('\r\n'), function(index, node) {
var indent = 0;
if (node.match( /.+<\/\w[^>]*>$/ )) {
indent = 0;
} else if (node.match( /^<\/\w/ )) {
if (pad != 0) {
pad -= 1;
}
} else if (node.match( /^<\w[^>]*[^\/]>.*$/ )) {
indent = 1;
} else {
indent = 0;
}
var padding = '';
for (var i = 0; i < pad; i++) {
padding += ' ';
}
formatted += padding + node + '\r\n';
pad += indent;
});
return formatted;
}
I then call the function like this:
jQuery('pre.formatted-xml').text(formatXml('<root><node1/></root>'));
This works perfectly fine for me but while I was writing the previous function I thought that there must be a better way. So my question is do you know of any better way given an XML string to pretty-print it in an html page? Any javascript frameworks and/or plugins that could do the job are welcome. My only requirement is this to be done on the client side.
This can be done using native javascript tools, without 3rd party libs, extending the #Dimitre Novatchev's answer:
var prettifyXml = function(sourceXml)
{
var xmlDoc = new DOMParser().parseFromString(sourceXml, 'application/xml');
var xsltDoc = new DOMParser().parseFromString([
// describes how we want to modify the XML - indent everything
'<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">',
' <xsl:strip-space elements="*"/>',
' <xsl:template match="para[content-style][not(text())]">', // change to just text() to strip space in text nodes
' <xsl:value-of select="normalize-space(.)"/>',
' </xsl:template>',
' <xsl:template match="node()|#*">',
' <xsl:copy><xsl:apply-templates select="node()|#*"/></xsl:copy>',
' </xsl:template>',
' <xsl:output indent="yes"/>',
'</xsl:stylesheet>',
].join('\n'), 'application/xml');
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xsltDoc);
var resultDoc = xsltProcessor.transformToDocument(xmlDoc);
var resultXml = new XMLSerializer().serializeToString(resultDoc);
return resultXml;
};
console.log(prettifyXml('<root><node/></root>'));
Outputs:
<root>
<node/>
</root>
JSFiddle
Note, as pointed out by #jat255, pretty printing with <xsl:output indent="yes"/> is not supported by firefox. It only seems to work in chrome, opera and probably the rest webkit-based browsers.
From the text of the question I get the impression that a string result is expected, as opposed to an HTML-formatted result.
If this is so, the simplest way to achieve this is to process the XML document with the identity transformation and with an <xsl:output indent="yes"/> instruction:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applying this transformation on the provided XML document:
<root><node/></root>
most XSLT processors (.NET XslCompiledTransform, Saxon 6.5.4 and Saxon 9.0.0.2, AltovaXML) produce the wanted result:
<root>
<node />
</root>
Found this thread when I had a similar requirement but I simplified OP's code as follows:
function formatXml(xml, tab) { // tab = optional indent value, default is tab (\t)
var formatted = '', indent= '';
tab = tab || '\t';
xml.split(/>\s*</).forEach(function(node) {
if (node.match( /^\/\w/ )) indent = indent.substring(tab.length); // decrease indent by one 'tab'
formatted += indent + '<' + node + '>\r\n';
if (node.match( /^<?\w[^>]*[^\/]$/ )) indent += tab; // increase indent
});
return formatted.substring(1, formatted.length-3);
}
works for me!
Slight modification of efnx clckclcks's javascript function. I changed the formatting from spaces to tab, but most importantly I allowed text to remain on one line:
var formatXml = this.formatXml = function (xml) {
var reg = /(>)\s*(<)(\/*)/g; // updated Mar 30, 2015
var wsexp = / *(.*) +\n/g;
var contexp = /(<.+>)(.+\n)/g;
xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
var pad = 0;
var formatted = '';
var lines = xml.split('\n');
var indent = 0;
var lastType = 'other';
// 4 types of tags - single, closing, opening, other (text, doctype, comment) - 4*4 = 16 transitions
var transitions = {
'single->single': 0,
'single->closing': -1,
'single->opening': 0,
'single->other': 0,
'closing->single': 0,
'closing->closing': -1,
'closing->opening': 0,
'closing->other': 0,
'opening->single': 1,
'opening->closing': 0,
'opening->opening': 1,
'opening->other': 1,
'other->single': 0,
'other->closing': -1,
'other->opening': 0,
'other->other': 0
};
for (var i = 0; i < lines.length; i++) {
var ln = lines[i];
// Luca Viggiani 2017-07-03: handle optional <?xml ... ?> declaration
if (ln.match(/\s*<\?xml/)) {
formatted += ln + "\n";
continue;
}
// ---
var single = Boolean(ln.match(/<.+\/>/)); // is this line a single tag? ex. <br />
var closing = Boolean(ln.match(/<\/.+>/)); // is this a closing tag? ex. </a>
var opening = Boolean(ln.match(/<[^!].*>/)); // is this even a tag (that's not <!something>)
var type = single ? 'single' : closing ? 'closing' : opening ? 'opening' : 'other';
var fromTo = lastType + '->' + type;
lastType = type;
var padding = '';
indent += transitions[fromTo];
for (var j = 0; j < indent; j++) {
padding += '\t';
}
if (fromTo == 'opening->closing')
formatted = formatted.substr(0, formatted.length - 1) + ln + '\n'; // substr removes line break (\n) from prev loop
else
formatted += padding + ln + '\n';
}
return formatted;
};
Personnaly, I use google-code-prettify with this function :
prettyPrintOne('<root><node1><root>', 'xml')
Or if you'd just like another js function to do it, I've modified Darin's (a lot):
var formatXml = this.formatXml = function (xml) {
var reg = /(>)(<)(\/*)/g;
var wsexp = / *(.*) +\n/g;
var contexp = /(<.+>)(.+\n)/g;
xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
var pad = 0;
var formatted = '';
var lines = xml.split('\n');
var indent = 0;
var lastType = 'other';
// 4 types of tags - single, closing, opening, other (text, doctype, comment) - 4*4 = 16 transitions
var transitions = {
'single->single' : 0,
'single->closing' : -1,
'single->opening' : 0,
'single->other' : 0,
'closing->single' : 0,
'closing->closing' : -1,
'closing->opening' : 0,
'closing->other' : 0,
'opening->single' : 1,
'opening->closing' : 0,
'opening->opening' : 1,
'opening->other' : 1,
'other->single' : 0,
'other->closing' : -1,
'other->opening' : 0,
'other->other' : 0
};
for (var i=0; i < lines.length; i++) {
var ln = lines[i];
var single = Boolean(ln.match(/<.+\/>/)); // is this line a single tag? ex. <br />
var closing = Boolean(ln.match(/<\/.+>/)); // is this a closing tag? ex. </a>
var opening = Boolean(ln.match(/<[^!].*>/)); // is this even a tag (that's not <!something>)
var type = single ? 'single' : closing ? 'closing' : opening ? 'opening' : 'other';
var fromTo = lastType + '->' + type;
lastType = type;
var padding = '';
indent += transitions[fromTo];
for (var j = 0; j < indent; j++) {
padding += ' ';
}
formatted += padding + ln + '\n';
}
return formatted;
};
All of the javascript functions given here won't work for an xml document having unspecified white spaces between the end tag '>' and the start tag '<'. To fix them, you just need to replace the first line in the functions
var reg = /(>)(<)(\/*)/g;
by
var reg = /(>)\s*(<)(\/*)/g;
what about creating a stub node (document.createElement('div') - or using your library equivalent), filling it with the xml string (via innerHTML) and calling simple recursive function for the root element/or the stub element in case you don't have a root. The function would call itself for all the child nodes.
You could then syntax-highlight along the way, be certain the markup is well-formed (done automatically by browser when appending via innerHTML) etc. It wouldn't be that much code and probably fast enough.
If you are looking for a JavaScript solution just take the code from the Pretty Diff tool at http://prettydiff.com/?m=beautify
You can also send files to the tool using the s parameter, such as:
http://prettydiff.com/?m=beautify&s=https://stackoverflow.com/
You can get pretty formatted xml with xml-beautify
var prettyXmlText = new XmlBeautify().beautify(xmlText,
{indent: " ",useSelfClosingElement: true});
indent:indent pattern like white spaces
useSelfClosingElement: true=>use self-closing element when empty element.
JSFiddle
Original(Before)
<?xml version="1.0" encoding="utf-8"?><example version="2.0">
<head><title>Original aTitle</title></head>
<body info="none" ></body>
</example>
Beautified(After)
<?xml version="1.0" encoding="utf-8"?>
<example version="2.0">
<head>
<title>Original aTitle</title>
</head>
<body info="none" />
</example>
For a current project I had the need to prettify and colorize XML without extra libraries. The following self contained code works quite well.
function formatXml(xml,colorize,indent) {
function esc(s){return s.replace(/[-\/&<> ]/g,function(c){ // Escape special chars
return c==' '?' ':'&#'+c.charCodeAt(0)+';';});}
var sm='<div class="xmt">',se='<div class="xel">',sd='<div class="xdt">',
sa='<div class="xat">',tb='<div class="xtb">',tc='<div class="xtc">',
ind=indent||' ',sz='</div>',tz='</div>',re='',is='',ib,ob,at,i;
if (!colorize) sm=se=sd=sa=sz='';
xml.match(/(?<=<).*(?=>)|$/s)[0].split(/>\s*</).forEach(function(nd){
ob=('<'+nd+'>').match(/^(<[!?\/]?)(.*?)([?\/]?>)$/s); // Split outer brackets
ib=ob[2].match(/^(.*?)>(.*)<\/(.*)$/s)||['',ob[2],'']; // Split inner brackets
at=ib[1].match(/^--.*--$|=|('|").*?\1|[^\t\n\f \/>"'=]+/g)||['']; // Split attributes
if (ob[1]=='</') is=is.substring(ind.length); // Decrease indent
re+=tb+tc+esc(is)+tz+tc+sm+esc(ob[1])+sz+se+esc(at[0])+sz;
for (i=1;i<at.length;i++) re+=(at[i]=="="?sm+"="+sz+sd+esc(at[++i]):sa+' '+at[i])+sz;
re+=ib[2]?sm+esc('>')+sz+sd+esc(ib[2])+sz+sm+esc('</')+sz+se+ib[3]+sz:'';
re+=sm+esc(ob[3])+sz+tz+tz;
if (ob[1]+ob[3]+ib[2]=='<>') is+=ind; // Increase indent
});
return re;
}
See https://jsfiddle.net/dkb0La16/
Or just print out the special HTML characters?
Ex: <xmlstuff>
<node />
</xmlstuff>
Horizontal tab
Line feed
XMLSpectrum formats XML, supports attribute indentation and also does syntax-highlighting for XML and any embedded XPath expressions:
XMLSpectrum is an open source project, coded in XSLT 2.0 - so you can run this server-side with a processor such as Saxon-HE (recommended) or client-side using Saxon-CE.
XMLSpectrum is not yet optimised to run in the browser - hence the recommendation to run this server-side.
here is another function to format xml
function formatXml(xml){
var out = "";
var tab = " ";
var indent = 0;
var inClosingTag=false;
var dent=function(no){
out += "\n";
for(var i=0; i < no; i++)
out+=tab;
}
for (var i=0; i < xml.length; i++) {
var c = xml.charAt(i);
if(c=='<'){
// handle </
if(xml.charAt(i+1) == '/'){
inClosingTag = true;
dent(--indent);
}
out+=c;
}else if(c=='>'){
out+=c;
// handle />
if(xml.charAt(i-1) == '/'){
out+="\n";
//dent(--indent)
}else{
if(!inClosingTag)
dent(++indent);
else{
out+="\n";
inClosingTag=false;
}
}
}else{
out+=c;
}
}
return out;
}
Xml formatting can be done by parsing the xml, adding or changing text nodes in the dom tree for indentation and then serializing the DOM back to xml.
Please check formatxml function in https://jsonbrowser.sourceforge.io/formatxml.js
You can see the function in action in https://jsonbrowser.sourceforge.io/
under the Xml tab.
Below is the simplified code.
formatxml.js adds error checking, optional removal of comments, indent as a parameter and handles non-space text between parent nodes.
const parser = new DOMParser();
const serializer = new XMLSerializer();
function formatXml(xml) {
let xmlDoc = parser.parseFromString(xml, 'application/xml');
let rootElement = xmlDoc.documentElement;
indentChildren(xmlDoc, rootElement, "\n", "\n ");
xml = serializer.serializeToString(xmlDoc);
return xml;
}
function indentChildren(xmlDoc, node, prevPrefix, prefix) {
let children = node.childNodes;
let i;
let prevChild = null;
let prevChildType = 1;
let child = null;
let childType;
for (i = 0; i < children.length; i++) {
child = children[i];
childType = child.nodeType;
if (childType != 3) {
if (prevChildType == 3) {
// Update prev text node with correct indent
prevChild.nodeValue = prefix;
} else {
// Create and insert text node with correct indent
let textNode = xmlDoc.createTextNode(prefix);
node.insertBefore(textNode, child);
i++;
}
if (childType == 1) {
let isLeaf = child.childNodes.length == 0 || child.childNodes.length == 1 && child.childNodes[0].nodeType != 1;
if (!isLeaf) {
indentChildren(xmlDoc, child, prefix, prefix + " ");
}
}
}
prevChild = child;
prevChildType =childType;
}
if (child != null) {
// Previous level indentation after last child
if (childType == 3) {
child.nodeValue = prevPrefix;
} else {
let textNode = xmlDoc.createTextNode(prevPrefix);
node.append(textNode);
}
}
}
Reference: https://www.w3schools.com/XML/dom_intro.asp
var formatXml = this.formatXml = function (xml) {
var reg = /(>)(<)(\/*)/g;
var wsexp = / *(.*) +\n/g;
var contexp = /(<.+>)(.+\n)/g;
xml = xml.replace(reg, '$1\n$2$3').replace(wsexp, '$1\n').replace(contexp, '$1\n$2');
var pad = 0;
var formatted = '';
var lines = xml.split('\n');
var indent = 0;
var lastType = 'other';
var reg = /(>)\s*(<)(\/*)/g;
xml = xml.replace(/\r|\n/g, ''); //deleting already existing whitespaces
xml = xml.replace(reg, '$1\r\n$2$3');
Use above method for pretty print and then add this in any div by using jquery text() method. for example id of div is xmldiv then use :
$("#xmldiv").text(formatXml(youXmlString));
You could also use Saxon-JS client-side:
<script src="SaxonJS/SaxonJS2.js"></script>
<script>
let myXML = `<root><node/></root>`;
SaxonJS.getResource({
text: myXML.replace(`xml:space="preserve"`, ''),
type: "xml"
}).then(doc => {
const output = SaxonJS.serialize(doc, {method: "xml", indent: true, "omit-xml-declaration":true});
console.log(output);
})
</script>
Saxon-JS Installation client-side
Saxon-JS Download page
This may involve creating nodes as objects, but you can have total control over exporting pretty formatted xml.
The following will return a string array of the lines which you can join with a new line delimiter "\n".
/**
* The child of an XML node can be raw text or another xml node.
*/
export type PossibleNode = XmlNode | string;
/**
* Base XML Node type.
*/
export interface XmlNode {
tag: string;
attrs?: { [key: string]: string };
children?: PossibleNode[];
}
/**
* Exports the given XML node to a string array.
*
* #param node XML Node
* #param autoClose Auto close the tag
* #param indent Indentation level
* #returns String array
*/
export function xmlNodeToString(
node: XmlNode,
autoClose: boolean = true,
indent: number = 0
): string[] {
const indentStr = " ".repeat(indent);
const sb: string[] = [];
sb.push(`${indentStr}<${node.tag}`);
if (node.attrs) {
for (const key in node.attrs) {
sb.push(`${indentStr} ${key}="${node.attrs[key]}"`);
}
}
if (node.children) {
if (node.children.length === 1 && typeof node.children[0] === "string") {
sb[sb.length - 1] += ">" + node.children[0];
} else {
sb.push(`${indentStr}>`);
for (const child of node.children) {
if (typeof child === "string") {
sb.push(`${indentStr} ${child}`);
} else {
const lines = xmlNodeToString(child, autoClose, indent + 1);
sb.push(...lines.map((line) => `${indentStr} ${line}`));
}
}
}
if (autoClose) {
if (node.children.length === 1 && typeof node.children[0] === "string") {
sb[sb.length - 1] += `</${node.tag}>`;
} else {
sb.push(`${indentStr}</${node.tag}>`);
}
}
} else {
if (autoClose) {
sb.push(`${indentStr}/>`);
} else {
sb.push(`${indentStr}>`);
}
}
return sb;
}
Updates appreciated on the gist: https://gist.github.com/rodydavis/acd609560ab0416b60681fddabc43eee
Xml-to-json library has method formatXml(xml). I am the maintainer of the project.
var prettyXml = formatXml("<a><b/></a>");
// <a>
// <b/>
// </a>
This my version, maybe usefull for others, using String builder
Saw that someone had the same piece of code.
public String FormatXml(String xml, String tab)
{
var sb = new StringBuilder();
int indent = 0;
// find all elements
foreach (string node in Regex.Split(xml,#">\s*<"))
{
// if at end, lower indent
if (Regex.IsMatch(node, #"^\/\w")) indent--;
sb.AppendLine(String.Format("{0}<{1}>", string.Concat(Enumerable.Repeat(tab, indent).ToArray()), node));
// if at start, increase indent
if (Regex.IsMatch(node, #"^<?\w[^>]*[^\/]$")) indent++;
}
// correct first < and last > from the output
String result = sb.ToString().Substring(1);
return result.Remove(result.Length - Environment.NewLine.Length-1);
}

Categories