JS Mojo Geocoder Demo

Added a JS example that demos encapsulating Google's geocoder web service with a Mojo application.

BUG=
R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/848213003
diff --git a/examples/js/maps/geocoder_service.js b/examples/js/maps/geocoder_service.js
new file mode 100644
index 0000000..351a460
--- /dev/null
+++ b/examples/js/maps/geocoder_service.js
@@ -0,0 +1,128 @@
+#!mojo mojo:js_content_handler
+
+define("main", [
+  "console",
+  "examples/js/maps/geocoder.mojom",
+  "mojo/public/js/core",
+  "mojo/public/js/unicode",
+  "mojo/services/public/js/application",
+  "mojo/services/network/public/interfaces/network_service.mojom",
+  "mojo/services/network/public/interfaces/url_loader.mojom",
+  "third_party/js/url",
+], function(console, geocoder, core, unicode, application, network, loader, url) {
+
+  const Application = application.Application;
+  const Bounds = geocoder.Bounds;
+  const Geocoder = geocoder.Geocoder;
+  const Geometry = geocoder.Geometry;
+  const Location = geocoder.Location;
+  const NetworkService = network.NetworkService;
+  const Result = geocoder.Result;
+  const Status = geocoder.Status;
+  const URLRequest = loader.URLRequest;
+  const URL = url.URL;
+
+  var netService;
+
+  Location.prototype.queryString = function() {
+    // TBD: format floats to 6 decimal places
+    return this.latitude + ", " + this.longitude;
+  }
+
+  Location.fromJSON = function(json) {
+    return !json ? null : new Location({
+      latitude: json.lat,
+      longitude: json.lng,
+    });
+  }
+
+  Bounds.fromJSON = function(json) {
+    return !json ? null : new Bounds({
+      northeast: Location.fromJSON(json.northeast),
+      southwest: Location.fromJSON(json.southwest),
+    });
+  }
+
+  Geometry.fromJSON = function(json) {
+    return !json ? null : new Geometry({
+      location: Location.fromJSON(json.location),
+      location_type: json.location_type,
+      viewport: Bounds.fromJSON(json.viewport),
+      bounds: Bounds.fromJSON(json.bounds),
+    });
+  }
+
+  Result.fromJSON = function(json) {
+    return !json ? null : new Result({
+      partial_match: !!json.partial_match,
+      formatted_address: json.formatted_address,
+      geometry: Geometry.fromJSON(json.geometry),
+      types: json.types,
+      // TBD: address_components
+    });
+  }
+
+  function parseGeocodeResponse(arrayBuffer) {
+    return JSON.parse(unicode.decodeUtf8String(new Uint8Array(arrayBuffer)));
+  }
+
+  function geocodeRequest(url) {
+    return new Promise(function(resolveRequest) {
+      var urlLoader;
+      netService.createURLLoader(function(urlLoaderProxy) {
+        urlLoader = urlLoaderProxy;
+      });
+
+      var urlRequest = new URLRequest({
+        url: url.format(),
+        method: "GET",
+        auto_follow_redirects: true
+      });
+
+      urlLoader.start(urlRequest).then(function(urlLoaderResult) {
+        core.drainData(urlLoaderResult.response.body).then(
+          function(drainDataResult) {
+            // TBD: handle drainData failure
+            var value = parseGeocodeResponse(drainDataResult.buffer);
+            resolveRequest({
+              status: value.status,
+              results: value.results.map(Result.fromJSON),
+            });
+          });
+      });
+    });
+  }
+
+  function geocodeURL(key, value, options) {
+    var url = new URL("https://maps.googleapis.com/maps/api/geocode/json");
+    url.query = {};
+    url.query[key] = value;
+    // TBD: add options url.query
+    return url;
+  }
+
+  class GeocoderImpl {
+    addressToLocation(address, options) {
+      return geocodeRequest(geocodeURL("address", address, options));
+    }
+
+    locationToAddress(location, options) {
+      return geocodeRequest(
+          geocodeURL("latlng", location.queryString(), options));
+    }
+  }
+
+  class GeocoderService extends Application {
+    initialize() {
+      netService = this.shell.connectToService(
+        "mojo:network_service", NetworkService);
+    }
+
+    acceptConnection(initiatorURL, serviceProvider) {
+      serviceProvider.provideService(Geocoder, GeocoderImpl);
+    }
+  }
+
+  return GeocoderService;
+});
+