UNPKG

ngmap

Version:
260 lines (222 loc) 8.74 kB
/** * @ngdoc directive * @memberof ngmap * @name custom-marker * @param Attr2Options {service} convert html attribute to Google map api options * @param $timeout {service} AngularJS $timeout * @description * Marker with html * Requires: map directive * Restrict To: Element * * @attr {String} position required, position on map * @attr {Number} z-index optional * @attr {Boolean} visible optional * @example * * Example: * <map center="41.850033,-87.6500523" zoom="3"> * <custom-marker position="41.850033,-87.6500523"> * <div> * <b>Home</b> * </div> * </custom-marker> * </map> * */ /* global document */ (function() { 'use strict'; var parser, $timeout, $compile, NgMap; var supportedTransform = (function getSupportedTransform() { var prefixes = 'transform WebkitTransform MozTransform OTransform msTransform'.split(' '); var div = document.createElement('div'); for(var i = 0; i < prefixes.length; i++) { if(div && div.style[prefixes[i]] !== undefined) { return prefixes[i]; } } return false; })(); var CustomMarker = function(options) { options = options || {}; this.el = document.createElement('div'); this.el.style.display = 'block'; this.el.style.visibility = "hidden"; this.visible = true; for (var key in options) { /* jshint ignore:line */ this[key] = options[key]; } }; var setCustomMarker = function() { CustomMarker.prototype = new google.maps.OverlayView(); CustomMarker.prototype.setContent = function(html, scope) { this.el.innerHTML = html; this.el.style.position = 'absolute'; this.el.style.top = 0; this.el.style.left = 0; if (scope) { $compile(angular.element(this.el).contents())(scope); } }; CustomMarker.prototype.getDraggable = function() { return this.draggable; }; CustomMarker.prototype.setDraggable = function(draggable) { this.draggable = draggable; }; CustomMarker.prototype.getPosition = function() { return this.position; }; CustomMarker.prototype.setPosition = function(position) { position && (this.position = position); /* jshint ignore:line */ var _this = this; if (this.getProjection() && typeof this.position.lng == 'function') { console.log(_this.getProjection()); var setPosition = function() { if (!_this.getProjection()) { return; } var posPixel = _this.getProjection().fromLatLngToDivPixel(_this.position); var x = Math.round(posPixel.x - (_this.el.offsetWidth/2)); var y = Math.round(posPixel.y - _this.el.offsetHeight - 10); // 10px for anchor if (supportedTransform) { _this.el.style[supportedTransform] = "translate(" + x + "px, " + y + "px)"; } else { _this.el.style.left = x + "px"; _this.el.style.top = y + "px"; } _this.el.style.visibility = "visible"; }; if (_this.el.offsetWidth && _this.el.offsetHeight) { setPosition(); } else { //delayed left/top calculation when width/height are not set instantly $timeout(setPosition, 300); } } }; CustomMarker.prototype.setZIndex = function(zIndex) { if (zIndex === undefined) return; (this.zIndex !== zIndex) && (this.zIndex = zIndex); /* jshint ignore:line */ (this.el.style.zIndex !== this.zIndex) && (this.el.style.zIndex = this.zIndex); }; CustomMarker.prototype.getVisible = function() { return this.visible; }; CustomMarker.prototype.setVisible = function(visible) { if (this.el.style.display === 'none' && visible) { this.el.style.display = 'block'; } else if (this.el.style.display !== 'none' && !visible) { this.el.style.display = 'none'; } this.visible = visible; }; CustomMarker.prototype.addClass = function(className) { var classNames = this.el.className.trim().split(' '); (classNames.indexOf(className) == -1) && classNames.push(className); /* jshint ignore:line */ this.el.className = classNames.join(' '); }; CustomMarker.prototype.removeClass = function(className) { var classNames = this.el.className.split(' '); var index = classNames.indexOf(className); (index > -1) && classNames.splice(index, 1); /* jshint ignore:line */ this.el.className = classNames.join(' '); }; CustomMarker.prototype.onAdd = function() { this.getPanes().overlayMouseTarget.appendChild(this.el); }; CustomMarker.prototype.draw = function() { this.setPosition(); this.setZIndex(this.zIndex); this.setVisible(this.visible); }; CustomMarker.prototype.onRemove = function() { this.el.parentNode.removeChild(this.el); //this.el = null; }; }; var linkFunc = function(orgHtml, varsToWatch) { //console.log('orgHtml', orgHtml, 'varsToWatch', varsToWatch); return function(scope, element, attrs, mapController) { mapController = mapController[0]||mapController[1]; var orgAttrs = parser.orgAttributes(element); var filtered = parser.filter(attrs); var options = parser.getOptions(filtered, {scope: scope}); var events = parser.getEvents(scope, filtered); /** * build a custom marker element */ element[0].style.display = 'none'; console.log("custom-marker options", options); var customMarker = new CustomMarker(options); // Do we really need a timeout with $scope.$apply() here? setTimeout(function() { //apply contents, class, and location after it is compiled scope.$watch('[' + varsToWatch.join(',') + ']', function(newVal, oldVal) { customMarker.setContent(orgHtml, scope); }, true); customMarker.setContent(element[0].innerHTML, scope); var classNames = (element[0].firstElementChild) && (element[0].firstElementChild.className || ''); customMarker.class && (classNames += " " + customMarker.class); customMarker.addClass('custom-marker'); classNames && customMarker.addClass(classNames); console.log('customMarker', customMarker, 'classNames', classNames); if (!(options.position instanceof google.maps.LatLng)) { NgMap.getGeoLocation(options.position).then( function(latlng) { customMarker.setPosition(latlng); } ); } }); console.log("custom-marker events", events); for (var eventName in events) { /* jshint ignore:line */ google.maps.event.addDomListener( customMarker.el, eventName, events[eventName]); } mapController.addObject('customMarkers', customMarker); //set observers mapController.observeAttrSetObj(orgAttrs, attrs, customMarker); element.bind('$destroy', function() { //Is it required to remove event listeners when DOM is removed? mapController.deleteObject('customMarkers', customMarker); }); }; // linkFunc }; var customMarkerDirective = function( _$timeout_, _$compile_, $interpolate, Attr2MapOptions, _NgMap_, escapeRegExp ) { parser = Attr2MapOptions; $timeout = _$timeout_; $compile = _$compile_; NgMap = _NgMap_; var exprStartSymbol = $interpolate.startSymbol(); var exprEndSymbol = $interpolate.endSymbol(); var exprRegExp = new RegExp(escapeRegExp(exprStartSymbol) + '([^' + exprEndSymbol.substring(0, 1) + ']+)' + escapeRegExp(exprEndSymbol), 'g'); return { restrict: 'E', require: ['?^map','?^ngMap'], compile: function(element) { console.log('el', element); setCustomMarker(); element[0].style.display ='none'; var orgHtml = element.html(); var matches = orgHtml.match(exprRegExp); var varsToWatch = []; //filter out that contains '::', 'this.' (matches || []).forEach(function(match) { var toWatch = match.replace(exprStartSymbol,'').replace(exprEndSymbol,''); if (match.indexOf('::') == -1 && match.indexOf('this.') == -1 && varsToWatch.indexOf(toWatch) == -1) { varsToWatch.push(match.replace(exprStartSymbol,'').replace(exprEndSymbol,'')); } }); return linkFunc(orgHtml, varsToWatch); } }; // return };// function customMarkerDirective.$inject = ['$timeout', '$compile', '$interpolate', 'Attr2MapOptions', 'NgMap', 'escapeRegexpFilter']; angular.module('ngMap').directive('customMarker', customMarkerDirective); })();