I'm currently using the following function to 'convert' a relative URL to an absolute one:
function qualifyURL(url) {
var a = document.createElement('a');
a.href = url;
return a.href;
}
This works quite well in most browsers but IE6 insists on returning the relative URL still! It does the same if I use getAttribute('href').
The only way I've been able to get a qualified URL out of IE6 is to create an img element and query it's 'src' attribute - the problem with this is that it generates a server request; something I want to avoid.
So my question is: Is there any way to get a fully qualified URL in IE6 from a relative one (without a server request)?
Before you recommend a quick regex/string fix I assure you it's not that simple. Base elements + double period relative urls + a tonne of other potential variables really make it hell!
There must be a way to do it without having to create a mammoth of a regex'y solution??
How strange! IE does, however, understand it when you use innerHTML instead of DOM methods.
function escapeHTML(s) {
return s.split('&').join('&').split('<').join('<').split('"').join('"');
}
function qualifyURL(url) {
var el= document.createElement('div');
el.innerHTML= 'x';
return el.firstChild.href;
}
A bit ugly, but more concise than Doing It Yourself.
As long as the browser implements the <base> tag correctly, which browsers tend to:
function resolve(url, base_url) {
var doc = document
, old_base = doc.getElementsByTagName('base')[0]
, old_href = old_base && old_base.href
, doc_head = doc.head || doc.getElementsByTagName('head')[0]
, our_base = old_base || doc_head.appendChild(doc.createElement('base'))
, resolver = doc.createElement('a')
, resolved_url
;
our_base.href = base_url || '';
resolver.href = url;
resolved_url = resolver.href; // browser magic at work here
if (old_base) old_base.href = old_href;
else doc_head.removeChild(our_base);
return resolved_url;
}
Here's a jsfiddle where you can experiment with it: http://jsfiddle.net/ecmanaut/RHdnZ/
You can make it work on IE6 just cloning the element:
function qualifyURL(url) {
var a = document.createElement('a');
a.href = url;
return a.cloneNode(false).href;
}
(Tested using IETester on IE6 and IE5.5 modes)
I found on this blog another method that really looks like #bobince solution.
function canonicalize(url) {
var div = document.createElement('div');
div.innerHTML = "<a></a>";
div.firstChild.href = url; // Ensures that the href is properly escaped
div.innerHTML = div.innerHTML; // Run the current innerHTML back through the parser
return div.firstChild.href;
}
I found it a little more elegant, not a big deal.
URI.js seems to solve the issue:
URI("../foobar.html").absoluteTo("http://example.org/hello/world.html").toString()
See also http://medialize.github.io/URI.js/docs.html#absoluteto
Not testeed with IE6, but maybe helpful for others searching to the general issue.
I actually wanted an approach to this that didn't require modifying the original document (not even temporarily) but still used the browser's builtin url parsing and such. Also, I wanted to be able to provide my own base (like ecmanaught's answer). It's rather straightforward, but uses createHTMLDocument (could be replaced with createDocument to be a bit more compatible possibly):
function absolutize(base, url) {
d = document.implementation.createHTMLDocument();
b = d.createElement('base');
d.head.appendChild(b);
a = d.createElement('a');
d.body.appendChild(a);
b.href = base;
a.href = url;
return a.href;
}
http://jsfiddle.net/5u6j403k/
This solution works in all browsers.
/**
* Given a filename for a static resource, returns the resource's absolute
* URL. Supports file paths with or without origin/protocol.
*/
function toAbsoluteURL (url) {
// Handle absolute URLs (with protocol-relative prefix)
// Example: //domain.com/file.png
if (url.search(/^\/\//) != -1) {
return window.location.protocol + url
}
// Handle absolute URLs (with explicit origin)
// Example: http://domain.com/file.png
if (url.search(/:\/\//) != -1) {
return url
}
// Handle absolute URLs (without explicit origin)
// Example: /file.png
if (url.search(/^\//) != -1) {
return window.location.origin + url
}
// Handle relative URLs
// Example: file.png
var base = window.location.href.match(/(.*\/)/)[0]
return base + url
However, it doesn't support relative URLs with ".." in them, like "../file.png".
This is the function I use to resolve basic relative URLs:
function resolveRelative(path, base) {
// Absolute URL
if (path.match(/^[a-z]*:\/\//)) {
return path;
}
// Protocol relative URL
if (path.indexOf("//") === 0) {
return base.replace(/\/\/.*/, path)
}
// Upper directory
if (path.indexOf("../") === 0) {
return resolveRelative(path.slice(3), base.replace(/\/[^\/]*$/, ''));
}
// Relative to the root
if (path.indexOf('/') === 0) {
var match = base.match(/(\w*:\/\/)?[^\/]*\//) || [base];
return match[0] + path.slice(1);
}
//relative to the current directory
return base.replace(/\/[^\/]*$/, "") + '/' + path.replace(/^\.\//, '');
}
Test it on jsfiddle: https://jsfiddle.net/n11rg255/
It works both in the browser and in node.js or other environments.
I found this blog post that suggests using an image element instead of an anchor:
http://james.padolsey.com/javascript/getting-a-fully-qualified-url/
That works to reliably expand a URL, even in IE6. But the problem is that the browsers that I have tested will immediately download the resource upon setting the image src attribute - even if you set the src to null on the next line.
I am going to give bobince's solution a go instead.
If url does not begin with '/'
Take the current page's url, chop off everything past the last '/'; then append the relative url.
Else if url begins with '/'
Take the current page's url and chop off everything to the right of the single '/'; then append the url.
Else if url starts with # or ?
Take the current page's url and simply append url
Hope it works for you
If it runs in the browser, this sort of works for me..
function resolveURL(url, base){
if(/^https?:/.test(url))return url; // url is absolute
// let's try a simple hack..
var basea=document.createElement('a'), urla=document.createElement('a');
basea.href=base, urla.href=url;
urla.protocol=basea.protocol;// "inherit" the base's protocol and hostname
if(!/^\/\//.test(url))urla.hostname=basea.hostname; //..hostname only if url is not protocol-relative though
if( /^\//.test(url) )return urla.href; // url starts with /, we're done
var urlparts=url.split(/\//); // create arrays for the url and base directory paths
var baseparts=basea.pathname.split(/\//);
if( ! /\/$/.test(base) )baseparts.pop(); // if base has a file name after last /, pop it off
while( urlparts[0]=='..' ){baseparts.pop();urlparts.shift();} // remove .. parts from url and corresponding directory levels from base
urla.pathname=baseparts.join('/')+'/'+urlparts.join('/');
return urla.href;
}
Related
I create a function using Javascript for related post on my Blogger template,
Here is my code:
function toHttps(link) {
var protocol=link.replace(/\:/g,'');
if(protocol=='http') {
var url=link.replace('http','https');
return link.replace(url);
}
}
if my original url is
https://dpawoncatering.blogspot.com/2008/08/nasi-box-murah.html
Why is the result like this?
https://dpawoncatering.blogspot.com/2008/08/undefined?
Naren Murali's answer is correct. I'd just like to add a different way of doing "protocol" swap using javascript's own URL parser that might be interesting for other people.
You can instantiate an a element and use its href attribute to parse your URL, then you can access and change the protocol attribute of the href and retrieve the resulting URL:
function toHttps(link) {
var url = document.createElement('a');
url.href = link;
url.protocol = 'https';
return url.href;
}
Since the URL contains https already it does not enter the if condition, hence nothing is returned hence we get undefined, please check my corrected function. Let me know if you have any issues!
function toHttps(link) {
if(link.indexOf('http://') > -1){
var url=link.replace('http','https');
return url;
}
return link
}
console.log(toHttps('http://dpawoncatering.blogspot.com/2008/08/nasi-box-murah.html'))
console.log(toHttps('https://dpawoncatering.blogspot.com/2008/08/nasi-box-murah.html'))
I want to know a protocol of a site which is not my current page
E.g. I execute code and the current page which the code is executed on is http://www.example.org, and I want to get the protocol of which is https:
What I know is window.location.protocol should return the current page, which is http:, but is there something like 'google.com'.protocol to return https: ..
Thank you :)
My code:
var returnProtocolOf(site_url) = function {
return (String(site_url)).location.protocol // VIRTUAL COMMAND
};
You can do so with a elements:
function getProtocol(url){
var link = document.createElement('a');
link.href = url;
return link.protocol;
}
You can also just match it with an expression:
var protocol = url.match(/^([a-z]+?:)\/\//)[1];
Checks might be needed.
I have a c# MVC project with some jQuery driven interface. Some actions are performed with a $.post().
Sometimes, the site would be deployed to the root of the domain (e.g. www.mydomain.com) and sometimes it would be deployed in a folder (e.g. www.mydomain.com/Super).
I want to make posts to controllers relative to the location of the view that the user is accessing. However, the user may access the view both with and without an ending /, so i need to cater for both scenarios to avoid weird requests to www.mydomain.com/Super//somecontroller.
Currently, i have a function that does the following:
function getlocation() {
var loc = "";
if (location.pathname != "/") {
loc = (window.location.href.match("/$")) ? window.location.href : window.location.href + "/";
}
return loc;
}
Ultimately, this looks pretty ugly to me. Besides, every time i want to make a post or insert a relative URL when parsing data, i have to insert the result of that function before the actual URL which contributes to tag soup.
What is the best way to approach this?
I think your code is not ugly, but you can look to Backbone.js code:
url: function() {
var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
},
This is a case of me not asking the question correctly.
The problem as i had it is best solved using Url.Action() method from the UrlHelper.
In making a function that validates a user URL and prepends http: at the front, I have to take cases of www, https and // into account as being valid urls. The way I have it written now (see below), I only prepend http: , so that cases of //stackoverflow.com don't turn into http: ////stackoverflow.com.
This means that a url like stackoverflow.com becomes http:stackoverflow.com.
In Firefox and Chrome, this works just fine, but these URLS will be clicked from a variety of browsers and devices. Is it something that'll work universally? It'll be easy to rewrite this check for a // case, but I'm interested in the answer.
Prepend method:
function prependHTTPtoWebURL() {
var url = (el('org_website').value);
var httpVar;
var testFor;
if (url) {// If there's a website URL value
testFor = url.toLowerCase();
if (testFor.indexOf("http") != 0){
httpVar = 'http:'; //add it
url = httpVar + url;
el('org_website').value = url;
}
}
}
Try playing with regex. Check this code for instance:
var someurl = "www.google.com";
var otherurl = "google.com";
var anotherurl = "//google.com";
function prependHTTPtoWebURL(url) {
var newurl = url.replace(/^(http)?(:)?(\/\/)?/i,'');
return 'http://' + newurl;
}
console.log(prependHTTPtoWebURL(someurl));
console.log(prependHTTPtoWebURL(otherurl));
console.log(prependHTTPtoWebURL(anotherurl));
The ouput in console.log will be:
http://www.google.com
http://google.com
http://google.com
Since you are specifying a subdomain (www) on the first one, that is respected. It avoids ending with four diagonals, like http:////. If your url was something like :google.com, it would also fix it correctly.
You can see it live here: http://jsfiddle.net/zRBUj/
Edit: Adding the /i Kate mentioned.
Change http: to http://
See these links for more info:
Anatomy of a URL
How the web works
<script src="myscript.js?someParameter=123"></script>
From within myscript.js, is there any way to obtain that someParameter was set to 123? Or is the only way to use server side scripts that generate the javascript file with the parameters in it?
Well, you get URL parameters from window.location.href. As the name says, it refers to the current window. What the <script> tag does it to embed the linked file into the current document, thus into the same window. If you parsed window.location.href from the linked JavaScript file, you'd only get the URL from the embedding document.
There are two ways to pass parameters to another JavaScript file:
As #Dave Newton suggested, just declare a variable, then embed the JS file like you did (without the parameters of course, because they have no effect).
Create an iframe, pass the parameters you want to the URL of the iframe, then embed the JavaScript file inside the iframe. An iframe will create a new window instance.
Jquery Address does this, so i've been checking their code out and this is the improved solution I just created myself:
$.each($('script'), function(id, val){ //loop trough all script-elements
var tmp_src = String($(this).attr('src'));//store the src-attr
var qs_index = tmp_src.indexOf('?');//check if src has a querystring and get the index
//Check if the script is the script we are looking for and if it has QS-params
if(tmp_src.indexOf('myscript.js') >= 0 && qs_index >= 0)
{
//this is myscript.js and has a querystring
//we want an array of param-pairs: var1 = value1, var2 = value2, ...
var params_raw = tmp_src.substr(qs_index + 1).split('&');
//create empty options array
var options = [];
//loop troug raw params
$.each(params_raw, function(id, param_pair){
//split names from values
var pp_raw = param_pair.split('=');
//store in options array
options[pp_raw[0]] = pp_raw[1];
});
//check the results out in the console!
console.log(options);
}
});
I hope this does what you need?
The answer is a definite "YES". I've been doing this on various projects for over a decade. The solution is actually easy, it's just non-intuitive (you have to generate an error). To be clear, the following code lets you do something like this:
<script src="https://example.com/script.js?id=1&bar=this works!" />
All you need to do is initiate a silent error, which takes less than 1/1000 of a second even on the worst outdated mobile browsers. You shouldn't do it a ton, but you only need to do it once. This error is processed, so it won't show up as an error in telemetry or 3rd party error trackers either.
// Generic function used to see if a param exists in a URL string.
// Provided here in case you don't know how to do it.
// This is not needed for the solution.
function getParameter (name, url) {
if (!url) url = scriptName()
name = name.replace(/[\[\]]/g, '\\$&')
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)')
var results = regex.exec(url)
if (!results) return null
if (!results[2]) return ''
return decodeURIComponent(results[2].replace(/\+/g, ' '))
}
// Gets the name of this script (whatever this file runs in)
// You can use this name to get parameters just like you would for the window URL :)
function getScriptName () {
var error = new Error(),
source,
lastStackFrameRegex = new RegExp(/.+\/(.*?):\d+(:\d+)*$/),
currentStackFrameRegex = new RegExp(/getScriptName \(.+\/(.*):\d+:\d+\)/)
if ((source = lastStackFrameRegex.exec(error.stack.trim())) && source[1] !== '')
return source[1]
else if ((source = currentStackFrameRegex.exec(error.stack.trim())))
return source[1]
else if (error.fileName !== undefined)
return error.fileName
}