source : custom-marker.js

  1. /**
  2. * @ngdoc directive
  3. * @memberof ngmap
  4. * @name custom-marker
  5. * @param Attr2Options {service} convert html attribute to Google map api options
  6. * @param $timeout {service} AngularJS $timeout
  7. * @description
  8. * Marker with html
  9. * Requires: map directive
  10. * Restrict To: Element
  11. *
  12. * @attr {String} position required, position on map
  13. * @attr {Number} z-index optional
  14. * @attr {Boolean} visible optional
  15. * @example
  16. *
  17. * Example:
  18. * <map center="41.850033,-87.6500523" zoom="3">
  19. * <custom-marker position="41.850033,-87.6500523">
  20. * <div>
  21. * <b>Home</b>
  22. * </div>
  23. * </custom-marker>
  24. * </map>
  25. *
  26. */
  27. /* global document */
  28. (function() {
  29. 'use strict';
  30. var parser, $timeout, $compile, NgMap;
  31. var supportedTransform = (function getSupportedTransform() {
  32. var prefixes = 'transform WebkitTransform MozTransform OTransform msTransform'.split(' ');
  33. var div = document.createElement('div');
  34. for(var i = 0; i < prefixes.length; i++) {
  35. if(div && div.style[prefixes[i]] !== undefined) {
  36. return prefixes[i];
  37. }
  38. }
  39. return false;
  40. })();
  41. var CustomMarker = function(options) {
  42. options = options || {};
  43. this.el = document.createElement('div');
  44. this.el.style.display = 'block';
  45. this.el.style.visibility = "hidden";
  46. this.visible = true;
  47. for (var key in options) { /* jshint ignore:line */
  48. this[key] = options[key];
  49. }
  50. };
  51. var setCustomMarker = function() {
  52. CustomMarker.prototype = new google.maps.OverlayView();
  53. CustomMarker.prototype.setContent = function(html, scope) {
  54. this.el.innerHTML = html;
  55. this.el.style.position = 'absolute';
  56. this.el.style.top = 0;
  57. this.el.style.left = 0;
  58. if (scope) {
  59. $compile(angular.element(this.el).contents())(scope);
  60. }
  61. };
  62. CustomMarker.prototype.getDraggable = function() {
  63. return this.draggable;
  64. };
  65. CustomMarker.prototype.setDraggable = function(draggable) {
  66. this.draggable = draggable;
  67. };
  68. CustomMarker.prototype.getPosition = function() {
  69. return this.position;
  70. };
  71. CustomMarker.prototype.setPosition = function(position) {
  72. position && (this.position = position); /* jshint ignore:line */
  73. var _this = this;
  74. if (this.getProjection() && typeof this.position.lng == 'function') {
  75. console.log(_this.getProjection());
  76. var setPosition = function() {
  77. if (!_this.getProjection()) { return; }
  78. var posPixel = _this.getProjection().fromLatLngToDivPixel(_this.position);
  79. var x = Math.round(posPixel.x - (_this.el.offsetWidth/2));
  80. var y = Math.round(posPixel.y - _this.el.offsetHeight - 10); // 10px for anchor
  81. if (supportedTransform) {
  82. _this.el.style[supportedTransform] = "translate(" + x + "px, " + y + "px)";
  83. } else {
  84. _this.el.style.left = x + "px";
  85. _this.el.style.top = y + "px";
  86. }
  87. _this.el.style.visibility = "visible";
  88. };
  89. if (_this.el.offsetWidth && _this.el.offsetHeight) {
  90. setPosition();
  91. } else {
  92. //delayed left/top calculation when width/height are not set instantly
  93. $timeout(setPosition, 300);
  94. }
  95. }
  96. };
  97. CustomMarker.prototype.setZIndex = function(zIndex) {
  98. if (zIndex === undefined) return;
  99. (this.zIndex !== zIndex) && (this.zIndex = zIndex); /* jshint ignore:line */
  100. (this.el.style.zIndex !== this.zIndex) && (this.el.style.zIndex = this.zIndex);
  101. };
  102. CustomMarker.prototype.getVisible = function() {
  103. return this.visible;
  104. };
  105. CustomMarker.prototype.setVisible = function(visible) {
  106. if (this.el.style.display === 'none' && visible)
  107. {
  108. this.el.style.display = 'block';
  109. } else if (this.el.style.display !== 'none' && !visible) {
  110. this.el.style.display = 'none';
  111. }
  112. this.visible = visible;
  113. };
  114. CustomMarker.prototype.addClass = function(className) {
  115. var classNames = this.el.className.trim().split(' ');
  116. (classNames.indexOf(className) == -1) && classNames.push(className); /* jshint ignore:line */
  117. this.el.className = classNames.join(' ');
  118. };
  119. CustomMarker.prototype.removeClass = function(className) {
  120. var classNames = this.el.className.split(' ');
  121. var index = classNames.indexOf(className);
  122. (index > -1) && classNames.splice(index, 1); /* jshint ignore:line */
  123. this.el.className = classNames.join(' ');
  124. };
  125. CustomMarker.prototype.onAdd = function() {
  126. this.getPanes().overlayMouseTarget.appendChild(this.el);
  127. };
  128. CustomMarker.prototype.draw = function() {
  129. this.setPosition();
  130. this.setZIndex(this.zIndex);
  131. this.setVisible(this.visible);
  132. };
  133. CustomMarker.prototype.onRemove = function() {
  134. this.el.parentNode.removeChild(this.el);
  135. //this.el = null;
  136. };
  137. };
  138. var linkFunc = function(orgHtml, varsToWatch) {
  139. //console.log('orgHtml', orgHtml, 'varsToWatch', varsToWatch);
  140. return function(scope, element, attrs, mapController) {
  141. mapController = mapController[0]||mapController[1];
  142. var orgAttrs = parser.orgAttributes(element);
  143. var filtered = parser.filter(attrs);
  144. var options = parser.getOptions(filtered, {scope: scope});
  145. var events = parser.getEvents(scope, filtered);
  146. /**
  147. * build a custom marker element
  148. */
  149. element[0].style.display = 'none';
  150. console.log("custom-marker options", options);
  151. var customMarker = new CustomMarker(options);
  152. // Do we really need a timeout with $scope.$apply() here?
  153. setTimeout(function() { //apply contents, class, and location after it is compiled
  154. scope.$watch('[' + varsToWatch.join(',') + ']', function(newVal, oldVal) {
  155. customMarker.setContent(orgHtml, scope);
  156. }, true);
  157. customMarker.setContent(element[0].innerHTML, scope);
  158. var classNames =
  159. (element[0].firstElementChild) && (element[0].firstElementChild.className || '');
  160. customMarker.class && (classNames += " " + customMarker.class);
  161. customMarker.addClass('custom-marker');
  162. classNames && customMarker.addClass(classNames);
  163. console.log('customMarker', customMarker, 'classNames', classNames);
  164. if (!(options.position instanceof google.maps.LatLng)) {
  165. NgMap.getGeoLocation(options.position).then(
  166. function(latlng) {
  167. customMarker.setPosition(latlng);
  168. }
  169. );
  170. }
  171. });
  172. console.log("custom-marker events", events);
  173. for (var eventName in events) { /* jshint ignore:line */
  174. google.maps.event.addDomListener(
  175. customMarker.el, eventName, events[eventName]);
  176. }
  177. mapController.addObject('customMarkers', customMarker);
  178. //set observers
  179. mapController.observeAttrSetObj(orgAttrs, attrs, customMarker);
  180. element.bind('$destroy', function() {
  181. //Is it required to remove event listeners when DOM is removed?
  182. mapController.deleteObject('customMarkers', customMarker);
  183. });
  184. }; // linkFunc
  185. };
  186. var customMarkerDirective = function(
  187. _$timeout_, _$compile_, $interpolate, Attr2MapOptions, _NgMap_, escapeRegExp
  188. ) {
  189. parser = Attr2MapOptions;
  190. $timeout = _$timeout_;
  191. $compile = _$compile_;
  192. NgMap = _NgMap_;
  193. var exprStartSymbol = $interpolate.startSymbol();
  194. var exprEndSymbol = $interpolate.endSymbol();
  195. var exprRegExp = new RegExp(escapeRegExp(exprStartSymbol) + '([^' + exprEndSymbol.substring(0, 1) + ']+)' + escapeRegExp(exprEndSymbol), 'g');
  196. return {
  197. restrict: 'E',
  198. require: ['?^map','?^ngMap'],
  199. compile: function(element) {
  200. console.log('el', element);
  201. setCustomMarker();
  202. element[0].style.display ='none';
  203. var orgHtml = element.html();
  204. var matches = orgHtml.match(exprRegExp);
  205. var varsToWatch = [];
  206. //filter out that contains '::', 'this.'
  207. (matches || []).forEach(function(match) {
  208. var toWatch = match.replace(exprStartSymbol,'').replace(exprEndSymbol,'');
  209. if (match.indexOf('::') == -1 &&
  210. match.indexOf('this.') == -1 &&
  211. varsToWatch.indexOf(toWatch) == -1) {
  212. varsToWatch.push(match.replace(exprStartSymbol,'').replace(exprEndSymbol,''));
  213. }
  214. });
  215. return linkFunc(orgHtml, varsToWatch);
  216. }
  217. }; // return
  218. };// function
  219. customMarkerDirective.$inject =
  220. ['$timeout', '$compile', '$interpolate', 'Attr2MapOptions', 'NgMap', 'escapeRegexpFilter'];
  221. angular.module('ngMap').directive('customMarker', customMarkerDirective);
  222. })();