From 807b379b690af22785e0179d2a659baa51237083 Mon Sep 17 00:00:00 2001 From: Guillaume Royer Date: Mon, 4 Apr 2016 19:10:21 +0800 Subject: [PATCH] feat(autocomplete): handle async loading of google maps API --- dist/autocomplete.min.js | 2 +- src/autocomplete.js | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/dist/autocomplete.min.js b/dist/autocomplete.min.js index bf36300..8e2996f 100644 --- a/dist/autocomplete.min.js +++ b/dist/autocomplete.min.js @@ -1 +1 @@ -"use strict";angular.module("google.places",[]).factory("googlePlacesApi",["$window",function(a){if(!a.google)throw"Global `google` var missing. Did you forget to include the places API script?";return a.google}]).directive("gPlacesAutocomplete",["$parse","$compile","$timeout","$document","googlePlacesApi",function(a,b,c,d,e){return{restrict:"A",require:"^ngModel",scope:{model:"=ngModel",options:"=?",forceSelection:"=?",customPlaces:"=?"},controller:["$scope",function(a){}],link:function(a,f,g,h){function i(){f.bind("keydown",l),f.bind("blur",m),f.bind("submit",m),a.$watch("selected",n)}function j(){var c,e=angular.element("
"),f=angular.element(d[0].body);e.attr({input:"input",query:"query",predictions:"predictions",active:"active",selected:"selected"}),c=b(e)(a),f.append(c),a.$on("$destroy",function(){c.remove()})}function k(){h.$parsers.push(o),h.$formatters.push(p),h.$render=q}function l(b){0!==a.predictions.length&&-1!==w(A,b.which)&&(b.preventDefault(),b.which===z.down?(a.active=(a.active+1)%a.predictions.length,a.$digest()):b.which===z.up?(a.active=(a.active?a.active:a.predictions.length)-1,a.$digest()):13===b.which||9===b.which?(a.forceSelection&&(a.active=-1===a.active?0:a.active),a.$apply(function(){a.selected=a.active,-1===a.selected&&r()})):27===b.which&&a.$apply(function(){b.stopPropagation(),r()}))}function m(b){0!==a.predictions.length&&(a.forceSelection&&(a.selected=-1===a.selected?0:a.selected),a.$digest(),a.$apply(function(){-1===a.selected&&r()}))}function n(){var b;b=a.predictions[a.selected],b&&(b.is_custom?a.$apply(function(){a.model=b.place,a.$emit("g-places-autocomplete:select",b.place),c(function(){h.$viewChangeListeners.forEach(function(a){a()})})}):C.getDetails({placeId:b.place_id},function(b,d){d==e.maps.places.PlacesServiceStatus.OK&&a.$apply(function(){a.model=b,a.$emit("g-places-autocomplete:select",b),c(function(){h.$viewChangeListeners.forEach(function(a){a()})})})}),r())}function o(b){var c;return b&&u(b)?(a.query=b,c=angular.extend({input:b},a.options),B.getPlacePredictions(c,function(b,c){a.$apply(function(){var d;r(),a.customPlaces&&(d=s(a.query),a.predictions.push.apply(a.predictions,d)),c==e.maps.places.PlacesServiceStatus.OK&&a.predictions.push.apply(a.predictions,b),a.predictions.length>5&&(a.predictions.length=5)})}),a.forceSelection?h.$modelValue:b):b}function p(a){var b="";return u(a)?b=a:v(a)&&(b=a.formatted_address),b}function q(){return f.val(h.$viewValue)}function r(){a.active=-1,a.selected=-1,a.predictions=[]}function s(b){var c,d,e,f=[];for(e=0;e0&&f.push({is_custom:!0,custom_prediction_label:c.custom_prediction_label||"(Custom Non-Google Result)",description:c.formatted_address,place:c,matched_substrings:d.matched_substrings,terms:d.terms});return f}function t(a,b){var c,d,e,f=a+"",g=[],h=[];for(d=b.formatted_address.split(","),e=0;e0&&(c.length>=f.length?(x(c,f)&&h.push({length:f.length,offset:e}),f=""):x(f,c)?(h.push({length:c.length,offset:e}),f=f.replace(c,"").trim()):f=""),g.push({value:c,offset:b.formatted_address.indexOf(c)});return{matched_substrings:h,terms:g}}function u(a){return"[object String]"==Object.prototype.toString.call(a)}function v(a){return"[object Object]"==Object.prototype.toString.call(a)}function w(a,b){var c,d;if(null==a)return-1;for(d=a.length,c=0;d>c;c++)if(a[c]===b)return c;return-1}function x(a,b){return 0===y(a).lastIndexOf(y(b),0)}function y(a){return null==a?"":a.toLowerCase()}var z={tab:9,enter:13,esc:27,up:38,down:40},A=[z.tab,z.enter,z.esc,z.up,z.down],B=new e.maps.places.AutocompleteService,C=new e.maps.places.PlacesService(f[0]);!function(){a.query="",a.predictions=[],a.input=f,a.options=a.options||{},j(),i(),k()}()}}}]).directive("gPlacesAutocompleteDrawer",["$window","$document",function(a,b){var c=['
','
',"
","
"];return{restrict:"A",scope:{input:"=",query:"=",predictions:"=",active:"=",selected:"="},template:c.join(""),link:function(c,d){function e(c){var d=c[0],e=d.getBoundingClientRect(),f=b[0].documentElement,g=b[0].body,h=a.pageYOffset||f.scrollTop||g.scrollTop,i=a.pageXOffset||f.scrollLeft||g.scrollLeft;return{width:e.width,height:e.height,top:e.top+e.height+h,left:e.left+i}}d.bind("mousedown",function(a){a.preventDefault()}),a.onresize=function(){c.$apply(function(){c.position=e(c.input)})},c.isOpen=function(){return c.predictions.length>0},c.isActive=function(a){return c.active===a},c.selectActive=function(a){c.active=a},c.selectPrediction=function(a){c.selected=a},c.$watch("predictions",function(){c.position=e(c.input)},!0)}}}]).directive("gPlacesAutocompletePrediction",[function(){var a=['','','{{term.value | trailingComma:!$last}} ',' {{prediction.custom_prediction_label}}'];return{restrict:"A",scope:{index:"=",prediction:"=",query:"="},template:a.join("")}}]).filter("highlightMatched",["$sce",function(a){return function(b){var c,d="",e="";return b.matched_substrings.length>0&&b.terms.length>0&&(c=b.matched_substrings[0],d=b.terms[0].value.substr(c.offset,c.length),e=b.terms[0].value.substr(c.offset+c.length)),a.trustAsHtml(''+d+""+e)}}]).filter("unmatchedTermsOnly",[function(){return function(a,b){var c,d,e=[];for(c=0;c0&&d.offset>b.matched_substrings[0].length&&e.push(d);return e}}]).filter("trailingComma",[function(){return function(a,b){return b?a+",":a}}]); \ No newline at end of file +"use strict";angular.module("google.places",[]).factory("googlePlacesApi",["$window",function(a){return a.google||console.info("Global `google` var missing. Did you forget to include the places API script?"),a.google}]).directive("gPlacesAutocomplete",["$parse","$compile","$timeout","$document","googlePlacesApi",function(a,b,c,d,e){return{restrict:"A",require:"^ngModel",scope:{model:"=ngModel",options:"=?",forceSelection:"=?",customPlaces:"=?"},controller:["$scope",function(a){}],link:function(a,f,g,h){function i(){return D||new e.maps.places.AutocompleteService}function j(){return E||new e.maps.places.PlacesService(f[0])}function k(){f.bind("keydown",n),f.bind("blur",o),f.bind("submit",o),a.$watch("selected",p)}function l(){var c,e=angular.element("
"),f=angular.element(d[0].body);e.attr({input:"input",query:"query",predictions:"predictions",active:"active",selected:"selected"}),c=b(e)(a),f.append(c),a.$on("$destroy",function(){c.remove()})}function m(){h.$parsers.push(q),h.$formatters.push(r),h.$render=s}function n(b){0!==a.predictions.length&&-1!==y(C,b.which)&&(b.preventDefault(),b.which===B.down?(a.active=(a.active+1)%a.predictions.length,a.$digest()):b.which===B.up?(a.active=(a.active?a.active:a.predictions.length)-1,a.$digest()):13===b.which||9===b.which?(a.forceSelection&&(a.active=-1===a.active?0:a.active),a.$apply(function(){a.selected=a.active,-1===a.selected&&t()})):27===b.which&&a.$apply(function(){b.stopPropagation(),t()}))}function o(b){0!==a.predictions.length&&(a.forceSelection&&(a.selected=-1===a.selected?0:a.selected),a.$digest(),a.$apply(function(){-1===a.selected&&t()}))}function p(){var b;b=a.predictions[a.selected],b&&(b.is_custom?a.$apply(function(){a.model=b.place,a.$emit("g-places-autocomplete:select",b.place),c(function(){h.$viewChangeListeners.forEach(function(a){a()})})}):j().getDetails({placeId:b.place_id},function(b,d){d==e.maps.places.PlacesServiceStatus.OK&&a.$apply(function(){a.model=b,a.$emit("g-places-autocomplete:select",b),c(function(){h.$viewChangeListeners.forEach(function(a){a()})})})}),t())}function q(b){var c;return b&&w(b)?(a.query=b,c=angular.extend({input:b},a.options),i().getPlacePredictions(c,function(b,c){a.$apply(function(){var d;t(),a.customPlaces&&(d=u(a.query),a.predictions.push.apply(a.predictions,d)),c==e.maps.places.PlacesServiceStatus.OK&&a.predictions.push.apply(a.predictions,b),a.predictions.length>5&&(a.predictions.length=5)})}),a.forceSelection?h.$modelValue:b):b}function r(a){var b="";return w(a)?b=a:x(a)&&(b=a.formatted_address),b}function s(){return f.val(h.$viewValue)}function t(){a.active=-1,a.selected=-1,a.predictions=[]}function u(b){var c,d,e,f=[];for(e=0;e0&&f.push({is_custom:!0,custom_prediction_label:c.custom_prediction_label||"(Custom Non-Google Result)",description:c.formatted_address,place:c,matched_substrings:d.matched_substrings,terms:d.terms});return f}function v(a,b){var c,d,e,f=a+"",g=[],h=[];for(d=b.formatted_address.split(","),e=0;e0&&(c.length>=f.length?(z(c,f)&&h.push({length:f.length,offset:e}),f=""):z(f,c)?(h.push({length:c.length,offset:e}),f=f.replace(c,"").trim()):f=""),g.push({value:c,offset:b.formatted_address.indexOf(c)});return{matched_substrings:h,terms:g}}function w(a){return"[object String]"==Object.prototype.toString.call(a)}function x(a){return"[object Object]"==Object.prototype.toString.call(a)}function y(a,b){var c,d;if(null===a)return-1;for(d=a.length,c=0;d>c;c++)if(a[c]===b)return c;return-1}function z(a,b){return 0===A(a).lastIndexOf(A(b),0)}function A(a){return null===a?"":a.toLowerCase()}var B={tab:9,enter:13,esc:27,up:38,down:40},C=[B.tab,B.enter,B.esc,B.up,B.down],D=null,E=null;!function(){a.query="",a.predictions=[],a.input=f,a.options=a.options||{},l(),k(),m()}()}}}]).directive("gPlacesAutocompleteDrawer",["$window","$document",function(a,b){var c=['
','
',"
","
"];return{restrict:"A",scope:{input:"=",query:"=",predictions:"=",active:"=",selected:"="},template:c.join(""),link:function(c,d){function e(c){var d=c[0],e=d.getBoundingClientRect(),f=b[0].documentElement,g=b[0].body,h=a.pageYOffset||f.scrollTop||g.scrollTop,i=a.pageXOffset||f.scrollLeft||g.scrollLeft;return{width:e.width,height:e.height,top:e.top+e.height+h,left:e.left+i}}d.bind("mousedown",function(a){a.preventDefault()}),a.onresize=function(){c.$apply(function(){c.position=e(c.input)})},c.isOpen=function(){return c.predictions.length>0},c.isActive=function(a){return c.active===a},c.selectActive=function(a){c.active=a},c.selectPrediction=function(a){c.selected=a},c.$watch("predictions",function(){c.position=e(c.input)},!0)}}}]).directive("gPlacesAutocompletePrediction",[function(){var a=['','','{{term.value | trailingComma:!$last}} ',' {{prediction.custom_prediction_label}}'];return{restrict:"A",scope:{index:"=",prediction:"=",query:"="},template:a.join("")}}]).filter("highlightMatched",["$sce",function(a){return function(b){var c,d="",e="";return b.matched_substrings.length>0&&b.terms.length>0&&(c=b.matched_substrings[0],d=b.terms[0].value.substr(c.offset,c.length),e=b.terms[0].value.substr(c.offset+c.length)),a.trustAsHtml(''+d+""+e)}}]).filter("unmatchedTermsOnly",[function(){return function(a,b){var c,d,e=[];for(c=0;c0&&d.offset>b.matched_substrings[0].length&&e.push(d);return e}}]).filter("trailingComma",[function(){return function(a,b){return b?a+",":a}}]); \ No newline at end of file diff --git a/src/autocomplete.js b/src/autocomplete.js index 6b4d27d..9ca6814 100644 --- a/src/autocomplete.js +++ b/src/autocomplete.js @@ -15,7 +15,9 @@ angular.module('google.places', []) * Note: requires the Google Places API to already be loaded on the page. */ .factory('googlePlacesApi', ['$window', function ($window) { - if (!$window.google) throw 'Global `google` var missing. Did you forget to include the places API script?'; + if (!$window.google) { + console.info('Global `google` var missing. Did you forget to include the places API script?'); + } return $window.google; }]) @@ -48,8 +50,8 @@ angular.module('google.places', []) down: 40 }, hotkeys = [keymap.tab, keymap.enter, keymap.esc, keymap.up, keymap.down], - autocompleteService = new google.maps.places.AutocompleteService(), - placesService = new google.maps.places.PlacesService(element[0]); + _autocompleteService = null, + _placesService = null; (function init() { $scope.query = ''; @@ -62,6 +64,14 @@ angular.module('google.places', []) initNgModelController(); }()); + function autocompleteService() { + return _autocompleteService || new google.maps.places.AutocompleteService(); + } + + function placesService() { + return _placesService || new google.maps.places.PlacesService(element[0]); + } + function initEvents() { element.bind('keydown', onKeydown); element.bind('blur', onBlur); @@ -164,7 +174,7 @@ angular.module('google.places', []) }); }); } else { - placesService.getDetails({ placeId: prediction.place_id }, function (place, status) { + placesService().getDetails({ placeId: prediction.place_id }, function (place, status) { if (status == google.maps.places.PlacesServiceStatus.OK) { $scope.$apply(function () { $scope.model = place; @@ -188,7 +198,7 @@ angular.module('google.places', []) $scope.query = viewValue; request = angular.extend({ input: viewValue }, $scope.options); - autocompleteService.getPlacePredictions(request, function (predictions, status) { + autocompleteService().getPlacePredictions(request, function (predictions, status) { $scope.$apply(function () { var customPlacePredictions;