1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026 |
- var ngMap = angular.module('ngMap', []);
- /**
- * @ngdoc service
- * @name Attr2Options
- * @description
- * Converts tag attributes to options used by google api v3 objects, map, marker, polygon, circle, etc.
- */
- /*jshint -W030*/
- ngMap.service('Attr2Options', ['$parse', 'NavigatorGeolocation', 'GeoCoder', function($parse, NavigatorGeolocation, GeoCoder) {
- var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
- var MOZ_HACK_REGEXP = /^moz([A-Z])/;
- var orgAttributes = function(el) {
- (el.length > 0) && (el = el[0]);
- var orgAttributes = {};
- for (var i=0; i<el.attributes.length; i++) {
- var attr = el.attributes[i];
- orgAttributes[attr.name] = attr.value;
- }
- return orgAttributes;
- }
- var camelCase = function(name) {
- return name.
- replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
- return offset ? letter.toUpperCase() : letter;
- }).
- replace(MOZ_HACK_REGEXP, 'Moz$1');
- }
- var JSONize = function(str) {
- try { // if parsable already, return as it is
- JSON.parse(str);
- return str;
- } catch(e) { // if not parsable, change little
- return str
- // wrap keys without quote with valid double quote
- .replace(/([\$\w]+)\s*:/g, function(_, $1){return '"'+$1+'":'})
- // replacing single quote wrapped ones to double quote
- .replace(/'([^']+)'/g, function(_, $1){return '"'+$1+'"'})
- }
- }
- var toOptionValue = function(input, options) {
- var output, key=options.key, scope=options.scope;
- try { // 1. Number?
- var num = Number(input);
- if (isNaN(num)) {
- throw "Not a number";
- } else {
- output = num;
- }
- } catch(err) {
- try { // 2.JSON?
- if (input.match(/^[\+\-]?[0-9\.]+,[ ]*\ ?[\+\-]?[0-9\.]+$/)) { // i.e "-1.0, 89.89"
- input = "["+input+"]";
- }
- output = JSON.parse(JSONize(input));
- if (output instanceof Array) {
- var t1stEl = output[0];
- if (t1stEl.constructor == Object) { // [{a:1}] : not lat/lng ones
- } else if (t1stEl.constructor == Array) { // [[1,2],[3,4]]
- output = output.map(function(el) {
- return new google.maps.LatLng(el[0], el[1]);
- });
- } else if(!isNaN(parseFloat(t1stEl)) && isFinite(t1stEl)) {
- return new google.maps.LatLng(output[0], output[1]);
- }
- }
- } catch(err2) {
- // 3. Object Expression. i.e. LatLng(80,-49)
- if (input.match(/^[A-Z][a-zA-Z0-9]+\(.*\)$/)) {
- try {
- var exp = "new google.maps."+input;
- output = eval(exp); // TODO, still eval
- } catch(e) {
- output = input;
- }
- // 4. Object Expression. i.e. MayTypeId.HYBRID
- } else if (input.match(/^([A-Z][a-zA-Z0-9]+)\.([A-Z]+)$/)) {
- try {
- var matches = input.match(/^([A-Z][a-zA-Z0-9]+)\.([A-Z]+)$/);
- output = google.maps[matches[1]][matches[2]];
- } catch(e) {
- output = input;
- }
- // 5. Object Expression. i.e. HYBRID
- } else if (input.match(/^[A-Z]+$/)) {
- try {
- var capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
- if (key.match(/temperatureUnit|windSpeedUnit|labelColor/)) {
- capitalizedKey = capitalizedKey.replace(/s$/,"");
- output = google.maps.weather[capitalizedKey][input];
- } else {
- output = google.maps[capitalizedKey][input];
- }
- } catch(e) {
- output = input;
- }
- } else {
- output = input;
- }
- } // catch(err2)
- } // catch(err)
- return output;
- };
- var setDelayedGeoLocation = function(object, method, param, options) {
- options = options || {};
- var centered = object.centered || options.centered;
- var errorFunc = function() {
- console.log('error occurred while', object, method, param, options);
- var fallbackLocation = options.fallbackLocation || new google.maps.LatLng(0,0);
- object[method](fallbackLocation);
- };
- if (!param || param.match(/^current/i)) { // sensored position
- NavigatorGeolocation.getCurrentPosition().then(
- function(position) { // success
- var lat = position.coords.latitude;
- var lng = position.coords.longitude;
- var latLng = new google.maps.LatLng(lat,lng);
- object[method](latLng);
- if (centered) {
- object.map.setCenter(latLng);
- }
- options.callback && options.callback.apply(object);
- },
- errorFunc
- );
- } else { //assuming it is address
- GeoCoder.geocode({address: param}).then(
- function(results) { // success
- object[method](results[0].geometry.location);
- if (centered) {
- object.map.setCenter(results[0].geometry.location);
- }
- },
- errorFunc
- );
- }
- };
- var getAttrsToObserve = function(attrs) {
- var attrsToObserve = [];
- if (attrs["ng-repeat"] || attrs.ngRepeat) { // if element is created by ng-repeat, don't observe any
- } else {
- for (var attrName in attrs) {
- var attrValue = attrs[attrName];
- if (attrValue && attrValue.match(/\{\{.*\}\}/)) { // if attr value is {{..}}
- console.log('setting attribute to observe', attrName, camelCase(attrName), attrValue);
- attrsToObserve.push(camelCase(attrName));
- }
- }
- }
- return attrsToObserve;
- };
- var observeAttrSetObj = function(orgAttrs, attrs, obj) {
- var attrsToObserve = getAttrsToObserve(orgAttrs);
- if (Object.keys(attrsToObserve).length) {
- console.log(obj, "attributes to observe", attrsToObserve);
- }
- for (var i=0; i<attrsToObserve.length; i++) {
- observeAndSet(attrs, attrsToObserve[i], obj);
- }
- }
- var observeAndSet = function(attrs, attrName, object) {
- attrs.$observe(attrName, function(val) {
- if (val) {
- console.log('observing ', object, attrName, val);
- var setMethod = camelCase('set-'+attrName);
- var optionValue = toOptionValue(val, {key: attrName});
- console.log('setting ', object, attrName, 'with value', optionValue);
- if (object[setMethod]) { //if set method does exist
- /* if an location is being observed */
- if (attrName.match(/center|position/) &&
- typeof optionValue == 'string') {
- setDelayedGeoLocation(object, setMethod, optionValue);
- } else {
- object[setMethod](optionValue);
- }
- }
- }
- });
- };
- return {
- /**
- * filters attributes by skipping angularjs methods $.. $$..
- * @memberof Attr2Options
- * @param {Hash} attrs tag attributes
- * @returns {Hash} filterd attributes
- */
- filter: function(attrs) {
- var options = {};
- for(var key in attrs) {
- if (key.match(/^\$/) || key.match(/^ng[A-Z]/)) {
- } else {
- options[key] = attrs[key];
- }
- }
- return options;
- },
- /**
- * converts attributes hash to Google Maps API v3 options
- * ```
- * . converts numbers to number
- * . converts class-like string to google maps instance
- * i.e. `LatLng(1,1)` to `new google.maps.LatLng(1,1)`
- * . converts constant-like string to google maps constant
- * i.e. `MapTypeId.HYBRID` to `google.maps.MapTypeId.HYBRID`
- * i.e. `HYBRID"` to `google.maps.MapTypeId.HYBRID`
- * ```
- * @memberof Attr2Options
- * @param {Hash} attrs tag attributes
- * @param {scope} scope angularjs scope
- * @returns {Hash} options converted attributess
- */
- getOptions: function(attrs, scope) {
- var options = {};
- for(var key in attrs) {
- if (attrs[key]) {
- if (key.match(/^on[A-Z]/)) { //skip events, i.e. on-click
- continue;
- } else if (key.match(/ControlOptions$/)) { // skip controlOptions
- continue;
- } else {
- options[key] = toOptionValue(attrs[key], {scope:scope, key: key});
- }
- } // if (attrs[key])
- } // for(var key in attrs)
- return options;
- },
- /**
- * converts attributes hash to scope-specific event function
- * @memberof Attr2Options
- * @param {scope} scope angularjs scope
- * @param {Hash} attrs tag attributes
- * @returns {Hash} events converted events
- */
- getEvents: function(scope, attrs) {
- var events = {};
- var toLowercaseFunc = function($1){
- return "_"+$1.toLowerCase();
- };
- var eventFunc = function(attrValue) {
- var matches = attrValue.match(/([^\(]+)\(([^\)]*)\)/);
- var funcName = matches[1];
- var argsStr = matches[2].replace(/event[ ,]*/,''); //remove string 'event'
-
- var args = scope.$eval("["+argsStr+"]");
- return function(event) {
- function index(obj,i) {return obj[i]}
- f = funcName.split('.').reduce(index, scope)
- f.apply(this, [event].concat(args));
- scope.$apply();
- }
- }
- for(var key in attrs) {
- if (attrs[key]) {
- if (!key.match(/^on[A-Z]/)) { //skip if not events
- continue;
- }
-
- //get event name as underscored. i.e. zoom_changed
- var eventName = key.replace(/^on/,'');
- eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
- eventName = eventName.replace(/([A-Z])/g, toLowercaseFunc);
- var attrValue = attrs[key];
- events[eventName] = new eventFunc(attrValue);
- }
- }
- return events;
- },
- /**
- * control means map controls, i.e streetview, pan, etc, not a general control
- * @memberof Attr2Options
- * @param {Hash} filtered filtered tag attributes
- * @returns {Hash} Google Map options
- */
- getControlOptions: function(filtered) {
- var controlOptions = {};
- if (typeof filtered != 'object')
- return false;
- for (var attr in filtered) {
- if (filtered[attr]) {
- if (!attr.match(/(.*)ControlOptions$/)) {
- continue; // if not controlOptions, skip it
- }
- //change invalid json to valid one, i.e. {foo:1} to {"foo": 1}
- var orgValue = filtered[attr];
- var newValue = orgValue.replace(/'/g, '"');
- newValue = newValue.replace(/([^"]+)|("[^"]+")/g, function($0, $1, $2) {
- if ($1) {
- return $1.replace(/([a-zA-Z0-9]+?):/g, '"$1":');
- } else {
- return $2;
- }
- });
- try {
- var options = JSON.parse(newValue);
- for (var key in options) { //assign the right values
- if (options[key]) {
- var value = options[key];
- if (typeof value === 'string') {
- value = value.toUpperCase();
- } else if (key === "mapTypeIds") {
- value = value.map( function(str) {
- if (str.match(/^[A-Z]+$/)) { // if constant
- return google.maps.MapTypeId[str.toUpperCase()];
- } else { // else, custom map-type
- return str;
- }
- });
- }
-
- if (key === "style") {
- var str = attr.charAt(0).toUpperCase() + attr.slice(1);
- var objName = str.replace(/Options$/,'')+"Style";
- options[key] = google.maps[objName][value];
- } else if (key === "position") {
- options[key] = google.maps.ControlPosition[value];
- } else {
- options[key] = value;
- }
- }
- }
- controlOptions[attr] = options;
- } catch (e) {
- console.error('invald option for', attr, newValue, e, e.stack);
- }
- }
- } // for
- return controlOptions;
- }, // function
- toOptionValue: toOptionValue,
- camelCase: camelCase,
- setDelayedGeoLocation: setDelayedGeoLocation,
- getAttrsToObserve: getAttrsToObserve,
- observeAndSet: observeAndSet,
- observeAttrSetObj: observeAttrSetObj,
- orgAttributes: orgAttributes
- }; // return
- }]);
- /**
- * @ngdoc service
- * @name GeoCoder
- * @description
- * Provides [defered/promise API](https://docs.angularjs.org/api/ng/service/$q) service for Google Geocoder service
- */
- ngMap.service('GeoCoder', ['$q', function($q) {
- return {
- /**
- * @memberof GeoCoder
- * @param {Hash} options https://developers.google.com/maps/documentation/geocoding/#geocoding
- * @example
- * ```
- * GeoCoder.geocode({address: 'the cn tower'}).then(function(result) {
- * //... do something with result
- * });
- * ```
- * @returns {HttpPromise} Future object
- */
- geocode : function(options) {
- var deferred = $q.defer();
- var geocoder = new google.maps.Geocoder();
- geocoder.geocode(options, function (results, status) {
- if (status == google.maps.GeocoderStatus.OK) {
- deferred.resolve(results);
- } else {
- deferred.reject('Geocoder failed due to: '+ status);
- }
- });
- return deferred.promise;
- }
- }
- }]);
- /**
- * @ngdoc service
- * @name NavigatorGeolocation
- * @description
- * Provides [defered/promise API](https://docs.angularjs.org/api/ng/service/$q) service for navigator.geolocation methods
- */
- ngMap.service('NavigatorGeolocation', ['$q', function($q) {
- return {
- /**
- * @memberof NavigatorGeolocation
- * @param {function} success success callback function
- * @param {function} failure failure callback function
- * @example
- * ```
- * NavigatorGeolocation.getCurrentPosition()
- * .then(function(position) {
- * var lat = position.coords.latitude, lng = position.coords.longitude;
- * .. do something lat and lng
- * });
- * ```
- * @returns {HttpPromise} Future object
- */
- getCurrentPosition: function() {
- var deferred = $q.defer();
- if (navigator.geolocation) {
- navigator.geolocation.getCurrentPosition(
- function(position) {
- deferred.resolve(position);
- }, function(evt) {
- console.error(evt);
- deferred.reject(evt);
- }
- );
- } else {
- deferred.reject("Browser Geolocation service failed.");
- }
- return deferred.promise;
- },
- watchPosition: function() {
- return "TODO";
- },
- clearWatch: function() {
- return "TODO";
- }
- };
- }]);
- /**
- * @ngdoc service
- * @name StreetView
- * @description
- * Provides [defered/promise API](https://docs.angularjs.org/api/ng/service/$q) service
- * for [Google StreetViewService](https://developers.google.com/maps/documentation/javascript/streetview)
- */
- ngMap.service('StreetView', ['$q', function($q) {
- return {
- /**
- * Retrieves panorama id from the given map (and or position)
- * @memberof StreetView
- * @param {map} map Google map instance
- * @param {LatLng} latlng Google LatLng instance
- * default: the center of the map
- * @example
- * StreetView.getPanorama(map).then(function(panoId) {
- * $scope.panoId = panoId;
- * });
- * @returns {HttpPromise} Future object
- */
- getPanorama : function(map, latlng) {
- latlng = latlng || map.getCenter();
- var deferred = $q.defer();
- var svs = new google.maps.StreetViewService();
- svs.getPanoramaByLocation( (latlng||map.getCenter), 100, function (data, status) {
- // if streetView available
- if (status === google.maps.StreetViewStatus.OK) {
- deferred.resolve(data.location.pano);
- } else {
- // no street view available in this range, or some error occurred
- deferred.resolve(false);
- //deferred.reject('Geocoder failed due to: '+ status);
- }
- });
- return deferred.promise;
- },
- /**
- * Set panorama view on the given map with the panorama id
- * @memberof StreetView
- * @param {map} map Google map instance
- * @param {String} panoId Panorama id fro getPanorama method
- * @example
- * StreetView.setPanorama(map, panoId);
- */
- setPanorama : function(map, panoId) {
- var svp = new google.maps.StreetViewPanorama(map.getDiv(), {enableCloseButton: true});
- svp.setPano(panoId);
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name bicycling-layer
- * @requires Attr2Options
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- *
- * <map zoom="13" center="34.04924594193164, -118.24104309082031">
- * <bicycling-layer></bicycling-layer>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('bicyclingLayer', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
-
- var getLayer = function(options, events) {
- var layer = new google.maps.BicyclingLayer(options);
- for (var eventName in events) {
- google.maps.event.addListener(layer, eventName, events[eventName]);
- }
- return layer;
- };
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var events = parser.getEvents(scope, filtered);
- console.log('bicycling-layer options', options, 'events', events);
- var layer = getLayer(options, events);
- mapController.addObject('bicyclingLayers', layer);
- parser.observeAttrSetObj(orgAttrs, attrs, layer); //observers
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name cloud-layer
- * @requires Attr2Options
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- *
- * <map zoom="13" center="34.04924594193164, -118.24104309082031">
- * <cloud-layer></cloud-layer>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('cloudLayer', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
-
- var getLayer = function(options, events) {
- var layer = new google.maps.weather.CloudLayer(options);
- for (var eventName in events) {
- google.maps.event.addListener(layer, eventName, events[eventName]);
- }
- return layer;
- };
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var events = parser.getEvents(scope, filtered);
- console.log('cloud-layer options', options, 'events', events);
- var layer = getLayer(options, events);
- mapController.addObject('cloudLayers', layer);
- parser.observeAttrSetObj(orgAttrs, attrs, layer); //observers
- }
- }; // return
- }]);
- /*jshint -W030*/
- /**
- * @ngdoc directive
- * @name custom-control
- * @requires Attr2Options
- * @requires $compile
- * @description
- * Build custom control and set to the map with position
- *
- * Requires: map directive
- *
- * Restrict To: Element
- *
- * @param {String} position position of this control
- * i.e. TOP_RIGHT
- * @param {Number} index index of the control
- * @example
- *
- * Example:
- * <map center="41.850033,-87.6500523" zoom="3">
- * <custom-control id="home" position="TOP_LEFT" index="1">
- * <div style="background-color: white;">
- * <b>Home</b>
- * </div>
- * </custom-control>
- * </map>
- *
- */
- /*jshint -W089*/
- ngMap.directive('customControl', ['Attr2Options', '$compile', function(Attr2Options, $compile) {
- var parser = Attr2Options;
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- element.css('display','none');
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered, scope);
- var events = parser.getEvents(scope, filtered);
- console.log("custom-control options", options, "events", events);
- /**
- * build a custom control element
- */
- var compiled = $compile(element.html().trim())(scope);
- var customControlEl = compiled[0];
- /**
- * set events
- */
- for (var eventName in events) {
- google.maps.event.addDomListener(customControlEl, eventName, events[eventName]);
- }
- mapController.addObject('customControls', customControlEl);
- scope.$on('mapInitialized', function(evt, map) {
- var position = options.position;
- map.controls[google.maps.ControlPosition[position]].push(customControlEl);
- });
- } //link
- }; // return
- }]);// function
- /**
- * @ngdoc directive
- * @name drawing-manager
- * @requires Attr2Options
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- *
- * <map zoom="13" center="37.774546, -122.433523" map-type-id="SATELLITE">
- * <drawing-manager on-overlaycomplete="onMapOverlayCompleted()" position="ControlPosition.TOP_CENTER" drawingModes="POLYGON,CIRCLE" drawingControl="true" circleOptions="fillColor: '#FFFF00';fillOpacity: 1;strokeWeight: 5;clickable: false;zIndex: 1;editable: true;" ></drawing-manager>
- * </map>
- *
- * TODO: Add remove button.
- * currently, for out solution, we have the shapes/markers in our own controller, and we use some css classes to change the shape button
- * to a remove button (<div>X</div>) and have the remove operation in our own controller.
- */
- /*jshint -W089*/
- ngMap.directive('drawingManager', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var controlOptions = parser.getControlOptions(filtered);
- var events = parser.getEvents(scope, filtered);
- console.log("filtered", filtered, "options", options, 'controlOptions', controlOptions, 'events', events);
- /**
- * set options
- */
- var drawingManager = new google.maps.drawing.DrawingManager({
- drawingMode: options.drawingmode,
- drawingControl: options.drawingcontrol,
- drawingControlOptions: controlOptions.drawingControlOptions,
- circleOptions:options.circleoptions,
- markerOptions:options.markeroptions,
- polygonOptions:options.polygonoptions,
- polylineOptions:options.polylineoptions,
- rectangleOptions:options.rectangleoptions
- });
- /**
- * set events
- */
- var events = parser.getEvents(scope, filtered);
- for (var eventName in events) {
- google.maps.event.addListener(drawingManager, eventName, events[eventName]);
- }
- mapController.addObject('mapDrawingManager', drawingManager);
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name dynamic-maps-engine-layer
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- * <map zoom="14" center="[59.322506, 18.010025]">
- * <dynamic-maps-engine-layer layer-id="06673056454046135537-08896501997766553811"></dynamic-maps-engine-layer>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('dynamicMapsEngineLayer', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
- var getDynamicMapsEngineLayer = function(options, events) {
- var layer = new google.maps.visualization.DynamicMapsEngineLayer(options);
- for (var eventName in events) {
- google.maps.event.addListener(layer, eventName, events[eventName]);
- }
- return layer;
- };
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var events = parser.getEvents(scope, filtered, events);
- console.log('dynamic-maps-engine-layer options', options, 'events', events);
- var layer = getDynamicMapsEngineLayer(options, events);
- mapController.addObject('mapsEngineLayers', layer);
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name fusion-tables-layer
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- * <map zoom="11" center="41.850033, -87.6500523">
- * <fusion-tables-layer query="{
- * select: 'Geocodable address',
- * from: '1mZ53Z70NsChnBMm-qEYmSDOvLXgrreLTkQUvvg'}">
- * </fusion-tables-layer>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('fusionTablesLayer', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
- var getLayer = function(options, events) {
- var layer = new google.maps.FusionTablesLayer(options);
- for (var eventName in events) {
- google.maps.event.addListener(layer, eventName, events[eventName]);
- }
- return layer;
- };
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var events = parser.getEvents(scope, filtered, events);
- console.log('fusion-tables-layer options', options, 'events', events);
- var layer = getLayer(options, events);
- mapController.addObject('fusionTablesLayers', layer);
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name heatmap-layer
- * @requires Attr2Options
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- *
- * <map zoom="11" center="[41.875696,-87.624207]">
- * <heatmap-layer data="taxiData"></heatmap-layer>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('heatmapLayer', ['Attr2Options', '$window', function(Attr2Options, $window) {
- var parser = Attr2Options;
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var filtered = parser.filter(attrs);
- /**
- * set options
- */
- var options = parser.getOptions(filtered);
- options.data = $window[attrs.data] || scope[attrs.data];
- if (options.data instanceof Array) {
- options.data = new google.maps.MVCArray(options.data);
- } else {
- throw "invalid heatmap data";
- }
- var layer = new google.maps.visualization.HeatmapLayer(options);
- /**
- * set events
- */
- var events = parser.getEvents(scope, filtered);
- console.log('heatmap-layer options', layer, 'events', events);
- mapController.addObject('heatmapLayers', layer);
- }
- }; // return
- }]);
- /*jshint -W030*/
- /**
- * @ngdoc directive
- * @name info-window
- * @requires Attr2Options
- * @requires $compile
- * @description
- * Defines infoWindow and provides compile method
- *
- * Requires: map directive
- *
- * Restrict To: Element
- *
- * @param {Boolean} visible Indicates to show it when map is initialized
- * @param {Boolean} visible-on-marker Indicates to show it on a marker when map is initialized
- * @param {String} <InfoWindowOption> Any InfoWindow options,
- * https://developers.google.com/maps/documentation/javascript/reference?csw=1#InfoWindowOptions
- * @param {String} <InfoWindowEvent> Any InfoWindow events, https://developers.google.com/maps/documentation/javascript/reference
- * @example
- * Usage:
- * <map MAP_ATTRIBUTES>
- * <info-window id="foo" ANY_OPTIONS ANY_EVENTS"></info-window>
- * </map>
- *
- * Example:
- * <map center="41.850033,-87.6500523" zoom="3">
- * <info-window id="1" position="41.850033,-87.6500523" >
- * <div ng-non-bindable>
- * Chicago, IL<br/>
- * LatLng: {{chicago.lat()}}, {{chicago.lng()}}, <br/>
- * World Coordinate: {{worldCoordinate.x}}, {{worldCoordinate.y}}, <br/>
- * Pixel Coordinate: {{pixelCoordinate.x}}, {{pixelCoordinate.y}}, <br/>
- * Tile Coordinate: {{tileCoordinate.x}}, {{tileCoordinate.y}} at Zoom Level {{map.getZoom()}}
- * </div>
- * </info-window>
- * </map>
- */
- ngMap.directive('infoWindow', ['Attr2Options', '$compile', '$timeout', function(Attr2Options, $compile, $timeout) {
- var parser = Attr2Options;
- var getInfoWindow = function(options, events, element) {
- var infoWindow;
- /**
- * set options
- */
- if (options.position &&
- !(options.position instanceof google.maps.LatLng)) {
- var address = options.position;
- delete options.position;
- infoWindow = new google.maps.InfoWindow(options);
- var callback = function() {
- infoWindow.open(infoWindow.map);
- }
- parser.setDelayedGeoLocation(infoWindow, 'setPosition', address, {callback: callback});
- } else {
- infoWindow = new google.maps.InfoWindow(options);
- }
- /**
- * set events
- */
- if (Object.keys(events).length > 0) {
- console.log("infoWindow events", events);
- }
- for (var eventName in events) {
- if (eventName) {
- google.maps.event.addListener(infoWindow, eventName, events[eventName]);
- }
- }
- /**
- * set template ane template-relate functions
- * it must have a container element with ng-non-bindable
- */
- var template = element.html().trim();
- if (angular.element(template).length != 1) {
- throw "info-window working as a template must have a container";
- }
- infoWindow.__template = template.replace(/\s?ng-non-bindable[='"]+/,"");
- infoWindow.__compile = function(scope) {
- var el = $compile(infoWindow.__template)(scope);
- scope.$apply();
- infoWindow.setContent(el[0]);
- };
- infoWindow.__eval = function(event) {
- var template = infoWindow.__template;
- var _this = this;
- template = template.replace(/{{(event|this)[^;\}]+}}/g, function(match) {
- var expression = match.replace(/[{}]/g, "").replace("this.", "_this.");
- return eval(expression);
- });
- return template;
- };
- return infoWindow;
- };
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- element.css('display','none');
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered, scope);
- var events = parser.getEvents(scope, filtered);
- console.log('infoWindow', 'options', options, 'events', events);
- var infoWindow = getInfoWindow(options, events, element);
- mapController.addObject('infoWindows', infoWindow);
- parser.observeAttrSetObj(orgAttrs, attrs, infoWindow); /* observers */
- // show InfoWindow when initialized
- if (infoWindow.visible) {
- //if (!infoWindow.position) { throw "Invalid position"; }
- scope.$on('mapInitialized', function(evt, map) {
- $timeout(function() {
- infoWindow.__template = infoWindow.__eval.apply(this, [evt]);
- infoWindow.__compile(scope);
- infoWindow.map = map;
- infoWindow.position && infoWindow.open(map);
- });
- });
- }
- // show InfoWindow on a marker when initialized
- if (infoWindow.visibleOnMarker) {
- scope.$on('mapInitialized', function(evt, map) {
- $timeout(function() {
- var markerId = infoWindow.visibleOnMarker;
- var marker = map.markers[markerId];
- if (!marker) throw "Invalid marker id";
- infoWindow.__template = infoWindow.__eval.apply(this, [evt]);
- infoWindow.__compile(scope);
- infoWindow.open(map, marker);
- });
- });
- }
- /**
- * provide showInfoWindow method to scope
- */
- scope.showInfoWindow = scope.showInfoWindow ||
- function(event, id, anchor) {
- var infoWindow = mapController.map.infoWindows[id],
- tempTemplate = infoWindow.__template; // set template in a temporary variable
- infoWindow.__template = infoWindow.__eval.apply(this, [event]);
- infoWindow.__compile(scope);
- if (anchor) {
- infoWindow.open(mapController.map, anchor);
- } else if (this.getPosition) {
- infoWindow.open(mapController.map, this);
- } else {
- infoWindow.open(mapController.map);
- }
- infoWindow.__template = tempTemplate; // reset template to the object
- };
- /**
- * provide hideInfoWindow method to scope
- */
- scope.hideInfoWindow = scope.hideInfoWindow ||
- function(event, id, anchor) {
- var infoWindow = mapController.map.infoWindows[id];
- infoWindow.__template = infoWindow.__eval.apply(this, [event]);
- infoWindow.__compile(scope);
- infoWindow.close();
- };
- } //link
- }; // return
- }]);// function
- /**
- * @ngdoc directive
- * @name kml-layer
- * @requires Attr2Options
- * @description
- * renders Kml layer on a map
- * Requires: map directive
- * Restrict To: Element
- *
- * @param {Url} url url of the kml layer
- * @param {KmlLayerOptions} KmlLayerOptions
- * (https://developers.google.com/maps/documentation/javascript/reference#KmlLayerOptions)
- * @param {String} <KmlLayerEvent> Any KmlLayer events, https://developers.google.com/maps/documentation/javascript/reference
- * @example
- * Usage:
- * <map MAP_ATTRIBUTES>
- * <kml-layer ANY_KML_LAYER ANY_KML_LAYER_EVENTS"></kml-layer>
- * </map>
- *
- * Example:
- *
- * <map zoom="11" center="[41.875696,-87.624207]">
- * <kml-layer url="http://gmaps-samples.googlecode.com/svn/trunk/ggeoxml/cta.kml" ></kml-layer>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('kmlLayer', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
-
- var getKmlLayer = function(options, events) {
- var kmlLayer = new google.maps.KmlLayer(options);
- for (var eventName in events) {
- google.maps.event.addListener(kmlLayer, eventName, events[eventName]);
- }
- return kmlLayer;
- };
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var events = parser.getEvents(scope, filtered);
- console.log('kml-layer options', kmlLayer, 'events', events);
- var kmlLayer = getKmlLayer(options, events);
- mapController.addObject('kmlLayers', kmlLayer);
- parser.observeAttrSetObj(orgAttrs, attrs, kmlLayer); //observers
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name map-data
- * @description
- * set map data
- * Requires: map directive
- * Restrict To: Element
- *
- * @param {String} method-name, run map.data[method-name] with attribute value
- * @example
- * Example:
- *
- * <map zoom="11" center="[41.875696,-87.624207]">
- * <map-data load-geo-json="https://storage.googleapis.com/maps-devrel/google.json"></map-data>
- * </map>
- */
- ngMap.directive('mapData', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var events = parser.getEvents(scope, filtered, events);
- console.log('map-data options', options);
- scope.$on('mapInitialized', function(event, map) {
- /**
- * options
- */
- for (var key in options) {
- if (key) {
- var val = options[key];
- if (typeof scope[val] === "function") {
- map.data[key](scope[val]);
- } else {
- map.data[key](val);
- }
- } // if (key)
- }
- /**
- * events
- */
- for (var eventName in events) {
- if (events[eventName]) {
- map.data.addListener(eventName, events[eventName]);
- }
- }
- });
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name lazy-load
- * @requires Attr2Options
- * @description
- * Requires: Delay the initialization of directive until required .js loads
- * Restrict To: Attribute
- *
- * @param {String} lazy-load
- script source file location
- * example:
- * 'http://maps.googlecom/maps/api/js'
- * @example
- * Example:
- *
- * <div lazy-load="http://maps.google.com/maps/api/js">
- * <map center="Brampton" zoom="10">
- * <marker position="Brampton"></marker>
- * </map>
- * </div>
- */
- /*jshint -W089*/
- ngMap.directive('mapLazyLoad', ['$compile', '$timeout', function($compile, $timeout) {
- 'use strict';
- var directiveDefinitionObject = {
- compile: function(tElement, tAttrs) {
- (!tAttrs.mapLazyLoad) && console.error('requires src with map-lazy-load');
- var savedHtml = tElement.html(), src = tAttrs.mapLazyLoad;
- /**
- * if already loaded, stop processing it
- */
- if (document.querySelector('script[src="'+src+'"]')) {
- return false;
- }
- tElement.html(''); // will compile again after script is loaded
- return {
- pre: function(scope, element, attrs) {
- window.lazyLoadCallback = function() {
- console.log('script loaded,' + src);
- $timeout(function() { /* give some time to load */
- element.html(savedHtml);
- $compile(element.contents())(scope);
- }, 100);
- };
- var scriptEl = document.createElement('script');
- scriptEl.src = src + '?callback=lazyLoadCallback';
- document.body.appendChild(scriptEl);
- }
- };
- }
- };
- return directiveDefinitionObject;
- }]);
- /**
- * @ngdoc directive
- * @name map-type
- * @requires Attr2Options
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- *
- * <map zoom="13" center="34.04924594193164, -118.24104309082031">
- * <map-type name="coordinate" object="coordinateMapType"></map-type>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('mapType', ['Attr2Options', '$window', function(Attr2Options, $window) {
- var parser = Attr2Options;
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var mapTypeName = attrs.name, mapTypeObject;
- if (!mapTypeName) {
- throw "invalid map-type name";
- }
- if (attrs.object) {
- var __scope = scope[attrs.object] ? scope : $window;
- mapTypeObject = __scope[attrs.object];
- if (typeof mapTypeObject == "function") {
- mapTypeObject = new mapTypeObject();
- }
- }
- if (!mapTypeObject) {
- throw "invalid map-type object";
- }
- scope.$on('mapInitialized', function(evt, map) {
- map.mapTypes.set(mapTypeName, mapTypeObject);
- });
- mapController.addObject('mapTypes', mapTypeObject);
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name map
- * @requires Attr2Options
- * @description
- * Implementation of {@link MapController}
- * Initialize a Google map within a `<div>` tag with given options and register events
- * It accepts children directives; marker, shape, or marker-clusterer
- *
- * It initialize map, children tags, then emits message as soon as the action is done
- * The message emitted from this directive is;
- * . mapInitialized
- *
- * Restrict To:
- * Element
- *
- * @param {Array} geo-fallback-center
- * The center of map incase geolocation failed. i.e. [0,0]
- * @param {String} init-event The name of event to initialize this map.
- * If this option is given, the map won't be initialized until the event is received.
- * To invoke the event, use $scope.$emit or $scope.$broacast.
- * i.e. <map init-event="init-map" ng-click="$emit('init-map')" center=... ></map>
- * @param {String} <MapOption> Any Google map options,
- * https://developers.google.com/maps/documentation/javascript/reference?csw=1#MapOptions
- * @param {String} <MapEvent> Any Google map events,
- * https://rawgit.com/allenhwkim/angularjs-google-maps/master/build/map_events.html
- * @example
- * Usage:
- * <map MAP_OPTIONS_OR_MAP_EVENTS ..>
- * ... Any children directives
- * </map>
- *
- * Example:
- * <map center="[40.74, -74.18]" on-click="doThat()">
- * </map>
- *
- * <map geo-fallback-center="[40.74, -74.18]">
- * </map>
- */
- /*jshint -W030*/
- ngMap.directive('map', ['Attr2Options', '$timeout', function(Attr2Options, $timeout) {
- var parser = Attr2Options;
- function getStyle(el,styleProp)
- {
- if (el.currentStyle) {
- var y = el.currentStyle[styleProp];
- } else if (window.getComputedStyle) {
- var y = document.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp);
- }
- return y;
- }
- return {
- restrict: 'AE',
- controller: ngMap.MapController,
- /**
- * Initialize map and events
- * @memberof map
- * @param {$scope} scope
- * @param {angular.element} element
- * @param {Hash} attrs
- * @ctrl {MapController} ctrl
- */
- link: function (scope, element, attrs, ctrl) {
- var orgAttrs = parser.orgAttributes(element);
- scope.google = google; //used by $scope.eval in Attr2Options to avoid eval()
- /**
- * create a new `div` inside map tag, so that it does not touch map element
- * http://stackoverflow.com/questions/20955356
- */
- var el = document.createElement("div");
- el.style.width = "100%";
- el.style.height = "100%";
- element.prepend(el);
- /**
- * if style is not given to the map element, set display and height
- */
- if (getStyle(element[0], 'display') != "block") {
- element.css('display','block');
- }
- if (getStyle(element[0], 'height').match(/^(0|auto)/)) {
- element.css('height','300px');
- }
- /**
- * initialize function
- */
- var initializeMap = function(mapOptions, mapEvents) {
- var map = new google.maps.Map(el, {});
- map.markers = {};
- map.shapes = {};
-
- /**
- * resize the map to prevent showing partially, in case intialized too early
- */
- $timeout(function() {
- google.maps.event.trigger(map, "resize");
- });
- /**
- * set options
- */
- mapOptions.zoom = mapOptions.zoom || 15;
- var center = mapOptions.center;
- if (!center) {
- mapOptions.center = new google.maps.LatLng(0,0);
- } else if (!(center instanceof google.maps.LatLng)) {
- delete mapOptions.center;
- Attr2Options.setDelayedGeoLocation(map, 'setCenter',
- center, {fallbackLocation: options.geoFallbackCenter});
- }
- map.setOptions(mapOptions);
- /**
- * set events
- */
- for (var eventName in mapEvents) {
- if (eventName) {
- google.maps.event.addListener(map, eventName, mapEvents[eventName]);
- }
- }
- /**
- * set observers
- */
- parser.observeAttrSetObj(orgAttrs, attrs, map);
- /**
- * set controller and set objects
- * so that map can be used by other directives; marker or shape
- * ctrl._objects are gathered when marker and shape are initialized before map is set
- */
- ctrl.map = map; /* so that map can be used by other directives; marker or shape */
- ctrl.addObjects(ctrl._objects);
- // /* providing method to add a marker used by user scope */
- // map.addMarker = ctrl.addMarker;
- /**
- * set map for scope and controller and broadcast map event
- * scope.map will be overwritten if user have multiple maps in a scope,
- * thus the last map will be set as scope.map.
- * however an `mapInitialized` event will be emitted every time.
- */
- scope.map = map;
- scope.map.scope = scope;
- //google.maps.event.addListenerOnce(map, "idle", function() {
- scope.$emit('mapInitialized', map);
- //});
- // the following lines will be deprecated on behalf of mapInitialized
- // to collect maps, we should use scope.maps in your own controller, i.e. MyCtrl
- scope.maps = scope.maps || {};
- scope.maps[options.id||Object.keys(scope.maps).length] = map;
- scope.$emit('mapsInitialized', scope.maps);
- }; // function initializeMap()
- /**
- * get map options and events
- */
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered, scope);
- var controlOptions = parser.getControlOptions(filtered);
- var mapOptions = angular.extend(options, controlOptions);
- var mapEvents = parser.getEvents(scope, filtered);
- console.log("filtered", filtered, "mapOptions", mapOptions, 'mapEvents', mapEvents);
- if (attrs.initEvent) { // allows controlled initialization
- scope.$on(attrs.initEvent, function() {
- !ctrl.map && initializeMap(mapOptions, mapEvents); // init if not done
- });
- } else {
- initializeMap(mapOptions, mapEvents);
- } // if
- }
- };
- }]);
- /**
- * @ngdoc directive
- * @name MapController
- * @requires $scope
- * @property {Hash} controls collection of Controls initiated within `map` directive
- * @property {Hash} markersi collection of Markers initiated within `map` directive
- * @property {Hash} shapes collection of shapes initiated within `map` directive
- * @property {MarkerClusterer} markerClusterer MarkerClusterer initiated within `map` directive
- */
- /*jshint -W089*/
- ngMap.MapController = function() {
- this.map = null;
- this._objects = [];
- /**
- * Add a marker to map and $scope.markers
- * @memberof MapController
- * @name addMarker
- * @param {Marker} marker google map marker
- */
- this.addMarker = function(marker) {
- /**
- * marker and shape are initialized before map is initialized
- * so, collect _objects then will init. those when map is initialized
- * However the case as in ng-repeat, we can directly add to map
- */
- if (this.map) {
- this.map.markers = this.map.markers || {};
- marker.setMap(this.map);
- if (marker.centered) {
- this.map.setCenter(marker.position);
- }
- var len = Object.keys(this.map.markers).length;
- this.map.markers[marker.id || len] = marker;
- } else {
- this._objects.push(marker);
- }
- };
- /**
- * Add a shape to map and $scope.shapes
- * @memberof MapController
- * @name addShape
- * @param {Shape} shape google map shape
- */
- this.addShape = function(shape) {
- if (this.map) {
- this.map.shapes = this.map.shapes || {};
- shape.setMap(this.map);
- var len = Object.keys(this.map.shapes).length;
- this.map.shapes[shape.id || len] = shape;
- } else {
- this._objects.push(shape);
- }
- };
- this.addObject = function(groupName, obj) {
- if (this.map) {
- this.map[groupName] = this.map[groupName] || {};
- var len = Object.keys(this.map[groupName]).length;
- this.map[groupName][obj.id || len] = obj;
- if (groupName != "infoWindows" && obj.setMap) { //infoWindow.setMap works like infoWindow.open
- obj.setMap(this.map);
- }
- } else {
- obj.groupName = groupName;
- this._objects.push(obj);
- }
- }
- /**
- * Add a shape to map and $scope.shapes
- * @memberof MapController
- * @name addShape
- * @param {Shape} shape google map shape
- */
- this.addObjects = function(objects) {
- for (var i=0; i<objects.length; i++) {
- var obj=objects[i];
- if (obj instanceof google.maps.Marker) {
- this.addMarker(obj);
- } else if (obj instanceof google.maps.Circle ||
- obj instanceof google.maps.Polygon ||
- obj instanceof google.maps.Polyline ||
- obj instanceof google.maps.Rectangle ||
- obj instanceof google.maps.GroundOverlay) {
- this.addShape(obj);
- } else {
- this.addObject(obj.groupName, obj);
- }
- }
- };
- };
- /**
- * @ngdoc directive
- * @name maps-engine-layer
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- * <map zoom="14" center="[59.322506, 18.010025]">
- * <maps-engine-layer layer-id="06673056454046135537-08896501997766553811"></maps-engine-layer>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('mapsEngineLayer', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
- var getMapsEngineLayer = function(options, events) {
- var layer = new google.maps.visualization.MapsEngineLayer(options);
- for (var eventName in events) {
- google.maps.event.addListener(layer, eventName, events[eventName]);
- }
- return layer;
- };
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var events = parser.getEvents(scope, filtered, events);
- console.log('maps-engine-layer options', options, 'events', events);
- var layer = getMapsEngineLayer(options, events);
- mapController.addObject('mapsEngineLayers', layer);
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name marker
- * @requires Attr2Options
- * @requires NavigatorGeolocation
- * @description
- * Draw a Google map marker on a map with given options and register events
- *
- * Requires: map directive
- *
- * Restrict To: Element
- *
- * @param {String} position address, 'current', or [latitude, longitude]
- * example:
- * '1600 Pennsylvania Ave, 20500 Washingtion DC',
- * 'current position',
- * '[40.74, -74.18]'
- * @param {Boolean} centered if set, map will be centered with this marker
- * @param {String} <MarkerOption> Any Marker options, https://developers.google.com/maps/documentation/javascript/reference?csw=1#MarkerOptions
- * @param {String} <MapEvent> Any Marker events, https://developers.google.com/maps/documentation/javascript/reference
- * @example
- * Usage:
- * <map MAP_ATTRIBUTES>
- * <marker ANY_MARKER_OPTIONS ANY_MARKER_EVENTS"></MARKER>
- * </map>
- *
- * Example:
- * <map center="[40.74, -74.18]">
- * <marker position="[40.74, -74.18]" on-click="myfunc()"></div>
- * </map>
- *
- * <map center="the cn tower">
- * <marker position="the cn tower" on-click="myfunc()"></div>
- * </map>
- */
- ngMap.directive('marker', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
- var getMarker = function(options, events) {
- var marker;
- /**
- * set options
- */
- if (options.icon instanceof Object) {
- if ((""+options.icon.path).match(/^[A-Z_]+$/)) {
- options.icon.path = google.maps.SymbolPath[options.icon.path];
- }
- for (var key in options.icon) {
- var arr = options.icon[key];
- if (key == "anchor" || key == "origin") {
- options.icon[key] = new google.maps.Point(arr[0], arr[1]);
- } else if (key == "size" || key == "scaledSize") {
- options.icon[key] = new google.maps.Size(arr[0], arr[1]);
- }
- }
- }
- if (!(options.position instanceof google.maps.LatLng)) {
- var orgPosition = options.position;
- options.position = new google.maps.LatLng(0,0);
- marker = new google.maps.Marker(options);
- parser.setDelayedGeoLocation(marker, 'setPosition', orgPosition);
- } else {
- marker = new google.maps.Marker(options);
- }
- /**
- * set events
- */
- if (Object.keys(events).length > 0) {
- console.log("markerEvents", events);
- }
- for (var eventName in events) {
- if (eventName) {
- google.maps.event.addListener(marker, eventName, events[eventName]);
- }
- }
- return marker;
- };
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var markerOptions = parser.getOptions(filtered, scope);
- var markerEvents = parser.getEvents(scope, filtered);
- console.log('marker options', markerOptions, 'events', markerEvents);
- /**
- * set event to clean up removed marker
- * useful with ng-repeat
- */
- element.bind('$destroy', function() {
- var markers = marker.map.markers;
- for (var name in markers) {
- if (markers[name] == marker) {
- delete markers[name];
- }
- }
- marker.setMap(null);
- });
- var marker = getMarker(markerOptions, markerEvents);
- mapController.addMarker(marker);
- /**
- * set observers
- */
- parser.observeAttrSetObj(orgAttrs, attrs, marker); /* observers */
- } //link
- }; // return
- }]);//
- /**
- * @ngdoc directive
- * @name overlay-map-type
- * @requires Attr2Options
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- *
- * <map zoom="13" center="34.04924594193164, -118.24104309082031">
- * <overlay-map-type index="0" object="coordinateMapType"></map-type>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('overlayMapType', ['Attr2Options', '$window', function(Attr2Options, $window) {
- var parser = Attr2Options;
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var overlayMapTypeObject;
- var initMethod = attrs.initMethod || "insertAt";
- if (attrs.object) {
- var __scope = scope[attrs.object] ? scope : $window;
- overlayMapTypeObject = __scope[attrs.object];
- if (typeof overlayMapTypeObject == "function") {
- overlayMapTypeObject = new overlayMapTypeObject();
- }
- }
- if (!overlayMapTypeObject) {
- throw "invalid map-type object";
- }
- scope.$on('mapInitialized', function(evt, map) {
- if (initMethod == "insertAt") {
- var index = parseInt(attrs.index, 10);
- map.overlayMapTypes.insertAt(index, overlayMapTypeObject);
- } else if (initMethod == "push") {
- map.overlayMapTypes.push(overlayMapTypeObject);
- }
- });
- mapController.addObject('overlayMapTypes', overlayMapTypeObject);
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name shape
- * @requires Attr2Options
- * @description
- * Initialize a Google map shape in map with given options and register events
- * The shapes are:
- * . circle
- * . polygon
- * . polyline
- * . rectangle
- * . groundOverlay(or image)
- *
- * Requires: map directive
- *
- * Restrict To: Element
- *
- * @param {Boolean} centered if set, map will be centered with this marker
- * @param {String} <OPTIONS>
- * For circle, [any circle options](https://developers.google.com/maps/documentation/javascript/reference#CircleOptions)
- * For polygon, [any polygon options](https://developers.google.com/maps/documentation/javascript/reference#PolygonOptions)
- * For polyline, [any polyline options](https://developers.google.com/maps/documentation/javascript/reference#PolylineOptions)
- * For rectangle, [any rectangle options](https://developers.google.com/maps/documentation/javascript/reference#RectangleOptions)
- * For image, [any groundOverlay options](https://developers.google.com/maps/documentation/javascript/reference#GroundOverlayOptions)
- * @param {String} <MapEvent> Any Shape events, https://developers.google.com/maps/documentation/javascript/reference
- * @example
- * Usage:
- * <map MAP_ATTRIBUTES>
- * <shape name=SHAPE_NAME ANY_SHAPE_OPTIONS ANY_SHAPE_EVENTS"></MARKER>
- * </map>
- *
- * Example:
- *
- * <map zoom="11" center="[40.74, -74.18]">
- * <shape id="polyline" name="polyline" geodesic="true" stroke-color="#FF0000" stroke-opacity="1.0" stroke-weight="2"
- * path="[[40.74,-74.18],[40.64,-74.10],[40.54,-74.05],[40.44,-74]]" ></shape>
- * </map>
- *
- * <map zoom="11" center="[40.74, -74.18]">
- * <shape id="polygon" name="polygon" stroke-color="#FF0000" stroke-opacity="1.0" stroke-weight="2"
- * paths="[[40.74,-74.18],[40.64,-74.18],[40.84,-74.08],[40.74,-74.18]]" ></shape>
- * </map>
- *
- * <map zoom="11" center="[40.74, -74.18]">
- * <shape id="rectangle" name="rectangle" stroke-color='#FF0000' stroke-opacity="0.8" stroke-weight="2"
- * bounds="[[40.74,-74.18], [40.78,-74.14]]" editable="true" ></shape>
- * </map>
- *
- * <map zoom="11" center="[40.74, -74.18]">
- * <shape id="circle" name="circle" stroke-color='#FF0000' stroke-opacity="0.8"stroke-weight="2"
- * center="[40.70,-74.14]" radius="4000" editable="true" ></shape>
- * </map>
- *
- * <map zoom="11" center="[40.74, -74.18]">
- * <shape id="image" name="image" url="https://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg"
- * bounds="[[40.71,-74.22],[40.77,-74.12]]" opacity="0.7" clickable="true" ></shape>
- * </map>
- *
- * For full-working example, please visit
- * [shape example](https://rawgit.com/allenhwkim/angularjs-google-maps/master/build/shape.html)
- */
- ngMap.directive('shape', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
-
- var getBounds = function(points) {
- return new google.maps.LatLngBounds(points[0], points[1]);
- };
-
- var getShape = function(options, events) {
- var shape;
- var shapeName = options.name;
- delete options.name; //remove name bcoz it's not for options
- console.log("shape", shapeName, "options", options, 'events', events);
- /**
- * set options
- */
- if (options.icons) {
- for (var i=0; i<options.icons.length; i++) {
- var el = options.icons[i];
- if (el.icon.path.match(/^[A-Z_]+$/)) {
- el.icon.path = google.maps.SymbolPath[el.icon.path];
- }
- }
- }
- switch(shapeName) {
- case "circle":
- if (options.center instanceof google.maps.LatLng) {
- shape = new google.maps.Circle(options);
- } else {
- var orgCenter = options.center;
- options.center = new google.maps.LatLng(0,0);
- shape = new google.maps.Circle(options);
- parser.setDelayedGeoLocation(shape, 'setCenter', orgCenter);
- }
- break;
- case "polygon":
- shape = new google.maps.Polygon(options);
- break;
- case "polyline":
- shape = new google.maps.Polyline(options);
- break;
- case "rectangle":
- if (options.bounds) {
- options.bounds = getBounds(options.bounds);
- }
- shape = new google.maps.Rectangle(options);
- break;
- case "groundOverlay":
- case "image":
- var url = options.url;
- var bounds = getBounds(options.bounds);
- var opts = {opacity: options.opacity, clickable: options.clickable, id:options.id};
- shape = new google.maps.GroundOverlay(url, bounds, opts);
- break;
- }
- /**
- * set events
- */
- for (var eventName in events) {
- if (events[eventName]) {
- google.maps.event.addListener(shape, eventName, events[eventName]);
- }
- }
- return shape;
- };
-
- return {
- restrict: 'E',
- require: '^map',
- /**
- * link function
- * @private
- */
- link: function(scope, element, attrs, mapController) {
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var shapeOptions = parser.getOptions(filtered);
- var shapeEvents = parser.getEvents(scope, filtered);
- var shape = getShape(shapeOptions, shapeEvents);
- mapController.addShape(shape);
- /**
- * set observers
- */
- parser.observeAttrSetObj(orgAttrs, attrs, shape);
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name traffic-layer
- * @requires Attr2Options
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- *
- * <map zoom="13" center="34.04924594193164, -118.24104309082031">
- * <traffic-layer></traffic-layer>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('trafficLayer', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
-
- var getLayer = function(options, events) {
- var layer = new google.maps.TrafficLayer(options);
- for (var eventName in events) {
- google.maps.event.addListener(layer, eventName, events[eventName]);
- }
- return layer;
- };
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var events = parser.getEvents(scope, filtered);
- console.log('traffic-layer options', options, 'events', events);
- var layer = getLayer(options, events);
- mapController.addObject('trafficLayers', layer);
- parser.observeAttrSetObj(orgAttrs, attrs, layer); //observers
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name transit-layer
- * @requires Attr2Options
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- *
- * <map zoom="13" center="34.04924594193164, -118.24104309082031">
- * <transit-layer></transit-layer>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('transitLayer', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
-
- var getLayer = function(options, events) {
- var layer = new google.maps.TransitLayer(options);
- for (var eventName in events) {
- google.maps.event.addListener(layer, eventName, events[eventName]);
- }
- return layer;
- };
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var events = parser.getEvents(scope, filtered);
- console.log('transit-layer options', options, 'events', events);
- var layer = getLayer(options, events);
- mapController.addObject('transitLayers', layer);
- parser.observeAttrSetObj(orgAttrs, attrs, layer); //observers
- }
- }; // return
- }]);
- /**
- * @ngdoc directive
- * @name weather-layer
- * @requires Attr2Options
- * @description
- * Requires: map directive
- * Restrict To: Element
- *
- * @example
- * Example:
- *
- * <map zoom="13" center="34.04924594193164, -118.24104309082031">
- * <weather-layer></weather-layer>
- * </map>
- */
- /*jshint -W089*/
- ngMap.directive('weatherLayer', ['Attr2Options', function(Attr2Options) {
- var parser = Attr2Options;
-
- var getLayer = function(options, events) {
- var layer = new google.maps.weather.WeatherLayer(options);
- for (var eventName in events) {
- google.maps.event.addListener(layer, eventName, events[eventName]);
- }
- return layer;
- };
-
- return {
- restrict: 'E',
- require: '^map',
- link: function(scope, element, attrs, mapController) {
- var orgAttrs = parser.orgAttributes(element);
- var filtered = parser.filter(attrs);
- var options = parser.getOptions(filtered);
- var events = parser.getEvents(scope, filtered);
- console.log('weather-layer options', options, 'events', events);
- var layer = getLayer(options, events);
- mapController.addObject('weatherLayers', layer);
- parser.observeAttrSetObj(orgAttrs, attrs, layer); //observers
- }
- }; // return
- }]);
|