├── .gitignore ├── .jshintrc ├── LICENSE ├── README.md ├── angular-geocomplete.js ├── bower.json ├── demo ├── app.js └── index.html ├── gulpfile.js ├── karma.conf.js ├── package.json └── tests └── angular-geocomplete.spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | bower_components/ 3 | coverage/ 4 | npm-debug.log 5 | *.sw[nop] 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": false, 8 | "eqeqeq": true, 9 | "eqnull": true, 10 | "immed": true, 11 | "indent": 2, 12 | "latedef": true, 13 | "newcap": true, 14 | "noarg": true, 15 | "quotmark": "single", 16 | "regexp": true, 17 | "undef": true, 18 | "unused": true, 19 | "strict": true, 20 | "trailing": true, 21 | "smarttabs": true, 22 | "globals": { 23 | "angular": false, 24 | "_": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2014 Jose Luis Rivas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | angular-geocomplete 2 | =================== 3 | 4 | Simple Angular.js factory that gets GeoData from Google Maps's API with a simple name of a City or an Address. 5 | 6 | It returns arrays of data and as much options are available for the query. It has two methods: `cities` and `citiesJSON`. 7 | 8 | The first one returns an array with just a String with the following data and format: "City Name, State Name or abbreviature, Country Name". 9 | 10 | The second method returns an array with a bunch more data formatted as the Google Maps's API does. 11 | 12 | More docs on the code. 13 | 14 | Usage 15 | ----- 16 | 17 | Install with bower: 18 | 19 | bower install angular-geocomplete --save 20 | 21 | Add to your HTML files: 22 | 23 | 24 | 25 | Now, inject to your application: 26 | 27 | angular.module('myApp', ['geocomplete']); 28 | 29 | Ready to use in your controllers!: 30 | 31 | `controller.js:` 32 | 33 | ```js 34 | // using callbacks 35 | var DemoCtrl = [ '$scope', 'geoComplete', function ($scope, geoComplete) { 36 | geoComplete.cities("San Francisco", function (results) { 37 | $scope.results = results; 38 | }); 39 | }]; 40 | 41 | // using promises 42 | var DemoCtrl = [ '$scope', 'geoComplete', function ($scope, geoComplete) { 43 | geoComplete.cities("San Francisco").then(function (results) { 44 | $scope.results = results; 45 | }); 46 | }]; 47 | ``` 48 | 49 | Demo 50 | ---- 51 | 1. Plunker: http://plnkr.co/edit/yatsd3Cqg0te6TPMpjLV?p=preview 52 | 2. Local: Run ```gulp``` to run tests, generate coverage and load demo or ```gulp serve``` to just load the demo. (a browser window will automatically open to http://localhost:8000) 53 | 54 | Tests 55 | ----- 56 | - Run ```gulp test``` 57 | - Coverage is generated in the ```coverage/``` folder 58 | 59 | Author 60 | ------ 61 | © 2014, Jose Luis Rivas ``. 62 | 63 | Contributors 64 | ------------ 65 | Sha Alibhai (@shalotelli) 66 | 67 | License 68 | ------- 69 | The files are licensed under the MIT terms. 70 | -------------------------------------------------------------------------------- /angular-geocomplete.js: -------------------------------------------------------------------------------- 1 | // 2 | // geocomplete 3 | // =================== 4 | // 5 | (function () { 6 | 'use strict'; 7 | 8 | angular.module('geocomplete', []) 9 | .factory('geoComplete', [ '$http', function ($http) { 10 | var apiUrl = 'https://maps.googleapis.com/maps/api/geocode/json'; 11 | 12 | /* Public API */ 13 | return { 14 | // 15 | // 16 | // Public: cities 17 | // -------------------------- 18 | // 19 | // #### Arguments 20 | // 21 | // + `cityName`: `String` 22 | // + `options`: (Optional )`Object` of options, see more: https://developers.google.com/maps/documentation/geocoding/intro#ComponentFiltering 23 | // + `callback`: (Optional) `Callback` function 24 | // 25 | // #### Returns 26 | // 27 | // An `Array` with a `String` with the name of the City followed by the 28 | // State and the Country name, separated by commas. 29 | // 30 | // #### Example on a REPL: 31 | // 32 | // ``` 33 | // $ utils.autocompleteCities("San "); 34 | // => ["San Diego, CA, United States", "San Andreas, CA, United States", ...] 35 | // ``` 36 | // 37 | cities: function (cityName, options, callback) { 38 | return $http.get(apiUrl, { 39 | params: angular.extend({}, options, { 40 | address: cityName, 41 | sensor: false 42 | }) 43 | }).then(function (res) { 44 | var addresses = []; 45 | 46 | angular.forEach(res.data.results, function (item) { 47 | addresses.push(item.formatted_address) 48 | }); 49 | 50 | if (callback) { 51 | return callback.call(this, addresses); 52 | } 53 | 54 | return addresses; 55 | }); 56 | }, 57 | 58 | // 59 | // 60 | // Public: geocomplete Cities returning a JSON 61 | // -------------------------------------------- 62 | // 63 | // Just like autocompleteCities but instead of returning an Array with 64 | // Strings it returns an Array with a JSON with more details per city. 65 | // 66 | // #### Arguments 67 | // 68 | // + `cityName`: A `String` with the city/place name 69 | // + `options`: (Optional )`Object` of options, see more: https://developers.google.com/maps/documentation/geocoding/intro#ComponentFiltering 70 | // + `callback`: (Optional) `Callback` function. 71 | // 72 | // #### Returns 73 | // 74 | // An `Array` with an `Object` with the full response from Google's API: 75 | // 76 | // #### Example on REPL: 77 | // 78 | // ``` 79 | // $ utils.autocompleteCitiesJSON("San Cristobal"); 80 | // => [{ 81 | // "address_components":[ 82 | // { 83 | // "long_name":"San Cristobal", 84 | // "short_name":"San Cristobal", 85 | // "types":["locality","political"] 86 | // }, { 87 | // "long_name":"San Cristobal", 88 | // "short_name":"San Cristobal", 89 | // "types":["administrative_area_level_2","political"] 90 | // }, { 91 | // "long_name":"Táchira", 92 | // "short_name":"Táchira", 93 | // "types":["administrative_area_level_1","political"] 94 | // }, { 95 | // "long_name":"Venezuela", 96 | // "short_name":"VE", 97 | // "types":["country","political"] 98 | // } 99 | // ], 100 | // "formatted_address":"San Cristobal, Venezuela", 101 | // "geometry":{ 102 | // "bounds":{ 103 | // "northeast":{"lat":7.816616199999999,"lng":-72.19365119999999}, 104 | // "southwest":{"lat":7.718986699999999,"lng":-72.2554064} 105 | // }, 106 | // "location":{"lat":7.764951000000001,"lng":-72.226061}, 107 | // "location_type":"APPROXIMATE", 108 | // "viewport":{"northeast":{"lat":7.816616199999999,"lng":-72.19365119999999},"southwest":{"lat":7.718986699999999,"lng":-72.2554064}} 109 | // }, 110 | // "types":["locality","political"] 111 | // }, ...] 112 | // ``` 113 | // 114 | citiesJSON: function (cityName, options, callback) { 115 | return $http.get(apiUrl, { 116 | params: angular.extend({}, options, { 117 | address: cityName, 118 | sensor: false 119 | }) 120 | }).then(function (res) { 121 | var addresses = []; 122 | 123 | angular.forEach(res.data.results, function (item) { 124 | addresses.push(item); 125 | }); 126 | 127 | if (callback) { 128 | return callback.call(this, addresses); 129 | } 130 | 131 | return addresses; 132 | }); 133 | } 134 | }; 135 | }]); 136 | })(); 137 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-geocomplete", 3 | "version": "0.2.0", 4 | "authors": [ 5 | "Jose Luis Rivas " 6 | ], 7 | "description": "Angular Factory to get GeoData on Cities from Google's API", 8 | "main": "angular-geocomplete.js", 9 | "keywords": [ 10 | "angular", 11 | "geo", 12 | "geodata", 13 | "google", 14 | "google api", 15 | "city", 16 | "cities" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_components", 23 | "test", 24 | "tests", 25 | "demo", 26 | "gulpfile.js", 27 | "karma.conf.js" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /demo/app.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | angular.module('geocomplete-demo', [ 'geocomplete' ]) 5 | .controller('MainController', function ($scope, geoComplete) { 6 | $scope.model = { 7 | city: 'Lake Mary' 8 | }; 9 | 10 | $scope.search = function () { 11 | geoComplete.cities($scope.model.city).then(function (cities) { 12 | $scope.model.citiesArray = cities; 13 | }); 14 | 15 | geoComplete.citiesJSON($scope.model.city).then(function (cities) { 16 | $scope.model.citiesJSON = cities; 17 | }); 18 | }; 19 | }); 20 | })(); 21 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Angular Geocomplete 5 | 6 | 7 | 8 | 9 | 10 | 11 | Angular Geocomplete 12 | 13 | 14 | Search 15 | 16 | 17 | Array Formatted 18 | {{model.citiesArray|json}} 19 | 20 | 21 | 22 | JSON Formatted 23 | {{model.citiesJSON|json}} 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'), 4 | server = require('gulp-server-livereload'), 5 | karma = require('karma').server; 6 | 7 | gulp.task('serve', function () { 8 | gulp.src(__dirname) 9 | .pipe(server({ 10 | livereload: true, 11 | port: 8000, 12 | defaultFile: 'demo/index.html', 13 | open: true 14 | })); 15 | }); 16 | 17 | gulp.task('test', function (done) { 18 | karma.start({ 19 | configFile: __dirname + '/karma.conf.js' 20 | }, done); 21 | }); 22 | 23 | gulp.task('default', [ 'test', 'serve' ]); 24 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | module.exports = function(config) { 3 | config.set({ 4 | 5 | // base path that will be used to resolve all patterns (eg. files, exclude) 6 | basePath: '', 7 | 8 | 9 | // frameworks to use 10 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 11 | frameworks: ['jasmine'], 12 | 13 | 14 | // list of files / patterns to load in the browser 15 | files: [ 16 | 'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js', 17 | 'https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular-mocks.js', 18 | 'angular-geocomplete.js', 19 | 'tests/**/*.spec.js' 20 | ], 21 | 22 | 23 | // list of files to exclude 24 | exclude: [], 25 | 26 | 27 | // preprocess matching files before serving them to the browser 28 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 29 | preprocessors: { 30 | 'angular-geocomplete.js': ['coverage'] 31 | }, 32 | 33 | 34 | coverageReporter: { 35 | type: 'html', 36 | dir: 'coverage' 37 | }, 38 | 39 | 40 | // test results reporter to use 41 | // possible values: 'dots', 'progress' 42 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 43 | reporters: ['progress', 'coverage'], 44 | 45 | 46 | // web server port 47 | port: 9876, 48 | 49 | 50 | // enable / disable colors in the output (reporters and logs) 51 | colors: true, 52 | 53 | 54 | // level of logging 55 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 56 | logLevel: config.LOG_INFO, 57 | 58 | 59 | // enable / disable watching file and executing tests whenever any file changes 60 | autoWatch: true, 61 | 62 | 63 | // start these browsers 64 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 65 | browsers: ['PhantomJS'], 66 | 67 | 68 | // Continuous Integration mode 69 | // if true, Karma captures browsers, runs the tests and exits 70 | singleRun: true, 71 | 72 | 73 | plugins: [ 74 | 'karma-jasmine', 75 | 'karma-coverage', 76 | 'karma-phantomjs-launcher' 77 | ] 78 | }); 79 | }; 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-geocomplete", 3 | "version": "0.2.0", 4 | "description": "Angular Factory to get GeoData on Cities from Google's API", 5 | "main": "angular-geocomplete.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/ghostbar/angular-geocomplete" 9 | }, 10 | "author": "Jose Luis Rivas ", 11 | "license": "MIT", 12 | "bugs": { 13 | "url": "https://github.com/ghostbar/angular-geocomplete/issues" 14 | }, 15 | "homepage": "https://github.com/ghostbar/angular-geocomplete", 16 | "devDependencies": { 17 | "gulp": "^3.8.10", 18 | "gulp-server-livereload": "^1.0.2", 19 | "jasmine-core": "^2.1.3", 20 | "karma": "^0.12.31", 21 | "karma-coverage": "^0.2.7", 22 | "karma-jasmine": "^0.3.5", 23 | "karma-phantomjs-launcher": "^0.1.4" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/angular-geocomplete.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('factory: geoComplete', function () { 4 | var geoComplete, 5 | httpBackend, 6 | http, 7 | apiUrl = 'https://maps.googleapis.com/maps/api/geocode/json'; 8 | 9 | var mockData = { 10 | "results": [ 11 | { 12 | "address_components": [ 13 | { 14 | "long_name": "Lake Mary", 15 | "short_name": "Lake Mary", 16 | "types": [ 17 | "locality", 18 | "political" 19 | ] 20 | }, 21 | { 22 | "long_name": "Seminole County", 23 | "short_name": "Seminole County", 24 | "types": [ 25 | "administrative_area_level_2", 26 | "political" 27 | ] 28 | }, 29 | { 30 | "long_name": "Florida", 31 | "short_name": "FL", 32 | "types": [ 33 | "administrative_area_level_1", 34 | "political" 35 | ] 36 | }, 37 | { 38 | "long_name": "United States", 39 | "short_name": "US", 40 | "types": [ 41 | "country", 42 | "political" 43 | ] 44 | } 45 | ], 46 | "formatted_address": "Lake Mary, FL, USA", 47 | "geometry": { 48 | "bounds": { 49 | "northeast": { 50 | "lat": 28.786658, 51 | "lng": -81.3015168 52 | }, 53 | "southwest": { 54 | "lat": 28.7262928, 55 | "lng": -81.37203509999999 56 | } 57 | }, 58 | "location": { 59 | "lat": 28.7588833, 60 | "lng": -81.3178446 61 | }, 62 | "location_type": "APPROXIMATE", 63 | "viewport": { 64 | "northeast": { 65 | "lat": 28.786658, 66 | "lng": -81.3015168 67 | }, 68 | "southwest": { 69 | "lat": 28.7262928, 70 | "lng": -81.37203509999999 71 | } 72 | } 73 | }, 74 | "types": [ 75 | "locality", 76 | "political" 77 | ] 78 | } 79 | ], 80 | "status": "OK" 81 | }; 82 | 83 | beforeEach(module('geocomplete')); 84 | 85 | beforeEach(inject(function ($httpBackend, $http, _geoComplete_) { 86 | httpBackend = $httpBackend; 87 | http = $http, 88 | geoComplete = _geoComplete_; 89 | 90 | httpBackend.whenGET(apiUrl + '?address=Lake+Mary&sensor=false').respond(mockData); 91 | httpBackend.whenGET(apiUrl + '?address=Lake+Mary&components=country:BR&sensor=false').respond(mockData); 92 | })); 93 | 94 | afterEach(function () { 95 | httpBackend.verifyNoOutstandingExpectation(); 96 | httpBackend.verifyNoOutstandingRequest(); 97 | }); 98 | 99 | it('should return an array of matching city names using a promise and options', function () { 100 | var responseData; 101 | 102 | geoComplete.cities('Lake Mary', {components: 'country:BR'}).then(function (cities) { 103 | responseData = cities; 104 | }); 105 | 106 | httpBackend.expectGET(apiUrl + '?address=Lake+Mary&components=country:BR&sensor=false'); 107 | httpBackend.flush(); 108 | }); 109 | 110 | it('should search for a city', function () { 111 | var responseData; 112 | 113 | http.get(apiUrl + '?address=Lake+Mary&sensor=false').then(function (cities) { 114 | responseData = cities; 115 | }); 116 | 117 | httpBackend.flush(); 118 | 119 | expect(responseData).toBeDefined(); 120 | }); 121 | 122 | it('should return an array of matching city names via callback', function () { 123 | var responseData; 124 | 125 | geoComplete.cities('Lake Mary', {}, function (cities) { 126 | responseData = cities; 127 | }); 128 | 129 | httpBackend.flush(); 130 | 131 | expect(responseData).toEqual([ "Lake Mary, FL, USA" ]); 132 | }); 133 | 134 | it('should return an array of matching city names using a promise', function () { 135 | var responseData; 136 | 137 | geoComplete.cities('Lake Mary').then(function (cities) { 138 | responseData = cities; 139 | }); 140 | 141 | httpBackend.flush(); 142 | 143 | expect(responseData).toEqual([ "Lake Mary, FL, USA" ]); 144 | }); 145 | 146 | it('should return matching city info as a JSON object via callback', function () { 147 | var responseData; 148 | 149 | geoComplete.citiesJSON('Lake Mary', {}, function (cities) { 150 | responseData = cities; 151 | }); 152 | 153 | httpBackend.flush(); 154 | 155 | expect(responseData.length).not.toBe(0); 156 | }); 157 | 158 | it('should return matching city info as a JSON object using a promise', function () { 159 | var responseData; 160 | 161 | geoComplete.citiesJSON('Lake Mary').then(function (cities) { 162 | responseData = cities; 163 | }); 164 | 165 | httpBackend.flush(); 166 | 167 | expect(responseData.length).not.toBe(0); 168 | }); 169 | 170 | it('should return matching city info as a JSON object using a promise and options', function () { 171 | var responseData; 172 | 173 | geoComplete.citiesJSON('Lake Mary', {components: 'country:BR'}).then(function (cities) { 174 | responseData = cities; 175 | }); 176 | 177 | httpBackend.expectGET(apiUrl + '?address=Lake+Mary&components=country:BR&sensor=false'); 178 | httpBackend.flush(); 179 | }); 180 | }); 181 | --------------------------------------------------------------------------------
{{model.citiesArray|json}}
{{model.citiesJSON|json}}