↓ Twitter is updated more often, so read it! ↓

Decomposing, manipulating, and recomposing URLs in Javascript

I recently had a need to decompose a URL, do some stuff with the parameters, and reassemble it. I later had a need to take another URL and create a form on the fly, because using GET could overrun the URL character limit imposed by Internet Explorer.

Here’s my code for a decomposition and composition pair, plus the code to create a form dynamically using the output of the url_decompose() function.

/**
 * @param String url the URL to be decomposed
 * @return Object with full path in .path, params in arrays in .params
**/
function url_decompose(url){
    if(url.indexOf('?') == -1){ return {path: url, params: {} }; }
    path = url.substring(0, url.indexOf('?'));//get the path before the ?
    qs = url.substring(url.indexOf('?')+1);
    qsa = qs.split('&');
    params = new Object();
    for (var kv in qsa) {
        var kva = qsa[kv].split('=');
        var k = kva[0];
        var v = kva.slice(1, kva.length).join("=");
        params[k] = params[k] || new Array();
        params[k].push(v);
    }    
    return {path: path, params: params};
}
/**
 * @param Object output of url_decompose()
 * @return String reassembled URL
**/
function url_compose(urlObject){
    if(!urlObject.path){return urlObject;}
    var urlString = '';
    var params = [];
    for (var kv in urlObject.params){
        if(kv.length > 0){
            params.push(
                encodeURIComponent(kv) + 
                '=' + 
                encodeURIComponent(urlObject.params[kv][0]));
        }
    }
    urlString = urlObject.path + 
                    (params.length > 0 ? '?' + params.join('&') : '');
    return urlString;
}
/**
 * @param String path the path to be accessed
 * @param Array params associative array or object w/ params in key/value pairs
 * @param String method null for post or whatever method the form should use
**/
function do_post(path, params, method) {
    method = method || "post";
    var form = document.createElement("form");
    form.setAttribute("method", method);
    form.setAttribute("action", path);

    for(var key in params) {
      for(var idx in params[key]){
        var hiddenField = document.createElement("input");
        hiddenField.setAttribute("type", "hidden");
        hiddenField.setAttribute("name", key);
        hiddenField.setAttribute("value", params[key][idx]);
        form.appendChild(hiddenField);
      }
    }
    document.body.appendChild(form);
    form.submit();
}

If I’ve omitted an edge case, please do let me know and I’ll amend my code. I really do need to get one of those code highlighter plugins installed now that I’m doing more code examples.

Select/option dropdowns versus the back button: a workaround

When using select elements on a page (also known as drop downs) as a navigation item, it’s possible that the selected option will persist if the user use’s the browser’s back button.

Consider this workflow:

  1. User clicks on dropdown and selects an item.
  2. Javascript executes, doing something like “window.location = document.forms[0].selectElement.value”
  3. Browser goes to next page.
  4. User clicks back button, browser goes to previous page.
  5. User is confused because the dropdown item contains what they just selected, not what was there when the page loaded.

This is an unfortunate bug/feature in Firefox and Chrome. It actually behaves as one would think in IE and Safari.

The reason is because of Gecko and Webkit’s bfcache, a feature which caches forward and backward pages so that they do not have to be reevaluated in order to be rerendered to the screen. IE reevaluates to rerender. I’m not sure why Safari behaves.

The trick is to use window.onpageshow and window.onload to handle the value of the sortby.

You’ll want something like this:

window.onload = “restoreSelect();”;
window.onpageshow = “restoreSelect();”;

or whatever the equivalent is in your favorite Javascript framework.

Don’t store the value in cookies. It’ll create problems if a user is using multiple tabs. The value I needed is always in the URL, so I could use that for this particular instance.

This trick works for me in Firefox, Chrome, Safari, and IE. I have not tried it on Opera.

Accessibility on clickable spans and divs

I ran into an accessibility issue today wherein a user tabbing through an interface would not be able to toggle collapsable/expandable divs. The problem was that the span on which the user would click was inaccessible via the tab key and would not respond when hitting enter. So, I added this little gem to fix the problem.

<xsl:attribute name="onkeypress">
  <xsl:text>var wx; if(window.event){ wx = event.keyCode; } else { wx = event.which; };</xsl:text>
  <xsl:text>if(wx){ this.onclick(); }; return true;</xsl:text>
</xsl:attribute>
<xsl:attribute name="tabindex">
  <xsl:text>0</xsl:text>
</xsl:attribute>

If you don’t understand XSL, here’s a translation. I added a tabindex attribute with a value of zero and an onkeypress handler which simply activates the onclick of the same element.

Selecting all friends in a Facebook select friends dialog

Let’s say you’re on an event page on Facebook and you want to select all of your friends. Click on “Invite People to Come” and you’ll see the friend selection dialog pop up. Once it’s up, copy and paste this little Javascript gem into the URL bar of your browser:


javascript:e=document.getElementById('friends').getElementsByTagName('li');for(var f in e){if(typeof e[f] === 'object'){fs.click(e[f]);}}

This will select everyone. Be warned, though, that if you computer is slow or you have a lot of friends, it could take a while. It took about a second on my MacBook Pro with a 2.53 GHz Core2Duo.

How to rapidly expunge Facebook’s Friend Suggestions

I really admire Facebook’s Friend Suggestions feature. It has on rare occasion shown someone to whom I’d really like to connect. Most of the time, though, it shows people who I don’t know at all and have maybe one mutual friend. So, it’s largely useless to me.

However, being the often mindless user, when I see a suggestion that may be relevant to my interests, I do visit the full friend suggestion page and see if there are any others. Rarely am I presented with anyone to whom I find a dreadful desire to connect. I still feel obligated to click that damned little [X] as if to say, “I don’t know this person.” Unfortunately, I have to repeat this approximately 25 times in order to full expunge this list of people.

So, I decided to write a little script which takes care of clicking that button for me. Navigate to the Find Friends page on Facebook and execute this script in your Firebug console. Or, prefix it with javascript: and execute it in the URL bar.

d = document.getElementsByClassName("fg_action_hide"); for (var i in d){ d[i].onclick() }

The first part gets an array (a list) of all of the clickable elements with that class, the class used to denote the [X]. The next part loops over them and executes each’s onclick() function just like as if you’d clicked it yourself.

Please note that I tested this ONLY on Firefox and Chromium, but it should work on any modern browser (read: anything but IE).

Esperanto as the brower’s main language

A few days ago, I wrote at work a script which collected information useful to the developers when troubleshooting a customer’s experience on one of our sites. This script checked things such as browser version, operating system, Java existence and version, Flash existence and version, Javascript support, and CSS support. All of this information is either sent via the HTTP request headers or is determinable through interaction with the customer, asking if they can see any No items which should show Yes if the item is installed, enabled, and working.

One item I decided to include for the hell of it was language, through the HTTP request Accept-Language header. Tired of seeing the standard en-us displayed every time I loaded it, I decided to add Esperanto to the fore of the header. I did this by adding it in Firefox’s language options chooser in the Content option panel.

Lo and behold, the language code eo appeared before en-us on my script and I was happy. It was the end of the day, though, so I shutdown shortly thereafter and went home.

The next day, I was startled when the two sites I almost always open first, Google and Meebo, displayed text to me in Esperanto! I had totally forgotten that I had altered my Accept-Language header.

I haven’t changed it back. “Why?” a perplexed reader may ask. I continue to use Esperanto as my main browser language for a few reasons.

  1. Immersion. When a site appears in Esperanto, I’m forced to translate. I don’t let myself change it unless I am in a rush. So far, I have not changed it.
  2. Internationalization. I fancy myself a proponent of software i18n and l10n, so it is educational to me to see how other sites have performed their i18n. I assume that most use either gettext or defines, wherein every string is set in a file with a series of global constants.
  3. Obscurity. It keeps people off of my computer :-)

So, in there interests of determining the penetration of Esperanto as an internationalized and automatically-detected language for the web, I’ve compiled a list of sites which I often visit that have Esperanto strings shown when visiting with a browser which specifies that Esperanto is the preferred language.

  • Google (just search, from what I’ve seen)
  • Meebo (still missing a few strings, though)
  • Twitter (largely untranslated, though)

Some sites surprisingly lacked Esperanto internalization, but it’s not a problem: Esperanto was never meant to be anyone’s first language!

Some readers may be surprised that Wikipedia is omitted from these lists. Wikipedia does not internationalize its main page because all of the languages are listed as a links to the main page (cxefpagxo) of the language’s subdomain itself.

I’m neither surprised nor miffed that there number of sites that do have it are so few. As I stated previously, Esperanto isn’t meant to be anyone’s first language. It’s a quick, intermediary language. I assume that the aforementioned sites with Esperanto internationalization have someone one staff who speaks it fluently, and the translation process for it was effortless.

I hope to integrate i18n into a few projects I’m developing currently. By “hope”, I mean “will.”