Safari and long lines in Google Maps Javascript API Directions Service

11th October, 2012 - Posted by david

I’ve been working on building the mobile version of CarsIreland.ie lately and one of the features we want is to offer directions from a user’s current location to any of our car dealerships, assuming we have their latitude and longitude co-ordinates. Getting the directions and displaying them on a map are ridiculously simple, thanks to Google’s Direction Service API. Going into the detail of getting this working is beyond the scope of this post, but what I hope to show here is how to resolve an issue in mobile (and possibly desktop) Safari, and maybe even some other browsers.

When you have instantiated your new google.maps.DirectionsService() and called it’s route function, passing your source and destination (among other things) as parameters, you’re supposed to give a callback function with response and status parameters. Assuming the call to route was successful, the response parameter should be a big JSON block of at least one route and step by step guides detailing how to get from source to destination. Conveniently, Google also provide a very handy object called a DirectionsRenderer, which has a function called setDirections, which can generate a nicely formatted HTML table of directions. See https://google-developers.appspot.com/maps/documentation/javascript/examples/directions-panel for an example.

The problem I experienced and am aiming to solve here is that some of the directions (in Ireland at least) involve very long motorway/freeway names, where each junction is separated by a ‘/’, but with no spaces. This can lead to very long strings of the format ‘Long/And/Winding/Junction/Name’. Add in the fact that they also include the Irish/Gaelic translation for some words and it gets even longer! When viewing this steps on Android’s Dolphin or even a desktop Firefox, the browser recognizes that it can split the line at the ‘/’ and thus doesn’t widen the page. Safari unfortunately doesn’t do this and forces very wide page widths, which makes the page look awful. So, today I figured a way to resolve this.

What I did was traverse the response object you get back from Google, looking at each instruction step, trying to find one of these long strings, and replacing the ‘/’ with ‘ / ‘, i.e. a space character on either side, so Safari will then break the lines and not force a wide page. Doing a simple string replace wasn’t sufficient, as some of the instructions contain HTML tags, which can have ‘/’ in them that we ideally want to keep.

So first up is the Javascript regular expression to find matching instances of “long/string”. In simple terms, it’s any alpha-numeric character, followed by a ‘/’, followed by another alpha-numeric character. In Javascript I came up with:

1
var patt = /([a-z0-9]{1})\/([a-z0-9]{1})/gi;

The gi at the end of the pattern means global search (i.e. don’t stop at the first match), case insensitive (hence no ‘A-Z’ is required).

Now, all we have to do is cycle through the response, looking for routes, legs, steps and instructions and replacing as necessary, via a string’s replace function:

1
2
3
// e.g.
var repl = "$1 / $2";
instruction = instruction.replace(patt, repl);

So, to loop through the response and do the replacing, we need a few nested for loops, our pattern and our replace sequence, as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if (typeof response['routes'] != 'undefined') {
    var patt = /([a-z0-9]{1})\/([a-z0-9]{1})/gi;
    var repl = "$1 / $2";
    // cycle through each route
    for (var i=0; i<response['routes'].length; i++) {
        var route = response['routes'][i];
        if (typeof route['legs'] != 'undefined') {
            // cycle through each leg in that route
            for (var j=0; j<route['legs'].length; j++) {
                var leg = route['legs'][j];
                if (typeof leg['steps'] != 'undefined') {
                    // cycle through each step in that leg
                    for (var k=0; k<leg['steps'].length; k++) {
                        var instructions = leg['steps'][k]['instructions'];
                        // if we've found an instruction with a matching pattern
                       if (instructions.match(patt)) {
                           // do the replace
                           response['routes'][i]['legs'][j]['steps'][k]['instructions'] = instructions.replace(patt, repl);
                       }
                   }
               }
           }
       }
   }
}

So, hopefully this well help people experiencing the same problem I was having with the long strings caused by a lack of spaces between ‘/’ characters! As an alternative, one may just wish to only have a space after the ‘/’, in which case, the replace pattern becomes "$1/ $2".

Read more...

Regular expression (regex) to remove double encoding of html entities

30th March, 2011 - Posted by david

When you have users copying and pasting in data to forms on your website, which then gets stored in your database, you invariably end up with all sorts of ways of encoding and storing special characters. Ideally, these will end up in your database as the correct characters (such as € for the euro symbol), which will then get encoded as HTML entities when you display this data on your website (so, € becomes &euro; in the HTML).

However, with older systems, especially those built in-house, you end up with the HTML entity version of certain characters in your database. It’s pretty much a fact of web development. Let’s use the example of a string that says “Price: €100” but gets stored in the database as “Price: &euro;100”. When you go to display this text on your encoded web-page, you end up seeing things such as “Price: &amp;euro;100” in your browser. This is a result of double encoding, as the & in &euro; is first getting encoded as &amp;.

In order to remove these, I came up with the following function, that uses a simple regular expression to tidy such instances up.

1
2
3
4
function remove_double_encoding($in)
{
    return preg_replace('/&([a-zA-Z0-9]{2,7});/', '&$1;', $in);
}

What this does is looks for any 2 to 7 letter strings with &amp; immediately before them and ; immediately after. When it finds a match, it simply replaces the &amp; with &. It does this for all instances in your input string.

Update: Forgot that you can also have purely numeric codes here, so added ‘0-9’ to the regex.

Read more...