← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 20867: tracker/event-capture: google maps api is now loaded asynchronously

 

------------------------------------------------------------
revno: 20867
committer: Abyot Asalefew Gizaw <abyota@xxxxxxxxx>
branch nick: dhis2
timestamp: Fri 2015-10-23 17:42:36 +0200
message:
  tracker/event-capture: google maps api is now loaded asynchronously
added:
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.map.directive.js
modified:
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/index.html
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html
  dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/directives.js
  dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.directives.js


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/index.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/index.html	2015-09-25 14:43:20 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-event-capture/index.html	2015-10-23 15:42:36 +0000
@@ -6,14 +6,6 @@
         <meta name="description" content="DHIS 2">
         <meta name="keywords" content="DHIS 2">        
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-        
-        <script type="text/javascript">
-            window.google = null;
-        </script>
-
-        <script type="text/javascript" src="https://maps.google.com/maps/api/js?v=3.19";></script>
-        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.map.textoverlay.js"></script>
-        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.map.contextmenu.js"></script>
        
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/jquery.min.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/ui/jquery-ui.min.js"></script>
@@ -70,9 +62,10 @@
         <script type="text/javascript" src="../dhis-web-commons/javascripts/angular/plugins/angular-translate.min.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/angular/plugins/select.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.directives.js"></script>
+        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.map.directive.js"></script>
+        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.filters.js"></script>
-        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.validations.js"></script>        
-        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js"></script>
+        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.validations.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.controllers.js"></script>
         
         <script type="text/javascript" src="scripts/app.js"></script>

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html	2015-10-23 08:08:54 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/index.html	2015-10-23 15:42:36 +0000
@@ -8,18 +8,9 @@
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 
-        <script type="text/javascript">
-            window.google = null;
-        </script>
-
-        <script type="text/javascript" src="https://maps.google.com/maps/api/js?v=3.19";></script>
-        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.map.textoverlay.js"></script>
-        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.map.contextmenu.js"></script>
-    
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/jquery.min.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/ui/jquery-ui.min.js"></script>
-        <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/jquery.plugin.min.js"></script>
-        
+        <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/jquery.plugin.min.js"></script>        
         
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/calendars/jquery.calendars.min.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/jQuery/calendars/jquery.calendars.picker.min.js"></script>
@@ -82,6 +73,7 @@
         <script type="text/javascript" src="../dhis-web-commons/javascripts/angular/plugins/angular-translate.min.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/angular/plugins/ng-infinite-scroll.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.directives.js"></script>
+        <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.map.directive.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.services.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.validations.js"></script>
         <script type="text/javascript" src="../dhis-web-commons/javascripts/dhis2/dhis2.angular.filters.js"></script>

=== modified file 'dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/directives.js'
--- dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/directives.js	2015-10-23 08:08:54 +0000
+++ dhis-2/dhis-web/dhis-web-apps/src/main/webapp/dhis-web-tracker-capture/scripts/directives.js	2015-10-23 15:42:36 +0000
@@ -1,3 +1,5 @@
+/* global directive, selection, dhis2, angular */
+
 'use strict';
 
 /* Directives */
@@ -8,18 +10,18 @@
     return {
         restrict: 'A',
         link: function (scope, element, attrs) {
-                
+
             selection.load();
             $("#orgUnitTree").one("ouwtLoaded", function (event, ids, names) {
-                if( dhis2.tc && dhis2.tc.metaDataCached ){
+                if (dhis2.tc && dhis2.tc.metaDataCached) {
                     $timeout(function () {
                         scope.treeLoaded = true;
                         scope.$apply();
                     });
-                    selection.responseReceived(); 
+                    selection.responseReceived();
                 }
-                else{                    
-                    console.log('Finished loading orgunit tree');                        
+                else {
+                    console.log('Finished loading orgunit tree');
                     $("#orgUnitTree").addClass("disable-clicks"); //Disable ou selection until meta-data has downloaded
                     $timeout(function () {
                         scope.treeLoaded = true;
@@ -27,7 +29,7 @@
                     });
                     downloadMetaData();
                 }
-            });            
+            });
 
             //listen to user selection, and inform angular         
             selection.setListenerFunction(setSelectedOu, true);
@@ -43,7 +45,7 @@
 })
 
 .directive('eventPaginator', function factory() {
-    
+
     return {
         restrict: 'E',
         controller: function ($scope, Paginator) {
@@ -53,16 +55,16 @@
     };
 })
 
-.directive('stringToNumber', function() {
-  return {
-    require: 'ngModel',
-    link: function(scope, element, attrs, ngModel) {
-      ngModel.$parsers.push(function(value) {
-        return '' + value;
-      });
-      ngModel.$formatters.push(function(value) {
-        return parseFloat(value, 10);
-      });
-    }
-  }
+.directive('stringToNumber', function () {
+    return {
+        require: 'ngModel',
+        link: function (scope, element, attrs, ngModel) {
+            ngModel.$parsers.push(function (value) {
+                return '' + value;
+            });
+            ngModel.$formatters.push(function (value) {
+                return parseFloat(value, 10);
+            });
+        }
+    };
 });
\ No newline at end of file

=== modified file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.directives.js'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.directives.js	2015-10-23 08:08:54 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.directives.js	2015-10-23 15:42:36 +0000
@@ -169,293 +169,6 @@
     };
 })
 
-.directive('d2GoogleMap', function ($http, $translate, CurrentSelection) {
-    return {
-        restrict: 'E',
-        replace: true,
-        template: '<div></div>',
-        scope: {
-            location: '='
-        },
-        link: function (scope, element, attrs) {
-
-            CurrentSelection.setLocation(scope.location);
-
-            var contextMenuDisplayed = false;
-            var ouLevels = CurrentSelection.getOuLevels();
-
-            //remove angular bootstrap ui modal draggable
-            $(".modal-content").draggable({disabled: true});
-
-            //get a default center
-            var latCenter = 12.31, lngCenter = 51.48;
-
-            //if there is any marker already - use it as center
-            if (angular.isObject(scope.location)) {
-                if (scope.location.lat && scope.location.lng) {
-                    latCenter = scope.location.lat;
-                    lngCenter = scope.location.lng;
-                }
-            }
-
-            //default map configurations 
-            var mapOptions = {
-                zoom: 4,
-                center: new google.maps.LatLng(latCenter, lngCenter),
-                mapTypeId: google.maps.MapTypeId.ROADMAP
-            },
-            featureStyle = {
-                strokeWeight: 2,
-                strokeOpacity: 0.4,
-                fillOpacity: 0.2,
-                fillColor: '#99cc99'
-            };
-
-            var map = new google.maps.Map(document.getElementById(attrs.id), mapOptions);
-
-            var marker = new google.maps.Marker({
-                map: map
-            });
-
-            if (angular.isObject(scope.location)) {
-                if (scope.location.lat && scope.location.lng) {
-                    addMarker({lat: scope.location.lat, lng: scope.location.lng});
-                }
-            }
-
-            var currentLayer = 0, currentGeojson, currentGeojsonFeatures;
-
-            var contextMenuOptions = {};
-            contextMenuOptions.classNames = {menu: 'map_context_menu', menuSeparator: 'map_context_menu_separator'};
-
-            //create an array of MapContextMenuItem objects            
-            var menuItems = [];
-            menuItems.push({className: 'map_context_menu_item', eventName: 'captureCoordinate', label: '<i class="fa fa-map-marker"></i>   ' + $translate.instant('set_coordinate')});
-            menuItems.push({});
-            menuItems.push({className: 'map_context_menu_item', eventName: 'zoom_in', id: 'zoomIn', label: '<i class="fa fa-search-plus"></i>   ' + $translate.instant('zoom_in')});
-            menuItems.push({className: 'map_context_menu_item', eventName: 'zoom_out', id: 'zoomOut', label: '<i class="fa fa-search-minus"></i>   ' + $translate.instant('zoom_out')});
-            menuItems.push({});
-            menuItems.push({className: 'map_context_menu_item', eventName: 'centerMap', label: '<i class="fa fa-crosshairs"></i>   ' + $translate.instant('center_map')});
-            contextMenuOptions.menuItems = menuItems;
-            var mapContextMenu = new MapContextMenu(map, contextMenuOptions);
-
-            function getGeoJsonByOuLevel(initialize, event, mode) {
-                var url = '';
-                if (initialize) {
-                    currentLayer = 0;
-                    url = '../api/organisationUnits.geojson?level=' + ouLevels[currentLayer].level;
-                }
-                else {
-                    if (mode === 'IN') {
-                        currentLayer++;
-                        url = '../api/organisationUnits.geojson?level=' + ouLevels[currentLayer].level + '&parent=' + event.feature.D;
-                    }
-                    if (mode === 'OUT') {
-                        currentLayer--;
-                        var parents = event.feature.k.parentGraph.substring(1, event.feature.k.parentGraph.length - 1).split('/');
-                        url = '../api/organisationUnits.geojson?level=' + ouLevels[currentLayer].level + '&parent=' + parents[parents.length - 2];
-                    }
-                }
-
-                $http.get(url).then(function (response) {
-                    currentGeojson = response.data;
-                    currentGeojsonFeatures = map.data.addGeoJson(currentGeojson);
-
-                    if (initialize) {
-                        google.maps.event.addListenerOnce(map, 'idle', function () {
-                            google.maps.event.trigger(map, 'resize');
-                            map.data.setStyle(featureStyle);
-                            centerMap();
-                        });
-                    }
-                    else {
-                        centerMap();
-                    }
-                });
-            }
-
-            function addMarker(loc) {
-                var latLng = new google.maps.LatLng(loc.lat, loc.lng);
-                marker.setPosition(latLng);
-            }
-
-            function applyMarker(event) {
-                addMarker({
-                    lat: event.latLng.lat(),
-                    lng: event.latLng.lng()
-                });
-
-                scope.location = {lat: event.latLng.lat(), lng: event.latLng.lng()};
-                CurrentSelection.setLocation(scope.location);
-            }
-
-            function centerMap() {
-                if (currentGeojson && currentGeojson.features) {
-                    var latLngBounds = getMapCenter(currentGeojson);
-                    map.fitBounds(latLngBounds);
-                    map.panToBounds(latLngBounds);
-                }
-            }
-
-            var overLays = [];
-            function getMapCenter(geoJson) {
-                map.data.setStyle(featureStyle);
-                if (!geoJson || !geoJson.features) {
-                    return;
-                }
-
-                var latLngBounds = new google.maps.LatLngBounds();
-                overLays = [];
-                angular.forEach(geoJson.features, function (feature) {
-                    var customTxt = '<div>' + feature.properties.name + '</div>';
-                    if (feature.geometry.type === 'MultiPolygon') {
-                        var polygonPoints = new google.maps.LatLngBounds();
-                        angular.forEach(feature.geometry.coordinates[0][0], function (coordinate) {
-                            latLngBounds.extend(new google.maps.LatLng(coordinate[1], coordinate[0]));
-                            polygonPoints.extend(new google.maps.LatLng(coordinate[1], coordinate[0]));
-                        });
-                        var txt = new Dhis2TextOverlay(polygonPoints.getCenter(), customTxt, "polygon-name", map);
-                        overLays.push(txt);
-                    }
-                    else if (feature.geometry.type === 'Point') {
-                        latLngBounds.extend(new google.maps.LatLng(feature.geometry.coordinates[1], feature.geometry.coordinates[0]));
-                        var txt = new Dhis2TextOverlay(new google.maps.LatLng(feature.geometry.coordinates[1], feature.geometry.coordinates[0]), customTxt, "polygon-name", map);
-                        overLays.push(txt);
-                    }
-                });
-
-                return latLngBounds;
-            }
-
-            function initializeMap() {
-                getGeoJsonByOuLevel(true, null);
-            }
-
-            function zoomMap(event, mode) {
-
-                for (var i = 0; i < currentGeojsonFeatures.length; i++) {
-                    map.data.remove(currentGeojsonFeatures[i]);
-                }
-
-                for (var i = 0; i < overLays.length; i++) {
-                    overLays[i].setMap(null);
-                }
-
-                getGeoJsonByOuLevel(false, event, mode);
-            }
-
-            function enableDisableZoom() {
-                if (currentLayer >= ouLevels.length - 1) {
-                    $("#zoomIn").addClass('disabled-context-menu-item');
-                    $("#zoomIn").removeClass('mouse-pointer');
-                    $('#zoomIn').attr('disabled', "disabled");
-                } else {
-                    $("#zoomIn").removeClass('disabled-context-menu-item');
-                    $("#zoomIn").addClass('enable-context-menu-item');
-                    $('#zoomIn').attr('disabled', "");
-                }
-                if (currentLayer === 0) {
-                    $("#zoomOut").addClass('disabled-context-menu-item');
-                    $("#zoomOut").removeClass('enable-context-menu-item');
-                    $('#zoomOut').attr('disabled', "disabled");
-                }
-                else {
-                    $("#zoomOut").removeClass('disabled-context-menu-item');
-                    $("#zoomOut").addClass('enable-context-menu-item');
-                    $('#zoomIn').attr('disabled', "");
-                }
-            }
-
-            function showHideContextMenu(event, allowDisable) {
-                if (contextMenuDisplayed) {
-                    mapContextMenu.hide();
-                    contextMenuDisplayed = false;
-                }
-                else {
-                    mapContextMenu.show(event);
-                    if (allowDisable) {
-                        enableDisableZoom();
-                    }
-                }
-            }
-
-            //get lable for current polygon
-            map.data.addListener('mouseover', function (e) {
-                $("#polygon-label").text(e.feature.k.name);
-                map.data.revertStyle();
-                map.data.overrideStyle(e.feature, {fillOpacity: 0.2});
-            });
-
-            //remove polygon label
-            map.data.addListener('mouseout', function () {
-                $("#polygon-label").text('');
-                map.data.revertStyle();
-            });
-
-            //context menu based on polygons assigned to orgunits
-            map.data.addListener('rightclick', function (e) {
-                showHideContextMenu(e, true);
-            });
-
-            map.data.addListener('click', function (e) {
-                showHideContextMenu(e, true);
-            });
-
-            //context menu based on points assigned to orgunits
-            google.maps.event.addListener(marker, 'rightclick', function (e) {
-                showHideContextMenu(e, true);
-            });
-
-            google.maps.event.addListener(marker, 'click', function (e) {
-                showHideContextMenu(e, true);
-            });
-
-            //context menu anywhere in the map - incase no polygons are defined
-            google.maps.event.addListener(map, 'rightclick', function (e) {
-                showHideContextMenu(e, false);
-            });
-
-            google.maps.event.addListener(map, 'click', function (e) {
-                showHideContextMenu(e, false);
-            });
-
-            //listen for the clicks on mapContextMenu
-            google.maps.event.addListener(mapContextMenu, 'menu_item_selected', function (e, latLng, eventName) {
-                switch (eventName) {
-                    case 'zoom_in':
-                        if (e.feature) {
-                            zoomMap(e, 'IN');
-                        }
-                        else {
-                            map.setZoom(map.getZoom() + 1);
-                        }
-
-                        break;
-                    case 'zoom_out':
-                        if (e.feature) {
-                            zoomMap(e, 'OUT');
-                        }
-                        else {
-                            map.setZoom(map.getZoom() - 1);
-                        }
-
-                        break;
-                    case 'centerMap':
-                        contextMenuDisplayed = true;
-                        map.panTo(latLng);
-                        break;
-                    case 'captureCoordinate':
-                        contextMenuDisplayed = true;
-                        applyMarker(e);
-                        break;
-                }
-            });
-
-            initializeMap();
-        }
-    };
-})
-
 .directive('d2CustomForm', function ($compile) {
     return{
         restrict: 'E',

=== added file 'dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.map.directive.js'
--- dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.map.directive.js	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-commons-resources/src/main/webapp/dhis-web-commons/javascripts/dhis2/dhis2.angular.map.directive.js	2015-10-23 15:42:36 +0000
@@ -0,0 +1,557 @@
+d2Directives.directive('d2GoogleMap', function ($http, $translate, $q, $window, CurrentSelection) {
+    
+    function lazyLoadApi() {
+        
+        var deferred = $q.defer();
+        $window.initMap = function() {
+            deferred.resolve();
+        };
+        
+        var script = document.createElement('script');        
+        script.src = 'https://maps.google.com/maps/api/js?callback=initMap';
+        document.body.appendChild(script);
+        return deferred.promise;
+    }
+
+    return {
+        restrict: 'E',
+        replace: true,
+        template: '<div></div>',
+        scope: {
+            location: '='
+        },
+        link: function (scope, element, attrs) {
+            
+            function renderMap(){
+                
+                // https://developers.google.com/maps/documentation/javascript/customoverlays
+                /** @constructor */
+                function Dhis2TextOverlay(pos, txt, cls, map) {
+
+                    // Initialize properties.
+                    this.pos = pos;
+                    this.txt_ = txt;
+                    this.cls_ = cls;
+                    this.map_ = map;
+
+                    // Div to contain text
+                    this.div_ = null;
+
+                    // Explicitly call setMap() on this overlay
+                    this.setMap(map);
+                }
+
+                Dhis2TextOverlay.prototype = new google.maps.OverlayView();
+
+
+                /**
+                 * onAdd is called when the map's panes are ready and the overlay has been
+                 * added to the map.
+                 */
+                Dhis2TextOverlay.prototype.onAdd = function () {
+
+                    // Create the DIV and set some basic attributes.
+                    var div = document.createElement('DIV');
+                    div.className = this.cls_;
+                    div.innerHTML = this.txt_;
+
+                    // Set the overlay's div_ property to this DIV
+                    this.div_ = div;
+                    var overlayProjection = this.getProjection();
+                    var position = overlayProjection.fromLatLngToDivPixel(this.pos);
+                    div.style.left = position.x + 'px';
+                    div.style.top = position.y + 'px';
+
+                    // Add the element to the "overlayLayer" pane.
+                    var panes = this.getPanes();
+                    panes.floatPane.appendChild(div);
+                };
+
+                Dhis2TextOverlay.prototype.draw = function () {
+
+                    // We use the south-west and north-east
+                    // coordinates of the overlay to peg it to the correct position and size.
+                    // To do this, we need to retrieve the projection from the overlay.
+                    var overlayProjection = this.getProjection();
+
+                    // Retrieve the southwest and northeast coordinates of this overlay
+                    // in latlngs and convert them to pixels coordinates.
+                    // We'll use these coordinates to resize the DIV.
+                    var position = overlayProjection.fromLatLngToDivPixel(this.pos);
+
+                    // Resize the div to fit the indicated dimensions.
+                    var div = this.div_;
+                    div.style.left = position.x + 'px';
+                    div.style.top = position.y + 'px';
+                };
+
+                // The onRemove() method will be called automatically from the API if
+                // we ever set the overlay's map property to 'null'
+                Dhis2TextOverlay.prototype.onRemove = function () {
+                    this.div_.parentNode.removeChild(this.div_);
+                    this.div_ = null;
+                };
+
+                // Set the visibility to 'hidden' or 'visible'.
+                Dhis2TextOverlay.prototype.hide = function () {
+                    if (this.div_) {
+                        this.div_.style.visibility = "hidden";
+                    }
+                };
+
+                Dhis2TextOverlay.prototype.show = function () {
+                    if (this.div_) {
+                        this.div_.style.visibility = "visible";
+                    }
+                };
+
+                Dhis2TextOverlay.prototype.toggle = function () {
+                    if (this.div_) {
+                        if (this.div_.style.visibility === "hidden") {
+                            this.show();
+                        }
+                        else {
+                            this.hide();
+                        }
+                    }
+                };
+
+                // Detach the map from the DOM via toggleDOM().
+                // Note that if we later reattach the map, it will be visible again,
+                // because the containing <div> is recreated in the overlay's onAdd() method.
+                Dhis2TextOverlay.prototype.toggleDOM = function () {
+                    if (this.getMap()) {
+                        this.setMap(null);
+                    }
+                    else {
+                        this.setMap(this.map_);
+                    }
+                };
+                
+                
+                /*
+                MapContextMenu v1.0
+
+                A context menu for Google Maps API v3
+                http://code.martinpearman.co.uk/googlemapsapi/contextmenu/
+
+                Copyright Martin Pearman
+                Last updated 21st November 2011
+
+                developer@xxxxxxxxxxxxxxxxxxx
+
+                This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+
+                This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+                You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+                */
+
+                function MapContextMenu(map, options){
+                        options=options || {};
+
+                        this.setMap(map);
+
+                        this.classNames_=options.classNames || {};
+                        this.map_=map;
+                        this.mapDiv_=map.getDiv();
+                        this.menuItems_=options.menuItems || [];
+                        this.pixelOffset=options.pixelOffset || new google.maps.Point(10, -5);
+                }
+
+                MapContextMenu.prototype=new google.maps.OverlayView();
+
+                MapContextMenu.prototype.draw=function(){
+                        if(this.isVisible_){
+                                var mapSize=new google.maps.Size(this.mapDiv_.offsetWidth, this.mapDiv_.offsetHeight);
+                                var menuSize=new google.maps.Size(this.menu_.offsetWidth, this.menu_.offsetHeight);
+                                var mousePosition=this.getProjection().fromLatLngToDivPixel(this.position_);
+
+                                var left=mousePosition.x;
+                                var top=mousePosition.y;
+
+                                if(mousePosition.x>mapSize.width-menuSize.width-this.pixelOffset.x){
+                                        left=left-menuSize.width-this.pixelOffset.x;
+                                } else {
+                                        left+=this.pixelOffset.x;
+                                }
+
+                                if(mousePosition.y>mapSize.height-menuSize.height-this.pixelOffset.y){
+                                        top=top-menuSize.height-this.pixelOffset.y;
+                                } else {
+                                        top+=this.pixelOffset.y;
+                                }
+
+                                this.menu_.style.left=left+'px';
+                                this.menu_.style.top=top+'px';
+                        }
+                };
+
+                MapContextMenu.prototype.getVisible=function(){
+                        return this.isVisible_;
+                };
+
+                MapContextMenu.prototype.hide=function(){
+                        if(this.isVisible_){
+                                this.menu_.style.display='none';
+                                this.isVisible_=false;
+                        }
+                };
+
+                MapContextMenu.prototype.onAdd=function(){
+                        function createMenuItem(values){        
+                                var menuItem=document.createElement('div');
+                                menuItem.innerHTML=values.label;
+                                if(values.className){
+                                        menuItem.className=values.className;
+                                }
+                                if(values.id){
+                                        menuItem.id=values.id;
+                                }
+                                menuItem.style.cssText='white-space:nowrap; font-size: 12pt;';
+                                menuItem.onclick=function(){
+                                        google.maps.event.trigger($this, 'menu_item_selected', $this.event_, $this.position_, values.eventName);
+                                };
+                                return menuItem;
+                        }
+                        function createMenuSeparator(){
+                                var menuSeparator=document.createElement('div');
+                                if($this.classNames_.menuSeparator){
+                                        menuSeparator.className=$this.classNames_.menuSeparator;
+                                }
+                                return menuSeparator;
+                        }
+                        var $this=this;	//	used for closures
+
+                        var menu=document.createElement('div');
+                        if(this.classNames_.menu){
+                                menu.className=this.classNames_.menu;
+                        }
+                        menu.style.cssText='display:none; position:absolute';
+
+                        for(var i=0, j=this.menuItems_.length; i<j; i++){
+                                if(this.menuItems_[i].label && this.menuItems_[i].eventName){
+                                        menu.appendChild(createMenuItem(this.menuItems_[i]));
+                                } else {
+                                        menu.appendChild(createMenuSeparator());
+                                }
+                        }
+
+                        delete this.classNames_;
+                        delete this.menuItems_;
+
+                        this.isVisible_=false;
+                        this.menu_=menu;
+                        this.position_=new google.maps.LatLng(0, 0);
+
+                        google.maps.event.addListener(this.map_, 'click', function(mouseEvent){
+                                $this.hide();
+                        });
+
+                        this.getPanes().floatPane.appendChild(menu);
+                };
+
+                MapContextMenu.prototype.onRemove=function(){
+                        this.menu_.parentNode.removeChild(this.menu_);
+                        delete this.mapDiv_;
+                        delete this.menu_;
+                        delete this.position_;
+                };
+
+                MapContextMenu.prototype.show=function(e){
+                        if(!this.isVisible_){
+                                this.menu_.style.display='block';
+                                this.isVisible_=true;
+                        }
+                        this.position_=e.latLng;
+                    this.event_=e;
+                        this.draw();
+                };
+    
+    
+                CurrentSelection.setLocation(scope.location);
+
+                var contextMenuDisplayed = false;
+                var ouLevels = CurrentSelection.getOuLevels();
+
+                //remove angular bootstrap ui modal draggable
+                $(".modal-content").draggable({disabled: true});
+
+                //get a default center
+                var latCenter = 12.31, lngCenter = 51.48;
+
+                //if there is any marker already - use it as center
+                if (angular.isObject(scope.location)) {
+                    if (scope.location.lat && scope.location.lng) {
+                        latCenter = scope.location.lat;
+                        lngCenter = scope.location.lng;
+                    }
+                }
+
+                //default map configurations 
+                var mapOptions = {
+                    zoom: 4,
+                    center: new google.maps.LatLng(latCenter, lngCenter),
+                    mapTypeId: google.maps.MapTypeId.ROADMAP
+                },
+                featureStyle = {
+                    strokeWeight: 2,
+                    strokeOpacity: 0.4,
+                    fillOpacity: 0.2,
+                    fillColor: '#99cc99'
+                };
+
+                var map = new google.maps.Map(document.getElementById(attrs.id), mapOptions);
+
+                var marker = new google.maps.Marker({
+                    map: map
+                });
+
+                if (angular.isObject(scope.location)) {
+                    if (scope.location.lat && scope.location.lng) {
+                        addMarker({lat: scope.location.lat, lng: scope.location.lng});
+                    }
+                }
+
+                var currentLayer = 0, currentGeojson, currentGeojsonFeatures;
+
+                var contextMenuOptions = {};
+                contextMenuOptions.classNames = {menu: 'map_context_menu', menuSeparator: 'map_context_menu_separator'};
+
+                //create an array of MapContextMenuItem objects            
+                var menuItems = [];
+                menuItems.push({className: 'map_context_menu_item', eventName: 'captureCoordinate', label: '<i class="fa fa-map-marker"></i>   ' + $translate.instant('set_coordinate')});
+                menuItems.push({});
+                menuItems.push({className: 'map_context_menu_item', eventName: 'zoom_in', id: 'zoomIn', label: '<i class="fa fa-search-plus"></i>   ' + $translate.instant('zoom_in')});
+                menuItems.push({className: 'map_context_menu_item', eventName: 'zoom_out', id: 'zoomOut', label: '<i class="fa fa-search-minus"></i>   ' + $translate.instant('zoom_out')});
+                menuItems.push({});
+                menuItems.push({className: 'map_context_menu_item', eventName: 'centerMap', label: '<i class="fa fa-crosshairs"></i>   ' + $translate.instant('center_map')});
+                contextMenuOptions.menuItems = menuItems;
+                var mapContextMenu = new MapContextMenu(map, contextMenuOptions);
+
+                function getGeoJsonByOuLevel(initialize, event, mode) {                    
+                    var url = '';
+                    if (initialize) {
+                        currentLayer = 0;
+                        url = '../api/organisationUnits.geojson?level=' + ouLevels[currentLayer].level;
+                    }
+                    else {
+                        if (mode === 'IN') {
+                            currentLayer++;                            
+                            url = '../api/organisationUnits.geojson?level=' + ouLevels[currentLayer].level + '&parent=' + event.feature.getId();
+                        }
+                        if (mode === 'OUT') {
+                            currentLayer--;
+                            var parents = event.feature.getProperty('parentGraph').substring(1, event.feature.getProperty('parentGraph').length - 1).split('/');
+                            url = '../api/organisationUnits.geojson?level=' + ouLevels[currentLayer].level + '&parent=' + parents[parents.length - 2];
+                        }
+                    }
+
+                    $http.get(url).then(function (response) {
+                        currentGeojson = response.data;
+                        currentGeojsonFeatures = map.data.addGeoJson(currentGeojson);
+
+                        if (initialize) {
+                            google.maps.event.addListenerOnce(map, 'idle', function () {
+                                google.maps.event.trigger(map, 'resize');
+                                map.data.setStyle(featureStyle);
+                                centerMap();
+                            });
+                        }
+                        else {
+                            centerMap();
+                        }
+                    });
+                }
+
+                function addMarker(loc) {
+                    var latLng = new google.maps.LatLng(loc.lat, loc.lng);
+                    marker.setPosition(latLng);
+                }
+
+                function applyMarker(event) {
+                    addMarker({
+                        lat: event.latLng.lat(),
+                        lng: event.latLng.lng()
+                    });
+
+                    scope.location = {lat: event.latLng.lat(), lng: event.latLng.lng()};
+                    CurrentSelection.setLocation(scope.location);
+                }
+
+                function centerMap() {
+                    if (currentGeojson && currentGeojson.features) {
+                        var latLngBounds = getMapCenter(currentGeojson);
+                        map.fitBounds(latLngBounds);
+                        map.panToBounds(latLngBounds);
+                    }
+                }
+
+                var overLays = [];
+                function getMapCenter(geoJson) {
+                    map.data.setStyle(featureStyle);
+                    if (!geoJson || !geoJson.features) {
+                        return;
+                    }
+
+                    var latLngBounds = new google.maps.LatLngBounds();
+                    overLays = [];
+                    angular.forEach(geoJson.features, function (feature) {
+                        var customTxt = '<div>' + feature.properties.name + '</div>';
+                        if (feature.geometry.type === 'MultiPolygon') {
+                            var polygonPoints = new google.maps.LatLngBounds();
+                            angular.forEach(feature.geometry.coordinates[0][0], function (coordinate) {
+                                latLngBounds.extend(new google.maps.LatLng(coordinate[1], coordinate[0]));
+                                polygonPoints.extend(new google.maps.LatLng(coordinate[1], coordinate[0]));
+                            });
+                            var txt = new Dhis2TextOverlay(polygonPoints.getCenter(), customTxt, "polygon-name", map);
+                            overLays.push(txt);
+                        }
+                        else if (feature.geometry.type === 'Point') {
+                            latLngBounds.extend(new google.maps.LatLng(feature.geometry.coordinates[1], feature.geometry.coordinates[0]));
+                            var txt = new Dhis2TextOverlay(new google.maps.LatLng(feature.geometry.coordinates[1], feature.geometry.coordinates[0]), customTxt, "polygon-name", map);
+                            overLays.push(txt);
+                        }
+                    });
+
+                    return latLngBounds;
+                }
+
+                function initializeMap() {
+                    getGeoJsonByOuLevel(true, null);
+                }
+
+                function zoomMap(event, mode) {
+
+                    for (var i = 0; i < currentGeojsonFeatures.length; i++) {
+                        map.data.remove(currentGeojsonFeatures[i]);
+                    }
+
+                    for (var i = 0; i < overLays.length; i++) {
+                        overLays[i].setMap(null);
+                    }
+
+                    getGeoJsonByOuLevel(false, event, mode);
+                }
+
+                function enableDisableZoom() {
+                    if (currentLayer >= ouLevels.length - 1) {
+                        $("#zoomIn").addClass('disabled-context-menu-item');
+                        $("#zoomIn").removeClass('mouse-pointer');
+                        $('#zoomIn').attr('disabled', "disabled");
+                    } else {
+                        $("#zoomIn").removeClass('disabled-context-menu-item');
+                        $("#zoomIn").addClass('enable-context-menu-item');
+                        $('#zoomIn').attr('disabled', "");
+                    }
+                    if (currentLayer === 0) {
+                        $("#zoomOut").addClass('disabled-context-menu-item');
+                        $("#zoomOut").removeClass('enable-context-menu-item');
+                        $('#zoomOut').attr('disabled', "disabled");
+                    }
+                    else {
+                        $("#zoomOut").removeClass('disabled-context-menu-item');
+                        $("#zoomOut").addClass('enable-context-menu-item');
+                        $('#zoomIn').attr('disabled', "");
+                    }
+                }
+
+                function showHideContextMenu(event, allowDisable) {
+                    if (contextMenuDisplayed) {
+                        mapContextMenu.hide();
+                        contextMenuDisplayed = false;
+                    }
+                    else {
+                        mapContextMenu.show(event);
+                        if (allowDisable) {
+                            enableDisableZoom();
+                        }
+                    }
+                }
+
+                //get lable for current polygon
+                map.data.addListener('mouseover', function (e) {
+                    $("#polygon-label").text(e.feature.getProperty('name'));
+                    map.data.revertStyle();
+                    map.data.overrideStyle(e.feature, {fillOpacity: 0.2});
+                });
+
+                //remove polygon label
+                map.data.addListener('mouseout', function () {
+                    $("#polygon-label").text('');
+                    map.data.revertStyle();
+                });
+
+                //context menu based on polygons assigned to orgunits
+                map.data.addListener('rightclick', function (e) {
+                    showHideContextMenu(e, true);
+                });
+
+                map.data.addListener('click', function (e) {
+                    showHideContextMenu(e, true);
+                });
+
+                //context menu based on points assigned to orgunits
+                google.maps.event.addListener(marker, 'rightclick', function (e) {
+                    showHideContextMenu(e, true);
+                });
+
+                google.maps.event.addListener(marker, 'click', function (e) {
+                    showHideContextMenu(e, true);
+                });
+
+                //context menu anywhere in the map - incase no polygons are defined
+                google.maps.event.addListener(map, 'rightclick', function (e) {
+                    showHideContextMenu(e, false);
+                });
+
+                google.maps.event.addListener(map, 'click', function (e) {
+                    showHideContextMenu(e, false);
+                });
+
+                //listen for the clicks on mapContextMenu
+                google.maps.event.addListener(mapContextMenu, 'menu_item_selected', function (e, latLng, eventName) {
+                    switch (eventName) {
+                        case 'zoom_in':
+                            if (e.feature) {
+                                zoomMap(e, 'IN');
+                            }
+                            else {
+                                map.setZoom(map.getZoom() + 1);
+                            }
+
+                            break;
+                        case 'zoom_out':
+                            if (e.feature) {
+                                zoomMap(e, 'OUT');
+                            }
+                            else {
+                                map.setZoom(map.getZoom() - 1);
+                            }
+
+                            break;
+                        case 'centerMap':
+                            contextMenuDisplayed = true;
+                            map.panTo(latLng);
+                            break;
+                        case 'captureCoordinate':
+                            contextMenuDisplayed = true;
+                            applyMarker(e);
+                            break;
+                    }
+                });
+
+                initializeMap();
+            } 
+            if ($window.google && $window.google.maps) {                
+                renderMap();
+            } else {
+                lazyLoadApi().then(function () {                  
+                    renderMap();            
+                }, function () {
+                    console.log('GM loading failed.');
+                });
+            }
+        }
+    };
+})
\ No newline at end of file