(function() {
  var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    hasProp = {}.hasOwnProperty;

  (function($) {
    this.AddressPickerResult = (function() {
      function AddressPickerResult(placeResult, fromReverseGeocoding) {
        this.placeResult = placeResult;
        this.fromReverseGeocoding = fromReverseGeocoding != null ? fromReverseGeocoding : false;
        this.latitude = this.placeResult.geometry.location.lat();
        this.longitude = this.placeResult.geometry.location.lng();
      }

      AddressPickerResult.prototype.addressTypes = function() {
        var component, i, j, len, len1, ref, ref1, type, types;
        types = [];
        ref = this.addressComponents();
        for (i = 0, len = ref.length; i < len; i++) {
          component = ref[i];
          ref1 = component.types;
          for (j = 0, len1 = ref1.length; j < len1; j++) {
            type = ref1[j];
            if (types.indexOf(type) === -1) {
              types.push(type);
            }
          }
        }
        return types;
      };

      AddressPickerResult.prototype.addressComponents = function() {
        return this.placeResult.address_components || [];
      };

      AddressPickerResult.prototype.address = function() {
        return this.placeResult.formatted_address;
      };

      AddressPickerResult.prototype.nameForType = function(type, shortName) {
        var component, i, len, ref;
        if (shortName == null) {
          shortName = false;
        }
        ref = this.addressComponents();
        for (i = 0, len = ref.length; i < len; i++) {
          component = ref[i];
          if (component.types.indexOf(type) !== -1) {
            return (shortName ? component.short_name : component.long_name);
          }
        }
        return null;
      };

      AddressPickerResult.prototype.lat = function() {
        return this.latitude;
      };

      AddressPickerResult.prototype.lng = function() {
        return this.longitude;
      };

      AddressPickerResult.prototype.setLatLng = function(latitude, longitude) {
        this.latitude = latitude;
        this.longitude = longitude;
      };

      AddressPickerResult.prototype.isAccurate = function() {
        return !this.placeResult.geometry.viewport;
      };

      AddressPickerResult.prototype.isReverseGeocoding = function() {
        return this.fromReverseGeocoding;
      };

      return AddressPickerResult;

    })();
    return this.AddressPicker = (function(superClass) {
      extend(AddressPicker, superClass);

      function AddressPicker(options) {
        if (options == null) {
          options = {};
        }
        this.markerDragged = bind(this.markerDragged, this);
        this.updateBoundsForPlace = bind(this.updateBoundsForPlace, this);
        this.updateMap = bind(this.updateMap, this);
        this.options = $.extend({
          local: [],
          datumTokenizer: function(d) {
            return Bloodhound.tokenizers.whitespace(d.num);
          },
          queryTokenizer: Bloodhound.tokenizers.whitespace,
          autocompleteService: {
            types: ["geocode"]
          },
          zoomForLocation: 16,
          reverseGeocoding: false,
          placeDetails: true,
          remote: 'fakeRemote'
        }, options);
        AddressPicker.__super__.constructor.call(this, this.options);
        if (this.options.map) {
          this.initMap();
        }
        this.placeService = new google.maps.places.PlacesService(document.createElement('div'));
      }

      AddressPicker.prototype.bindDefaultTypeaheadEvent = function(typeahead) {
        typeahead.bind("typeahead:selected", this.updateMap);
        typeahead.bind("typeahead:autocompleted", this.updateMap);
        return typeahead.bind("typeahead:cursorchanged", this.updateMap);
      };

      AddressPicker.prototype.initMap = function() {
        var markerOptions, ref, ref1;
        if ((ref = this.options) != null ? (ref1 = ref.map) != null ? ref1.gmap : void 0 : void 0) {
          this.map = this.options.map.gmap;
        } else {
          this.mapOptions = $.extend({
            zoom: 3,
            center: new google.maps.LatLng(0, 0),
            mapTypeId: google.maps.MapTypeId.ROADMAP,
            boundsForLocation: this.updateBoundsForPlace
          }, this.options.map);
          this.map = new google.maps.Map($(this.mapOptions.id)[0], this.mapOptions);
        }
        this.lastResult = null;
        markerOptions = $.extend({
          draggable: true,
          visible: false,
          position: this.map.getCenter(),
          map: this.map
        }, this.options.marker || {});
        this.marker = new google.maps.Marker(markerOptions);
        if (markerOptions.draggable) {
          return google.maps.event.addListener(this.marker, 'dragend', this.markerDragged);
        }
      };

      AddressPicker.prototype.search = function(query, sync, async) {
        var service;
        service = new google.maps.places.AutocompleteService();
        this.options.autocompleteService.input = query;
        return service.getPlacePredictions(this.options.autocompleteService, (function(_this) {
          return function(predictions) {
            $(_this).trigger('addresspicker:predictions', [predictions]);
            return async(predictions);
          };
        })(this));
      };

      AddressPicker.prototype.updateMap = function(event, place) {
        if (this.options.placeDetails) {
          return this.placeService.getDetails(place, (function(_this) {
            return function(response) {
              var ref;
              _this.lastResult = new AddressPickerResult(response);
              if (_this.marker) {
                _this.marker.setPosition(response.geometry.location);
                _this.marker.setVisible(true);
              }
              if (_this.map) {
                if ((ref = _this.mapOptions) != null) {
                  ref.boundsForLocation(response);
                }
              }
              return $(_this).trigger('addresspicker:selected', _this.lastResult);
            };
          })(this));
        } else {
          return $(this).trigger('addresspicker:selected', place);
        }
      };

      AddressPicker.prototype.updateBoundsForPlace = function(response) {
        if (response.geometry.viewport) {
          return this.map.fitBounds(response.geometry.viewport);
        } else {
          this.map.setCenter(response.geometry.location);
          return this.map.setZoom(this.options.zoomForLocation);
        }
      };

      AddressPicker.prototype.markerDragged = function() {
        if (this.options.reverseGeocoding) {
          return this.reverseGeocode(this.marker.getPosition());
        } else {
          if (this.lastResult) {
            this.lastResult.setLatLng(this.marker.getPosition().lat(), this.marker.getPosition().lng());
          } else {
            this.lastResult = new AddressPickerResult({
              geometry: {
                location: this.marker.getPosition()
              }
            });
          }
          return $(this).trigger('addresspicker:selected', this.lastResult);
        }
      };

      AddressPicker.prototype.reverseGeocode = function(position) {
        if (this.geocoder == null) {
          this.geocoder = new google.maps.Geocoder();
        }
        return this.geocoder.geocode({
          location: position
        }, (function(_this) {
          return function(results) {
            if (results && results.length > 0) {
              _this.lastResult = new AddressPickerResult(results[0], true);
              return $(_this).trigger('addresspicker:selected', _this.lastResult);
            }
          };
        })(this));
      };

      AddressPicker.prototype.getGMap = function() {
        return this.map;
      };

      AddressPicker.prototype.getGMarker = function() {
        return this.marker;
      };

      return AddressPicker;

    })(Bloodhound);
  })(jQuery);

}).call(this);
