JavaScript and Flash are great for putting Google Maps on your website, but sometimes they just won't do. For mobile browsers or users with dial-up connections, simpler is better. So I wrote an open source non-JavaScript version of Google Maps which is designed to show how easy it is to write an application on App Engine that makes use of two new APIs from Google: The Static Maps API and the Local Search API's REST interface. It doesn't have advanced features like street view and public transportation, but it gives you a searchable map that you can zoom in/out on as well as save locations. It also automatically saves your last map view so that every time you go back to the site it will show you what you were last looking at. Check out the source code.
It uses App Engine to store saved points, the AJAX LocalSearch REST API for search functionality, and the Static Maps API to display maps. App Engine is easy to learn and the data store is useful for this kind of application. The REST API for LocalSearch is also very simple. For more information on it, go here.
To use the Static Maps API, you just need to create a URL with the proper parameters for your desired map view. Keep in mind that you need to set the zoom level (unless you are specifying multiple points — then it's calculated for you). In the vast majority of cases, this is completely fine. In my case, though, I needed to know what the zoom level was, so that I could give the user the option to zoom in/out. That meant coming up with calculations of the zoom both for the multiple points and single point case, and that was the trickiest part of the app.
If you use the AJAX Local Search and it returns one result then there will be a viewport object returned with it. This viewport contains the Northeast and Southwest latitude/longitude bounds that are optimal for displaying this point. However, Static Maps only accept zoom levels and center points. Here's the Python to generate that information:
At this point you will have everything you need to construct the map: the center point (the Local Search point), zoom level, marker point.
Then there's the case where you have multiple points returned by the AJAX Local Search. Since we will have a collection of latitudes and longitude points that we want to display we can just find the min/maxes, do some rounding, and voilĂ you get a bounding box. With a bounding box and a calculated center point, you can repeat the same steps as before.
From line 121 to about 285 you'll find all the necessary functions for the situations described above. Try using this code to create your own interactive version of Static Maps, and let us know in the forum if you have questions or just want to show off your nifty app.
MooTools v1.2.1 recently got released and we have received the thumbs up from the maintainers to host it, so the bits have been flipped and the pipes are ready to MOOve it over to you guys.
But wait!! Before you get started with 1.2.1, please note that the previous version we are hosting (1.11) and the new version (1.2.1) are incompatible. So as not to break anyone out of the blue (and put them in a bad MOOd) we have frozen the version "1" alias to 1.11. Here's what the aliases look like for MooTools: google.load('mootools', '1') // gives you version 1.11 google.load('mootools', '1.11') // gives you version 1.11 google.load('mootools', '1.2.1') // gives you version 1.2.1 ... you get the picture.
If you have some extra time, stop by the group and show us what you've been up to with MooTools. Happy grazing!
So, the other day, I was asked by the AJAX APIs dev team if I'd like to write a guest blog post, but they didn't tell me what to write about. I thought about telling you how the AJAX APIs revolutionized how I think about life, design websites, and slice bread, but then I realized that I buy my bread pre-sliced. So I started going back through the group looking for common questions or themes to threads, and I realized that a huge portion of the questions asked can be summed up like this: "How do I style the google.search.SearchControl?" For instance, how would one make it so that only a result's title and URL appear (i.e., the description is not visible), or even just the URL? What if you want to change some of the default colors? What if you, being the stylish computer geek that you are, want to make your SearchControl into a 24th century Starfleet console to fit in with that first-season spandex Star Trek: The Next Generation uniform you're wearing right now?
Well, you're in luck! Using Mozilla Firefox with the Firebug add-on installed (an absolute must-have for any web developer, by the way), you can do all this and more simply by inspecting the structure of the default search control and taking advantage of the fact that almost every one of its individual elements is given at least one className that can be used with Cascading Style Sheets to apply different style rules. We've put together a spectacular video to provide a very brief overview of using Firefox and Firebug to inspect the structure (and tinker with) the structure of the control.
For even more information on how you can do more with Firefox and Firebug, you'll want to check out Ben Lisbakken's excellent tutorial, which includes even more video! And for good measure, we've included the control's structure (complete with a few of my own comments) below:
<div class="gsc-control"> <!-- FYI: This form is the same as the google.search.SearchForm --> <form class="gsc-search-box"> <table class="gsc-search-box"> <tbody> <tr> <td class="gsc-input"> <!-- This next input is the search box itself --> <input class="gsc-input/> </td> <td class="gsc-search-button"> <!-- This next input is the search button itself --> <input class="gsc-search-button"/> </td> <td class="gsc-clear-button"> <!-- This next div is the clear button (i.e., the little x) --> <div class="gsc-clear-button"/> </td> </tr> </tbody> </table> <table class="gsc-branding"> <tbody> <tr> <td class="gsc-branding-user-defined"/> <td class="gsc-branding-text"> <div class="gsc-branding-text">powered by</div> </td> <td class="gsc-branding-img"> <img class="gsc-branding-img"/> </td> </tr> </tbody> </table> </form> <!-- In tabbed mode, this is where the tabs will appear; in stacked mode, this will be absent! --> <div class="gsc-tabsArea"> <!-- The following div would be the active tab --> <div class="gsc-tabHeader gsc-tabhActive"/> <!-- These spacer divs will appear AFTER every tab to do exactly what their className would imply --> <div class="gs-spacer"/> <!-- And this one would be an inactive tab --> <div class="gsc-tabHeader gsc-tabhInactive"/> <div class="gs-spacer"/> </div> <div class="gsc-resultsBox-visible"> <!-- The next divs contain the actual results. The classes in square brackets are ONLY present in TABBED mode --> <!-- This would be the active tab --> <div class="gsc-resultsRoot [gsc-tabData gsc-tabdActive]"> <table class="gsc-resultsHeader"> <tbody> <tr> <td class="gsc-twiddleRegionCell gsc-twiddleRegion-opened"> <div class="gsc-twiddle"> <!-- This next div will contain your searcher's title or label (e.g., Local), but it won't be visible in tabbed mode --> <div class="gsc-title"/> </div> <!-- This next div will contain your search's estimated result count, but it's invisible in tabbed mode, too! --> <div class="gsc-stats"> <!-- This is the selector that chooses 1, 4, or 8 visible results. Please note that only ONE of the options in square brackets will be visible --> <div class="gsc-results-selector [gsc-one-result-active OR gsc-more-results-active OR gsc-all-results-active]"> <div class="gsc-result-selector gsc-one-result"/> <div class="gsc-result-selector gsc-more-results"/> <div class="gsc-result-selector gsc-all-results"/> </div> </td> <td class="gsc-configLabelCell"> <!-- This next span will only be present if the searcher has configuration options --> <!-- Also, it will only have ONE of the options in square brackets, depending on whether or not the configuration form is visible or not --> <span class="gsc-configLabel [gsc-twiddle-closed OR gsc-twiddle-opened]"/> </td> </tr> </tbody> </table> <!-- This next div is the configuration form for a searcher. It is only present if the searcher has configuration options --> <!-- The exact className of the config form will depend on the type of searcher. So you'll only have ONE of the options below. You can probably figure out which one your searcher will have --> <!-- By the way, I have no idea why it's gsc-locationConfig instead of gsc-localConfig :) --> <div class="gsc-config [gsc-locationConfig OR gsc-videoConfig OR gsc-blogConfig OR gsc-newsConfig OR gsc-patentConfig]"> <!-- The exact content of the config div will vary depending on your searcher's options --> <!-- Use Firefox with Firebug to explore the possibilities here! --> </div> <div class="gsc-results [gsc-localResult OR gsc-webResult OR gsc-blogResult OR gsc-newsResult OR gsc-imageResult OR gsc-bookResult OR gsc-patentResult OR gsc-videoResult]"> <!-- This is your FIRST actual search result. All results will follow this pattern --> <!-- Please note, again, that only ONE of the classNames in the square brackets will apply, depending on the searcher --> <div class="gsc-result [gsc-localResult OR gsc-webResult OR gsc-blogResult OR gsc-newsResult OR gsc-imageResult OR gsc-bookResult OR gsc-patentResult OR gsc-videoResult]"> <!-- The contents of this div will be the same as outlined in the documentation for your searcher's results --> </div> <div class="gsc-expansionArea"> <!-- This is where the REST of your search results show up, again following the same pattern as above --> </div> </div> </div> <!-- And the inactive one --> <div class="gsc-resultsRoot [gsc-tabData gsc-tabdInactive]"> <!-- All the rest of the structure of this is the same as above --> </div> </div>
So, thanks to Firefox and Firebug, we have access to the SearchControl's structure. Now we can get to work making our control look like one of those 24th-century consoles that we see every day on the starship Enterprise! To get this done, we're going to start with the stock "Hello, world" example for the AJAX Search API. Then we're going to remove the style element that comes with it and plug in a new external stylesheet below the inline script that initializes the whole thing.
Once that's done, we need to (a) change a bunch of colors; (b) rework a number of background images; and (c) open a small hole in the space-time continuum to get ourselves a starship console to put it all on. Okay, so that last one isn't exactly possible, but we can do all the rest with a little CSS. And the whole thing, put together, looks like this.
Now, you will notice that a number of those rules have complex selectors (e.g., .gsc-resultsHeader td.gsc-twiddle-opened...). This is because Google's default CSS is rather specific in places. And it's also why Firefox with Firebug is so very important. It really makes the whole process almost painless.
So there you have it: style. Well, for your SearchControl, at least. Next time, we'll talk about why the Starfleet quartermaster abandoned spandex.
Until then, happy styling!
Jeremy R. Geerdes Generally Cool Guy jrgeerdes@gmail.com
p.s. What cool custom designs have you come up with? Share them in the Google Group! Ben Lisbakken has promised some Google schwag for interesting designs...
If you're a Flash nut then you probably know about the SWFObject Javascript library. I'm not, so I didn't. However, since I promised myself I would play with Flex soon I am very happy that a "Flasher" suggested that we add it to our AJAX Libraries API. I took a look at it and found out that it's a great little must-have library!
See, embedding a Flash video on a page is actually more complicated than it should be. You can't just throw a tag on the page with some attributes and expect it to work. In fact, to embed a Flash file there are different methods for different browsers such as using an <embed> vs. <object> as well as setting the parameters for the file.
The SWFObject library simplifies the process so that all you need to worry about is including their Javascript library and using a single method to embed your Flash on the page in a safe, cross-browser manner. It also has a few extra utility functions, such as setting a load event for the Flash object and detecting the user's Flash version.
We've been hosting most of the big powerhouse Javascript libraries and so far it's been a huge success. However, without YUI!, our collection has been incomplete. Well, I'm not saying that now we're complete and won't add more libraries, just that we're less incomplete than we were... you know what I mean.
Anyways, after getting legal approval (people actually read those licenses), we pushed it live. We are equally as excited about this as Yahoo! and the Javascript community. So let's take a look at how you use it:
<head> <script src="http://www.google.com/jsapi" type="text/javascript" charset="utf-8"></script> <script type="text/javascript" charset="utf-8"> google.load('yui', '2.6.0'); function init() { var loader = new YAHOO.util.YUILoader({ require: ["button", "calendar"], base: "http://ajax.googleapis.com/ajax/libs/yui/2.6.0/build/", onSuccess: function() { // start playing with buttons and calendars! } }); loader.insert(); } google.setOnLoadCallback(init); </script> </head> <body> <div id="calContainer"></div> </body>
That code will load the YUI loader which then loads the button and calendar widgets (combined as one script) and calls onSuccess when they are loaded. If lazy loading scripts isn't your thing, then you can use the dependency configurator to configure the perfect script URL. In my case, to achieve the same results as above, I would use:
I recently launched a new product to help developers learn our APIs. It's an open source framework for showing Javascript code samples. The tool allows users to click through samples so that they can edit code and see their changes live. In order to be a more practical tool for code editing, I put "save" and "export" features in there. You can read more about it on the official blog post.
I would also like to point you at a new blog the AJAX APIs team will be maintaining: the AJAX APIs Alert Blog. Whether you know it or not (or believe it or not!) we are constantly making changes to the APIs. New features, bug fixes, tweaks, etc. In the past we haven't had a way of communicating these changes to you since they can be small changes and not worth a whole blog post here. Furthermore, there are those of you that want to know when we update the Javascript libraries that we host. So from here on out, that blog will be the new home for these "alerts". Posts will be short, to the point and we will try to update it with all public changes, so subscribe now!
We have updated versions of jQuery and YUI on our CDN. jQuery is now at version 1.3.2 which will be what version 1 and 1.3 aliases point to.
YUI's new version, 2.7.0, includes a new stylesheet utility to do dynamic CSS without looping through DOM nodes as well as some enhancements to existing functionality such as charts, DOM collection, and tree view. Furthermore, YUI is maintaining their image as a library for professionals by beginning to add IE8 support in preparation for Microsoft's upcoming launch.
I'd also like to remind you that all public updates to our APIs can be followed on our Google AJAX API Alerts blog. That blog is meant for minor API additions/updates/fixes, and in the future these alerts will only be posted there.
We started nextstop.com with the idea that we could make it significantly easier and more fun to discover new and interesting things to do anywhere in the world, based on recommendations from people who know a place well. Whether it was a neat museum, a hidden local restaurant, or a great place to go shopping we wanted to make it super easy and fun for people to share recommendations for their favorite places, wherever they might live.
The trick of course was in how to do this. It was important for us to combine ease of making a recommendation -- our goal was that it should be as simple as entering the name of a place, and a few sentences about why you liked it -- with rich information about a place so it was really useful to others -- photos, contact information, maps, etc. The solution, not surprisingly since I'm writing here, was to use a number of Google's APIs to gather related information about the recommendation and make it easy for our members to include it in their recommendation.
You can best see how this works by going through our recommendation flow, or watching the video below.
Let me walk you through how this is working under the hood:
1) When the page loads, the first thing we do is use the Google loader to load the JQuery and JQuery UI libraries, as well as Google Maps. As part of this, we also grab the user's current location using google.loader.ClientLocation and store the lat/lng if available to use later.
2) In step 1, we ask the user for what's being recommended. We use this string to do a Google local search for business listings and KML results that match, using the user's current location to bound the local search by setting the sll and sspn parameters. Between local business listings and KML results, we can offer incredible global coverage of everything from restaurants to tourist attractions to hole-in-the-wall bars and clubs. We're using the JSON version of the local search API, which we call from our servers using Python's urlopen() so that we can supplement the results with our own database of results.
3) In step 2 we do an image search for related images using Google's image search API. While we let users change the search terms to find just the right picture, often our default image search (which combines the name of the place and a city name) returns great results. There are photos of almost everything, so you can even recommend a particular dish at a restaurant in Taipei and have the photos to go along with it.
4) In step 3, we ask for a few sentences about why that place or activity really stands out to them. After the recommendation has been submitted, we use the Google Language APIs to detect the language of the recommendation, which we can later use to filter content by your language, and we hope to someday integrate the ability to translate recommendations into your language of choice.
Its a very simple and fast process for the user making the recommendation, but the result is a recommendation with address, phone number, map, and photo that is really useful to another user looking to discover something new.
We've built our whole product around the Google APIs, and feel like we're just scratching the surface of what's possible. We're planning to let users add other information (like related websites, searches, news, etc.) using Google's APIs as well.
We'll be at Google I/O on May 27-28 talking about what we've done so far, and will hopefully have a few new uses of the Google APIs to show off at that time. Please come say hello -- we'd love to hear your feedback on nextstop, or share tips on using the Google APIs. You can also check out some of the places recommended near the Moscone center, or add a few of your own!
I am very pleased to announce version 2 of the AJAX APIs Playground. For those of you not familiar with it, the Playground is an educational application designed to show interactive code samples for some of Google's coolest Javascript APIs. Of the new changes, the most obvious is the sweet new UI, thanks to help from Roman Nurik of the Google Earth team.
The new features are: * Break points (simulated in Javascript) * Firebug Lite in output for debugging * Line numbers in code editor * Ability to edit HTML of samples
The breakpoints and Firebug Lite additions are my favorite new features. But why did I include Firebug Lite when all web developers (should!) have Firebug installed? Because when code runs on the Playground, it runs in an iFrame. That iFrame does not have the Firebug object initialized in it. Since it is a cross-domain iFrame, there's no simple way to add Firebug to the iFrame's window object, so adding Firebug Lite was the best approach. This makes it so you can now use all of your favorite Firebug debugging convenience functions in the Playground!
To use Firebug Lite and breakpoints, simply click on the line number you want to add a breakpoint to and hit "Debug Code". This will insert Firebug Lite into the output and pause the execution on the breakpoint line number until you to click the play button to continue. Try adding a breakpoint to a line, clicking "Debug Code", then opening Firebug Lite and typing in a variable name to inspect the contents/value of the variable at that point in the code.
Also, it's really important that you share your feedback so that I know what you'd like to see in the next version of the Playground! Thanks, and enjoy the Playground!
Recently, the guys over at Ext JS released ext-core under an MIT license, which was a big win for open source! Today, they released a stable (non-beta) version of this library and we are proud to be part of that announcement by hosting the new version. You can now pull ext-core from Google servers:
// directly access it http://ajax.googleapis.com/ajax/libs/ext-core/3.0.0/ext-core.js // alias the newest 3.0.x version http://ajax.googleapis.com/ajax/libs/ext-core/3.0/ext-core.js // alias the newest 3.x.x version http://ajax.googleapis.com/ajax/libs/ext-core/3/ext-core.js // directly access the uncompressed code http://ajax.googleapis.com/ajax/libs/ext-core/3.0.0/ext-core-debug.js
Thanks to all for the requests to add ext-core to our Libraries API, and big thanks to Ext JS for providing their library so openly! For more information, head over to their blog post.