Tag Archives: Javascript

Using the Google Maps API to Get Locations from Zip Codes

14 Sep 2012

Google offers many API’s, among them is the Maps API. In this example we’ll show all the code necessary to hit Google with a zip code to get the location in the form of City, State and Country.

As of June 22, 2016 you **must** apply for a Google Maps API key. All sites that use the service prior to June 22 will be grandfathered into the Google Maps service for keyless usage (Read this post from the Google Geo Developers Blog). Any new applications must include an API key so that Google can enforce rate limiting. If you see this error message: “MissingKeyMapError” – you are trying to use the Google Maps Service without a key – the fix is simply to acquire and start using an API key.

First thing to do is to reference Google’s Map API:

...
<script language="javascript" src="https://maps.google.com/maps/api/js?sensor=false&key=YOUR-API-KEY"></script>
...

Next is a little form used to enter your zip code:

...
<form>
zip: \<input type="text" name="zip" value="46032"> <a href="#" onclick="getLocation()">Get Address</a>
</form>
...

You can see that I have a link which fires a function called “getLocation()” – below you’ll find that function and the related code:

...
function getLocation(){
  getAddressInfoByZip(document.forms[0].zip.value);
}

function response(obj){
  console.log(obj);
}
function getAddressInfoByZip(zip){
  if(zip.length >= 5 && typeof google != 'undefined'){
    var addr = {};
    var geocoder = new google.maps.Geocoder();
    geocoder.geocode({ 'address': zip }, function(results, status){
      if (status == google.maps.GeocoderStatus.OK){
        if (results.length >= 1) {
	  for (var ii = 0; ii < results[0].address_components.length; ii++){
	    var street_number = route = street = city = state = zipcode = country = formatted_address = '';
	    var types = results[0].address_components[ii].types.join(",");
	    if (types == "street_number"){
	      addr.street_number = results[0].address_components[ii].long_name;
	    }
	    if (types == "route" || types == "point_of_interest,establishment"){
	      addr.route = results[0].address_components[ii].long_name;
	    }
	    if (types == "sublocality,political" || types == "locality,political" || types == "neighborhood,political" || types == "administrative_area_level_3,political"){
	      addr.city = (city == '' || types == "locality,political") ? results[0].address_components[ii].long_name : city;
	    }
	    if (types == "administrative_area_level_1,political"){
	      addr.state = results[0].address_components[ii].short_name;
	    }
	    if (types == "postal_code" || types == "postal_code_prefix,postal_code"){
	      addr.zipcode = results[0].address_components[ii].long_name;
	    }
	    if (types == "country,political"){
	      addr.country = results[0].address_components[ii].long_name;
	    }
	  }
	  addr.success = true;
	  for (name in addr){
	      console.log('### google maps api ### ' + name + ': ' + addr[name] );
	  }
	  response(addr);
        } else {
          response({success:false});
        }
      } else {
        response({success:false});
      }
    });
  } else {
    response({success:false});
  }
}
...

Thats it - open up the console in Chrome and notice the output as you enter different zip codes.

Playing Audio in Android/iOS Web Apps

11 Sep 2012

If you’re familiar with the state of HTML5 in Android land you know that many CSS3 features are not supported, among them is the AUDIO tag. There is a library out called SoundManager2 that claims to offer a “reliable cross-platform audio” solution but after trying it on mobile platforms I think it falls short of that promise.

SoundManager 2’s approach is to use HTML5 where possible with Flash as a fallback mechanism. Makes sense for desktop OS’s, not so much for mobile. iOS, even with its own quirks (no inline playing, no autoplay, etc) has functional HTML5 audio/video so that’s not much of an issue. Android is very different in that the HTML5 AUDIO tag is not implemented. Flash exists on Android but that has its own issues, among them: we are forced to assume that the Flash Player exists on the Android device in the first place which isn’t always the case. Add to that the fact that many users have to install the Flash Player manually on their devices as there is an absence of any auto-install capability. The final nail in that coffin is Adobe’s abandonment of the Android Flash Player altogether meaning no one can install it.

SoundManager 2 can no longer claim to be useful for mobile when its completely unusable on Android. Maybe when a version of Android with HTML5 Audio support takes the lion’s share of the Android ecosystem SoundManager 2 may again have use within the mobile space.

In any event, as mentioned, the lack of HTML5 AUDIO tag support continues to make supporting sound in Android Web Apps a challenge. The solution I’ve come up with (not that its a novel one) is to use the HTML5 VIDEO tag instead. VIDEO has support at least back to Android 2.2. Most events are supported though some don’t do quite behave in the manner that you would like for them to which makes the creation of a custom audio player somewhat of a challenge. In the end you may just have to bail on some lofty ideas/features that you may have for your own custom player but at least you can still get some sound working reliably.

It has been said that you can in fact feed the VIDEO tag an MP3. However, what happens within Android differs across its varied flavors. In Android 2.2 what will happen is that Android will not play the audio in-page but instead launch the native media player – and what you have is a black screen, devoid of controls albeit with your MP3 playing. In Android 4 you can get MP3 audio to work inline.

Due to the different behaviors and presentations between Android versions I’ve personally switched to using video files(mp4) to deliver sound. The result is that in Android 2.2 the native media player will display the video content – an image keyframed every second – which satisfies some mystery requirement that allows the on-screen controls to appear. This is much better than a black screen. On Android 4 we don’t care about the visual aspect of the video because my implementation avoids it entirely.

The concept is to use single VIDEO tag that is placed off-screen, use Javascript to change its src and control the sound via the relevant HTML5 media events.

It is important to note that some VIDEO tag attributes are necessary for this to work as without them you’ll be greeted by silence. Below is the HTML to use:

...
<video id="videoEl" autobuffer height="10" width="10" controls style="position:absolute;top:0px;left:-99px;">
  <source src="something.mp4">
</video>
...

The autobuffer is there because it can’t hurt. The one thing that is necessary is the presence of the video controls (yes, even though you won’t see them). Otherwise this is all pretty simple – you can see that the VIDEO tag is placed off screen.

I won’t post my complete setup as thats a bit of code, however, here’s a great start; first some HTML to be styled however you like:

...
<ul id="songs" class="music_list">
  <li src="vid/song_1.mp4">This is song number one</li>
  <li src="vid/song_2.mp4">This is the second song</li>
  <li src="vid/song_3.mmp4">Number three</li>
  <li src="vid/song_4.mp4">The last song</li>
  <li qwe="noSound">Just a list item, no video/sound to play here</li>
</ul>
...

In this case I’ve elected to keep the URL to the video in the LI tag. Here’s my JavaScript that utilizes the above, populates the VIDEO src, and controls the VIDEO (our sound):

...
var videoPlayer = (function(){
  var _isPlaying = false;
  var _elTarget;
  var _video;
  function _resetAllVideoStyles(){
    var lists = document.getElementsByTagName('ul');
    for (var i=0;i<lists.length;i++){
      if (lists[i].getAttribute('class').indexOf('music_list') != -1){
        var desc = lists[i].childNodes;
        for (var j=0;j<desc.length;j++){
          if (desc[j].nodeType == 1){
            desc[j].setAttribute('class','');
          }
        }
      }
    }
  }
  function _ifOldAndroid(){
    return navigator.userAgent.indexOf('Android 2.3.3') != -1 ? true : false;
  }
  function _loadVideo(t,str){
    t.setAttribute('class','mp_selected');
    _video.src = str;
    _elTarget = t;
    _video.load();
    setTimeout(function(){
      _video.play();
      _isPlaying = true;
    },1000); // NECESSARY timeout, play too soon and nothing will happen
  }
  function _stopVideo(){
    _video.pause();
    _isPlaying = false;
    _resetAllVideoStyles();
  }
  function init(v_id){;
    var el = _video = document.getElementById(v_id);
    el.addEventListener('error',function(){
      console.log('Cannot load the file');
    });
    el.addEventListener('pause', function(){
      console.log('paused/stopped');
    });
    el.addEventListener('loadstart', function(){
      console.log('starting to lo
    });
    el.addEventListener('play', function(){
      console.log('playing');
    });
    el.addEventListener('durationc
      console.log('duration changed');
    });
    el.addEventListener('progress', function(){
      console.log('progress: file is being downloaded');
    });
  }
  function dispatcher(t,str){
    if (t == _elTarget){
      _elTarget = null;
      _stopVideo();
    } else if (_isPlaying){
      _stopVideo();
      setTimeout(function(){
        _loadVideo(t,str);
      },100);
    } else {
      _loadVideo(t,str);
    }
  }
	
  return{
    init:init,
    dispatcher:dispatcher
  }
})();
window.addEventListener('load',function(){
  var list = document.getElementsByTagName('li');
  for (var i=0;i < list.length;i++){
    if (list[i].getAttribute('qwe') !== 'noSound'){
      list[i].addEventListener('click',function(e){
        videoPlayer.dispatcher(e.target,e.target.attributes[0].nodeValue);
      },false);
    }
  }
},false);
...

As you can see events are bound to all qualifying LI tags – the qualifier being that the parent UL must have a class called “music_list” assigned to it.

Thats a decent start for you – all the events are setup – you will quickly see that they don’t really fire when you expect them to. For example, the onplay event seems to start as the video is buffering, not when the video actually starts to play. ondurationchange may fire more than once – etc…. so as i mentioned before making a slick UI will be a bit of a chore. One thing that should be useful, though granted maybe not a cureall is the video.paused property. At least you will know when the video is playing or not, but as I already noted the video is considered playing when what I believe based on observation is buffering and not actual playing.

As far as iOS the video does not play inline, rather, the native video player will come up full screen. This happens because contrary to every other mobile browser Apple has decided in its totalitarian wisdom to buck the standard and suspend the mobile app in favor of its built-in media player. That’s a rather ironic tactic IMO as they profess to be on the side of HTML 5 & standardization but as usual they change the game per their whim and there’s nothing that can be done about it. Apple gives no explanation for this so it is what it is.

So that’s about – doing the above gets you something that works on both major mobile OS’s – Android with its caveats, and iOS with its mobile media player hijacking the

Scrolling in LungoJS HTML5 Framework

12 Jul 2012

I’ve been looking at other frameworks lately, among them LungoJS. After fiddling with the framework I had difficulties creating scrolling views. The docs say that getting a scrolling area is as simple as adding a “scrollable” class to a div – not quite, there are some other requirements.

To get scrolling articles you must have the following structure – notice the DIV that wraps the content:

...
<article id="something" class="scrollable">
   <div> <!-- This DIV is important!! -->
      Your content in here
   </div>
</article>
...

If you attempt to create a scrollable div inside an article tag you **must** also set a height for that div, like so:

...
<article id="something">
   <div id="myDiv" class="scrollable" style="height:300px;">
      <div> <!-- This DIV is important!! -->
         Your content in here
      </div>
   </div>
</article>
...

Notice the ID in the scrollable divs – ID is **required**, you will see as much in the console if you forget this.

Note the DIV that is the immediate child of the scrollable div – that is also required. No need to style it or add an ID. Your content must be wrapped by a DIV – P’s, Lists and header elements also work. Spans do not.

If the scrolling area is not an article tag then you **must** include the height for the scrolling div, which is illustrated in the second example above.

I noticed a lot of confusion in the forums and was confused myself – IMO the docs should be amended with the above.

Cache Busting Your Web Service Requests

29 Jun 2012

While working with web services you always need to keep in mind that web browsers cache files. if you web service request doesn’t change ever so slightly you will run into the issue of the web browser not pulling the new response but instead pulling the previously cached response. This is easily mitigated by adding a variable that itself is always unique – notice that i didn’t say “random” – random may work, sure, but “random” means that you run the risk of encountering that number again at some point. Yes, feel free to come up with a random 20 digit number and it may be for all intents and purposes appear to be “unique” but the risk of that number re-appearing is still there, however obscure it may be. As most web service calls are GET requests its as simple as adding a new name/value pair with a unique value to every request. The service ignores it and the browser takes note that the request is different than the previous request and so accepts the new response from the server.

Add the following somewhere within your code to get the unique value:

...
   var d = new Date();
   var unique = d.getTime();
...

That gives you a number similar to 1340979406482. That value is the number of milliseconds since January 1, 1970 so naturally it increments every millisecond. Ideally the getTime() method would accept a date as an argument and would then provide the milliseconds between Jan 1, 1970 and the specified date but since we don’t provide that value the current date is assumed. So, that number represents the number of milliseconds from that date in 1970 and the millisecond in time when the code was executed.

I have seen people blog this topic but include dividing the millisecond value by 1000 and rounding the result. Not a great idea. Dividing by 1000 and rounding the result shortens the length of the number by 3 digits. If you shorten it too much it won’t be useful. Already, by dividing by 1000 I’m not getting a number that updates every millisecond but instead a number that updates every second. I suppose it depends on your idea of “unique” and your specific application, but I’d rather have the guarantee of uniqueness than have something that is fuzzy like doing Math on the already guaranteed unique number… makes no sense.

As to applying this, here’s a jQuery example:

...
var d = new Date();
var unique = d.getTime();
var dataStr = 'name_value_pairs_here';
$.ajax({
  url:      	'/some_service_here/',
  data:      	dataStr + '&nocache=' + unique,
  contentType:	'application/x-www-form-urlencoded',
  dataType: 	'json',
  error:function(obj,status,xhr){
    // handle error here
  },
  success:function(obj,status,xhr){
    // handle response here
  }
});
...

And better yet – jQuery has such a thing built into it, simply set the cache attribute as in this example:

...
var dataStr = 'name_value_pairs_here';
$.ajax({
  url:      	'/some_service_here/',
  data:      	dataStr,
  cache:        false, // cache buster
  contentType:	'application/x-www-form-urlencoded',
  dataType: 	'json',
  error:function(obj,status,xhr){
    // handle error here
  },
  success:function(obj,status,xhr){
    // handle response here
  }
});
...

When using the “cache” attribute within the jQuery Ajax config object jQuery will append a new name/value pair to the GET request in the form of:

  1. _=[timestamp]

Where “[timestamp]” is a unique value.

The usefullness of using d.getTime() is still there outside of jQuery. For example any call to a file that you don’t want to be cached, such as web pages, JS files, CSS, etc.

Javascript/Chrome as a Dataset Manipulation Tool

29 May 2012

You might have caught on that I’m a front-end developer – while I *have* done some ASP, the occasional PHP, minor SQL and even the rather obscure MIVAScript (DBF’s with index files) I definitely don’t consider myself a back-end guy. So, recently I was given the task of updating an e-commerce site, a global change across all products where the range of colors for certain items have all changed. As I originally setup the site (its a CoreCommerce site) I am the most familiar within the organization as to how it works and what such a change entails. My process would be to export the Personalization Table (this describes product-specific options), modify as needed and then import/apply the updates. Simple enough, right?

Most people I imagine would import the csv into Access or import into a temporary database on a server, do some SQL, export a new CSV, import into the system and be done with it. Well, as I’ve said, thats not quite my bag.

Excel has some macro functionality and I’m sure that given enough time I might be able to pick up enough VB to do what I want, but, I really don’t care to learn any VB. After spending an hour or two in Excell messing around with Macros I decided to apply what I already know and work with/in everyday: Javascript.

First things first – I need to get the csv into the browser in such a way as to make it usable. That means turning it into a Javascript-friendly string. This would involve surrounding all of it in single quotes, adding a comma to the end of every line and removing all the newlines/carriage returns so as to have a single string instead of 11,408 individual rows (yes, that many rows, with 41 columns).

Easiest way to accomplish the above is in Notepad++. In a few minutes I had what I wanted. I did a search and replace on the single quote character, replacing it with the escaped version (\’) – I probably should have just replaced all single quotes with the entity equivalent but, oh well, either way works. Next, I did a search/replace on the newline character utilizing the “Extended” feature of Notepadd++’s Replace dialogue (a regular expression would have worked here as well) and replaced it with a comma so as to have each line ending with a comma. Finally, I removed all carriage returns with another Extended Search operation – the search looked for \r and replaced with nothing resulted with one single line. All that’s needed then is to assign the result to variable by placing “var data = ” before the string, surrounding the string with single quotes and tacking a semi-colon on the end. The finished string had a length of 1,939,099 characters.

Ok, now we’re ready to write some Javascript!

Continuing in Notepad++ I created a simple HTML document and “imported” the data string via a Script tag, which looked like this:


  



Lets load the page in Chrome and consult the console (F12):

Note the message – the file has been loaded. No need to worry about the mime type. Lets verify that its there – lets check for the length of the string. Recall that I declared the variable as “data”, so lets do the following: data.length:

As you can see the data variable is present by virtue of the fact that we could discover its length. Note that its a bit shorter than the length we found inside NotePad++ – this shouldn’t be a surprise as I escaped all single quotes and the escape character doesn’t apply towards the character account. In addition, I added a tiny bit of Javascript to the text which doesn’t count either. Lets prove this – back in NotePad++ I did a search for the backslash (\) character. The result was 6,046 hits. 1,933,039 from Chrome’s reporting of the string’s length + the 6,046 backslashes = 1,939,085. Still a bit short, lets add the number of characters caused by the presence of the bit of Javascript and we get the total number of characters as reported by Notepad++. So, Chrome isn’t under reporting the string length but rather is reporting the exact length of the actual string excluding the above factors.

Ok then, lets move on. Lets convert this thing to an array. We know that the comma is the value delimiter so lets use the split array method using the comma as the required argument:


  
  



As you can see my function is called “rip” so I’ll reload the page and fire it manually by typing “rip()”:

A few milliseconds later its done. Note the “undefined” – there’s nothing wrong here – the rip() function doesn’t return anything so the “undefined” as the result of my function call is Chrome’s way of telling me as much. Its not that *nothing* has happened, its just that nothing was returned. If you like you could add a return statement to the end of the function but its totaly unecesary as we know why “undefined” appears.

So anyway now that we’ve fired rip() lets see if it worked. Lets check the length of the new array that was assigned to the “dataStepOne” variable:

You can see that to check the array’s length I typed the following into the console: “dataStepOne.length” – the length of the array is 467,728.

At this point I know that the data had 41 columns. What I want to do now is to recreate each row in some way – I decide to create a multidimensional array where each index of the 1st dimension represents a row and where the array at that dimension reflects the values for the row. This next bit accomplishes that task:


  
  



Again, within Chrome's console I execute the rip() function. This time I check the length of the dataStepTwo array by typing "dataStepTwo.length" and then hitting the enter key - the length is 11,408 - which is the number of rows in the original spreadsheet. To verify that the resulting data construct is good I decide to take a look at a "random" row by typing dataStepTwo[200]. Chrome displays the array at index 200 for me. See the following image:

I cross reference the above array values against the 200th row in the spreadsheet and the values are identical. For kicks I also check the first and second rows of the array - the first row should contain all the column names, which it does, and the second row shouldn't have any columns names in it - a sanity to check to ensure that the column count is correct and that no row data is being erroneously placed in other rows. Yes, given the first test I already knew this to be the case but I wanted to look anyway. Everything is correct! Moving on....

So now that I have a representation of the data its time to start manipulating it. The way CoreCommerce's table is setup is a bit odd - you can see that one of the values has a pipe in it - this is a delimiter as there are actually two values in that field. I've no idea why they designed it this way - whatever, it is what it is. The thing to note here is that this reflects only a single color for the given product. There are 21 colors, so there are 21 rows representing the color options per drop-down selection list per product with the only difference being the name of the color in each row. See, the left value before the pipe represents the name of the drop-down list and the right side of the pipe represents the value that goes in that list. Yeah, I know, kinda crazy but that's what it is.

So knowing how things are structured I need to loop through the dataStepTwo array and remove/modify as needed.

First thing though is to create the new data that is to replace the old data. Here is the new data to replace the old:


  
  



Note that newData[x][0] is just the value that I'm concerned with - in the actual table its the pipe delimited value that I mentioned previously. As you continue reading you will see in my code that I am splitting the originally pipe-delimited value, replacing the [1] with a new value then joining and inserting the updated string.

After putting the above together and thinking about what I needed to do I realized that the only things that need to change are the values in index 4 and index 15 of the arrays representing each individual table row. So, most of the newData array as seen above is not necessary as I will only be using newData[x][0] and newData[x][9]. At this point I let it be as I wasn't sure if I would need it for some reason yet to be discovered. Anyway, no harm done, lets continue...

So, now about the logic - there are 14 new colors. Any color name that contains the words "Powder Coat" are ones that need to have their values changed. Caveat is that some existing products have more colors than what we will end up with. So I will need to edit the first 14 colors to reflect the new values and then delete any left over colors. Here's what I came up with:


  
  



To test this I capped the for loop at 100 and then as before took a look at specific dataStepTwo indexes via the console. I discovered that the script was working fine, modifying/deleting rows as needed. Next I wanted to do the full monty - rip through everything, rebuild the CSV and output to the screen via a textarea tag. Here then is the entire script:


  
  


  


And a pic of the final product - 10,752 rows, which is quite a few less than the original but that's expected as I have deleted unnecessary rows. All that is left is to copy and paste into a text file:

So.... JavaScript does all this work quite easily. The join, split and splice array methods proved to be quite handy. Also, Chrome's performance was near instantaneous. The only things that took 2-3 seconds to do were to determine the length of the strings/arrays when I wanted them output to the console for testing. Otherwise, without doing those things it takes no time at all for Chrome to crunch all the strings/loop through the arrays.

The best part is how little code was necessary to do this and there was no need to setup a temporary database and mess with SQL, it was more straightforward than what I thought it would be.

Logo

All content © 2012-2017.