How to get SVG document data to be inserted into the DOM? - html

I'm trying to load an SVG file from a CDN using this code:
<object type="image/svg+xml" data="https://cdn.mywebsite.com/path/file.svg"></object>
The SVG file is in an S3 bucket served by CloudFront CDN. It's content-type is set to "image/svg+xml". To the best of my knowledge, my S3 bucket and CDN are configured to allow CORS requests from any origin.
If I look at the browser Network tab, the HTTP GET request succeeds and it receives the SVG data in response. However, the document data is not inserted into the DOM, so it doesn't display and cannot be accessed by JavaScript.
How can I embed the SVG document into my webpage so that it becomes an accessible part of the DOM?

As Robert Longson kindly commented, embedding directly is impossible. But I did find a way.
I used this JavaScript (uses axios library):
var parser = new DOMParser();
let response = await axios.get('https://cdn.mywebsite.com/path/file.svg');
let svgDocument = parser.parseFromString(response.data, "image/svg+xml")
If CORS is configured correctly it allows the request, then you can parse the response body into a document. From there you can process the document or embed it.

Provided you are allowed to read the SVG, this native Web Component <load-svg>
reads the SVG as text
adds SVG to shadowDOM as DOM element
moves the style element from lightDOM to shadowDOM
So style is only applied to current SVG
<load-svg src="//graphviz.org/Gallery/directed/fsm.svg">
<style>
svg { height:180px }
text { stroke: green }
path { stroke: red ; stroke-width:3 }
</style>
</load-svg>
<script>
customElements.define('load-svg', class extends HTMLElement {
async connectedCallback() {
let src = this.getAttribute("src");
let svg = await (await fetch(src)).text();
this.attachShadow({ mode: 'open' }).innerHTML = svg;
this.shadowRoot.append(this.querySelector("style"));
}
});
</script>
More complex example: How to make an svg interactive to gather comments/annotations on depicted elements

Related

How to access document/DOM in background.js?

I'm trying to access the DOM in my background script to create a canvas element. How do I access the DOM in background.js in manifest v3? I know it's accessible in v2.
I'm creating this element as part of chrome.tabs.captureVisibleTab(), which is an API method specific to background.js
chrome.tabs.get(tabId, function(tab) {
chrome.tabs.captureVisibleTab(tab.windowId, { format: "png" }, function(dataUrl) {
(console.log("chrome.tabs.captureVisibleTab() called"))
if (!canvas) {
canvas = document.createElement("canvas");
document.body.appendChild(canvas);
...
I get an error currently: Error handling response: ReferenceError: document is not defined at chrome-extension://.../background.js
I tried passing the document to background script from content script like this:
chrome.runtime.sendMessage({document: document}, function(response) {})
but the passed document isn't an object and doesn't have normal DOM methods like createElement

Problem setting up multiple 3D models using a url as source with ar.js and A-frame

I'm just recently learning to code for A-frame projects. When trying to set up a scene with multiple targets in A-fram, the 3D model that was supposed to be displayed by one of them doesn´t appear. The model, like the marker, uses an URL as the original but only the latter works.
Separating the ar.js and A-frame proved that the problem was apparently on ar.js, however, I may be mistaken.
You can find my code in this link since it is giving me too much trouble to post here due to the URL in the code. The file's name is "Problem code":
https://github.com/BrandexGlobal/ARDuratex/tree/master
In HTML, you can use base64 data uris OR blobs for turning raw content into a viable url. This url should work in any place that any other url would work. I say should because I am not familiar with the framework that you are using (I'm anti-framework and pro-vanilla), so there is a slight chance that your framework might mess something up.
🢔 Because it appears as though you are new to Stackoverflow, I shall explain this checkmark here. After reading and reviewing my answer, if (and only if) you are thoroughly satisfied with the answer I have posted here, then you can reward me by clicking this checkmark to accept this answer as the best answer. If someone else posts a better answer, then click their checkmark. Clicking a checkmark is not permanent: if someone later on post a better answer, then you can click their checkmark and switch the best answer over to the new answer posted.
Example of plain ordinary image:
(function(mainImage) {
mainImage.title = mainImage.src;
})(document.getElementById("mainImage"));
<img src="https://i.stack.imgur.com/Lr5Kg.jpg" id="mainImage"/>
Example of using base64 data URI (notice how the url is the image itself). Also, see Jpillora's base64 encoder and this demo page.
(function(mainImage) {
mainImage.title = mainImage.src;
})(document.getElementById("mainImage"));
<img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAgACADASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAABQYHCAT/xAAwEAABAwMBBQYFBQAAAAAAAAABAgMEBQYRAAcSEyExIkFRYYGRFSMyUpJCQ3Gxwf/EABcBAAMBAAAAAAAAAAAAAAAAAAEFBgT/xAAnEQABAwMCBQUBAAAAAAAAAAABAgMEAAURIcETFDFxgRJBUWGR0f/aAAwDAQACEQMRAD8Arm0G/Ztv1V6mUimImS0xEPArcCUpWtZAz/ATnHmNJlmVRFx14wbqjvVefwy86t5zeisjPIBv6MemlG5p7tQr1VkuOElya/knuCFlAHoEDQ6FVYASFxXHDNc+SXVIcAS31yMYBORjn46WQrhJekmO0jIJ8/vsM/VO7jaIzELmHVkEDxntkZ7ZqwXjCp9GgLdtxj4TUm+20uCCyhXiChHZPqDo7ZV5yJ7VNZrbYQ5Nb3o0lGNx0/accs94I5Hy6aiFz3/LQ1wGVr4RRub+/wCXjoxRZ7tI2OpqS3kuFK0TISRyLR4wXufmF9O7Ti+nkG2lA5JUAR85/lS1iDktxSVDQjSuDatCNr7QpbTzWKZU1mSyru7f1p/PPuNBZbLrMNC6dDjTkI/YcOCPMH/NaS2mWfHvK23IL7aS8k77Ln6m1eR1nCRaV32w8ph+lyZsYckLZQV8tTsuE407x2N9iDV3CuDMuPy75AIGNcYOOnUEZpQlRriuyayy5TRT22zucVbIZSBn3XqyxUpmzKTalLjrTGdIQsobwhLAOVnPTpn30t0eFX57m5FtyoIe6Bb4KAg+PMHVw2Z2tJocZ2TV18epSAAt1fUD7B4D+9BaJVzcb46AhtvoNdyc1icEW3BamletxXbbSv/Z" id="mainImage"/>
Example of using a Blob URI. Notice how the url is dynamically generated similar to a memory address pointer.
(function(){
var rawData = atob("/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAgACADASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAABQYHCAT/xAAwEAABAwMBBQYFBQAAAAAAAAABAgMEBQYRAAcSEyExIkFRYYGRFSMyUpJCQ3Gxwf/EABcBAAMBAAAAAAAAAAAAAAAAAAEFBgT/xAAnEQABAwMCBQUBAAAAAAAAAAABAgMEAAURIcETFDFxgRJBUWGR0f/aAAwDAQACEQMRAD8Arm0G/Ztv1V6mUimImS0xEPArcCUpWtZAz/ATnHmNJlmVRFx14wbqjvVefwy86t5zeisjPIBv6MemlG5p7tQr1VkuOElya/knuCFlAHoEDQ6FVYASFxXHDNc+SXVIcAS31yMYBORjn46WQrhJekmO0jIJ8/vsM/VO7jaIzELmHVkEDxntkZ7ZqwXjCp9GgLdtxj4TUm+20uCCyhXiChHZPqDo7ZV5yJ7VNZrbYQ5Nb3o0lGNx0/accs94I5Hy6aiFz3/LQ1wGVr4RRub+/wCXjoxRZ7tI2OpqS3kuFK0TISRyLR4wXufmF9O7Ti+nkG2lA5JUAR85/lS1iDktxSVDQjSuDatCNr7QpbTzWKZU1mSyru7f1p/PPuNBZbLrMNC6dDjTkI/YcOCPMH/NaS2mWfHvK23IL7aS8k77Ln6m1eR1nCRaV32w8ph+lyZsYckLZQV8tTsuE407x2N9iDV3CuDMuPy75AIGNcYOOnUEZpQlRriuyayy5TRT22zucVbIZSBn3XqyxUpmzKTalLjrTGdIQsobwhLAOVnPTpn30t0eFX57m5FtyoIe6Bb4KAg+PMHVw2Z2tJocZ2TV18epSAAt1fUD7B4D+9BaJVzcb46AhtvoNdyc1icEW3BamletxXbbSv/Z");
var rawDataLength = rawData.length;
var typedArrayView = new Uint8Array(rawDataLength);
for (var i=0; i<rawDataLength; i=i+1|0)
typedArrayView[i] = rawData.charCodeAt(i);
var img = document.body.appendChild(document.createElement("img"));
img.src = img.title = URL.createObjectURL(new Blob([
typedArrayView
], {type: "image/jpeg"}));
})();
Another example is that we can encode the image as a Base64 data URI, then use an XMLHttpRequest to HTTP-get the data URI's contents, but set the returnType property of the XMLHttpRequest to Blob so that we get a blob from the base64 data URI.
(function(){
var xhr = new XMLHttpRequest;
xhr.responseType = "blob";
xhr.open("GET", document.getElementById("base64DataURIImage").src);
xhr.onload = function(){
var img=document.body.appendChild(document.createElement("img"));
img.src = img.title = URL.createObjectURL(xhr.response);
}
xhr.send();
})();
(function(mainImage) {
mainImage.title = mainImage.src;
})(document.getElementById("base64DataURIImage"));
As a base64 URI: <img id="base64DataURIImage" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAgACADASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAABQYHCAT/xAAwEAABAwMBBQYFBQAAAAAAAAABAgMEBQYRAAcSEyExIkFRYYGRFSMyUpJCQ3Gxwf/EABcBAAMBAAAAAAAAAAAAAAAAAAEFBgT/xAAnEQABAwMCBQUBAAAAAAAAAAABAgMEAAURIcETFDFxgRJBUWGR0f/aAAwDAQACEQMRAD8Arm0G/Ztv1V6mUimImS0xEPArcCUpWtZAz/ATnHmNJlmVRFx14wbqjvVefwy86t5zeisjPIBv6MemlG5p7tQr1VkuOElya/knuCFlAHoEDQ6FVYASFxXHDNc+SXVIcAS31yMYBORjn46WQrhJekmO0jIJ8/vsM/VO7jaIzELmHVkEDxntkZ7ZqwXjCp9GgLdtxj4TUm+20uCCyhXiChHZPqDo7ZV5yJ7VNZrbYQ5Nb3o0lGNx0/accs94I5Hy6aiFz3/LQ1wGVr4RRub+/wCXjoxRZ7tI2OpqS3kuFK0TISRyLR4wXufmF9O7Ti+nkG2lA5JUAR85/lS1iDktxSVDQjSuDatCNr7QpbTzWKZU1mSyru7f1p/PPuNBZbLrMNC6dDjTkI/YcOCPMH/NaS2mWfHvK23IL7aS8k77Ln6m1eR1nCRaV32w8ph+lyZsYckLZQV8tTsuE407x2N9iDV3CuDMuPy75AIGNcYOOnUEZpQlRriuyayy5TRT22zucVbIZSBn3XqyxUpmzKTalLjrTGdIQsobwhLAOVnPTpn30t0eFX57m5FtyoIe6Bb4KAg+PMHVw2Z2tJocZ2TV18epSAAt1fUD7B4D+9BaJVzcb46AhtvoNdyc1icEW3BamletxXbbSv/Z" />
<br /><br />
Then turned into a blob:
Resources:
For base64 data URIs, see this MDN page
For my own library for correctly encoding base64 URIs with high unicode characters, see this Github repository
For blobs, see this MDN page

"Tainted canvases may not be loaded" Cross domain issue with WebGL textures

I've learnt a lot in the last 48 hours about cross domain policies, but apparently not enough.
Following on from this question. My HTML5 game supports Facebook login. I'm trying to download profile pictures of people's friends. In the HTML5 version of my game I get the following error in Chrome.
detailMessage: "com.google.gwt.core.client.JavaScriptException:
(SecurityError) ↵ stack: Error: Failed to execute 'texImage2D' on
'WebGLRenderingContext': Tainted canvases may not be loaded.
As I understand it, this error occurs because I'm trying to load an image from a different domain, but this can be worked around with an Access-Control-Allow-Origin header, as detailed in this question.
The URL I'm trying to download from is
https://graph.facebook.com/1387819034852828/picture?width=150&height=150
Looking at the network tab in Chrome I can see this has the required access-control-allow-origin header and responds with a 302 redirect to a new URL. That URL varies, I guess depending on load balancing, but here's an example URL.
https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xap1/v/t1.0-1/c0.0.160.160/p160x160/11046398_1413754142259317_606640341449680402_n.jpg?oh=6738b578bc134ff207679c832ecd5fe5&oe=562F72A4&gda=1445979187_2b0bf0ad3272047d64c7bfc2dbc09a29
This URL also has the access-control-allow-origin header. So I don't understand why this is failing.
Being Facebook, and the fact that thousands of apps, games and websites display users profile pictures, I'm assuming this is possible. I'm aware that I can bounce through my own server, but I'm not sure why I should have to.
Answer
I eventually got cross domain image loading working in libgdx with the following code (which is pretty hacky and I'm sure can be improved). I've not managed to get it working with the AssetDownloader yet. I'll hopefully work that out eventually.
public void downloadPixmap(final String url, final DownloadPixmapResponse response) {
final RootPanel root = RootPanel.get("embed-html");
final Image img = new Image(url);
img.getElement().setAttribute("crossOrigin", "anonymous");
img.addLoadHandler(new LoadHandler() {
#Override
public void onLoad(LoadEvent event) {
HtmlLauncher.application.getPreloader().images.put(url, ImageElement.as(img.getElement()));
response.downloadComplete(new Pixmap(Gdx.files.internal(url)));
root.remove(img);
}
});
root.add(img);
}
interface DownloadPixmapResponse {
void downloadComplete(Pixmap pixmap);
void downloadFailed(Throwable e);
}
are you setting the crossOrigin attribute on your img before requesting it?
var img = new Image();
img.crossOrigin = "anonymous";
img.src = "https://graph.facebook.com/1387819034852828/picture?width=150&height=150";
It's was working for me when this question was asked. Unfortunately the URL above no longer points to anything so I've changed it in the example below
var img = new Image();
img.crossOrigin = "anonymous"; // COMMENT OUT TO SEE IT FAIL
img.onload = uploadTex;
img.src = "https://i.imgur.com/ZKMnXce.png";
function uploadTex() {
var gl = document.createElement("canvas").getContext("webgl");
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
try {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
log("DONE: ", gl.getError());
} catch (e) {
log("FAILED to use image because of security:", e);
}
}
function log() {
var div = document.createElement("div");
div.innerHTML = Array.prototype.join.call(arguments, " ");
document.body.appendChild(div);
}
<body></body>
How to check you're receiving the headers
Open your devtools, pick the network tab, reload the page, select the image in question, look at both the REQUEST headers and the RESPONSE headers.
The request should show your browser sent an Origin: header
The response should show you received
Access-Control-Allow-Methods: GET, OPTIONS, ...
Access-Control-Allow-Origin: *
Note, both the response AND THE REQUEST must show the entries above. If the request is missing Origin: then you didn't set img.crossOrigin and the browser will not let you use the image even if the response said it was ok.
If your request has the Origin: header and the response does not have the other headers than that server did not give permission to use the image to display it. In other words it will work in an image tag and you can draw it to a canvas but you can't use it in WebGL and any 2d canvas you draw it into will become tainted and toDataURL and getImageData will stop working
this is a classic crossdomain issue that happens when you're developing locally.
I use python's simple server as a quick fix for this.
navigate to your directory in the terminal, then type:
$ python -m SimpleHTTPServer
and you'll get
Serving HTTP on 0.0.0.0 port 8000 ...
so go to 0.0.0.0:8000/ and you should see the problem resolved.
You can base64 encode your texture.

PhantomJS: setContent not working when HTML has assets

This script works:
var page = require('webpage').create();
var html = '<h1>Test</h1><img>'; //works with page.setContent and page.content
//var html = '<h1>Test</h1><img src=".">'; //only works with page.content
page.setContent(html, 'http://github.com');
//page.content = html;
page.render('test.png');
phantom.exit();
but adding a src attribute to the img makes it fail silently (page.render returns false and no image is generated).
Setting page.content directly works in both cases but then relative URLs don't. The same thing happens with other tags that load a resource such as link. It doesn't matter whether the linked resource exists or not. Tested in 1.8.1 and 1.9.2.
Is this a bug or have I misunderstood the API?
You can not render webpage if it is not fully loaded.
When you are setting link or src to <img>, It will try to load image asynchronously.
So, it requires to wait for loading finished.
Try following code.
page.onLoadFinished = function(status) {
page.render('test.png');
phantom.exit();
};
page.setContent(html, 'http://github.com');

How to share java script methods between html and svg?

I have got a html page embedded with a .svg. I have written most of the logic in java script and would like to share the same .js between html and .svg file:
for ex: I have got the following in mylogic.js:
var xmlDoc = null;
function loadXmlDoc() {
xmlDoc = new XmlDoc.....
}
function onHtmlLoad() {
loadXmlDoc();
}
function onSvgLoad() {
xmlDoc.getElemementById(...
}`
and in the Html, I have got
<html>
<script src="mylogic.js"></script>
<body onLoad='onHtmlLoad();">
...
<object id='svgid' data='sample.svg'.../>
...
</body>
</html>
and the java script included in the html page is not visible in the embedded .svg file so I have to include them again like...
<svg...onload="window.onSvgLoad();">
<script xlink:href="mylogic.js></script>
</svg>
since I included the .js file twice, I am getting the xmlDoc as null inside onSvgLoad() function. I believe, there should be a way to get the same instance of xmlDoc loaded from html in svg. but I am not getting it..?
alternatively I tried not to include .js file in svg instead tried changing onload to onload="window.top.onSvgLoad();" ; then to onload="window.parent.onSvgLoad();" but still did not work!
*Please note that I am using IE9 with its native support for Html5.
javascript included in the html page IS visible from the embedded .svg file. Remove the script tag from the embedded svg and call the methods you want as parent.method e.g. if mylogic.js had
function hello()
{
return "hello";
}
you could write
<script>
alert(parent.hello());
</script>
in the embedded .svg file to call it.