text: 'use strict';
  
  /**
   * A directive for adding google places autocomplete to a text box
   * google places autocomplete info: https://developers.google.com/maps/documentation/javascript/places
   *
   * Usage:
   *
   * <input type="text"  ng-autocomplete ng-model="autocomplete" options="options" details="details/>
   *
   * + ng-model - autocomplete textbox value
   *
   * + details - more detailed autocomplete result, includes address parts, latlng, etc. (Optional)
   *
   * + options - configuration for the autocomplete (Optional)
   *
   *       + types: type,        String, values can be 'geocode', 'establishment', '(regions)', or '(cities)'
   *       + bounds: bounds,     Google maps LatLngBounds Object, biases results to bounds, but may return results outside these bounds
   *       + country: country    String, ISO 3166-1 Alpha-2 compatible country code. examples; 'ca', 'us', 'gb'
   *       + watchEnter:         Boolean, true; on Enter select top autocomplete result. false(default); enter ends autocomplete
   *
   * example:
   *
   *    options = {
   *        types: '(cities)',
   *        country: 'ca'
   *    }
  **/
  
  angular.module("ngAutocomplete", [])
    .directive('ngAutocomplete', function () {
        return {
            require: 'ngModel',
            scope: {
                ngModel: '=',
                options: '=?',
                details: '=?'
            },
  
            link: function (scope, element, attrs, controller) {
  
                //options for autocomplete
                var opts
                var watchEnter = false
                //convert options provided to opts
                var initOpts = function () {
  
                    opts = {}
                    if (scope.options) {
  
                        if (scope.options.watchEnter !== true) {
                            watchEnter = false
                        } else {
                            watchEnter = true
                        }
  
                        if (scope.options.types) {
                            opts.types = []
                            opts.types.push(scope.options.types)
                            scope.gPlace.setTypes(opts.types)
                        } else {
                            scope.gPlace.setTypes([])
                        }
  
                        if (scope.options.bounds) {
                            opts.bounds = scope.options.bounds
                            scope.gPlace.setBounds(opts.bounds)
                        } else {
                            scope.gPlace.setBounds(null)
                        }
  
                        if (scope.options.country) {
                            opts.componentRestrictions = {
                                country: scope.options.country
                            }
                            scope.gPlace.setComponentRestrictions(opts.componentRestrictions)
                        } else {
                            scope.gPlace.setComponentRestrictions(null)
                        }
                    }
                }
  
                if (scope.gPlace == undefined) {
                    scope.gPlace = new google.maps.places.Autocomplete(element[0], {});
                }
                google.maps.event.addListener(scope.gPlace, 'place_changed', function () {
                    var result = scope.gPlace.getPlace();
                    if (result !== undefined) {
                        if (result.address_components !== undefined) {
  
                            scope.$apply(function () {
  
                                scope.details = result;
  
                                controller.$setViewValue(element.val());
                            });
                        }
                        else {
                            if (watchEnter) {
                                getPlace(result)
                            }
                        }
                    }
                })
  
                //function to get retrieve the autocompletes first result using the AutocompleteService 
                var getPlace = function (result) {
                    var autocompleteService = new google.maps.places.AutocompleteService();
                    if (result.name.length > 0) {
                        autocompleteService.getPlacePredictions(
                          {
                              input: result.name,
                              offset: result.name.length
                          },
                          function listentoresult(list, status) {
                              if (list == null || list.length == 0) {
  
                                  scope.$apply(function () {
                                      scope.details = null;
                                  });
  
                              } else {
                                  var placesService = new google.maps.places.PlacesService(element[0]);
                                  placesService.getDetails(
                                    { 'reference': list[0].reference },
                                    function detailsresult(detailsResult, placesServiceStatus) {
  
                                        if (placesServiceStatus == google.maps.GeocoderStatus.OK) {
                                            scope.$apply(function () {
  
                                                controller.$setViewValue(detailsResult.formatted_address);
                                                element.val(detailsResult.formatted_address);
  
                                                scope.details = detailsResult;
  
                                                //on focusout the value reverts, need to set it again.
                                                var watchFocusOut = element.on('focusout', function (event) {
                                                    element.val(detailsResult.formatted_address);
                                                    element.unbind('focusout')
                                                })
  
                                            });
                                        }
                                    }
                                  );
                              }
                          });
                    }
                }
  
                controller.$render = function () {
                    var location = controller.$viewValue;
                    element.val(location);
                };
  
                //watch options provided to directive
                scope.watchOptions = function () {
                    return scope.options
                };
                scope.$watch(scope.watchOptions, function () {
                    initOpts()
                }, true);
  
            }
        };
    });