tags:

views:

332

answers:

4

Analyzing the location.hash with this simple javascript code:

<script type="text/javascript">alert(location.hash);</script>

I have a difficult time separating out GET variables that contain a & (encoded as %26) and a & used to separate variables.

Example one:

  • #code=php&age=15d

Example two:

  • #code=php%20%26%20code&age=15d

As you can see, example 1 has no problems, but getting javascript to know that "code=php & code" in example two is beyond my abilities:

(Note: I'm not really using these variable names, and changing them to something else will only work so long as a search term does not match a search key, so I wouldn't consider that a valid solution.)

A: 

Just do the same as you do with the first example, but after you have split on the & then call unescape() to convert the %26 to & and the %20 to a space.

Edit:

Looks like I'm a bit out of date and you should be using decodeURIComponent() now, though I don't see any clear explanation on what it does differently to unescape(), apart from a suggestion that it doesn't handle Unicode properly.

andynormancx
Andrey Fedorov
Eric Caron
+1  A: 

Testing on Firefox 3.1, it looks as if the browser converts hex codes to the appropriate characters when populating the location.hash variable, so there is no way JavaScript can know how the original was a single character or a hex code.

If you're trying to encode a character like & inside of your hash variables, I would suggest replacing it with another string.

You can also parse the string in weird ways, like (JS 1.6 here):

function pairs(xs) {
  return xs.length > 1 ? [[xs[0], xs[1]]].concat(pairs(xs.slice(2))) : []
}

function union(xss) {
  return xss.length == 0 ? [] : xss[0].concat(union(xss.slice(1)));
}

function splitOnLast(s, sub) {
  return s.indexOf(sub) == -1 ? [s] : 
                                [s.substr(0, s.lastIndexOf(sub)),
                                 s.substr(s.lastIndexOf(sub) + sub.length)];
}

function objFromPairs(ps) {
  var o = {};
  for (var i = 0; i < ps.length; i++) {
    o[ps[i][0]] = ps[i][1];
  }
  return o;
}

function parseHash(hash) {
  return objFromPairs(
           pairs(
             union(
               location.hash
                       .substr(1)
                       .split("=")
                       .map(
                         function (s) splitOnLast(s, '&')))))
}

>>> location.hash
"#code=php & code&age=15d"
>>> parseHash(location.hash)
{ "code": "php & code", "age": "15d" }
Andrey Fedorov
+3  A: 

There is no difference between %26 and & in a fragment identifier (‘hash’). ‘&’ is only a reserved character with special meaning in a query (‘search’) segment of a URI. Escaping ‘&’ to ‘%26’ need be given no more application-level visibility than escaping ‘a’ to ‘%61’.

Since there is no standard encoding scheme for hiding structured data within a fragment identifier, you could make your own. For example, use ‘+XX’ hex-encoding to encode a character in a component:

hxxp://www.example.com/page#code=php+20+2B+20php&age=15d

function encodeHashComponent(x) {
    return encodeURIComponent(x).split('%').join('+');
}
function decodeHashComponent(x) {
    return decodeURIComponent(x.split('+').join('%'));
}

function getHashParameters() {
    var parts= location.hash.substring(1).split('&');
    var pars= {};
    for (var i= parts.length; i-->0;) {
        var kv= parts[i].split('=');
        var k= kv[0];
        var v= kv.slice(1).join('=');
        pars[decodeHashComponent(k)]= decodeHashComponent(v);
    }
    return pars;
}
bobince
A: 

This worked fine for me:

var hash = [];
if (location.hash) {
 hash = location.href.split('#')[1].split('&');
}
Sebastian