Safari Won't Work With Microsoft.XMLDOM ActiveX Object - javascript

I'm designing a client side script that will read an XML file and display it, like this:
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
function loadXML(xmlFile) {
xmlDoc.async = "false";
xmlDoc.onreadystatechange = verify;
xmlDoc.load(xmlFile);
}
function verify() {
if(xmlDoc.readyState != 4) {
return false;
}
}
function traverse(tree) {
if(tree.hasChildNodes()) {
document.write('<ul><li>');
document.write('<b>' + tree.tagName + ': </b>');
var nodes = tree.childNodes.length;
for(var i = 0; i < tree.childNodes.length; i++) {
traverse(tree.childNodes(i));
}
document.write('</il></ul>');
} else {
document.write(tree.text);
}
}
function initTraverse(file) {
loadXML(file);
var doc = xmlDoc.documentElement;
traverse(doc);
}
When I fired Safari I saw that nothing was displayed, then I've opened the Error Console and what I got was this:
ReferenceError: Can't find variable: ActiveXObject
What should I do to make this work?
PS: I would prefer if this page could be capable of running at Mobile Safari

ActiveXObject do not work outside of internet explorer.
There are a few alternative xml parser's and handlers like E4X. Although E4X is currently only done in firefox (https://developer.mozilla.org/En/E4X/Processing_XML_with_E4X).
If using jQuery is an option then you can look into marcgrabanski.com/articles/jquery-makes-parsing-xml-easy

Some interesting stuff going on there. Most interesting is the async = false line. You probably want to re-consider that bit. In order to change to an asynchronous request, you would have to re-write some other code and remove the document.write calls.
Regardless, here is a (untested but hopefully) drop in replacement for what you have using XMLHttpRequest instead of an xml document.
var xmlDoc = null;
function loadXML(xmlFile) {
var request = new XMLHttpRequest();
request.open('GET', xmlFile, false); // false is synchronous
request.send();
xmlDoc = request.responseXML;
}
You may have to do some debugging...

You should have something cross-browser compatible with either DOMParser or DOMDocument. Of course, I'm not sure if you're wanting to parse a XML URL or a XML string. For a XML URL, I recommend:
if (window.XMLHttpRequest) return new window.XMLHttpRequest();
else if (window.ActiveXObject) {
// the many versions of IE's XML fetchers
var AXOs = [
'MSXML2.XMLHTTP.6.0',
'MSXML2.XMLHTTP.5.0',
'MSXML2.XMLHTTP.4.0',
'MSXML2.XMLHTTP.3.0',
'MSXML2.XMLHTTP',
'Microsoft.XMLHTTP',
'MSXML.XMLHTTP'
];
for (var i = 0; i < AXOs.length; i++) {
try { return new ActiveXObject(AXOs[i]); }
catch() { continue; }
}
return null;
}
For a XML string, this code block would work better:
if (window.DOMParser) return (new DOMParser()).parseFromString(str, 'text/xml');
else if (window.ActiveXObject) {
var doc;
// the many versions of IE's DOM parsers
var AXOs = [
'MSXML2.DOMDocument.6.0',
'MSXML2.DOMDocument.5.0',
'MSXML2.DOMDocument.4.0',
'MSXML2.DOMDocument.3.0',
'MSXML2.DOMDocument',
'Microsoft.XMLDOM',
'MSXML.DOMDocument'
];
for (var i = 0; i < AXOs.length; i++) {
try { doc = new ActiveXObject(AXOs[i]); break; }
catch() { continue; }
}
if (!doc) return createElement('div', null);
if (doc.async) doc.async = false;
doc.loadXML(str);
return doc;
}
return createElement('div', null);
The DOMDocument objects do support a load() method for loading XML from a URL, but it's a different syntax than the XMLHttpRequest and XMLHTTP methods.
The DOMDocument appears (at least from the MSDN docs) to also contain the XMLHTTP methods, so you could interlace DOMDocument in the AXOs array, but I'm not certain about that. Plus, I can't imagine DOMDocument being in place without XMLHTTP.

Related

XSLT not working on IE 11, doesn't transform xml

Trying to render XSLT stylesheet that's coming from API, thought it's working fine on Chrome, FF except IE.
I tried using the example from w3c which works but that's calling the XML and XSLT from a file, where as mine is coming from AJAX call success response.
W3school sample XSLT sample
My version is this
function getJson() {
$.get(url)..
var json2XMLResult = J2XML.json2xml_str(data);
getResultXsl(json2XMLResult )
}
function getResultXsl(json2xml) {
$.get(url)
.then(function (data) {
let resDefinition = data.Results.ResponseDisplayDefinition;
let xmlString = '<?xml version="1.0"?><Response>' + json2xml + '</Response>';
if (typeof DOMParser != "undefined") {
parseXml = function (xmlStr) {
return (new DOMParser()).parseFromString(xmlStr, "text/xml");
};
}
else if (typeof ActiveXObject != "undefined" &&
new ActiveXObject("Microsoft.XMLDOM")) {
parseXml = function (xmlStr) {
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
xmlDoc.loadXML(xmlStr);
return xmlDoc;
};
}
else {
throw new Error("No XML parser found");
}
displayResult(xmlString, resDefinition);
})
}
Displaying XSLT in the html, the alert() below does show whether you're trying to render it on Chrome or IE,
function displayResult(xmlStrToConvert, xslStrToConvert) {
var xmlConverted = parseXml(xmlStrToConvert);
var xslConverted = parseXml(xslStrToConvert);
if (window.ActiveXObject || "ActiveXObject" in window) {
alert('It is IE but not showing anything');
var ex = xmlConverted.transformNode(xslConverted)
$('#xmlJson').append(ex);
} else {
alert('its not IE');
// code for Chrome, Firefox, Opera, etc.
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslConverted);
var resultDocument = xsltProcessor.transformToFragment(xmlConverted, document);
$('#xmlJson').append(resultDocument);
}
}
Also tried var ex= xmlConverted.transformToFragment(xslConverted, document);
Can someone point-out what's wrong with this? Also couldn't open dev tool on IE11 which is harder to debug, but I can tell its something wrong with my code above.
Edit
Ajax Call with beforeSend can someone check if the below code is fine, though the transformNode() is returning Object doesn't support property or method 'transformNode' or XSLTProcessor() not defined
function transformXML(json2xml) {
$.ajax({
type: 'GET',
url: window.parent.__env.apiManagement + 'Preview/TypeDefinition?objectName=' + apiObjectResponse,
beforeSend: function (xhr, settings) {
if (window.ActiveXObject) {
xhr = new ActiveXObject("Msxml2.XMLHTTP");
}
else {
xhr = new XMLHttpRequest();
}
try { xhr.responseType = "msxml-document"; } catch (err) { }
},
success: function (data, status, xhr) {
var parseXml = new DOMParser();
var xslStylesheet = parseXml.parseFromString(data.Results.ResponseDisplayDefinition, "text/xml");
var xmlString = '<?xml version="1.0"?><Response>' + json2xml + '</Response>';
var convertedXML = parseXml.parseFromString(xmlString, "text/xml");
// // cross-browser logic omitted for simplicity
if(window.ActiveXObject || xhr.responseType == "msxml-document") {
var ex = convertedXML.transformNode(xslStylesheet);
console.log('>>> ', convertedXML)
alert(xmlString)
$('#xmlJson').append(ex);
}
// code for Chrome, Firefox, Opera, etc.
else if (document.implementation && document.implementation.createDocument) {
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(xslStylesheet);
var resultDocument = xsltProcessor.transformToFragment(convertedXML, document);
$('#xmlJson').append(resultDocument);
}
}
});
}
IE 11 supports DOMParser but using it builds an IE XML DOM document which does not have any support for XSLT. So you at least need to change the order of checks, if you are coding for IE and want to do XSLT then make sure you create an MSXML DOM document using ActiveXObject, then you can use transformNode on it.
As you seem to want to parse XML and XSLT from strings and then use client-side XSLT transformation I would suggest to use an approach like in https://martin-honnen.github.io/xslt/2016/test2016123001.html, which does
function parseXmlStringForTransformation(xml) {
try {
var doc = new ActiveXObject('Msxml2.DOMDocument.6.0');
doc.loadXML(xml);
return doc;
}
catch (e) {
var domParser = new DOMParser();
var doc = domParser.parseFromString(xml, 'application/xml');
return doc;
}
}
and then uses XSLTProcessor where supported or the corresponding MSXML 6 ActiveX XSLT API to run the transformation:
function transform(xmlDoc, xslDoc, xsltParams, targetElement) {
if (typeof XSLTProcessor !== 'undefined') {
var proc = new XSLTProcessor();
proc.importStylesheet(xslDoc);
for (var prop in xsltParams) {
proc.setParameter(null, prop, xsltParams[prop]);
}
var resultFrag = proc.transformToFragment(xmlDoc, targetElement.ownerDocument);
targetElement.textContent = '';
targetElement.appendChild(resultFrag);
}
else {
var template = new ActiveXObject('Msxml2.XslTemplate.6.0');
template.stylesheet = xslDoc;
var proc = template.createProcessor();
for (var prop in xsltParams) {
proc.addParameter(prop, xsltParams[prop]);
}
proc.input = xmlDoc;
proc.transform();
var resultHTML = proc.output;
targetElement.innerHTML = resultHTML;
}
}
You can then use that as in
document.addEventListener('DOMContentLoaded', function() {
transform(
parseXmlStringForTransformation('<root>...<\/root>'),
parseXmlStringForTransformation('<xsl:stylesheet ...>...<\/xsl:stylesheet>'),
{ }, // empty parameter object if you don't want to pass parameters from Javascript to XSLT
document.getElementById('d1') // target element in your HTML to insert the transformation result into
);
})

Linearize or unindent XML in javascript

I wonder how to linearize or unindent an XML text using Javascript.
This post Unindent or linearize XML describes how to do it using Java, but I saw no example using JavaScript.
As I indicated in the comment on your answer, regex is not a reliable way to do this. A much more reliable approach is to parse the XML, remove any text nodes that are all whitespace, and then re-serialize it:
function parseXml(txt) {
var parser, xmlDoc;
if (window.DOMParser) {
parser = new DOMParser();
xmlDoc = parser.parseFromString(txt, "text/xml");
} else // Internet Explorer
{
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.loadXML(txt);
}
return xmlDoc;
}
function serializeXml(node) {
try {
// XMLSerializer exists in certain browsers
var serializer = new XMLSerializer();
return serializer.serializeToString(node);
} catch (e) {
// Internet Explorer has a different approach to serializing XML
return elem.xml;
}
}
function removeWhitespace(node) {
if (node.childNodes && node.childNodes.length) {
Array.prototype.slice.call(node.childNodes).forEach(removeWhitespace);
}
if ((node.nodeType === 3 || node.nodeType === 4) &&
/^[ \r\n\t]*$/.test(node.textContent)) {
node.parentNode.removeChild(node);
}
}
var startXml = '<products>\n\t<product>\n\t\t<code>1234</code>\n\t\t<name>Widget 3000</name>\n\t</product>\n</products>'
console.log('Before:');
console.log(startXml);
var dom = parseXml(startXml);
removeWhitespace(dom);
var endXml = serializeXml(dom);
console.log('After:');
console.log(endXml);
As tebs1200 suggested, I ported (and even improved) the regular expression from the Java post to Javascript.
Here it is:
// This Javascript function is to linearize and return the XML input String
function linearize(xml) {
return (xml!= null) ? xml.trim().replace(/(>|>){1,1}( |\t|\n|\r|\s)*(<|<){1,1}/g, "$1$3") : null;
}

getElementsByTagName not working in IE when returning XML file?

I'm trying to select certain elements from an XML file via JavaScript, I've got it working in Firefox, Chrome and IE9+ but IE8 is proving to be a real stumbling block, I'm using the code below to return the XML file:
function httpGet(theUrl) {
var xmlHttp = null;
xmlHttp = new XMLHttpRequest();
xmlHttp.open('GET', theUrl, false);
xmlHttp.send();
if(window.DOMParser) {
var parser = new DOMParser();
var doc = parser.parseFromString(xmlHttp.responseText, 'text/xml');
return doc;
}
else {
var xmlDocument = new ActiveXObject('Microsoft.XMLDOM');
xmlDocument.async = false;
xmlDocument.loadXML(xmlHttp.responseText);
return xmlDocument;
}
}
I'm then using the below selectors to make a list of certain items in the XML file:
var rssData = httpGet(rssFeed);
var allTitles = convertToArray(rssData.getElementsByTagName('title'));
var allDates = convertToArray(rssData.getElementsByTagName('pubDate'));
var allText = convertToArray(rssData.getElementsByTagName('text'));
I then concatenate the array items together and set an items innerHTML to that value (I can provide this code if needed)
the strange thing is that IE8 returns the right number of items, but each item has the value undefined as oppsed to the actual value e.g. "Hello world"
I've been battling this for hours and still come up blank ... does anybody have any idea what I'm doing wrong?
EDIT: as requested here is the convert to array function
function convertToArray(htmlCollection) {
var nodes = [];
var collectionLength = htmlCollection.length;
for(i = 0; i < collectionLength; i++) {
nodes.push(htmlCollection[i]);
}
return nodes;
}
The problem was that you'd have to use .text and not .textContent for ie8, because the textContent property doesn't exist in ie.
You can see in MDN that textContent is available only for ie 9+

Can't get feed in Windows 8 app

I am creating blog reader app for Windows 8 by using RSS feeds. Part of code:
function downloadBlogFeed() {
WinJS.xhr({ url: "http://feeds.feedburner.com/CssTricks" }).then(function (rss) {
var items = rss.responseXML.querySelectorAll("item");
for (var n = 0; n < items.length; n++) {
var article = {};
article.title = items[n].querySelector("title").textContent;
var thumbs = items[n].querySelectorAll("thumbnail");
if (thumbs.length > 1) {
article.thumbnail = thumbs[1].attributes.getNamedItem("url").textContent;
article.content = items[n].textContent;
articlesList.push(article);
}
}
});
}
So, my app can't read feed from FeedBurner. I get this error
Can't load http://feeds.feedburner.com/~d/styles/itemcontent.css. An app can’t load remote web content in the local context.
I've tried http://feeds.feedburner.com/CssTricks?format=xml and http://feeds.feedburner.com/CssTricks?fmt=xml, but the same error.
EDIT: Full code: http://jsfiddle.net/8n67y/
The error you're encountering isn't because you can't read from Feedburner. It's because somewhere in the content that you're attempting to load into the DOM is a reference to a CSS file on the web (itemcontent.css).
When you're operating in the local context, you cannot dynamically load script or CSS from the web, because that poses security risks in the local context.
See here:
http://msdn.microsoft.com/en-us/library/windows/apps/hh465380.aspx
and here:
http://msdn.microsoft.com/en-us/library/windows/apps/hh465373.aspx
for more information on the differences between local and web context, and the restrictions that apply to each.
For your particular case, I think what you should try is parsing the content further (you can set a breakpoint in the above code to examine the XML content being returned by the feed) to determine where the CSS file reference is being returned and either remove it programmatically, if it's in a consistent place, or find another means of eliminating the CSS reference, which appears to be what's causing the exception (based on the limited information above).
What you are trying to do can be done with the below code, instead of WinJS.xhr use the xmlHTTPRequest.
The code below is part of the code I use um my RSS reader and it works pretty well in all situations, we can donwload pictures, text, links.. whatever you can find in the feed (http://feeds.feedburner.com/CssTricks) getting the thumbnails, and all worked fine.
Also I tested it with the following modification,
function connectToURL() {
var url = "";
xmlHttp = GetXmlHttpObject();
if (xmlHttp == null) {
return;
}
xmlHttp.onreadystatechange = stateChanged;
xmlHttp.open("GET", url,true);
xmlHttp.send(null);
}
// your job will actually start on this one...
function stateChanged() {
if(xmlHttp != null )
if (xmlHttp[item.key].readyState == 4 ) {
try {
var xmlDoc = xmlHttp.responseXML.documentElement.getElementsByTagName("TAGYOUWANTTOGET");
for (var i = 0; i < xmlDoc.length; i++) {
xmlDoc[i].getElementsByTagName("TAG")[0].childNodes[0].nodeValue
}
} catch (e) {
//work on the exception
}
}
}
}
function GetXmlHttpObject() {
var xmlHttp = null;
try {
xmlHttp = new XMLHttpRequest();
}
catch(e) {
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xmlHttp;
}

TypeError: Value undefined (result of expression xmlDoc.load) is not object

I am trying to load an XML file using Javascript and I have yet to find a good function that works in IE, Firefox, and Safari. The load function I am currently using is basically the one straight out of the w3schools tutorials:
http://www.w3schools.com/XML/tryit.asp?filename=tryxml_dom_createelement
The exact code looks like:
if (window.ActiveXObject)
{
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
}
// code for Mozilla, Firefox, Opera, etc.
else if (document.implementation.createDocument)
{
xmlDoc=document.implementation.createDocument("","",null);
}
else
{
alert('Your browser cannot handle this script');
}
xmlDoc.async=false;
xmlDoc.load(dname);
Where dname = the url of the xml file. This code gets a "TypeError: Value undefined (result of expression xmlDoc.load) is not object." in Safari.
I have also tried the code on this site:
http://developer.apple.com/internet/webcontent/xmlhttpreq.html
However, it yields a null XML file. Can anyone help?
Sounds like the problem is that Safari does not support document.implementation.createDocument as a method to fetch and load XML sources. You must use an XMLHttpRequest to fetch and parse the XML AFAIK.
I've tried a modified version of the code from the Apple tutorial you linked and it seemed to work for me. This code is not the best in the world, and it's missing a lot of error handling, but it's the only proof of concept I had on hand.
Note: I highly recommend using a library. There are browser inconsistencies abound with XMLHttpRequests and XML parsing. It's worth the investment!
For a non library version I used a modified version of the safari code to get the XMLHttpRequest:
function getXHR(url,callback) {
var req = false;
// branch for native XMLHttpRequest object
if(window.XMLHttpRequest && !(window.ActiveXObject)) {
try {
req = new XMLHttpRequest();
} catch(e) {
req = false;
}
// branch for IE/Windows ActiveX version
} else if(window.ActiveXObject) {
try {
req = new ActiveXObject("Msxml2.XMLHTTP");
} catch(e) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
req = false;
}
}
}
if(req) {
req.onreadystatechange = function() { callback( req ) };
req.open("GET", url, true);
req.send("");
}
}
Grabbing the XML from the result is not without its own quirks as well:
function getXML( response ) {
if( response.readyState==4 ) {
//Get the xml document element for IE or firefox
var xml;
if ( response.responseXML ) {
xml = new ActiveXObject("Microsoft.XMLDOM");
xml.async = false;
xml.loadXML(response.responseText);
} else {
xml = response.responseXML;
}
return xml;
}
return null;
}
Finally use what you get:
function callback( response ) {
var xmlDoc = getXML( response );
if( xmlDoc ) {
//do your work here
...
}
}
If you still find yourself having trouble there are a few things you can check that will likely solve your problem.
Did you set your content type to text/xml?
Is your request actually making it to the server and back?
When you alert/examine the responseText, do you see anything that does not belong?
Is your XML properly formatted? Run it through a validator.
Best of luck! Cheers.
You might want to look at XML for <Script>. I've seen some posts that indicate that they've solved the problem on Safari with it.

Categories