ng-map.debug.js 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026
  1. var ngMap = angular.module('ngMap', []);
  2. /**
  3. * @ngdoc service
  4. * @name Attr2Options
  5. * @description
  6. * Converts tag attributes to options used by google api v3 objects, map, marker, polygon, circle, etc.
  7. */
  8. /*jshint -W030*/
  9. ngMap.service('Attr2Options', ['$parse', 'NavigatorGeolocation', 'GeoCoder', function($parse, NavigatorGeolocation, GeoCoder) {
  10. var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
  11. var MOZ_HACK_REGEXP = /^moz([A-Z])/;
  12. var orgAttributes = function(el) {
  13. (el.length > 0) && (el = el[0]);
  14. var orgAttributes = {};
  15. for (var i=0; i<el.attributes.length; i++) {
  16. var attr = el.attributes[i];
  17. orgAttributes[attr.name] = attr.value;
  18. }
  19. return orgAttributes;
  20. }
  21. var camelCase = function(name) {
  22. return name.
  23. replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
  24. return offset ? letter.toUpperCase() : letter;
  25. }).
  26. replace(MOZ_HACK_REGEXP, 'Moz$1');
  27. }
  28. var JSONize = function(str) {
  29. try { // if parsable already, return as it is
  30. JSON.parse(str);
  31. return str;
  32. } catch(e) { // if not parsable, change little
  33. return str
  34. // wrap keys without quote with valid double quote
  35. .replace(/([\$\w]+)\s*:/g, function(_, $1){return '"'+$1+'":'})
  36. // replacing single quote wrapped ones to double quote
  37. .replace(/'([^']+)'/g, function(_, $1){return '"'+$1+'"'})
  38. }
  39. }
  40. var toOptionValue = function(input, options) {
  41. var output, key=options.key, scope=options.scope;
  42. try { // 1. Number?
  43. var num = Number(input);
  44. if (isNaN(num)) {
  45. throw "Not a number";
  46. } else {
  47. output = num;
  48. }
  49. } catch(err) {
  50. try { // 2.JSON?
  51. if (input.match(/^[\+\-]?[0-9\.]+,[ ]*\ ?[\+\-]?[0-9\.]+$/)) { // i.e "-1.0, 89.89"
  52. input = "["+input+"]";
  53. }
  54. output = JSON.parse(JSONize(input));
  55. if (output instanceof Array) {
  56. var t1stEl = output[0];
  57. if (t1stEl.constructor == Object) { // [{a:1}] : not lat/lng ones
  58. } else if (t1stEl.constructor == Array) { // [[1,2],[3,4]]
  59. output = output.map(function(el) {
  60. return new google.maps.LatLng(el[0], el[1]);
  61. });
  62. } else if(!isNaN(parseFloat(t1stEl)) && isFinite(t1stEl)) {
  63. return new google.maps.LatLng(output[0], output[1]);
  64. }
  65. }
  66. } catch(err2) {
  67. // 3. Object Expression. i.e. LatLng(80,-49)
  68. if (input.match(/^[A-Z][a-zA-Z0-9]+\(.*\)$/)) {
  69. try {
  70. var exp = "new google.maps."+input;
  71. output = eval(exp); // TODO, still eval
  72. } catch(e) {
  73. output = input;
  74. }
  75. // 4. Object Expression. i.e. MayTypeId.HYBRID
  76. } else if (input.match(/^([A-Z][a-zA-Z0-9]+)\.([A-Z]+)$/)) {
  77. try {
  78. var matches = input.match(/^([A-Z][a-zA-Z0-9]+)\.([A-Z]+)$/);
  79. output = google.maps[matches[1]][matches[2]];
  80. } catch(e) {
  81. output = input;
  82. }
  83. // 5. Object Expression. i.e. HYBRID
  84. } else if (input.match(/^[A-Z]+$/)) {
  85. try {
  86. var capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
  87. if (key.match(/temperatureUnit|windSpeedUnit|labelColor/)) {
  88. capitalizedKey = capitalizedKey.replace(/s$/,"");
  89. output = google.maps.weather[capitalizedKey][input];
  90. } else {
  91. output = google.maps[capitalizedKey][input];
  92. }
  93. } catch(e) {
  94. output = input;
  95. }
  96. } else {
  97. output = input;
  98. }
  99. } // catch(err2)
  100. } // catch(err)
  101. return output;
  102. };
  103. var setDelayedGeoLocation = function(object, method, param, options) {
  104. options = options || {};
  105. var centered = object.centered || options.centered;
  106. var errorFunc = function() {
  107. console.log('error occurred while', object, method, param, options);
  108. var fallbackLocation = options.fallbackLocation || new google.maps.LatLng(0,0);
  109. object[method](fallbackLocation);
  110. };
  111. if (!param || param.match(/^current/i)) { // sensored position
  112. NavigatorGeolocation.getCurrentPosition().then(
  113. function(position) { // success
  114. var lat = position.coords.latitude;
  115. var lng = position.coords.longitude;
  116. var latLng = new google.maps.LatLng(lat,lng);
  117. object[method](latLng);
  118. if (centered) {
  119. object.map.setCenter(latLng);
  120. }
  121. options.callback && options.callback.apply(object);
  122. },
  123. errorFunc
  124. );
  125. } else { //assuming it is address
  126. GeoCoder.geocode({address: param}).then(
  127. function(results) { // success
  128. object[method](results[0].geometry.location);
  129. if (centered) {
  130. object.map.setCenter(results[0].geometry.location);
  131. }
  132. },
  133. errorFunc
  134. );
  135. }
  136. };
  137. var getAttrsToObserve = function(attrs) {
  138. var attrsToObserve = [];
  139. if (attrs["ng-repeat"] || attrs.ngRepeat) { // if element is created by ng-repeat, don't observe any
  140. } else {
  141. for (var attrName in attrs) {
  142. var attrValue = attrs[attrName];
  143. if (attrValue && attrValue.match(/\{\{.*\}\}/)) { // if attr value is {{..}}
  144. console.log('setting attribute to observe', attrName, camelCase(attrName), attrValue);
  145. attrsToObserve.push(camelCase(attrName));
  146. }
  147. }
  148. }
  149. return attrsToObserve;
  150. };
  151. var observeAttrSetObj = function(orgAttrs, attrs, obj) {
  152. var attrsToObserve = getAttrsToObserve(orgAttrs);
  153. if (Object.keys(attrsToObserve).length) {
  154. console.log(obj, "attributes to observe", attrsToObserve);
  155. }
  156. for (var i=0; i<attrsToObserve.length; i++) {
  157. observeAndSet(attrs, attrsToObserve[i], obj);
  158. }
  159. }
  160. var observeAndSet = function(attrs, attrName, object) {
  161. attrs.$observe(attrName, function(val) {
  162. if (val) {
  163. console.log('observing ', object, attrName, val);
  164. var setMethod = camelCase('set-'+attrName);
  165. var optionValue = toOptionValue(val, {key: attrName});
  166. console.log('setting ', object, attrName, 'with value', optionValue);
  167. if (object[setMethod]) { //if set method does exist
  168. /* if an location is being observed */
  169. if (attrName.match(/center|position/) &&
  170. typeof optionValue == 'string') {
  171. setDelayedGeoLocation(object, setMethod, optionValue);
  172. } else {
  173. object[setMethod](optionValue);
  174. }
  175. }
  176. }
  177. });
  178. };
  179. return {
  180. /**
  181. * filters attributes by skipping angularjs methods $.. $$..
  182. * @memberof Attr2Options
  183. * @param {Hash} attrs tag attributes
  184. * @returns {Hash} filterd attributes
  185. */
  186. filter: function(attrs) {
  187. var options = {};
  188. for(var key in attrs) {
  189. if (key.match(/^\$/) || key.match(/^ng[A-Z]/)) {
  190. } else {
  191. options[key] = attrs[key];
  192. }
  193. }
  194. return options;
  195. },
  196. /**
  197. * converts attributes hash to Google Maps API v3 options
  198. * ```
  199. * . converts numbers to number
  200. * . converts class-like string to google maps instance
  201. * i.e. `LatLng(1,1)` to `new google.maps.LatLng(1,1)`
  202. * . converts constant-like string to google maps constant
  203. * i.e. `MapTypeId.HYBRID` to `google.maps.MapTypeId.HYBRID`
  204. * i.e. `HYBRID"` to `google.maps.MapTypeId.HYBRID`
  205. * ```
  206. * @memberof Attr2Options
  207. * @param {Hash} attrs tag attributes
  208. * @param {scope} scope angularjs scope
  209. * @returns {Hash} options converted attributess
  210. */
  211. getOptions: function(attrs, scope) {
  212. var options = {};
  213. for(var key in attrs) {
  214. if (attrs[key]) {
  215. if (key.match(/^on[A-Z]/)) { //skip events, i.e. on-click
  216. continue;
  217. } else if (key.match(/ControlOptions$/)) { // skip controlOptions
  218. continue;
  219. } else {
  220. options[key] = toOptionValue(attrs[key], {scope:scope, key: key});
  221. }
  222. } // if (attrs[key])
  223. } // for(var key in attrs)
  224. return options;
  225. },
  226. /**
  227. * converts attributes hash to scope-specific event function
  228. * @memberof Attr2Options
  229. * @param {scope} scope angularjs scope
  230. * @param {Hash} attrs tag attributes
  231. * @returns {Hash} events converted events
  232. */
  233. getEvents: function(scope, attrs) {
  234. var events = {};
  235. var toLowercaseFunc = function($1){
  236. return "_"+$1.toLowerCase();
  237. };
  238. var eventFunc = function(attrValue) {
  239. var matches = attrValue.match(/([^\(]+)\(([^\)]*)\)/);
  240. var funcName = matches[1];
  241. var argsStr = matches[2].replace(/event[ ,]*/,''); //remove string 'event'
  242. var args = scope.$eval("["+argsStr+"]");
  243. return function(event) {
  244. function index(obj,i) {return obj[i]}
  245. f = funcName.split('.').reduce(index, scope)
  246. f.apply(this, [event].concat(args));
  247. scope.$apply();
  248. }
  249. }
  250. for(var key in attrs) {
  251. if (attrs[key]) {
  252. if (!key.match(/^on[A-Z]/)) { //skip if not events
  253. continue;
  254. }
  255. //get event name as underscored. i.e. zoom_changed
  256. var eventName = key.replace(/^on/,'');
  257. eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1);
  258. eventName = eventName.replace(/([A-Z])/g, toLowercaseFunc);
  259. var attrValue = attrs[key];
  260. events[eventName] = new eventFunc(attrValue);
  261. }
  262. }
  263. return events;
  264. },
  265. /**
  266. * control means map controls, i.e streetview, pan, etc, not a general control
  267. * @memberof Attr2Options
  268. * @param {Hash} filtered filtered tag attributes
  269. * @returns {Hash} Google Map options
  270. */
  271. getControlOptions: function(filtered) {
  272. var controlOptions = {};
  273. if (typeof filtered != 'object')
  274. return false;
  275. for (var attr in filtered) {
  276. if (filtered[attr]) {
  277. if (!attr.match(/(.*)ControlOptions$/)) {
  278. continue; // if not controlOptions, skip it
  279. }
  280. //change invalid json to valid one, i.e. {foo:1} to {"foo": 1}
  281. var orgValue = filtered[attr];
  282. var newValue = orgValue.replace(/'/g, '"');
  283. newValue = newValue.replace(/([^"]+)|("[^"]+")/g, function($0, $1, $2) {
  284. if ($1) {
  285. return $1.replace(/([a-zA-Z0-9]+?):/g, '"$1":');
  286. } else {
  287. return $2;
  288. }
  289. });
  290. try {
  291. var options = JSON.parse(newValue);
  292. for (var key in options) { //assign the right values
  293. if (options[key]) {
  294. var value = options[key];
  295. if (typeof value === 'string') {
  296. value = value.toUpperCase();
  297. } else if (key === "mapTypeIds") {
  298. value = value.map( function(str) {
  299. if (str.match(/^[A-Z]+$/)) { // if constant
  300. return google.maps.MapTypeId[str.toUpperCase()];
  301. } else { // else, custom map-type
  302. return str;
  303. }
  304. });
  305. }
  306. if (key === "style") {
  307. var str = attr.charAt(0).toUpperCase() + attr.slice(1);
  308. var objName = str.replace(/Options$/,'')+"Style";
  309. options[key] = google.maps[objName][value];
  310. } else if (key === "position") {
  311. options[key] = google.maps.ControlPosition[value];
  312. } else {
  313. options[key] = value;
  314. }
  315. }
  316. }
  317. controlOptions[attr] = options;
  318. } catch (e) {
  319. console.error('invald option for', attr, newValue, e, e.stack);
  320. }
  321. }
  322. } // for
  323. return controlOptions;
  324. }, // function
  325. toOptionValue: toOptionValue,
  326. camelCase: camelCase,
  327. setDelayedGeoLocation: setDelayedGeoLocation,
  328. getAttrsToObserve: getAttrsToObserve,
  329. observeAndSet: observeAndSet,
  330. observeAttrSetObj: observeAttrSetObj,
  331. orgAttributes: orgAttributes
  332. }; // return
  333. }]);
  334. /**
  335. * @ngdoc service
  336. * @name GeoCoder
  337. * @description
  338. * Provides [defered/promise API](https://docs.angularjs.org/api/ng/service/$q) service for Google Geocoder service
  339. */
  340. ngMap.service('GeoCoder', ['$q', function($q) {
  341. return {
  342. /**
  343. * @memberof GeoCoder
  344. * @param {Hash} options https://developers.google.com/maps/documentation/geocoding/#geocoding
  345. * @example
  346. * ```
  347. * GeoCoder.geocode({address: 'the cn tower'}).then(function(result) {
  348. * //... do something with result
  349. * });
  350. * ```
  351. * @returns {HttpPromise} Future object
  352. */
  353. geocode : function(options) {
  354. var deferred = $q.defer();
  355. var geocoder = new google.maps.Geocoder();
  356. geocoder.geocode(options, function (results, status) {
  357. if (status == google.maps.GeocoderStatus.OK) {
  358. deferred.resolve(results);
  359. } else {
  360. deferred.reject('Geocoder failed due to: '+ status);
  361. }
  362. });
  363. return deferred.promise;
  364. }
  365. }
  366. }]);
  367. /**
  368. * @ngdoc service
  369. * @name NavigatorGeolocation
  370. * @description
  371. * Provides [defered/promise API](https://docs.angularjs.org/api/ng/service/$q) service for navigator.geolocation methods
  372. */
  373. ngMap.service('NavigatorGeolocation', ['$q', function($q) {
  374. return {
  375. /**
  376. * @memberof NavigatorGeolocation
  377. * @param {function} success success callback function
  378. * @param {function} failure failure callback function
  379. * @example
  380. * ```
  381. * NavigatorGeolocation.getCurrentPosition()
  382. * .then(function(position) {
  383. * var lat = position.coords.latitude, lng = position.coords.longitude;
  384. * .. do something lat and lng
  385. * });
  386. * ```
  387. * @returns {HttpPromise} Future object
  388. */
  389. getCurrentPosition: function() {
  390. var deferred = $q.defer();
  391. if (navigator.geolocation) {
  392. navigator.geolocation.getCurrentPosition(
  393. function(position) {
  394. deferred.resolve(position);
  395. }, function(evt) {
  396. console.error(evt);
  397. deferred.reject(evt);
  398. }
  399. );
  400. } else {
  401. deferred.reject("Browser Geolocation service failed.");
  402. }
  403. return deferred.promise;
  404. },
  405. watchPosition: function() {
  406. return "TODO";
  407. },
  408. clearWatch: function() {
  409. return "TODO";
  410. }
  411. };
  412. }]);
  413. /**
  414. * @ngdoc service
  415. * @name StreetView
  416. * @description
  417. * Provides [defered/promise API](https://docs.angularjs.org/api/ng/service/$q) service
  418. * for [Google StreetViewService](https://developers.google.com/maps/documentation/javascript/streetview)
  419. */
  420. ngMap.service('StreetView', ['$q', function($q) {
  421. return {
  422. /**
  423. * Retrieves panorama id from the given map (and or position)
  424. * @memberof StreetView
  425. * @param {map} map Google map instance
  426. * @param {LatLng} latlng Google LatLng instance
  427. * default: the center of the map
  428. * @example
  429. * StreetView.getPanorama(map).then(function(panoId) {
  430. * $scope.panoId = panoId;
  431. * });
  432. * @returns {HttpPromise} Future object
  433. */
  434. getPanorama : function(map, latlng) {
  435. latlng = latlng || map.getCenter();
  436. var deferred = $q.defer();
  437. var svs = new google.maps.StreetViewService();
  438. svs.getPanoramaByLocation( (latlng||map.getCenter), 100, function (data, status) {
  439. // if streetView available
  440. if (status === google.maps.StreetViewStatus.OK) {
  441. deferred.resolve(data.location.pano);
  442. } else {
  443. // no street view available in this range, or some error occurred
  444. deferred.resolve(false);
  445. //deferred.reject('Geocoder failed due to: '+ status);
  446. }
  447. });
  448. return deferred.promise;
  449. },
  450. /**
  451. * Set panorama view on the given map with the panorama id
  452. * @memberof StreetView
  453. * @param {map} map Google map instance
  454. * @param {String} panoId Panorama id fro getPanorama method
  455. * @example
  456. * StreetView.setPanorama(map, panoId);
  457. */
  458. setPanorama : function(map, panoId) {
  459. var svp = new google.maps.StreetViewPanorama(map.getDiv(), {enableCloseButton: true});
  460. svp.setPano(panoId);
  461. }
  462. }; // return
  463. }]);
  464. /**
  465. * @ngdoc directive
  466. * @name bicycling-layer
  467. * @requires Attr2Options
  468. * @description
  469. * Requires: map directive
  470. * Restrict To: Element
  471. *
  472. * @example
  473. * Example:
  474. *
  475. * <map zoom="13" center="34.04924594193164, -118.24104309082031">
  476. * <bicycling-layer></bicycling-layer>
  477. * </map>
  478. */
  479. /*jshint -W089*/
  480. ngMap.directive('bicyclingLayer', ['Attr2Options', function(Attr2Options) {
  481. var parser = Attr2Options;
  482. var getLayer = function(options, events) {
  483. var layer = new google.maps.BicyclingLayer(options);
  484. for (var eventName in events) {
  485. google.maps.event.addListener(layer, eventName, events[eventName]);
  486. }
  487. return layer;
  488. };
  489. return {
  490. restrict: 'E',
  491. require: '^map',
  492. link: function(scope, element, attrs, mapController) {
  493. var orgAttrs = parser.orgAttributes(element);
  494. var filtered = parser.filter(attrs);
  495. var options = parser.getOptions(filtered);
  496. var events = parser.getEvents(scope, filtered);
  497. console.log('bicycling-layer options', options, 'events', events);
  498. var layer = getLayer(options, events);
  499. mapController.addObject('bicyclingLayers', layer);
  500. parser.observeAttrSetObj(orgAttrs, attrs, layer); //observers
  501. }
  502. }; // return
  503. }]);
  504. /**
  505. * @ngdoc directive
  506. * @name cloud-layer
  507. * @requires Attr2Options
  508. * @description
  509. * Requires: map directive
  510. * Restrict To: Element
  511. *
  512. * @example
  513. * Example:
  514. *
  515. * <map zoom="13" center="34.04924594193164, -118.24104309082031">
  516. * <cloud-layer></cloud-layer>
  517. * </map>
  518. */
  519. /*jshint -W089*/
  520. ngMap.directive('cloudLayer', ['Attr2Options', function(Attr2Options) {
  521. var parser = Attr2Options;
  522. var getLayer = function(options, events) {
  523. var layer = new google.maps.weather.CloudLayer(options);
  524. for (var eventName in events) {
  525. google.maps.event.addListener(layer, eventName, events[eventName]);
  526. }
  527. return layer;
  528. };
  529. return {
  530. restrict: 'E',
  531. require: '^map',
  532. link: function(scope, element, attrs, mapController) {
  533. var orgAttrs = parser.orgAttributes(element);
  534. var filtered = parser.filter(attrs);
  535. var options = parser.getOptions(filtered);
  536. var events = parser.getEvents(scope, filtered);
  537. console.log('cloud-layer options', options, 'events', events);
  538. var layer = getLayer(options, events);
  539. mapController.addObject('cloudLayers', layer);
  540. parser.observeAttrSetObj(orgAttrs, attrs, layer); //observers
  541. }
  542. }; // return
  543. }]);
  544. /*jshint -W030*/
  545. /**
  546. * @ngdoc directive
  547. * @name custom-control
  548. * @requires Attr2Options
  549. * @requires $compile
  550. * @description
  551. * Build custom control and set to the map with position
  552. *
  553. * Requires: map directive
  554. *
  555. * Restrict To: Element
  556. *
  557. * @param {String} position position of this control
  558. * i.e. TOP_RIGHT
  559. * @param {Number} index index of the control
  560. * @example
  561. *
  562. * Example:
  563. * <map center="41.850033,-87.6500523" zoom="3">
  564. * <custom-control id="home" position="TOP_LEFT" index="1">
  565. * <div style="background-color: white;">
  566. * <b>Home</b>
  567. * </div>
  568. * </custom-control>
  569. * </map>
  570. *
  571. */
  572. /*jshint -W089*/
  573. ngMap.directive('customControl', ['Attr2Options', '$compile', function(Attr2Options, $compile) {
  574. var parser = Attr2Options;
  575. return {
  576. restrict: 'E',
  577. require: '^map',
  578. link: function(scope, element, attrs, mapController) {
  579. element.css('display','none');
  580. var orgAttrs = parser.orgAttributes(element);
  581. var filtered = parser.filter(attrs);
  582. var options = parser.getOptions(filtered, scope);
  583. var events = parser.getEvents(scope, filtered);
  584. console.log("custom-control options", options, "events", events);
  585. /**
  586. * build a custom control element
  587. */
  588. var compiled = $compile(element.html().trim())(scope);
  589. var customControlEl = compiled[0];
  590. /**
  591. * set events
  592. */
  593. for (var eventName in events) {
  594. google.maps.event.addDomListener(customControlEl, eventName, events[eventName]);
  595. }
  596. mapController.addObject('customControls', customControlEl);
  597. scope.$on('mapInitialized', function(evt, map) {
  598. var position = options.position;
  599. map.controls[google.maps.ControlPosition[position]].push(customControlEl);
  600. });
  601. } //link
  602. }; // return
  603. }]);// function
  604. /**
  605. * @ngdoc directive
  606. * @name drawing-manager
  607. * @requires Attr2Options
  608. * @description
  609. * Requires: map directive
  610. * Restrict To: Element
  611. *
  612. * @example
  613. * Example:
  614. *
  615. * <map zoom="13" center="37.774546, -122.433523" map-type-id="SATELLITE">
  616. * <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>
  617. * </map>
  618. *
  619. * TODO: Add remove button.
  620. * currently, for out solution, we have the shapes/markers in our own controller, and we use some css classes to change the shape button
  621. * to a remove button (<div>X</div>) and have the remove operation in our own controller.
  622. */
  623. /*jshint -W089*/
  624. ngMap.directive('drawingManager', ['Attr2Options', function(Attr2Options) {
  625. var parser = Attr2Options;
  626. return {
  627. restrict: 'E',
  628. require: '^map',
  629. link: function(scope, element, attrs, mapController) {
  630. var orgAttrs = parser.orgAttributes(element);
  631. var filtered = parser.filter(attrs);
  632. var options = parser.getOptions(filtered);
  633. var controlOptions = parser.getControlOptions(filtered);
  634. var events = parser.getEvents(scope, filtered);
  635. console.log("filtered", filtered, "options", options, 'controlOptions', controlOptions, 'events', events);
  636. /**
  637. * set options
  638. */
  639. var drawingManager = new google.maps.drawing.DrawingManager({
  640. drawingMode: options.drawingmode,
  641. drawingControl: options.drawingcontrol,
  642. drawingControlOptions: controlOptions.drawingControlOptions,
  643. circleOptions:options.circleoptions,
  644. markerOptions:options.markeroptions,
  645. polygonOptions:options.polygonoptions,
  646. polylineOptions:options.polylineoptions,
  647. rectangleOptions:options.rectangleoptions
  648. });
  649. /**
  650. * set events
  651. */
  652. var events = parser.getEvents(scope, filtered);
  653. for (var eventName in events) {
  654. google.maps.event.addListener(drawingManager, eventName, events[eventName]);
  655. }
  656. mapController.addObject('mapDrawingManager', drawingManager);
  657. }
  658. }; // return
  659. }]);
  660. /**
  661. * @ngdoc directive
  662. * @name dynamic-maps-engine-layer
  663. * @description
  664. * Requires: map directive
  665. * Restrict To: Element
  666. *
  667. * @example
  668. * Example:
  669. * <map zoom="14" center="[59.322506, 18.010025]">
  670. * <dynamic-maps-engine-layer layer-id="06673056454046135537-08896501997766553811"></dynamic-maps-engine-layer>
  671. * </map>
  672. */
  673. /*jshint -W089*/
  674. ngMap.directive('dynamicMapsEngineLayer', ['Attr2Options', function(Attr2Options) {
  675. var parser = Attr2Options;
  676. var getDynamicMapsEngineLayer = function(options, events) {
  677. var layer = new google.maps.visualization.DynamicMapsEngineLayer(options);
  678. for (var eventName in events) {
  679. google.maps.event.addListener(layer, eventName, events[eventName]);
  680. }
  681. return layer;
  682. };
  683. return {
  684. restrict: 'E',
  685. require: '^map',
  686. link: function(scope, element, attrs, mapController) {
  687. var filtered = parser.filter(attrs);
  688. var options = parser.getOptions(filtered);
  689. var events = parser.getEvents(scope, filtered, events);
  690. console.log('dynamic-maps-engine-layer options', options, 'events', events);
  691. var layer = getDynamicMapsEngineLayer(options, events);
  692. mapController.addObject('mapsEngineLayers', layer);
  693. }
  694. }; // return
  695. }]);
  696. /**
  697. * @ngdoc directive
  698. * @name fusion-tables-layer
  699. * @description
  700. * Requires: map directive
  701. * Restrict To: Element
  702. *
  703. * @example
  704. * Example:
  705. * <map zoom="11" center="41.850033, -87.6500523">
  706. * <fusion-tables-layer query="{
  707. * select: 'Geocodable address',
  708. * from: '1mZ53Z70NsChnBMm-qEYmSDOvLXgrreLTkQUvvg'}">
  709. * </fusion-tables-layer>
  710. * </map>
  711. */
  712. /*jshint -W089*/
  713. ngMap.directive('fusionTablesLayer', ['Attr2Options', function(Attr2Options) {
  714. var parser = Attr2Options;
  715. var getLayer = function(options, events) {
  716. var layer = new google.maps.FusionTablesLayer(options);
  717. for (var eventName in events) {
  718. google.maps.event.addListener(layer, eventName, events[eventName]);
  719. }
  720. return layer;
  721. };
  722. return {
  723. restrict: 'E',
  724. require: '^map',
  725. link: function(scope, element, attrs, mapController) {
  726. var filtered = parser.filter(attrs);
  727. var options = parser.getOptions(filtered);
  728. var events = parser.getEvents(scope, filtered, events);
  729. console.log('fusion-tables-layer options', options, 'events', events);
  730. var layer = getLayer(options, events);
  731. mapController.addObject('fusionTablesLayers', layer);
  732. }
  733. }; // return
  734. }]);
  735. /**
  736. * @ngdoc directive
  737. * @name heatmap-layer
  738. * @requires Attr2Options
  739. * @description
  740. * Requires: map directive
  741. * Restrict To: Element
  742. *
  743. * @example
  744. * Example:
  745. *
  746. * <map zoom="11" center="[41.875696,-87.624207]">
  747. * <heatmap-layer data="taxiData"></heatmap-layer>
  748. * </map>
  749. */
  750. /*jshint -W089*/
  751. ngMap.directive('heatmapLayer', ['Attr2Options', '$window', function(Attr2Options, $window) {
  752. var parser = Attr2Options;
  753. return {
  754. restrict: 'E',
  755. require: '^map',
  756. link: function(scope, element, attrs, mapController) {
  757. var filtered = parser.filter(attrs);
  758. /**
  759. * set options
  760. */
  761. var options = parser.getOptions(filtered);
  762. options.data = $window[attrs.data] || scope[attrs.data];
  763. if (options.data instanceof Array) {
  764. options.data = new google.maps.MVCArray(options.data);
  765. } else {
  766. throw "invalid heatmap data";
  767. }
  768. var layer = new google.maps.visualization.HeatmapLayer(options);
  769. /**
  770. * set events
  771. */
  772. var events = parser.getEvents(scope, filtered);
  773. console.log('heatmap-layer options', layer, 'events', events);
  774. mapController.addObject('heatmapLayers', layer);
  775. }
  776. }; // return
  777. }]);
  778. /*jshint -W030*/
  779. /**
  780. * @ngdoc directive
  781. * @name info-window
  782. * @requires Attr2Options
  783. * @requires $compile
  784. * @description
  785. * Defines infoWindow and provides compile method
  786. *
  787. * Requires: map directive
  788. *
  789. * Restrict To: Element
  790. *
  791. * @param {Boolean} visible Indicates to show it when map is initialized
  792. * @param {Boolean} visible-on-marker Indicates to show it on a marker when map is initialized
  793. * @param {String} &lt;InfoWindowOption> Any InfoWindow options,
  794. * https://developers.google.com/maps/documentation/javascript/reference?csw=1#InfoWindowOptions
  795. * @param {String} &lt;InfoWindowEvent> Any InfoWindow events, https://developers.google.com/maps/documentation/javascript/reference
  796. * @example
  797. * Usage:
  798. * <map MAP_ATTRIBUTES>
  799. * <info-window id="foo" ANY_OPTIONS ANY_EVENTS"></info-window>
  800. * </map>
  801. *
  802. * Example:
  803. * <map center="41.850033,-87.6500523" zoom="3">
  804. * <info-window id="1" position="41.850033,-87.6500523" >
  805. * <div ng-non-bindable>
  806. * Chicago, IL<br/>
  807. * LatLng: {{chicago.lat()}}, {{chicago.lng()}}, <br/>
  808. * World Coordinate: {{worldCoordinate.x}}, {{worldCoordinate.y}}, <br/>
  809. * Pixel Coordinate: {{pixelCoordinate.x}}, {{pixelCoordinate.y}}, <br/>
  810. * Tile Coordinate: {{tileCoordinate.x}}, {{tileCoordinate.y}} at Zoom Level {{map.getZoom()}}
  811. * </div>
  812. * </info-window>
  813. * </map>
  814. */
  815. ngMap.directive('infoWindow', ['Attr2Options', '$compile', '$timeout', function(Attr2Options, $compile, $timeout) {
  816. var parser = Attr2Options;
  817. var getInfoWindow = function(options, events, element) {
  818. var infoWindow;
  819. /**
  820. * set options
  821. */
  822. if (options.position &&
  823. !(options.position instanceof google.maps.LatLng)) {
  824. var address = options.position;
  825. delete options.position;
  826. infoWindow = new google.maps.InfoWindow(options);
  827. var callback = function() {
  828. infoWindow.open(infoWindow.map);
  829. }
  830. parser.setDelayedGeoLocation(infoWindow, 'setPosition', address, {callback: callback});
  831. } else {
  832. infoWindow = new google.maps.InfoWindow(options);
  833. }
  834. /**
  835. * set events
  836. */
  837. if (Object.keys(events).length > 0) {
  838. console.log("infoWindow events", events);
  839. }
  840. for (var eventName in events) {
  841. if (eventName) {
  842. google.maps.event.addListener(infoWindow, eventName, events[eventName]);
  843. }
  844. }
  845. /**
  846. * set template ane template-relate functions
  847. * it must have a container element with ng-non-bindable
  848. */
  849. var template = element.html().trim();
  850. if (angular.element(template).length != 1) {
  851. throw "info-window working as a template must have a container";
  852. }
  853. infoWindow.__template = template.replace(/\s?ng-non-bindable[='"]+/,"");
  854. infoWindow.__compile = function(scope) {
  855. var el = $compile(infoWindow.__template)(scope);
  856. scope.$apply();
  857. infoWindow.setContent(el[0]);
  858. };
  859. infoWindow.__eval = function(event) {
  860. var template = infoWindow.__template;
  861. var _this = this;
  862. template = template.replace(/{{(event|this)[^;\}]+}}/g, function(match) {
  863. var expression = match.replace(/[{}]/g, "").replace("this.", "_this.");
  864. return eval(expression);
  865. });
  866. return template;
  867. };
  868. return infoWindow;
  869. };
  870. return {
  871. restrict: 'E',
  872. require: '^map',
  873. link: function(scope, element, attrs, mapController) {
  874. element.css('display','none');
  875. var orgAttrs = parser.orgAttributes(element);
  876. var filtered = parser.filter(attrs);
  877. var options = parser.getOptions(filtered, scope);
  878. var events = parser.getEvents(scope, filtered);
  879. console.log('infoWindow', 'options', options, 'events', events);
  880. var infoWindow = getInfoWindow(options, events, element);
  881. mapController.addObject('infoWindows', infoWindow);
  882. parser.observeAttrSetObj(orgAttrs, attrs, infoWindow); /* observers */
  883. // show InfoWindow when initialized
  884. if (infoWindow.visible) {
  885. //if (!infoWindow.position) { throw "Invalid position"; }
  886. scope.$on('mapInitialized', function(evt, map) {
  887. $timeout(function() {
  888. infoWindow.__template = infoWindow.__eval.apply(this, [evt]);
  889. infoWindow.__compile(scope);
  890. infoWindow.map = map;
  891. infoWindow.position && infoWindow.open(map);
  892. });
  893. });
  894. }
  895. // show InfoWindow on a marker when initialized
  896. if (infoWindow.visibleOnMarker) {
  897. scope.$on('mapInitialized', function(evt, map) {
  898. $timeout(function() {
  899. var markerId = infoWindow.visibleOnMarker;
  900. var marker = map.markers[markerId];
  901. if (!marker) throw "Invalid marker id";
  902. infoWindow.__template = infoWindow.__eval.apply(this, [evt]);
  903. infoWindow.__compile(scope);
  904. infoWindow.open(map, marker);
  905. });
  906. });
  907. }
  908. /**
  909. * provide showInfoWindow method to scope
  910. */
  911. scope.showInfoWindow = scope.showInfoWindow ||
  912. function(event, id, anchor) {
  913. var infoWindow = mapController.map.infoWindows[id],
  914. tempTemplate = infoWindow.__template; // set template in a temporary variable
  915. infoWindow.__template = infoWindow.__eval.apply(this, [event]);
  916. infoWindow.__compile(scope);
  917. if (anchor) {
  918. infoWindow.open(mapController.map, anchor);
  919. } else if (this.getPosition) {
  920. infoWindow.open(mapController.map, this);
  921. } else {
  922. infoWindow.open(mapController.map);
  923. }
  924. infoWindow.__template = tempTemplate; // reset template to the object
  925. };
  926. /**
  927. * provide hideInfoWindow method to scope
  928. */
  929. scope.hideInfoWindow = scope.hideInfoWindow ||
  930. function(event, id, anchor) {
  931. var infoWindow = mapController.map.infoWindows[id];
  932. infoWindow.__template = infoWindow.__eval.apply(this, [event]);
  933. infoWindow.__compile(scope);
  934. infoWindow.close();
  935. };
  936. } //link
  937. }; // return
  938. }]);// function
  939. /**
  940. * @ngdoc directive
  941. * @name kml-layer
  942. * @requires Attr2Options
  943. * @description
  944. * renders Kml layer on a map
  945. * Requires: map directive
  946. * Restrict To: Element
  947. *
  948. * @param {Url} url url of the kml layer
  949. * @param {KmlLayerOptions} KmlLayerOptions
  950. * (https://developers.google.com/maps/documentation/javascript/reference#KmlLayerOptions)
  951. * @param {String} &lt;KmlLayerEvent> Any KmlLayer events, https://developers.google.com/maps/documentation/javascript/reference
  952. * @example
  953. * Usage:
  954. * <map MAP_ATTRIBUTES>
  955. * <kml-layer ANY_KML_LAYER ANY_KML_LAYER_EVENTS"></kml-layer>
  956. * </map>
  957. *
  958. * Example:
  959. *
  960. * <map zoom="11" center="[41.875696,-87.624207]">
  961. * <kml-layer url="http://gmaps-samples.googlecode.com/svn/trunk/ggeoxml/cta.kml" ></kml-layer>
  962. * </map>
  963. */
  964. /*jshint -W089*/
  965. ngMap.directive('kmlLayer', ['Attr2Options', function(Attr2Options) {
  966. var parser = Attr2Options;
  967. var getKmlLayer = function(options, events) {
  968. var kmlLayer = new google.maps.KmlLayer(options);
  969. for (var eventName in events) {
  970. google.maps.event.addListener(kmlLayer, eventName, events[eventName]);
  971. }
  972. return kmlLayer;
  973. };
  974. return {
  975. restrict: 'E',
  976. require: '^map',
  977. link: function(scope, element, attrs, mapController) {
  978. var orgAttrs = parser.orgAttributes(element);
  979. var filtered = parser.filter(attrs);
  980. var options = parser.getOptions(filtered);
  981. var events = parser.getEvents(scope, filtered);
  982. console.log('kml-layer options', kmlLayer, 'events', events);
  983. var kmlLayer = getKmlLayer(options, events);
  984. mapController.addObject('kmlLayers', kmlLayer);
  985. parser.observeAttrSetObj(orgAttrs, attrs, kmlLayer); //observers
  986. }
  987. }; // return
  988. }]);
  989. /**
  990. * @ngdoc directive
  991. * @name map-data
  992. * @description
  993. * set map data
  994. * Requires: map directive
  995. * Restrict To: Element
  996. *
  997. * @param {String} method-name, run map.data[method-name] with attribute value
  998. * @example
  999. * Example:
  1000. *
  1001. * <map zoom="11" center="[41.875696,-87.624207]">
  1002. * <map-data load-geo-json="https://storage.googleapis.com/maps-devrel/google.json"></map-data>
  1003. * </map>
  1004. */
  1005. ngMap.directive('mapData', ['Attr2Options', function(Attr2Options) {
  1006. var parser = Attr2Options;
  1007. return {
  1008. restrict: 'E',
  1009. require: '^map',
  1010. link: function(scope, element, attrs, mapController) {
  1011. var filtered = parser.filter(attrs);
  1012. var options = parser.getOptions(filtered);
  1013. var events = parser.getEvents(scope, filtered, events);
  1014. console.log('map-data options', options);
  1015. scope.$on('mapInitialized', function(event, map) {
  1016. /**
  1017. * options
  1018. */
  1019. for (var key in options) {
  1020. if (key) {
  1021. var val = options[key];
  1022. if (typeof scope[val] === "function") {
  1023. map.data[key](scope[val]);
  1024. } else {
  1025. map.data[key](val);
  1026. }
  1027. } // if (key)
  1028. }
  1029. /**
  1030. * events
  1031. */
  1032. for (var eventName in events) {
  1033. if (events[eventName]) {
  1034. map.data.addListener(eventName, events[eventName]);
  1035. }
  1036. }
  1037. });
  1038. }
  1039. }; // return
  1040. }]);
  1041. /**
  1042. * @ngdoc directive
  1043. * @name lazy-load
  1044. * @requires Attr2Options
  1045. * @description
  1046. * Requires: Delay the initialization of directive until required .js loads
  1047. * Restrict To: Attribute
  1048. *
  1049. * @param {String} lazy-load
  1050. script source file location
  1051. * example:
  1052. * 'http://maps.googlecom/maps/api/js'
  1053. * @example
  1054. * Example:
  1055. *
  1056. * <div lazy-load="http://maps.google.com/maps/api/js">
  1057. * <map center="Brampton" zoom="10">
  1058. * <marker position="Brampton"></marker>
  1059. * </map>
  1060. * </div>
  1061. */
  1062. /*jshint -W089*/
  1063. ngMap.directive('mapLazyLoad', ['$compile', '$timeout', function($compile, $timeout) {
  1064. 'use strict';
  1065. var directiveDefinitionObject = {
  1066. compile: function(tElement, tAttrs) {
  1067. (!tAttrs.mapLazyLoad) && console.error('requires src with map-lazy-load');
  1068. var savedHtml = tElement.html(), src = tAttrs.mapLazyLoad;
  1069. /**
  1070. * if already loaded, stop processing it
  1071. */
  1072. if (document.querySelector('script[src="'+src+'"]')) {
  1073. return false;
  1074. }
  1075. tElement.html(''); // will compile again after script is loaded
  1076. return {
  1077. pre: function(scope, element, attrs) {
  1078. window.lazyLoadCallback = function() {
  1079. console.log('script loaded,' + src);
  1080. $timeout(function() { /* give some time to load */
  1081. element.html(savedHtml);
  1082. $compile(element.contents())(scope);
  1083. }, 100);
  1084. };
  1085. var scriptEl = document.createElement('script');
  1086. scriptEl.src = src + '?callback=lazyLoadCallback';
  1087. document.body.appendChild(scriptEl);
  1088. }
  1089. };
  1090. }
  1091. };
  1092. return directiveDefinitionObject;
  1093. }]);
  1094. /**
  1095. * @ngdoc directive
  1096. * @name map-type
  1097. * @requires Attr2Options
  1098. * @description
  1099. * Requires: map directive
  1100. * Restrict To: Element
  1101. *
  1102. * @example
  1103. * Example:
  1104. *
  1105. * <map zoom="13" center="34.04924594193164, -118.24104309082031">
  1106. * <map-type name="coordinate" object="coordinateMapType"></map-type>
  1107. * </map>
  1108. */
  1109. /*jshint -W089*/
  1110. ngMap.directive('mapType', ['Attr2Options', '$window', function(Attr2Options, $window) {
  1111. var parser = Attr2Options;
  1112. return {
  1113. restrict: 'E',
  1114. require: '^map',
  1115. link: function(scope, element, attrs, mapController) {
  1116. var mapTypeName = attrs.name, mapTypeObject;
  1117. if (!mapTypeName) {
  1118. throw "invalid map-type name";
  1119. }
  1120. if (attrs.object) {
  1121. var __scope = scope[attrs.object] ? scope : $window;
  1122. mapTypeObject = __scope[attrs.object];
  1123. if (typeof mapTypeObject == "function") {
  1124. mapTypeObject = new mapTypeObject();
  1125. }
  1126. }
  1127. if (!mapTypeObject) {
  1128. throw "invalid map-type object";
  1129. }
  1130. scope.$on('mapInitialized', function(evt, map) {
  1131. map.mapTypes.set(mapTypeName, mapTypeObject);
  1132. });
  1133. mapController.addObject('mapTypes', mapTypeObject);
  1134. }
  1135. }; // return
  1136. }]);
  1137. /**
  1138. * @ngdoc directive
  1139. * @name map
  1140. * @requires Attr2Options
  1141. * @description
  1142. * Implementation of {@link MapController}
  1143. * Initialize a Google map within a `<div>` tag with given options and register events
  1144. * It accepts children directives; marker, shape, or marker-clusterer
  1145. *
  1146. * It initialize map, children tags, then emits message as soon as the action is done
  1147. * The message emitted from this directive is;
  1148. * . mapInitialized
  1149. *
  1150. * Restrict To:
  1151. * Element
  1152. *
  1153. * @param {Array} geo-fallback-center
  1154. * The center of map incase geolocation failed. i.e. [0,0]
  1155. * @param {String} init-event The name of event to initialize this map.
  1156. * If this option is given, the map won't be initialized until the event is received.
  1157. * To invoke the event, use $scope.$emit or $scope.$broacast.
  1158. * i.e. <map init-event="init-map" ng-click="$emit('init-map')" center=... ></map>
  1159. * @param {String} &lt;MapOption> Any Google map options,
  1160. * https://developers.google.com/maps/documentation/javascript/reference?csw=1#MapOptions
  1161. * @param {String} &lt;MapEvent> Any Google map events,
  1162. * https://rawgit.com/allenhwkim/angularjs-google-maps/master/build/map_events.html
  1163. * @example
  1164. * Usage:
  1165. * <map MAP_OPTIONS_OR_MAP_EVENTS ..>
  1166. * ... Any children directives
  1167. * </map>
  1168. *
  1169. * Example:
  1170. * <map center="[40.74, -74.18]" on-click="doThat()">
  1171. * </map>
  1172. *
  1173. * <map geo-fallback-center="[40.74, -74.18]">
  1174. * </map>
  1175. */
  1176. /*jshint -W030*/
  1177. ngMap.directive('map', ['Attr2Options', '$timeout', function(Attr2Options, $timeout) {
  1178. var parser = Attr2Options;
  1179. function getStyle(el,styleProp)
  1180. {
  1181. if (el.currentStyle) {
  1182. var y = el.currentStyle[styleProp];
  1183. } else if (window.getComputedStyle) {
  1184. var y = document.defaultView.getComputedStyle(el,null).getPropertyValue(styleProp);
  1185. }
  1186. return y;
  1187. }
  1188. return {
  1189. restrict: 'AE',
  1190. controller: ngMap.MapController,
  1191. /**
  1192. * Initialize map and events
  1193. * @memberof map
  1194. * @param {$scope} scope
  1195. * @param {angular.element} element
  1196. * @param {Hash} attrs
  1197. * @ctrl {MapController} ctrl
  1198. */
  1199. link: function (scope, element, attrs, ctrl) {
  1200. var orgAttrs = parser.orgAttributes(element);
  1201. scope.google = google; //used by $scope.eval in Attr2Options to avoid eval()
  1202. /**
  1203. * create a new `div` inside map tag, so that it does not touch map element
  1204. * http://stackoverflow.com/questions/20955356
  1205. */
  1206. var el = document.createElement("div");
  1207. el.style.width = "100%";
  1208. el.style.height = "100%";
  1209. element.prepend(el);
  1210. /**
  1211. * if style is not given to the map element, set display and height
  1212. */
  1213. if (getStyle(element[0], 'display') != "block") {
  1214. element.css('display','block');
  1215. }
  1216. if (getStyle(element[0], 'height').match(/^(0|auto)/)) {
  1217. element.css('height','300px');
  1218. }
  1219. /**
  1220. * initialize function
  1221. */
  1222. var initializeMap = function(mapOptions, mapEvents) {
  1223. var map = new google.maps.Map(el, {});
  1224. map.markers = {};
  1225. map.shapes = {};
  1226. /**
  1227. * resize the map to prevent showing partially, in case intialized too early
  1228. */
  1229. $timeout(function() {
  1230. google.maps.event.trigger(map, "resize");
  1231. });
  1232. /**
  1233. * set options
  1234. */
  1235. mapOptions.zoom = mapOptions.zoom || 15;
  1236. var center = mapOptions.center;
  1237. if (!center) {
  1238. mapOptions.center = new google.maps.LatLng(0,0);
  1239. } else if (!(center instanceof google.maps.LatLng)) {
  1240. delete mapOptions.center;
  1241. Attr2Options.setDelayedGeoLocation(map, 'setCenter',
  1242. center, {fallbackLocation: options.geoFallbackCenter});
  1243. }
  1244. map.setOptions(mapOptions);
  1245. /**
  1246. * set events
  1247. */
  1248. for (var eventName in mapEvents) {
  1249. if (eventName) {
  1250. google.maps.event.addListener(map, eventName, mapEvents[eventName]);
  1251. }
  1252. }
  1253. /**
  1254. * set observers
  1255. */
  1256. parser.observeAttrSetObj(orgAttrs, attrs, map);
  1257. /**
  1258. * set controller and set objects
  1259. * so that map can be used by other directives; marker or shape
  1260. * ctrl._objects are gathered when marker and shape are initialized before map is set
  1261. */
  1262. ctrl.map = map; /* so that map can be used by other directives; marker or shape */
  1263. ctrl.addObjects(ctrl._objects);
  1264. // /* providing method to add a marker used by user scope */
  1265. // map.addMarker = ctrl.addMarker;
  1266. /**
  1267. * set map for scope and controller and broadcast map event
  1268. * scope.map will be overwritten if user have multiple maps in a scope,
  1269. * thus the last map will be set as scope.map.
  1270. * however an `mapInitialized` event will be emitted every time.
  1271. */
  1272. scope.map = map;
  1273. scope.map.scope = scope;
  1274. //google.maps.event.addListenerOnce(map, "idle", function() {
  1275. scope.$emit('mapInitialized', map);
  1276. //});
  1277. // the following lines will be deprecated on behalf of mapInitialized
  1278. // to collect maps, we should use scope.maps in your own controller, i.e. MyCtrl
  1279. scope.maps = scope.maps || {};
  1280. scope.maps[options.id||Object.keys(scope.maps).length] = map;
  1281. scope.$emit('mapsInitialized', scope.maps);
  1282. }; // function initializeMap()
  1283. /**
  1284. * get map options and events
  1285. */
  1286. var filtered = parser.filter(attrs);
  1287. var options = parser.getOptions(filtered, scope);
  1288. var controlOptions = parser.getControlOptions(filtered);
  1289. var mapOptions = angular.extend(options, controlOptions);
  1290. var mapEvents = parser.getEvents(scope, filtered);
  1291. console.log("filtered", filtered, "mapOptions", mapOptions, 'mapEvents', mapEvents);
  1292. if (attrs.initEvent) { // allows controlled initialization
  1293. scope.$on(attrs.initEvent, function() {
  1294. !ctrl.map && initializeMap(mapOptions, mapEvents); // init if not done
  1295. });
  1296. } else {
  1297. initializeMap(mapOptions, mapEvents);
  1298. } // if
  1299. }
  1300. };
  1301. }]);
  1302. /**
  1303. * @ngdoc directive
  1304. * @name MapController
  1305. * @requires $scope
  1306. * @property {Hash} controls collection of Controls initiated within `map` directive
  1307. * @property {Hash} markersi collection of Markers initiated within `map` directive
  1308. * @property {Hash} shapes collection of shapes initiated within `map` directive
  1309. * @property {MarkerClusterer} markerClusterer MarkerClusterer initiated within `map` directive
  1310. */
  1311. /*jshint -W089*/
  1312. ngMap.MapController = function() {
  1313. this.map = null;
  1314. this._objects = [];
  1315. /**
  1316. * Add a marker to map and $scope.markers
  1317. * @memberof MapController
  1318. * @name addMarker
  1319. * @param {Marker} marker google map marker
  1320. */
  1321. this.addMarker = function(marker) {
  1322. /**
  1323. * marker and shape are initialized before map is initialized
  1324. * so, collect _objects then will init. those when map is initialized
  1325. * However the case as in ng-repeat, we can directly add to map
  1326. */
  1327. if (this.map) {
  1328. this.map.markers = this.map.markers || {};
  1329. marker.setMap(this.map);
  1330. if (marker.centered) {
  1331. this.map.setCenter(marker.position);
  1332. }
  1333. var len = Object.keys(this.map.markers).length;
  1334. this.map.markers[marker.id || len] = marker;
  1335. } else {
  1336. this._objects.push(marker);
  1337. }
  1338. };
  1339. /**
  1340. * Add a shape to map and $scope.shapes
  1341. * @memberof MapController
  1342. * @name addShape
  1343. * @param {Shape} shape google map shape
  1344. */
  1345. this.addShape = function(shape) {
  1346. if (this.map) {
  1347. this.map.shapes = this.map.shapes || {};
  1348. shape.setMap(this.map);
  1349. var len = Object.keys(this.map.shapes).length;
  1350. this.map.shapes[shape.id || len] = shape;
  1351. } else {
  1352. this._objects.push(shape);
  1353. }
  1354. };
  1355. this.addObject = function(groupName, obj) {
  1356. if (this.map) {
  1357. this.map[groupName] = this.map[groupName] || {};
  1358. var len = Object.keys(this.map[groupName]).length;
  1359. this.map[groupName][obj.id || len] = obj;
  1360. if (groupName != "infoWindows" && obj.setMap) { //infoWindow.setMap works like infoWindow.open
  1361. obj.setMap(this.map);
  1362. }
  1363. } else {
  1364. obj.groupName = groupName;
  1365. this._objects.push(obj);
  1366. }
  1367. }
  1368. /**
  1369. * Add a shape to map and $scope.shapes
  1370. * @memberof MapController
  1371. * @name addShape
  1372. * @param {Shape} shape google map shape
  1373. */
  1374. this.addObjects = function(objects) {
  1375. for (var i=0; i<objects.length; i++) {
  1376. var obj=objects[i];
  1377. if (obj instanceof google.maps.Marker) {
  1378. this.addMarker(obj);
  1379. } else if (obj instanceof google.maps.Circle ||
  1380. obj instanceof google.maps.Polygon ||
  1381. obj instanceof google.maps.Polyline ||
  1382. obj instanceof google.maps.Rectangle ||
  1383. obj instanceof google.maps.GroundOverlay) {
  1384. this.addShape(obj);
  1385. } else {
  1386. this.addObject(obj.groupName, obj);
  1387. }
  1388. }
  1389. };
  1390. };
  1391. /**
  1392. * @ngdoc directive
  1393. * @name maps-engine-layer
  1394. * @description
  1395. * Requires: map directive
  1396. * Restrict To: Element
  1397. *
  1398. * @example
  1399. * Example:
  1400. * <map zoom="14" center="[59.322506, 18.010025]">
  1401. * <maps-engine-layer layer-id="06673056454046135537-08896501997766553811"></maps-engine-layer>
  1402. * </map>
  1403. */
  1404. /*jshint -W089*/
  1405. ngMap.directive('mapsEngineLayer', ['Attr2Options', function(Attr2Options) {
  1406. var parser = Attr2Options;
  1407. var getMapsEngineLayer = function(options, events) {
  1408. var layer = new google.maps.visualization.MapsEngineLayer(options);
  1409. for (var eventName in events) {
  1410. google.maps.event.addListener(layer, eventName, events[eventName]);
  1411. }
  1412. return layer;
  1413. };
  1414. return {
  1415. restrict: 'E',
  1416. require: '^map',
  1417. link: function(scope, element, attrs, mapController) {
  1418. var filtered = parser.filter(attrs);
  1419. var options = parser.getOptions(filtered);
  1420. var events = parser.getEvents(scope, filtered, events);
  1421. console.log('maps-engine-layer options', options, 'events', events);
  1422. var layer = getMapsEngineLayer(options, events);
  1423. mapController.addObject('mapsEngineLayers', layer);
  1424. }
  1425. }; // return
  1426. }]);
  1427. /**
  1428. * @ngdoc directive
  1429. * @name marker
  1430. * @requires Attr2Options
  1431. * @requires NavigatorGeolocation
  1432. * @description
  1433. * Draw a Google map marker on a map with given options and register events
  1434. *
  1435. * Requires: map directive
  1436. *
  1437. * Restrict To: Element
  1438. *
  1439. * @param {String} position address, 'current', or [latitude, longitude]
  1440. * example:
  1441. * '1600 Pennsylvania Ave, 20500 Washingtion DC',
  1442. * 'current position',
  1443. * '[40.74, -74.18]'
  1444. * @param {Boolean} centered if set, map will be centered with this marker
  1445. * @param {String} &lt;MarkerOption> Any Marker options, https://developers.google.com/maps/documentation/javascript/reference?csw=1#MarkerOptions
  1446. * @param {String} &lt;MapEvent> Any Marker events, https://developers.google.com/maps/documentation/javascript/reference
  1447. * @example
  1448. * Usage:
  1449. * <map MAP_ATTRIBUTES>
  1450. * <marker ANY_MARKER_OPTIONS ANY_MARKER_EVENTS"></MARKER>
  1451. * </map>
  1452. *
  1453. * Example:
  1454. * <map center="[40.74, -74.18]">
  1455. * <marker position="[40.74, -74.18]" on-click="myfunc()"></div>
  1456. * </map>
  1457. *
  1458. * <map center="the cn tower">
  1459. * <marker position="the cn tower" on-click="myfunc()"></div>
  1460. * </map>
  1461. */
  1462. ngMap.directive('marker', ['Attr2Options', function(Attr2Options) {
  1463. var parser = Attr2Options;
  1464. var getMarker = function(options, events) {
  1465. var marker;
  1466. /**
  1467. * set options
  1468. */
  1469. if (options.icon instanceof Object) {
  1470. if ((""+options.icon.path).match(/^[A-Z_]+$/)) {
  1471. options.icon.path = google.maps.SymbolPath[options.icon.path];
  1472. }
  1473. for (var key in options.icon) {
  1474. var arr = options.icon[key];
  1475. if (key == "anchor" || key == "origin") {
  1476. options.icon[key] = new google.maps.Point(arr[0], arr[1]);
  1477. } else if (key == "size" || key == "scaledSize") {
  1478. options.icon[key] = new google.maps.Size(arr[0], arr[1]);
  1479. }
  1480. }
  1481. }
  1482. if (!(options.position instanceof google.maps.LatLng)) {
  1483. var orgPosition = options.position;
  1484. options.position = new google.maps.LatLng(0,0);
  1485. marker = new google.maps.Marker(options);
  1486. parser.setDelayedGeoLocation(marker, 'setPosition', orgPosition);
  1487. } else {
  1488. marker = new google.maps.Marker(options);
  1489. }
  1490. /**
  1491. * set events
  1492. */
  1493. if (Object.keys(events).length > 0) {
  1494. console.log("markerEvents", events);
  1495. }
  1496. for (var eventName in events) {
  1497. if (eventName) {
  1498. google.maps.event.addListener(marker, eventName, events[eventName]);
  1499. }
  1500. }
  1501. return marker;
  1502. };
  1503. return {
  1504. restrict: 'E',
  1505. require: '^map',
  1506. link: function(scope, element, attrs, mapController) {
  1507. var orgAttrs = parser.orgAttributes(element);
  1508. var filtered = parser.filter(attrs);
  1509. var markerOptions = parser.getOptions(filtered, scope);
  1510. var markerEvents = parser.getEvents(scope, filtered);
  1511. console.log('marker options', markerOptions, 'events', markerEvents);
  1512. /**
  1513. * set event to clean up removed marker
  1514. * useful with ng-repeat
  1515. */
  1516. element.bind('$destroy', function() {
  1517. var markers = marker.map.markers;
  1518. for (var name in markers) {
  1519. if (markers[name] == marker) {
  1520. delete markers[name];
  1521. }
  1522. }
  1523. marker.setMap(null);
  1524. });
  1525. var marker = getMarker(markerOptions, markerEvents);
  1526. mapController.addMarker(marker);
  1527. /**
  1528. * set observers
  1529. */
  1530. parser.observeAttrSetObj(orgAttrs, attrs, marker); /* observers */
  1531. } //link
  1532. }; // return
  1533. }]);//
  1534. /**
  1535. * @ngdoc directive
  1536. * @name overlay-map-type
  1537. * @requires Attr2Options
  1538. * @description
  1539. * Requires: map directive
  1540. * Restrict To: Element
  1541. *
  1542. * @example
  1543. * Example:
  1544. *
  1545. * <map zoom="13" center="34.04924594193164, -118.24104309082031">
  1546. * <overlay-map-type index="0" object="coordinateMapType"></map-type>
  1547. * </map>
  1548. */
  1549. /*jshint -W089*/
  1550. ngMap.directive('overlayMapType', ['Attr2Options', '$window', function(Attr2Options, $window) {
  1551. var parser = Attr2Options;
  1552. return {
  1553. restrict: 'E',
  1554. require: '^map',
  1555. link: function(scope, element, attrs, mapController) {
  1556. var overlayMapTypeObject;
  1557. var initMethod = attrs.initMethod || "insertAt";
  1558. if (attrs.object) {
  1559. var __scope = scope[attrs.object] ? scope : $window;
  1560. overlayMapTypeObject = __scope[attrs.object];
  1561. if (typeof overlayMapTypeObject == "function") {
  1562. overlayMapTypeObject = new overlayMapTypeObject();
  1563. }
  1564. }
  1565. if (!overlayMapTypeObject) {
  1566. throw "invalid map-type object";
  1567. }
  1568. scope.$on('mapInitialized', function(evt, map) {
  1569. if (initMethod == "insertAt") {
  1570. var index = parseInt(attrs.index, 10);
  1571. map.overlayMapTypes.insertAt(index, overlayMapTypeObject);
  1572. } else if (initMethod == "push") {
  1573. map.overlayMapTypes.push(overlayMapTypeObject);
  1574. }
  1575. });
  1576. mapController.addObject('overlayMapTypes', overlayMapTypeObject);
  1577. }
  1578. }; // return
  1579. }]);
  1580. /**
  1581. * @ngdoc directive
  1582. * @name shape
  1583. * @requires Attr2Options
  1584. * @description
  1585. * Initialize a Google map shape in map with given options and register events
  1586. * The shapes are:
  1587. * . circle
  1588. * . polygon
  1589. * . polyline
  1590. * . rectangle
  1591. * . groundOverlay(or image)
  1592. *
  1593. * Requires: map directive
  1594. *
  1595. * Restrict To: Element
  1596. *
  1597. * @param {Boolean} centered if set, map will be centered with this marker
  1598. * @param {String} &lt;OPTIONS>
  1599. * For circle, [any circle options](https://developers.google.com/maps/documentation/javascript/reference#CircleOptions)
  1600. * For polygon, [any polygon options](https://developers.google.com/maps/documentation/javascript/reference#PolygonOptions)
  1601. * For polyline, [any polyline options](https://developers.google.com/maps/documentation/javascript/reference#PolylineOptions)
  1602. * For rectangle, [any rectangle options](https://developers.google.com/maps/documentation/javascript/reference#RectangleOptions)
  1603. * For image, [any groundOverlay options](https://developers.google.com/maps/documentation/javascript/reference#GroundOverlayOptions)
  1604. * @param {String} &lt;MapEvent> Any Shape events, https://developers.google.com/maps/documentation/javascript/reference
  1605. * @example
  1606. * Usage:
  1607. * <map MAP_ATTRIBUTES>
  1608. * <shape name=SHAPE_NAME ANY_SHAPE_OPTIONS ANY_SHAPE_EVENTS"></MARKER>
  1609. * </map>
  1610. *
  1611. * Example:
  1612. *
  1613. * <map zoom="11" center="[40.74, -74.18]">
  1614. * <shape id="polyline" name="polyline" geodesic="true" stroke-color="#FF0000" stroke-opacity="1.0" stroke-weight="2"
  1615. * path="[[40.74,-74.18],[40.64,-74.10],[40.54,-74.05],[40.44,-74]]" ></shape>
  1616. * </map>
  1617. *
  1618. * <map zoom="11" center="[40.74, -74.18]">
  1619. * <shape id="polygon" name="polygon" stroke-color="#FF0000" stroke-opacity="1.0" stroke-weight="2"
  1620. * paths="[[40.74,-74.18],[40.64,-74.18],[40.84,-74.08],[40.74,-74.18]]" ></shape>
  1621. * </map>
  1622. *
  1623. * <map zoom="11" center="[40.74, -74.18]">
  1624. * <shape id="rectangle" name="rectangle" stroke-color='#FF0000' stroke-opacity="0.8" stroke-weight="2"
  1625. * bounds="[[40.74,-74.18], [40.78,-74.14]]" editable="true" ></shape>
  1626. * </map>
  1627. *
  1628. * <map zoom="11" center="[40.74, -74.18]">
  1629. * <shape id="circle" name="circle" stroke-color='#FF0000' stroke-opacity="0.8"stroke-weight="2"
  1630. * center="[40.70,-74.14]" radius="4000" editable="true" ></shape>
  1631. * </map>
  1632. *
  1633. * <map zoom="11" center="[40.74, -74.18]">
  1634. * <shape id="image" name="image" url="https://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg"
  1635. * bounds="[[40.71,-74.22],[40.77,-74.12]]" opacity="0.7" clickable="true" ></shape>
  1636. * </map>
  1637. *
  1638. * For full-working example, please visit
  1639. * [shape example](https://rawgit.com/allenhwkim/angularjs-google-maps/master/build/shape.html)
  1640. */
  1641. ngMap.directive('shape', ['Attr2Options', function(Attr2Options) {
  1642. var parser = Attr2Options;
  1643. var getBounds = function(points) {
  1644. return new google.maps.LatLngBounds(points[0], points[1]);
  1645. };
  1646. var getShape = function(options, events) {
  1647. var shape;
  1648. var shapeName = options.name;
  1649. delete options.name; //remove name bcoz it's not for options
  1650. console.log("shape", shapeName, "options", options, 'events', events);
  1651. /**
  1652. * set options
  1653. */
  1654. if (options.icons) {
  1655. for (var i=0; i<options.icons.length; i++) {
  1656. var el = options.icons[i];
  1657. if (el.icon.path.match(/^[A-Z_]+$/)) {
  1658. el.icon.path = google.maps.SymbolPath[el.icon.path];
  1659. }
  1660. }
  1661. }
  1662. switch(shapeName) {
  1663. case "circle":
  1664. if (options.center instanceof google.maps.LatLng) {
  1665. shape = new google.maps.Circle(options);
  1666. } else {
  1667. var orgCenter = options.center;
  1668. options.center = new google.maps.LatLng(0,0);
  1669. shape = new google.maps.Circle(options);
  1670. parser.setDelayedGeoLocation(shape, 'setCenter', orgCenter);
  1671. }
  1672. break;
  1673. case "polygon":
  1674. shape = new google.maps.Polygon(options);
  1675. break;
  1676. case "polyline":
  1677. shape = new google.maps.Polyline(options);
  1678. break;
  1679. case "rectangle":
  1680. if (options.bounds) {
  1681. options.bounds = getBounds(options.bounds);
  1682. }
  1683. shape = new google.maps.Rectangle(options);
  1684. break;
  1685. case "groundOverlay":
  1686. case "image":
  1687. var url = options.url;
  1688. var bounds = getBounds(options.bounds);
  1689. var opts = {opacity: options.opacity, clickable: options.clickable, id:options.id};
  1690. shape = new google.maps.GroundOverlay(url, bounds, opts);
  1691. break;
  1692. }
  1693. /**
  1694. * set events
  1695. */
  1696. for (var eventName in events) {
  1697. if (events[eventName]) {
  1698. google.maps.event.addListener(shape, eventName, events[eventName]);
  1699. }
  1700. }
  1701. return shape;
  1702. };
  1703. return {
  1704. restrict: 'E',
  1705. require: '^map',
  1706. /**
  1707. * link function
  1708. * @private
  1709. */
  1710. link: function(scope, element, attrs, mapController) {
  1711. var orgAttrs = parser.orgAttributes(element);
  1712. var filtered = parser.filter(attrs);
  1713. var shapeOptions = parser.getOptions(filtered);
  1714. var shapeEvents = parser.getEvents(scope, filtered);
  1715. var shape = getShape(shapeOptions, shapeEvents);
  1716. mapController.addShape(shape);
  1717. /**
  1718. * set observers
  1719. */
  1720. parser.observeAttrSetObj(orgAttrs, attrs, shape);
  1721. }
  1722. }; // return
  1723. }]);
  1724. /**
  1725. * @ngdoc directive
  1726. * @name traffic-layer
  1727. * @requires Attr2Options
  1728. * @description
  1729. * Requires: map directive
  1730. * Restrict To: Element
  1731. *
  1732. * @example
  1733. * Example:
  1734. *
  1735. * <map zoom="13" center="34.04924594193164, -118.24104309082031">
  1736. * <traffic-layer></traffic-layer>
  1737. * </map>
  1738. */
  1739. /*jshint -W089*/
  1740. ngMap.directive('trafficLayer', ['Attr2Options', function(Attr2Options) {
  1741. var parser = Attr2Options;
  1742. var getLayer = function(options, events) {
  1743. var layer = new google.maps.TrafficLayer(options);
  1744. for (var eventName in events) {
  1745. google.maps.event.addListener(layer, eventName, events[eventName]);
  1746. }
  1747. return layer;
  1748. };
  1749. return {
  1750. restrict: 'E',
  1751. require: '^map',
  1752. link: function(scope, element, attrs, mapController) {
  1753. var orgAttrs = parser.orgAttributes(element);
  1754. var filtered = parser.filter(attrs);
  1755. var options = parser.getOptions(filtered);
  1756. var events = parser.getEvents(scope, filtered);
  1757. console.log('traffic-layer options', options, 'events', events);
  1758. var layer = getLayer(options, events);
  1759. mapController.addObject('trafficLayers', layer);
  1760. parser.observeAttrSetObj(orgAttrs, attrs, layer); //observers
  1761. }
  1762. }; // return
  1763. }]);
  1764. /**
  1765. * @ngdoc directive
  1766. * @name transit-layer
  1767. * @requires Attr2Options
  1768. * @description
  1769. * Requires: map directive
  1770. * Restrict To: Element
  1771. *
  1772. * @example
  1773. * Example:
  1774. *
  1775. * <map zoom="13" center="34.04924594193164, -118.24104309082031">
  1776. * <transit-layer></transit-layer>
  1777. * </map>
  1778. */
  1779. /*jshint -W089*/
  1780. ngMap.directive('transitLayer', ['Attr2Options', function(Attr2Options) {
  1781. var parser = Attr2Options;
  1782. var getLayer = function(options, events) {
  1783. var layer = new google.maps.TransitLayer(options);
  1784. for (var eventName in events) {
  1785. google.maps.event.addListener(layer, eventName, events[eventName]);
  1786. }
  1787. return layer;
  1788. };
  1789. return {
  1790. restrict: 'E',
  1791. require: '^map',
  1792. link: function(scope, element, attrs, mapController) {
  1793. var orgAttrs = parser.orgAttributes(element);
  1794. var filtered = parser.filter(attrs);
  1795. var options = parser.getOptions(filtered);
  1796. var events = parser.getEvents(scope, filtered);
  1797. console.log('transit-layer options', options, 'events', events);
  1798. var layer = getLayer(options, events);
  1799. mapController.addObject('transitLayers', layer);
  1800. parser.observeAttrSetObj(orgAttrs, attrs, layer); //observers
  1801. }
  1802. }; // return
  1803. }]);
  1804. /**
  1805. * @ngdoc directive
  1806. * @name weather-layer
  1807. * @requires Attr2Options
  1808. * @description
  1809. * Requires: map directive
  1810. * Restrict To: Element
  1811. *
  1812. * @example
  1813. * Example:
  1814. *
  1815. * <map zoom="13" center="34.04924594193164, -118.24104309082031">
  1816. * <weather-layer></weather-layer>
  1817. * </map>
  1818. */
  1819. /*jshint -W089*/
  1820. ngMap.directive('weatherLayer', ['Attr2Options', function(Attr2Options) {
  1821. var parser = Attr2Options;
  1822. var getLayer = function(options, events) {
  1823. var layer = new google.maps.weather.WeatherLayer(options);
  1824. for (var eventName in events) {
  1825. google.maps.event.addListener(layer, eventName, events[eventName]);
  1826. }
  1827. return layer;
  1828. };
  1829. return {
  1830. restrict: 'E',
  1831. require: '^map',
  1832. link: function(scope, element, attrs, mapController) {
  1833. var orgAttrs = parser.orgAttributes(element);
  1834. var filtered = parser.filter(attrs);
  1835. var options = parser.getOptions(filtered);
  1836. var events = parser.getEvents(scope, filtered);
  1837. console.log('weather-layer options', options, 'events', events);
  1838. var layer = getLayer(options, events);
  1839. mapController.addObject('weatherLayers', layer);
  1840. parser.observeAttrSetObj(orgAttrs, attrs, layer); //observers
  1841. }
  1842. }; // return
  1843. }]);