diff --git a/www/js/services/utils-services.js b/www/js/services/utils-services.js
index af46cf9b0f6d2ea46a480630fba3b5444ec6da7c..5a058037662fb25f88b06294fa364a945d4f78e5 100644
--- a/www/js/services/utils-services.js
+++ b/www/js/services/utils-services.js
@@ -26,17 +26,17 @@ angular.module('cesium.utils.services', [])
   var
     loadingTextCache=null,
     CONST = {
-      MAX_HEIGHT: 400,
-      MAX_WIDTH: 400,
-      THUMB_MAX_HEIGHT: 100,
-      THUMB_MAX_WIDTH: 100
+      MAX_HEIGHT: 480,
+      MAX_WIDTH: 640,
+      THUMB_MAX_HEIGHT: 200,
+      THUMB_MAX_WIDTH: 200
     },
     data = {
       smallscreen: screenmatch.bind('xs, sm', $rootScope)
     },
     exports,
     raw = {}
-  ;
+    ;
 
   function alertError(err, subtitle) {
     if (!err) {
@@ -45,45 +45,45 @@ angular.module('cesium.utils.services', [])
 
     return $q(function(resolve) {
       $translate([err, subtitle, 'ERROR.POPUP_TITLE', 'ERROR.UNKNOWN_ERROR', 'COMMON.BTN_OK'])
-      .then(function (translations) {
-        var message = err.message || translations[err];
-        return $ionicPopup.show({
-          template: '<p>' + (message || translations['ERROR.UNKNOWN_ERROR']) + '</p>',
-          title: translations['ERROR.POPUP_TITLE'],
-          subTitle: translations[subtitle],
-          buttons: [
-            {
-              text: '<b>'+translations['COMMON.BTN_OK']+'</b>',
-              type: 'button-assertive',
-              onTap: function(e) {
-                resolve(e);
+        .then(function (translations) {
+          var message = err.message || translations[err];
+          return $ionicPopup.show({
+            template: '<p>' + (message || translations['ERROR.UNKNOWN_ERROR']) + '</p>',
+            title: translations['ERROR.POPUP_TITLE'],
+            subTitle: translations[subtitle],
+            buttons: [
+              {
+                text: '<b>'+translations['COMMON.BTN_OK']+'</b>',
+                type: 'button-assertive',
+                onTap: function(e) {
+                  resolve(e);
+                }
               }
-            }
-          ]
+            ]
+          });
         });
-      });
     });
   }
 
   function alertInfo(message, subtitle) {
     return $q(function(resolve) {
       $translate([message, subtitle, 'INFO.POPUP_TITLE', 'COMMON.BTN_OK'])
-      .then(function (translations) {
-        $ionicPopup.show({
-          template: '<p>' + translations[message] + '</p>',
-          title: translations['INFO.POPUP_TITLE'],
-          subTitle: translations[subtitle],
-          buttons: [
-            {
-              text: translations['COMMON.BTN_OK'],
-              type: 'button-positive',
-              onTap: function(e) {
-                resolve(e);
+        .then(function (translations) {
+          $ionicPopup.show({
+            template: '<p>' + translations[message] + '</p>',
+            title: translations['INFO.POPUP_TITLE'],
+            subTitle: translations[subtitle],
+            buttons: [
+              {
+                text: translations['COMMON.BTN_OK'],
+                type: 'button-positive',
+                onTap: function(e) {
+                  resolve(e);
+                }
               }
-            }
-          ]
+            ]
+          });
         });
-      });
     });
   }
 
@@ -243,41 +243,82 @@ angular.module('cesium.utils.services', [])
   function getSelectionText(){
     var selectedText = "";
     if (window.getSelection){ // all modern browsers and IE9+
-        selectedText = $window.getSelection().toString();
+      selectedText = $window.getSelection().toString();
     }
     return selectedText;
   }
 
   function imageOnLoadResize(resolve, reject, thumbnail) {
     return function(event) {
-          var width = event.target.width;
-          var height = event.target.height;
-       var maxWidth = (thumbnail ? CONST.THUMB_MAX_WIDTH : CONST.MAX_WIDTH);
-       var maxHeight = (thumbnail ? CONST.THUMB_MAX_HEIGHT : CONST.MAX_HEIGHT);
-
-          if (width > height) {
-         if (width > maxWidth) {
-           height *= maxWidth / width;
-           width = maxWidth;
-            }
-          } else {
-         if (height > maxHeight) {
-           width *= maxHeight / height;
-           height = maxHeight;
-            }
+      var width = event.target.width,
+        height = event.target.height,
+        maxWidth = (thumbnail ? CONST.THUMB_MAX_WIDTH : CONST.MAX_WIDTH),
+        maxHeight = (thumbnail ? CONST.THUMB_MAX_HEIGHT : CONST.MAX_HEIGHT)
+        ;
+
+      var canvas = document.createElement("canvas");
+      var ctx;
+
+      // Thumbnail: resize and crop (to the expected size)
+      if (thumbnail) {
+
+        // landscape
+        if (width > height) {
+          width *= maxHeight / height;
+          height = maxHeight;
+        }
+
+        // portrait
+        else {
+          height *= maxWidth / width;
+          width = maxWidth;
+        }
+        canvas.width = maxWidth;
+        canvas.height = maxHeight;
+        ctx = canvas.getContext("2d");
+        var xoffset = Math.trunc((maxWidth - width) / 2 + 0.5);
+        var yoffset = Math.trunc((maxHeight - height) / 2 + 0.5);
+        ctx.drawImage(event.target,
+          xoffset, // x1
+          yoffset, // y1
+          maxWidth + -2 * xoffset, // x2
+          maxHeight + -2 * yoffset // y2
+        );
+      }
+
+      // Resize, but keep the full image
+      else {
+
+        // landscape
+        if (width > height) {
+          if (width > maxWidth) {
+            height *= maxWidth / width;
+            width = maxWidth;
           }
-          var canvas = document.createElement("canvas");
-          canvas.width = width;
-          canvas.height = height;
-          var ctx = canvas.getContext("2d");
-          ctx.drawImage(event.target, 0, 0,  canvas.width, canvas.height);
+        }
 
-          var dataurl = canvas.toDataURL();
+        // portrait
+        else {
+          if (height > maxHeight) {
+            width *= maxHeight / height;
+            height = maxHeight;
+          }
+        }
 
-          canvas.remove();
+        canvas.width = width;
+        canvas.height = height;
+        ctx = canvas.getContext("2d");
 
-          resolve(dataurl);
-        };
+        // Resize the whole image
+        ctx.drawImage(event.target, 0, 0,  canvas.width, canvas.height);
+      }
+
+      var dataurl = canvas.toDataURL();
+
+      canvas.remove();
+
+      resolve(dataurl);
+    };
   }
 
   function resizeImageFromFile(file, thumbnail) {
@@ -387,34 +428,34 @@ angular.module('cesium.utils.services', [])
       angular.merge(popover.scope, options.bindings);
       $timeout(function() { // This is need for Firefox
         popover.show(event)
-        .then(function() {
-          var element;
-          // Auto select text
-          if (options.autoselect) {
-            element = document.querySelectorAll(options.autoselect)[0];
-            if (element) {
-              if ($window.getSelection && !$window.getSelection().toString()) {
-                element.setSelectionRange(0, element.value.length);
-                element.focus();
-              }
-              else {
-                element.focus();
+          .then(function() {
+            var element;
+            // Auto select text
+            if (options.autoselect) {
+              element = document.querySelectorAll(options.autoselect)[0];
+              if (element) {
+                if ($window.getSelection && !$window.getSelection().toString()) {
+                  element.setSelectionRange(0, element.value.length);
+                  element.focus();
+                }
+                else {
+                  element.focus();
+                }
               }
             }
-          }
-          else {
-            // Auto focus on a element
-            if (options.autofocus) {
-              element = document.querySelectorAll(options.autofocus)[0];
-              if (element) element.focus();
+            else {
+              // Auto focus on a element
+              if (options.autofocus) {
+                element = document.querySelectorAll(options.autofocus)[0];
+                if (element) element.focus();
+              }
             }
-          }
 
-          popover.scope.$parent.$emit('popover.shown');
+            popover.scope.$parent.$emit('popover.shown');
 
-          // Callback 'afterShow'
-          if (options.afterShow) options.afterShow(popover);
-        });
+            // Callback 'afterShow'
+            if (options.afterShow) options.afterShow(popover);
+          });
       });
     };
 
@@ -424,9 +465,9 @@ angular.module('cesium.utils.services', [])
         delete options.scope.popovers[options.templateUrl];
         // Remove the popover
         popover.remove()
-          // Workaround for issue #244
-          // See also https://github.com/driftyco/ionic-v1/issues/71
-          // and https://github.com/driftyco/ionic/issues/9069
+        // Workaround for issue #244
+        // See also https://github.com/driftyco/ionic-v1/issues/71
+        // and https://github.com/driftyco/ionic/issues/9069
           .then(function() {
             var bodyEl = angular.element($window.document.querySelectorAll('body')[0]);
             bodyEl.removeClass('popover-open');
@@ -628,7 +669,7 @@ angular.module('cesium.utils.services', [])
   function motionDelegate(delegate, ionListClass) {
     var motionTimeout = isSmallScreen() ? 100 : 10;
     var defaultSelector = '.list.{0} .item, .list .{0} .item'.format(ionListClass, ionListClass);
-      return {
+    return {
       ionListClass: ionListClass,
       show: function(options) {
         options = options || {};
@@ -688,8 +729,8 @@ angular.module('cesium.utils.services', [])
     ripple: motionDelegate(ionicMaterialMotion.ripple, 'animate-ripple'),
     slideUp: motionDelegate(ionicMaterialMotion.slideUp, 'slide-up'),
     fadeIn: motionDelegate(function(options) {
-        toggleOn(options);
-      }, 'fade-in'),
+      toggleOn(options);
+    }, 'fade-in'),
     toggleOn: toggleOn,
     toggleOff: toggleOff
   };
@@ -743,10 +784,8 @@ angular.module('cesium.utils.services', [])
     }, timeout || 900);
   }
 
-
-
   csSettings.api.data.on.changed($rootScope, function(data) {
-   setEffects(data.uiEffects);
+    setEffects(data.uiEffects);
   });
 
   exports = {
diff --git a/www/plugins/es/js/controllers/common-controllers.js b/www/plugins/es/js/controllers/common-controllers.js
index 330c40e0aa69c5ffb8e93fbb7b9cfdf2b650544e..5b4e5bde214ac36ccacbff5079bb8a24f5e5f841 100644
--- a/www/plugins/es/js/controllers/common-controllers.js
+++ b/www/plugins/es/js/controllers/common-controllers.js
@@ -51,19 +51,23 @@ function ESPicturesEditController($scope, UIUtils, $q, Device) {
   };
 
   $scope.fileChanged = function(event) {
+    if (!event.target.files || !event.target.files.length) return;
     UIUtils.loading.show();
-    return $q(function(resolve, reject) {
-      var file = event.target.files[0];
-      UIUtils.image.resizeFile(file)
-        .then(function(imageData) {
-          $scope.pictures.push({
-            src: imageData,
-            isnew: true // use to prevent visibility hidden (if animation)
-          });
-          UIUtils.loading.hide(100);
-          resolve();
+    var file = event.target.files[0];
+    return UIUtils.image.resizeFile(file)
+      .then(function(imageData) {
+        $scope.pictures.push({
+          src: imageData,
+          isnew: true // use to prevent visibility hidden (if animation)
         });
-    });
+        event.target.value = ""; // reset input[type=file]
+        UIUtils.loading.hide(100);
+      })
+      .catch(function(err) {
+        console.error(err);
+        event.target.value = ""; // reset input[type=file]
+        UIUtils.loading.hide();
+      });
   };
 
   $scope.removePicture = function(index){
@@ -141,7 +145,7 @@ function ESCategoryModalController($scope, UIUtils, $timeout, parameters) {
 
 
 
-function ESCommentsController($scope, $filter, $state, $focus, UIUtils) {
+function ESCommentsController($scope, $filter, $state, $focus, $timeout, $anchorScroll, UIUtils) {
   'ngInject';
 
   $scope.loading = true;
@@ -150,8 +154,12 @@ function ESCommentsController($scope, $filter, $state, $focus, UIUtils) {
   $scope.comments = {};
 
   $scope.$on('$recordView.enter', function(e, state) {
+    // First enter
+    if ($scope.loading) {
+      $scope.anchor = state && state.stateParams.anchor;
+    }
     // second call (when using cached view)
-    if (!$scope.loading && $scope.id) {
+    else if (!$scope.loading && $scope.id) {
       $scope.load($scope.id, {animate: false});
     }
   });
@@ -159,15 +167,21 @@ function ESCommentsController($scope, $filter, $state, $focus, UIUtils) {
   $scope.$on('$recordView.load', function(event, id, service) {
     $scope.id = id || $scope.id;
     $scope.service = service || $scope.service;
-    console.debug("[ES] [comment] Initialized service with: " + service.id);
+    console.debug("[ES] [comment] Will use {" + service.index + "} service");
     if ($scope.id) {
-      $scope.load($scope.id);
+      $scope.load($scope.id)
+        .then(function() {
+          // Scroll to anchor
+          $scope.scrollToAnchor();
+        });
     }
   });
 
   $scope.load = function(id, options) {
     options = options || {};
     options.from = options.from || 0;
+    // If anchor has been defined, load all comments
+    options.size = options.size || ($scope.anchor && -1/*all*/);
     options.size = options.size || $scope.defaultCommentSize;
     options.animate = angular.isDefined(options.animate) ? options.animate : true;
     options.loadAvatarAllParent = angular.isDefined(options.loadAvatarAllParent) ? options.loadAvatarAllParent : true;
@@ -202,6 +216,25 @@ function ESCommentsController($scope, $filter, $state, $focus, UIUtils) {
     }
   });
 
+  $scope.scrollToAnchor = function() {
+    if (!$scope.anchor) return;
+    var elemList = document.getElementsByName($scope.anchor);
+    // Waiting for the element
+    if (!elemList || !elemList.length) {
+      return $timeout($scope.scrollToAnchor, 500);
+    }
+    // If many, remove all anchor except the last one
+    for (var i = 0; i<elemList.length-1; i++) {
+      angular.element(elemList[i]).remove();
+    }
+    // Scroll to the anchor
+    $anchorScroll($scope.anchor);
+    // Remove the anchor. This will the CSS class 'positive-100-bg' on the comment
+    $timeout(function () {
+      $scope.anchor = null;
+    }, 1500);
+  };
+
   $scope.showMore = function(){
     var from = 0;
     var size = -1;
@@ -355,8 +388,6 @@ function ESSocialsEditController($scope, $focus, $filter, UIUtils, SocialUtils)
       selector: '#social-' + $filter('formatSlug')(social.url),
       startVelocity: 10000
     });
-
-
   };
 
   $scope.editSocialNetwork = function(index) {
@@ -381,9 +412,9 @@ function ESSocialsEditController($scope, $focus, $filter, UIUtils, SocialUtils)
 function ESSocialsViewController($scope)  {
   'ngInject';
 
-  $scope.openSocial = function($event, social) {
-    $event.stopPropagation();
-    return $scope.openLink($event, social.url, {
+  $scope.openSocial = function(event, social) {
+    event.stopPropagation();
+    return $scope.openLink(event, social.url, {
       type: social.type
     });
   };
@@ -482,7 +513,6 @@ function ESPositionEditController($scope, csConfig, esGeo, ModalUtils) {
         .catch(function(err) {
           console.error(err); // Silent
           loadingCurrentPosition = false;
-          //$scope.form.geoPoint.$setValidity('required', false);
         });
     }
 
@@ -507,22 +537,24 @@ function ESPositionEditController($scope, csConfig, esGeo, ModalUtils) {
       });
   };
 
-  $scope.onCityChanged = function() {
-    if ($scope.loading) return;
-    if ($scope.form) {
-      $scope.form.$valid = undefined;
-    }
-    if ($scope.formPosition.enable) {
-      return $scope.tryToLocalize();
-    }
-  };
+    $scope.onCityChanged = function() {
+        if ($scope.loading) return;
+        if ($scope.formPosition.enable) {
+          if ($scope.formData.geoPoint) {
+            // Invalidate the position
+            $scope.formData.geoPoint.lat = undefined;
+            $scope.formData.geoPoint.lon = undefined;
+          }
+          return $scope.tryToLocalize();
+        }
+    };
 
   $scope.onUseGeopointChanged = function() {
     if ($scope.loading) return;
     if (!$scope.formPosition.enable) {
       if ($scope.formData.geoPoint) {
-        $scope.formData.geoPoint = null;
-        //$scope.form.geoPoint.$setValidity('required', true);
+        $scope.formData.geoPoint.lat = undefined;
+        $scope.formData.geoPoint.lon = undefined;
         $scope.dirty = true;
       }
     }
@@ -696,7 +728,7 @@ function ESLookupPositionController($scope, $q, csConfig, esGeo, ModalUtils) {
   };
 }
 
-function ESSearchPositionItemController($scope, $q, $timeout, ModalUtils, csConfig, esGeo) {
+function ESSearchPositionItemController($scope, $timeout, ModalUtils, csConfig, esGeo) {
   'ngInject';
 
   // The default country used for address localisation
@@ -780,15 +812,20 @@ function ESSearchPositionItemController($scope, $q, $timeout, ModalUtils, csConf
   $scope.showDropdown = function() {
     var text = $scope.search.location && $scope.search.location.trim();
     if (!text || text.length < minLength) {
-      $scope.locations = undefined;
-      return $q.when(); // nothing to search
+        return $scope.hideDropdown(true/*force, if still loading*/);
     }
 
+    // Compute a request id, to apply response only if current request
+    var requestId = ($scope.requestId && $scope.requestId + 1) || 1;
+    $scope.requestId = requestId;
+
     loadingPosition = true;
 
     // Execute the given query
     return esGeo.point.searchByAddress(text)
       .then(function(res) {
+        if ($scope.requestId != requestId) return; // Skip apply if not same request:
+
         loadingPosition = false;
         $scope.locations = res||[];
         $scope.license = res && res.length && res[0].license;
@@ -800,6 +837,7 @@ function ESSearchPositionItemController($scope, $q, $timeout, ModalUtils, csConf
   };
 
   $scope.hideDropdown = function(force) {
+    // force, even if still loading
     if (force) {
       $scope.locations = undefined;
       $scope.selectLocationIndex = -1;
diff --git a/www/plugins/map/js/controllers/user-controllers.js b/www/plugins/map/js/controllers/user-controllers.js
index 9fff394e579b9fed2a53f95ccdf11c76feb01ac6..5b429c2e5ed9531fca8050bdfbaca42372a7a01d 100644
--- a/www/plugins/map/js/controllers/user-controllers.js
+++ b/www/plugins/map/js/controllers/user-controllers.js
@@ -9,7 +9,16 @@ angular.module('cesium.map.user.controllers', ['cesium.services', 'cesium.map.se
 
       PluginServiceProvider
 
-        .extendState('app.user_edit_profile', {
+        .extendState('app.edit_profile', {
+          points: {
+            'after-position': {
+              templateUrl: 'plugins/map/templates/user/edit_profile_extend.html',
+              controller: 'MapEditProfileViewCtrl'
+            }
+          }
+        })
+
+        .extendState('app.edit_profile_by_id', {
           points: {
             'after-position': {
               templateUrl: 'plugins/map/templates/user/edit_profile_extend.html',