Expand partial URL into full (like in image.src) - javascript

I'm trying to write an onerror handler for images that replaces them with a loading image and then periodically tries to reload them. The problem I'm having is that if the loading image fails to load, it goes into an infinite loop of failure. I'm trying to deal with this by checking if the URL is the loading image:
if(photo.src != loadingImage) {
// Try to reload the image
}
Unfortunately, loadingImage can be a relative URL (/images/loadingImage.jpg), but photo.src is always a full URL (http://example.com/images/loadingImage.jpg). Is there any way to generate this full URL without passing the function any more information? Obviously I could pass it the host name, or require full URLs, but I'd like to keep this function's interface as simple as possible.
EDIT:
Basically what I want is to guarantee that if I do photo.src = loadingImage, that this will be true: photo.src === loadingImage. The constraint is that I know nothing about loadingImage except that it's a valid URL (it could be absolute, relative to the server, or relative to the current page). photo.src can be any (absolute) URL, so not necessarily on the same domain as loadingImage.

Here's a couple methods people have used to convert relative URLs to absolute ones in javascript:
StackOverflow - Getting an absolute URL from a relative one
Debuggable.com - Relative URLs in Javascript
Alternatively, have you considered doing the opposite - converting the absolute URL to a relative one? If loadingimage always contains the entire path section of the URL, then something like this would probably work:
var relativePhotoSrc = photo.src;
if (relativePhotoSrc.indexOf("/") > 0 && relativePhotoSrc.indexOf("http://") == 0) {
relativePhotoSrc = relativePhotoSrc.replace("http://", "");
relativePhotoSrc = relativePhotoSrc.substring(relativePhotoSrc.indexOf("/"), relativePhotoSrc.length);
}
alert(relativePhotoSrc);
if (relativePhotoSrc != loadingImage && photo.src != loadingImage) {
// Try to reload the image
}
There's probably a slightly more efficient/reliable way to do the string manipulation with a regular expression, but this seems to get the job done in my tests.

How about this? The photo should either be a full URL or relative to the current document.
var url;
// There probably other conditions to add here to make this bullet proof
if (photo.src.indexOf("http://") == 0 ||
photo.src.indexOf("https://") == 0 ||
photo.src.indexOf("//") == 0) {
url = photo.src;
} else {
url = location.href.substring(0, location.href.lastIndexOf('/')) + "/" + photo.src;
}

Just check if they end with the same string:
var u1 = '/images/loadingImage.jpg';
var u2 = 'http://example.com/images/loadingImage.jpg'
var len = Math.min(u1.length, u2.length);
var eq = u1.substring(u1.length - len) == u2.substring(u2.length - len);

Related

JavaScript changing iframe src causes http request conflict and iframe doesn't reload

I am new to web dev and have done a lot of research on my problem, but I still feel stuck:
What I am trying to achieve:
Pass data via URL parameters to an iframe src to be used in the iframe document. The parent page and the iframe are on different domains.
Setup:
I can have one or multiple iframe(s) with a src=”www.example.com”. After the DOM is fully loaded, I try to change the iframe(s) src in order to append some query parameters.
I am using pure JS to get the initial iframe(s) src > concatonate it with the query parameters > save the new URL in variable > change the iframe(s) src to the URL from the variable.
Problem:
Sometimes the iframe src doesn’t change – seems to be related to the internet connection. Every few refreshes javascript will not succeed changing the src, but doesn’t throw any errors.
My troubleshooting:
If I inspect the page and go to Network (Chrome), I can see there are 2 HTTP requests for the iframe: first with the initial src and second with the new src (even if the JS dones’t succeed changing it).
From here I encountered 3 scenarios:
First http request is cancelled and the second is finished – everything is fine – src is changed and data is passed.
both http requests remain in ‘pending’ status – the src is change and data is passed.
first http request is finished before the second one is started. The second one remains in ‘pending’ status – this is the problem – the src doesn’t change, it remains to the old one even though JS seems to have executed properly.
I understand that when the src of an iframe is changed it should cause the iframe to reload, thus triggering the second http request. However, when scenario 3 happens the iframe doesn’t reload. It’s like an http request ‘conflict’ (not sure if it's the correct way to put it).
Why changing the src would not properly reload the iframe consistently?
I appreciate any suggestions, best practices or possible alternatives to make this work.
Here is my code. I've put comments to describe what I intend to do:
paramToPass = ['parma1', 'param2', 'param3']
if (paramToPass) {
var myIframe = 'myIframeClass'; // define what iframes to find
var iframes = document.getElementsByClassName(myIframe); // get all iframes from the page with desired class
var paramStr = paramToPass.join('&'); // create the query parameter string
var iframesElm;
for (iframesElm = 0; iframesElm < iframes.length; iframesElm++) {
var initialSrc = iframes[iframesElm].getAttribute('src'); // find the initial src of the iframe
if(initialSrc.indexOf("?") > -1) { // if initialSrc already contains parameters
var newSrc = initialSrc + '&' + paramStr; // concatonate initial src with parameters
iframes[iframesElm].src = newSrc; // change the iframe src
// iframes[iframesElm].setAttribute('src', newSrc); // alternative to change iframe src
} else { // if initialSrc doesn't contain parameters
var newSrc = initialSrc + '?' + paramStr; // concatonate initial src with parameters
iframes[iframesElm].src = newSrc; // change iframe src
// iframes[iframesElm].setAttribute('src', newSrc); // alternative to change iframe src
};
};
};
Thank you!
EDIT:
New, working code. For wider browser compatibility, I've included polyfill for Array.from() and forEach():
paramToPass = ['parma1', 'param2', 'param3']
if (paramToPass.length > 0) {
var iframes = document.getElementsByClassName(myIframe);
var paramToPassStr = paramToPass.join('&');
Array.from(iframes).forEach(function(iframe) {
iframe.onload = function() { // execute after the iframe is loaded. Prevents http request conflict
var initialSrc = iframe.src;
var sep = initialSrc.indexOf("?") > -1 ? "&" : "?"; // choose separator based on whether or not params already exist in the initialSrc
var newSrc = initialSrc + sep + paramToPassStr;
iframe.src = newSrc;
console.log('iframe reload');
iframe.onload = null; // stops the infinite reload loop
};
});
} else { // paramToPass array is empty, throw error
console.log('no paramToPassStr to pass found')
};
You should probably put the code to reload the iframe into its onload handler, so it runs after the iframe successfully loaded the first time. Then there won't be any conflict.
Array.from(iframes).forEach(iframe => iframe.onload = () => {
var initialSrc = this.src;
var sep = initialSrc.indexOf("?") > -1 ? "&" : "?";
var newSrc = initialSrc + sep + paramStr;
this.src = newSrc;
this.onload = null;
});

Proper way to check if a URL is the result of a createObjectURL call?

I have a situation where an image is being set by the user either to a URL or with bytes that is converted into a blob object URL. My understanding is that in order to prevent resource leaks, I should be freeing the blob object URLs when they are changed, but I'm not sure if I'm checking the type of the old URL correctly. Currently, the way I'm doing it is to just check if the url starts with 'blob:'. Here is a toy example function that demonstrates that it indeed seems to work:
var url;
for (i = 0; i < 5; i++) {
var oldurl = url;
console.log('i = ' + i)
if (i == 0 || i == 2 || i == 3) {
console.log('Switching to Object URL')
url = URL.createObjectURL(new Blob(new Uint8Array(0),
{type: 'image/png'}));
} else {
console.log('Switching to URL')
url = 'https://example.com/example-image.png';
}
if (oldurl && oldurl.startsWith('blob:')) {
console.log('Freeing old object url')
URL.revokeObjectURL(oldurl);
}
}
Is this the right way to do this? Is there a better way to do it?
I'll note that I've tried calling URL.revokeObjectURL on strings that are not object URLs and it seems to work fine, so it's also not clear to me how much it matters that I correctly determine whether the URL needs to be freed.
Note: This is a TypeScript script, but I think the question is equally valid in Javascript, so I've tagged with both.
You are right, i think that currently there is no other way at all.

JavaScript - Get size in bytes from HTML img src

I would like to know how to get the size in bytes from the "src" from an "img" tag with HTML/JS.
<img src="https://ofbuckleyandbeatles.files.wordpress.com/2011/01/testpattern.gif"/>
In the above example I would basicly want to know how big "testpattern.gif" is (in bytes).
Thanks in advance.
Well, this is 2017 and you can now use Resource Timing API to extract an image's transferSize, encodedBodySize, decodedBodySize within javascript:
Below is the code to access size information for all the imgs on a page (see the caveat to all below):
var imgElems = document.getElementsByTagName('img');
for ( var i=0, len = imgElems.length; i < len; i++ )
{
var url = imgElems[i].src || imgElems[i].href;
if (url && url.length > 0)
{
var iTime = performance.getEntriesByName(url)[0];
console.log(iTime.transferSize); //or encodedBodySize, decodedBodySize
}
}
Make sure you run the above code after document onload (to be sure images are loaded)
Due to CORS, timing information for images coming from a different domain may not be available (unless the different domain's server has set Timing-Allow-Origin HTTP response header)
Be sure resource timing api is available for the browser(s) you are targetting : http://caniuse.com/#feat=resource-timing
Make sure you get a grasp of the difference between transferSize, encodedBodySize, decodedBodySize to be sure you use the right size attribute.
Sadly the proposed solution does not work since the returned value does not match the image actual size.
Assuming you are using a URL you can do the following:
const fileImg = await fetch(URL_TO_IMG).then(r => r.blob());
This will get the resource through HTTP and then returns the full binary as a blob object, then you can access its properties including its size in bytes as:
fileImg.size

Changing images src by javascript - cache issue

I am building a web site, using jQuery.
Two major things:
I want to change picture (image src) manually, but sometimes the picture isn't changed (I am using right now chrome, but I want a general solution).
Also, I want to get the image width + height, after I load the picture.
(The picture is a file from the server - I just use different file name).
When changing the image by JavaScript/jQuery - I realized that when I change the image once, it is kept in memory, so I run into some problems.
I have found that the image is not uploaded the second time due to cache problem, so as I did some workaround, I realized that I need to do JavaScript command such as:
$("#id_image").attr("src", "pictures\\mypicture.jpg" + "?" + Math.random());
That's how I am changing manually the image.
By using the Math.random() I got a second problem:
If I wrote before Math.random() the line:
$("#id_image").width("auto");
$("#id_image").height("auto");
I don't get the height + width after using the Math.random(), so I put another line, and finally my code is:
$("#id_image").width("auto");
$("#id_image").height("auto");
$("#id_image").attr("src", "pictures\\mypicture.jpg");
$("#id_image").attr("src", "pictures\\mypicture.jpg" + "?" + Math.random());
alert("#id_image").width()); // **** returns 0 sometimes due cache
alert("#id_image").height()); // **** returns 0 sometimes due cache
Still, I have some problem (see remarks on asterisk), and I don't know how to always get the width + height of loaded image.
You could try setting onload handler before setting image src, this way, even your image is cached, this should give you correct image size:
$("#id_image").on('load',function(){
alert($(this).width());
alert($(this).height());
});
$("#id_image").attr("src", "pictures\mypicture.jpg");
VersionNumber = System.Configuration.ConfigurationManager.AppSettings["Version"];
//Caching Issues For JS
public static string JSVersionUpdate(string JsFile)
{
StringBuilder str = new StringBuilder();
str.Append(GetQualifiedPath(JsFile));
str.Append("?Version=");
str.Append(VersionNumber);
return str.ToString();
}
public static void DiscardVersion()
{
VersionNumber = null;
}
//get Full Path for JS File
private static string GetQualifiedPath(string path)
{
var httpRequestBase = new System.Web.HttpContextWrapper(System.Web.HttpContext.Current);
path = path.Replace("~", string.Empty);
string appPath = string.Empty;
if (httpRequestBase != null)
{
//Formatting the fully qualified website url/name
appPath = string.Format("{0}://{1}{2}{3}{4}",
httpRequestBase.Request.Url.Scheme,
httpRequestBase.Request.Url.Host,
httpRequestBase.Request.Url.Port == 80 ? string.Empty : ":" + httpRequestBase.Request.Url.Port,
httpRequestBase.Request.ApplicationPath,
path);
}
appPath = appPath.TrimEnd('/');
return appPath;
}
}
In UI Page : script src="JSVersionUpdate("~/Scripts/Application/Abc.js")"
O/p : ~/Scripts/Application/Abc.js/version=1.0
Browser request for JS Files from server only if Version is Different else it will cached out.Now i don't need to tell someone to clear the cache again and again after deployment.Only thing i do is i change Version Number in Web COnfig.
Same can be applied to Image As well !! Hope it Helps

Generate canonical / real URL based on base.href or location

Is there a method/function to get the canonical / transformed URL, respecting any base.href setting of the page?
I can get the base URL via (in jQuery) using $("base").attr("href") and I could use string methods to parse the URL meant to being made relative to this, but
$("base").attr("href") has no host, path etc attributes (like window.location has)
manually putting this together is rather tedious
E.g., given a base.href of "http://example.com/foo/" and a relative URL "/bar.js", the result should be: "http://example.com/bar.js"
If base.href is not present, the URL should be made relative to window.location.
This should handle non-existing base.href (using location as base in this case).
Is there a standard method available for this already?
(I'm looking for this, since jQuery.getScript fails when using a relative URL like "/foo.js" and BASE tag is being used (FF3.6 makes an OPTIONS request, and nginx cannot handle this). When using the full URL (base.href.host + "/foo.js", it works).)
Does this do the trick?
function resolveUrl(url){
if (!url) {
throw new Error("url is undefined or empty");
}
var reParent = /[\-\w]+\/\.\.\//, // matches a foo/../ expression
reDoubleSlash = /([^:])\/\//g; // matches // anywhere but in the protocol
// replace all // except the one in proto with /
url = url.replace(reDoubleSlash, "$1/");
var base = (document.getElementsByTagName('BASE')[0] && document.getElementsByTagName('BASE')[0].href) || "";
// If the url is a valid url we do nothing
if (!url.match(/^(http||https):\/\//)) {
// If this is a relative path
var path = (url.substring(0, 1) === "/") ? base : location.pathname;
if (path.substring(path.length - 1) !== "/") {
path = path.substring(0, path.lastIndexOf("/") + 1);
}
if (!url.match(/^(http||https):\/\//)) {
url = location.protocol + "//" + location.host + path + url;
}
}
// reduce all 'xyz/../' to just ''
while (reParent.test(url)) {
url = url.replace(reParent, "");
}
return url;
}
It is modified from some code I had around, so it hasn't been tested
js-uri is a useful javascript library to accomplish this: http://code.google.com/p/js-uri/
You would still need to handle the base.href vs window.location parts, but the rest could be accomplished with the library.

Categories

Resources