Better crop mechanism for the grid image editor (#8023)
This commit is contained in:
@@ -5,38 +5,38 @@
|
||||
* @function
|
||||
**/
|
||||
angular.module("umbraco.directives")
|
||||
.directive('umbImageCrop',
|
||||
function ($timeout, cropperHelper) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/imaging/umb-image-crop.html',
|
||||
scope: {
|
||||
src: '=',
|
||||
width: '@',
|
||||
height: '@',
|
||||
crop: "=",
|
||||
center: "=",
|
||||
maxSize: '@'
|
||||
},
|
||||
.directive('umbImageCrop',
|
||||
function ($timeout, cropperHelper) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/imaging/umb-image-crop.html',
|
||||
scope: {
|
||||
src: '=',
|
||||
width: '@',
|
||||
height: '@',
|
||||
crop: "=",
|
||||
center: "=",
|
||||
maxSize: '@'
|
||||
},
|
||||
|
||||
link: function(scope, element, attrs) {
|
||||
link: function (scope, element, attrs) {
|
||||
|
||||
let sliderRef = null;
|
||||
|
||||
scope.width = 400;
|
||||
scope.height = 320;
|
||||
scope.width = 400;
|
||||
scope.height = 320;
|
||||
|
||||
scope.dimensions = {
|
||||
image: {},
|
||||
cropper:{},
|
||||
viewport:{},
|
||||
margin: 20,
|
||||
scale: {
|
||||
min: 0,
|
||||
max: 3,
|
||||
current: 1
|
||||
}
|
||||
scope.dimensions = {
|
||||
image: {},
|
||||
cropper: {},
|
||||
viewport: {},
|
||||
margin: 20,
|
||||
scale: {
|
||||
min: 0,
|
||||
max: 3,
|
||||
current: 1
|
||||
}
|
||||
};
|
||||
|
||||
scope.sliderOptions = {
|
||||
@@ -84,211 +84,232 @@ angular.module("umbraco.directives")
|
||||
}
|
||||
};
|
||||
|
||||
//live rendering of viewport and image styles
|
||||
scope.style = function () {
|
||||
return {
|
||||
'height': (parseInt(scope.dimensions.viewport.height, 10)) + 'px',
|
||||
'width': (parseInt(scope.dimensions.viewport.width, 10)) + 'px'
|
||||
};
|
||||
};
|
||||
//live rendering of viewport and image styles
|
||||
scope.style = function () {
|
||||
return {
|
||||
'height': (parseInt(scope.dimensions.viewport.height, 10)) + 'px',
|
||||
'width': (parseInt(scope.dimensions.viewport.width, 10)) + 'px'
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
//elements
|
||||
var $viewport = element.find(".viewport");
|
||||
var $image = element.find("img");
|
||||
var $overlay = element.find(".overlay");
|
||||
var $container = element.find(".crop-container");
|
||||
//elements
|
||||
var $viewport = element.find(".viewport");
|
||||
var $image = element.find("img");
|
||||
var $overlay = element.find(".overlay");
|
||||
var $container = element.find(".crop-container");
|
||||
|
||||
//default constraints for drag n drop
|
||||
var constraints = {left: {max: scope.dimensions.margin, min: scope.dimensions.margin}, top: {max: scope.dimensions.margin, min: scope.dimensions.margin} };
|
||||
scope.constraints = constraints;
|
||||
//default constraints for drag n drop
|
||||
var constraints = { left: { max: scope.dimensions.margin, min: scope.dimensions.margin }, top: { max: scope.dimensions.margin, min: scope.dimensions.margin } };
|
||||
scope.constraints = constraints;
|
||||
|
||||
|
||||
//set constaints for cropping drag and drop
|
||||
var setConstraints = function(){
|
||||
constraints.left.min = scope.dimensions.margin + scope.dimensions.cropper.width - scope.dimensions.image.width;
|
||||
constraints.top.min = scope.dimensions.margin + scope.dimensions.cropper.height - scope.dimensions.image.height;
|
||||
};
|
||||
//set constaints for cropping drag and drop
|
||||
var setConstraints = function () {
|
||||
constraints.left.min = scope.dimensions.margin + scope.dimensions.cropper.width - scope.dimensions.image.width;
|
||||
constraints.top.min = scope.dimensions.margin + scope.dimensions.cropper.height - scope.dimensions.image.height;
|
||||
};
|
||||
|
||||
var setDimensions = function(originalImage){
|
||||
originalImage.width("auto");
|
||||
originalImage.height("auto");
|
||||
var setDimensions = function (originalImage) {
|
||||
originalImage.width("auto");
|
||||
originalImage.height("auto");
|
||||
|
||||
var image = {};
|
||||
image.originalWidth = originalImage.width();
|
||||
image.originalHeight = originalImage.height();
|
||||
var image = {};
|
||||
image.originalWidth = originalImage.width();
|
||||
image.originalHeight = originalImage.height();
|
||||
|
||||
image.width = image.originalWidth;
|
||||
image.height = image.originalHeight;
|
||||
image.left = originalImage[0].offsetLeft;
|
||||
image.top = originalImage[0].offsetTop;
|
||||
image.width = image.originalWidth;
|
||||
image.height = image.originalHeight;
|
||||
image.left = originalImage[0].offsetLeft;
|
||||
image.top = originalImage[0].offsetTop;
|
||||
|
||||
scope.dimensions.image = image;
|
||||
scope.dimensions.image = image;
|
||||
|
||||
//unscaled editor size
|
||||
//var viewPortW = $viewport.width();
|
||||
//var viewPortH = $viewport.height();
|
||||
var _viewPortW = parseInt(scope.width, 10);
|
||||
var _viewPortH = parseInt(scope.height, 10);
|
||||
//unscaled editor size
|
||||
//var viewPortW = $viewport.width();
|
||||
//var viewPortH = $viewport.height();
|
||||
var _viewPortW = parseInt(scope.width, 10);
|
||||
var _viewPortH = parseInt(scope.height, 10);
|
||||
|
||||
//if we set a constraint we will scale it down if needed
|
||||
if(scope.maxSize){
|
||||
var ratioCalculation = cropperHelper.scaleToMaxSize(
|
||||
_viewPortW,
|
||||
_viewPortH,
|
||||
scope.maxSize);
|
||||
//if we set a constraint we will scale it down if needed
|
||||
if (scope.maxSize) {
|
||||
var ratioCalculation = cropperHelper.scaleToMaxSize(
|
||||
_viewPortW,
|
||||
_viewPortH,
|
||||
scope.maxSize);
|
||||
|
||||
//so if we have a max size, override the thumb sizes
|
||||
_viewPortW = ratioCalculation.width;
|
||||
_viewPortH = ratioCalculation.height;
|
||||
}
|
||||
//so if we have a max size, override the thumb sizes
|
||||
_viewPortW = ratioCalculation.width;
|
||||
_viewPortH = ratioCalculation.height;
|
||||
}
|
||||
|
||||
scope.dimensions.viewport.width = _viewPortW + 2 * scope.dimensions.margin;
|
||||
scope.dimensions.viewport.height = _viewPortH + 2 * scope.dimensions.margin;
|
||||
scope.dimensions.cropper.width = _viewPortW; // scope.dimensions.viewport.width - 2 * scope.dimensions.margin;
|
||||
scope.dimensions.cropper.height = _viewPortH; // scope.dimensions.viewport.height - 2 * scope.dimensions.margin;
|
||||
};
|
||||
scope.dimensions.viewport.width = _viewPortW + 2 * scope.dimensions.margin;
|
||||
scope.dimensions.viewport.height = _viewPortH + 2 * scope.dimensions.margin;
|
||||
scope.dimensions.cropper.width = _viewPortW; // scope.dimensions.viewport.width - 2 * scope.dimensions.margin;
|
||||
scope.dimensions.cropper.height = _viewPortH; // scope.dimensions.viewport.height - 2 * scope.dimensions.margin;
|
||||
};
|
||||
|
||||
//resize to a given ratio
|
||||
var resizeImageToScale = function(ratio){
|
||||
//do stuff
|
||||
var size = cropperHelper.calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio);
|
||||
scope.dimensions.image.width = size.width;
|
||||
scope.dimensions.image.height = size.height;
|
||||
//resize to a given ratio
|
||||
var resizeImageToScale = function (ratio) {
|
||||
//do stuff
|
||||
var size = cropperHelper.calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio);
|
||||
scope.dimensions.image.width = size.width;
|
||||
scope.dimensions.image.height = size.height;
|
||||
|
||||
setConstraints();
|
||||
validatePosition(scope.dimensions.image.left, scope.dimensions.image.top);
|
||||
};
|
||||
setConstraints();
|
||||
validatePosition(scope.dimensions.image.left, scope.dimensions.image.top);
|
||||
};
|
||||
|
||||
//resize the image to a predefined crop coordinate
|
||||
var resizeImageToCrop = function(){
|
||||
scope.dimensions.image = cropperHelper.convertToStyle(
|
||||
scope.crop,
|
||||
{width: scope.dimensions.image.originalWidth, height: scope.dimensions.image.originalHeight},
|
||||
scope.dimensions.cropper,
|
||||
scope.dimensions.margin);
|
||||
//resize the image to a predefined crop coordinate
|
||||
var resizeImageToCrop = function () {
|
||||
scope.dimensions.image = cropperHelper.convertToStyle(
|
||||
scope.crop,
|
||||
{ width: scope.dimensions.image.originalWidth, height: scope.dimensions.image.originalHeight },
|
||||
scope.dimensions.cropper,
|
||||
scope.dimensions.margin);
|
||||
|
||||
var ratioCalculation = cropperHelper.calculateAspectRatioFit(
|
||||
scope.dimensions.image.originalWidth,
|
||||
scope.dimensions.image.originalHeight,
|
||||
scope.dimensions.cropper.width,
|
||||
scope.dimensions.cropper.height,
|
||||
true);
|
||||
var ratioCalculation = cropperHelper.calculateAspectRatioFit(
|
||||
scope.dimensions.image.originalWidth,
|
||||
scope.dimensions.image.originalHeight,
|
||||
scope.dimensions.cropper.width,
|
||||
scope.dimensions.cropper.height,
|
||||
true);
|
||||
|
||||
scope.dimensions.scale.current = scope.dimensions.image.ratio;
|
||||
scope.dimensions.scale.current = scope.dimensions.image.ratio;
|
||||
|
||||
// Update min and max based on original width/height
|
||||
scope.dimensions.scale.min = ratioCalculation.ratio;
|
||||
// Update min and max based on original width/height
|
||||
scope.dimensions.scale.min = ratioCalculation.ratio;
|
||||
scope.dimensions.scale.max = 2;
|
||||
};
|
||||
};
|
||||
|
||||
var validatePosition = function(left, top){
|
||||
if(left > constraints.left.max)
|
||||
{
|
||||
left = constraints.left.max;
|
||||
}
|
||||
var validatePosition = function (left, top) {
|
||||
if (left > constraints.left.max) {
|
||||
left = constraints.left.max;
|
||||
}
|
||||
|
||||
if(left <= constraints.left.min){
|
||||
left = constraints.left.min;
|
||||
}
|
||||
if (left <= constraints.left.min) {
|
||||
left = constraints.left.min;
|
||||
}
|
||||
|
||||
if(top > constraints.top.max)
|
||||
{
|
||||
top = constraints.top.max;
|
||||
}
|
||||
if(top <= constraints.top.min){
|
||||
top = constraints.top.min;
|
||||
}
|
||||
if (top > constraints.top.max) {
|
||||
top = constraints.top.max;
|
||||
}
|
||||
if (top <= constraints.top.min) {
|
||||
top = constraints.top.min;
|
||||
}
|
||||
|
||||
if(scope.dimensions.image.left !== left){
|
||||
scope.dimensions.image.left = left;
|
||||
}
|
||||
if (scope.dimensions.image.left !== left) {
|
||||
scope.dimensions.image.left = left;
|
||||
}
|
||||
|
||||
if(scope.dimensions.image.top !== top){
|
||||
scope.dimensions.image.top = top;
|
||||
}
|
||||
};
|
||||
if (scope.dimensions.image.top !== top) {
|
||||
scope.dimensions.image.top = top;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//sets scope.crop to the recalculated % based crop
|
||||
var calculateCropBox = function(){
|
||||
scope.crop = cropperHelper.pixelsToCoordinates(scope.dimensions.image, scope.dimensions.cropper.width, scope.dimensions.cropper.height, scope.dimensions.margin);
|
||||
};
|
||||
//sets scope.crop to the recalculated % based crop
|
||||
var calculateCropBox = function () {
|
||||
scope.crop = cropperHelper.pixelsToCoordinates(scope.dimensions.image, scope.dimensions.cropper.width, scope.dimensions.cropper.height, scope.dimensions.margin);
|
||||
};
|
||||
|
||||
|
||||
//Drag and drop positioning, using jquery ui draggable
|
||||
var onStartDragPosition, top, left;
|
||||
$overlay.draggable({
|
||||
drag: function(event, ui) {
|
||||
scope.$apply(function(){
|
||||
validatePosition(ui.position.left, ui.position.top);
|
||||
});
|
||||
},
|
||||
stop: function(event, ui){
|
||||
scope.$apply(function(){
|
||||
//make sure that every validates one more time...
|
||||
validatePosition(ui.position.left, ui.position.top);
|
||||
//Drag and drop positioning, using jquery ui draggable
|
||||
var onStartDragPosition, top, left;
|
||||
$overlay.draggable({
|
||||
drag: function (event, ui) {
|
||||
scope.$apply(function () {
|
||||
validatePosition(ui.position.left, ui.position.top);
|
||||
});
|
||||
},
|
||||
stop: function (event, ui) {
|
||||
scope.$apply(function () {
|
||||
//make sure that every validates one more time...
|
||||
validatePosition(ui.position.left, ui.position.top);
|
||||
|
||||
calculateCropBox();
|
||||
scope.dimensions.image.rnd = Math.random();
|
||||
});
|
||||
}
|
||||
});
|
||||
calculateCropBox();
|
||||
scope.dimensions.image.rnd = Math.random();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var init = function(image){
|
||||
scope.loaded = false;
|
||||
var init = function (image) {
|
||||
scope.loaded = false;
|
||||
|
||||
//set dimensions on image, viewport, cropper etc
|
||||
setDimensions(image);
|
||||
//set dimensions on image, viewport, cropper etc
|
||||
setDimensions(image);
|
||||
|
||||
//create a default crop if we haven't got one already
|
||||
//create a default crop if we haven't got one already
|
||||
var createDefaultCrop = !scope.crop;
|
||||
if (createDefaultCrop) {
|
||||
calculateCropBox();
|
||||
}
|
||||
|
||||
resizeImageToCrop();
|
||||
resizeImageToCrop();
|
||||
|
||||
//if we're creating a new crop, make sure to zoom out fully
|
||||
if (createDefaultCrop) {
|
||||
scope.dimensions.scale.current = scope.dimensions.scale.min;
|
||||
resizeImageToScale(scope.dimensions.scale.min);
|
||||
resizeImageToScale(scope.dimensions.scale.min);
|
||||
|
||||
if (scope.center) {
|
||||
// Move image to focal point if set
|
||||
// Repeating a few calls here, but logic is too difficult to follow elsewhere
|
||||
var x1 = Math.min(
|
||||
Math.max(
|
||||
scope.center.left * scope.dimensions.image.width - scope.dimensions.cropper.width / 2,
|
||||
0
|
||||
),
|
||||
scope.dimensions.image.width - scope.dimensions.cropper.width
|
||||
);
|
||||
var y1 = Math.min(
|
||||
Math.max(
|
||||
scope.center.top * scope.dimensions.image.height - scope.dimensions.cropper.height / 2,
|
||||
0
|
||||
),
|
||||
scope.dimensions.image.height - scope.dimensions.cropper.height
|
||||
);
|
||||
scope.dimensions.image.left = x1;
|
||||
scope.dimensions.image.top = y1;
|
||||
calculateCropBox();
|
||||
resizeImageToCrop();
|
||||
}
|
||||
}
|
||||
|
||||
//sets constaints for the cropper
|
||||
setConstraints();
|
||||
scope.loaded = true;
|
||||
};
|
||||
//sets constaints for the cropper
|
||||
setConstraints();
|
||||
scope.loaded = true;
|
||||
};
|
||||
|
||||
|
||||
// Watchers
|
||||
scope.$watchCollection('[width, height]', function(newValues, oldValues){
|
||||
// We have to reinit the whole thing if
|
||||
// one of the external params changes
|
||||
if(newValues !== oldValues){
|
||||
setDimensions($image);
|
||||
setConstraints();
|
||||
}
|
||||
});
|
||||
// Watchers
|
||||
scope.$watchCollection('[width, height]', function (newValues, oldValues) {
|
||||
// We have to reinit the whole thing if
|
||||
// one of the external params changes
|
||||
if (newValues !== oldValues) {
|
||||
setDimensions($image);
|
||||
setConstraints();
|
||||
}
|
||||
});
|
||||
|
||||
var throttledResizing = _.throttle(function(){
|
||||
var throttledResizing = _.throttle(function () {
|
||||
resizeImageToScale(scope.dimensions.scale.current);
|
||||
calculateCropBox();
|
||||
}, 15);
|
||||
calculateCropBox();
|
||||
}, 15);
|
||||
|
||||
// Happens when we change the scale
|
||||
// Happens when we change the scale
|
||||
scope.$watch("dimensions.scale.current", function (newValue, oldValue) {
|
||||
if (scope.loaded) {
|
||||
throttledResizing();
|
||||
}
|
||||
});
|
||||
if (scope.loaded) {
|
||||
throttledResizing();
|
||||
}
|
||||
});
|
||||
|
||||
// Init
|
||||
$image.on("load", function(){
|
||||
$timeout(function(){
|
||||
init($image);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
// Init
|
||||
$image.on("load", function () {
|
||||
$timeout(function () {
|
||||
init($image);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -17,13 +17,11 @@ angular.module("umbraco")
|
||||
vm.changeSearch = changeSearch;
|
||||
vm.submitFolder = submitFolder;
|
||||
vm.enterSubmitFolder = enterSubmitFolder;
|
||||
vm.focalPointChanged = focalPointChanged;
|
||||
vm.changePagination = changePagination;
|
||||
|
||||
vm.clickHandler = clickHandler;
|
||||
vm.clickItemName = clickItemName;
|
||||
vm.gotoFolder = gotoFolder;
|
||||
vm.shouldShowUrl = shouldShowUrl;
|
||||
|
||||
var dialogOptions = $scope.model;
|
||||
|
||||
@@ -131,6 +129,7 @@ angular.module("umbraco")
|
||||
} else {
|
||||
// if a target is specified, go look it up - generally this target will just contain ids not the actual full
|
||||
// media object so we need to look it up
|
||||
var originalTarget = $scope.target;
|
||||
var id = $scope.target.udi ? $scope.target.udi : $scope.target.id;
|
||||
var altText = $scope.target.altText;
|
||||
|
||||
@@ -140,13 +139,16 @@ angular.module("umbraco")
|
||||
entityResource.getById(id, "Media")
|
||||
.then(function (node) {
|
||||
$scope.target = node;
|
||||
if (ensureWithinStartNode(node)) {
|
||||
// Moving directly to existing node's folder
|
||||
gotoFolder({ id: node.parentId }).then(function() {
|
||||
selectMedia(node);
|
||||
$scope.target.url = mediaHelper.resolveFileFromEntity(node);
|
||||
$scope.target.thumbnail = mediaHelper.resolveFileFromEntity(node, true);
|
||||
$scope.target.altText = altText;
|
||||
$scope.target.focalPoint = originalTarget.focalPoint;
|
||||
$scope.target.coordinates = originalTarget.coordinates;
|
||||
openDetailsDialog();
|
||||
}
|
||||
});
|
||||
}, gotoStartNode);
|
||||
} else {
|
||||
// No ID set - then this is going to be a tmpimg that has not been uploaded
|
||||
@@ -346,25 +348,31 @@ angular.module("umbraco")
|
||||
}
|
||||
|
||||
function openDetailsDialog() {
|
||||
localizationService.localize("defaultdialogs_editSelectedMedia").then(function (data) {
|
||||
vm.mediaPickerDetailsOverlay = {
|
||||
show: true,
|
||||
title: data,
|
||||
disableFocalPoint: $scope.disableFocalPoint,
|
||||
submit: function (model) {
|
||||
$scope.model.selection.push($scope.target);
|
||||
$scope.model.submit($scope.model);
|
||||
|
||||
const dialog = {
|
||||
view: "views/common/infiniteeditors/mediapicker/overlays/mediacropdetails.html",
|
||||
size: "small",
|
||||
cropSize: $scope.cropSize,
|
||||
target: $scope.target,
|
||||
disableFocalPoint: $scope.disableFocalPoint,
|
||||
submit: function (model) {
|
||||
console.log("model", model);
|
||||
|
||||
vm.mediaPickerDetailsOverlay.show = false;
|
||||
vm.mediaPickerDetailsOverlay = null;
|
||||
},
|
||||
close: function (oldModel) {
|
||||
vm.mediaPickerDetailsOverlay.show = false;
|
||||
vm.mediaPickerDetailsOverlay = null;
|
||||
$scope.model.selection.push($scope.target);
|
||||
$scope.model.submit($scope.model);
|
||||
|
||||
close();
|
||||
}
|
||||
};
|
||||
editorService.close();
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
|
||||
//close();
|
||||
}
|
||||
};
|
||||
|
||||
localizationService.localize("defaultdialogs_editSelectedMedia").then(value => {
|
||||
dialog.title = value;
|
||||
editorService.open(dialog);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -515,40 +523,6 @@ angular.module("umbraco")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the umbImageGravity component updates the focal point value
|
||||
* @param {any} left
|
||||
* @param {any} top
|
||||
*/
|
||||
function focalPointChanged(left, top) {
|
||||
// update the model focalpoint value
|
||||
$scope.target.focalPoint = {
|
||||
left: left,
|
||||
top: top
|
||||
};
|
||||
}
|
||||
|
||||
function setUpdatedMediaNodes(item) {
|
||||
// add udi to list of updated media items so we easily can update them in other editors
|
||||
if ($scope.model.updatedMediaNodes.indexOf(item.udi) === -1) {
|
||||
$scope.model.updatedMediaNodes.push(item.udi);
|
||||
}
|
||||
}
|
||||
|
||||
function shouldShowUrl() {
|
||||
if (!$scope.target) {
|
||||
return false;
|
||||
}
|
||||
if ($scope.target.id) {
|
||||
return false;
|
||||
}
|
||||
if ($scope.target.url && $scope.target.url.toLower().indexOf("blob:") === 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function submit() {
|
||||
if ($scope.model && $scope.model.submit) {
|
||||
$scope.model.submit($scope.model);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div ng-controller="Umbraco.Editors.MediaPickerController as vm">
|
||||
<umb-editor-view >
|
||||
<umb-editor-view>
|
||||
|
||||
<umb-editor-header
|
||||
name="model.title"
|
||||
@@ -132,64 +132,6 @@
|
||||
|
||||
</div>
|
||||
|
||||
<umb-overlay ng-if="vm.mediaPickerDetailsOverlay.show" model="vm.mediaPickerDetailsOverlay" position="right">
|
||||
|
||||
<div class="umb-control-group" ng-if="vm.shouldShowUrl()">
|
||||
<h5>
|
||||
<localize key="@general_url"></localize>
|
||||
</h5>
|
||||
<input type="text" localize="placeholder" placeholder="@general_url" class="umb-property-editor umb-textstring" ng-model="target.url" />
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group">
|
||||
<h5>
|
||||
<localize key="@content_altTextOptional"></localize>
|
||||
</h5>
|
||||
<input type="text" class="umb-property-editor umb-textstring" ng-model="target.altText" />
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group">
|
||||
|
||||
<div ng-if="vm.mediaPickerDetailsOverlay.disableFocalPoint && target.thumbnail">
|
||||
<h5>
|
||||
<localize key="general_preview">Preview</localize>
|
||||
</h5>
|
||||
|
||||
<img ng-src="{{target.thumbnail}}" alt="{{target.name}}" />
|
||||
</div>
|
||||
|
||||
<div ng-if="!vm.mediaPickerDetailsOverlay.disableFocalPoint">
|
||||
<h5>
|
||||
<localize key="@general_focalPoint">Focal point</localize>
|
||||
</h5>
|
||||
|
||||
<div ng-if="target.url">
|
||||
<umb-image-gravity src="target.url"
|
||||
center="target.focalPoint"
|
||||
on-value-changed="vm.focalPointChanged(left, top)">
|
||||
</umb-image-gravity>
|
||||
</div>
|
||||
|
||||
<div ng-if="cropSize">
|
||||
|
||||
<h5>
|
||||
<localize key="general_preview">Preview</localize>
|
||||
</h5>
|
||||
|
||||
<umb-image-thumbnail center="target.focalPoint"
|
||||
src="target.url"
|
||||
height="{{cropSize.height}}"
|
||||
width="{{cropSize.width}}"
|
||||
max-size="400">
|
||||
</umb-image-thumbnail>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</umb-overlay>
|
||||
|
||||
</form>
|
||||
|
||||
</umb-editor-container>
|
||||
@@ -218,4 +160,4 @@
|
||||
|
||||
</umb-editor-view>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.Editors.MediaCropDetailsController",
|
||||
function ($scope) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.submit = submit;
|
||||
vm.close = close;
|
||||
|
||||
if (!$scope.model.target.coordinates && !$scope.model.target.focalPoint) {
|
||||
$scope.model.target.focalPoint = { left: .5, top: .5 };
|
||||
}
|
||||
|
||||
vm.shouldShowUrl = shouldShowUrl;
|
||||
vm.focalPointChanged = focalPointChanged;
|
||||
|
||||
if (!$scope.model.target.image) {
|
||||
$scope.model.target.image = $scope.model.target.url;
|
||||
}
|
||||
|
||||
function shouldShowUrl() {
|
||||
if (!$scope.model.target) {
|
||||
return false;
|
||||
}
|
||||
if ($scope.model.target.id) {
|
||||
return false;
|
||||
}
|
||||
if ($scope.model.target.url && $scope.model.target.url.toLower().indexOf("blob:") === 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the umbImageGravity component updates the focal point value
|
||||
* @param {any} left
|
||||
* @param {any} top
|
||||
*/
|
||||
function focalPointChanged(left, top) {
|
||||
// update the model focalpoint value
|
||||
$scope.model.target.focalPoint = {
|
||||
left: left,
|
||||
top: top
|
||||
};
|
||||
}
|
||||
|
||||
function submit() {
|
||||
if ($scope.model && $scope.model.submit) {
|
||||
$scope.model.submit($scope.model);
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
if ($scope.model && $scope.model.close) {
|
||||
$scope.model.close($scope.model);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
<div ng-controller="Umbraco.Editors.MediaCropDetailsController as vm">
|
||||
<umb-editor-view>
|
||||
|
||||
<umb-editor-header name="model.title"
|
||||
name-locked="true"
|
||||
hide-alias="true"
|
||||
hide-icon="true"
|
||||
hide-description="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
<div class="umb-control-group" ng-if="vm.shouldShowUrl()">
|
||||
<h5>
|
||||
<localize key="@general_url"></localize>
|
||||
</h5>
|
||||
<input type="text" localize="placeholder" placeholder="@general_url" class="umb-property-editor umb-textstring" ng-model="model.target.url" />
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group" ng-if="model.target">
|
||||
<h5>
|
||||
<localize key="@content_altTextOptional"></localize>
|
||||
</h5>
|
||||
<input type="text" class="umb-property-editor umb-textstring" ng-model="model.target.altText" umb-auto-focus />
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group" ng-if="model.target">
|
||||
|
||||
<div ng-if="model.disableFocalPoint && model.target.thumbnail">
|
||||
<h5>
|
||||
<localize key="general_preview">Preview</localize>
|
||||
</h5>
|
||||
|
||||
<img ng-src="{{model.target.thumbnail}}" alt="{{model.target.name}}" />
|
||||
</div>
|
||||
|
||||
<div ng-if="!model.disableFocalPoint">
|
||||
<h5>
|
||||
<localize key="@general_cropSection">Crop section</localize>
|
||||
</h5>
|
||||
|
||||
<div>
|
||||
<umb-image-crop height="{{model.cropSize.height}}"
|
||||
width="{{model.cropSize.width}}"
|
||||
crop="model.target.coordinates"
|
||||
center="model.target.focalPoint"
|
||||
max-size="400"
|
||||
src="model.target.image">
|
||||
</umb-image-crop>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-editor-footer>
|
||||
<umb-editor-footer-content-right>
|
||||
|
||||
<umb-button action="vm.close()"
|
||||
button-style="link"
|
||||
shortcut="esc"
|
||||
label-key="general_close"
|
||||
type="button">
|
||||
</umb-button>
|
||||
|
||||
<umb-button button-style="success"
|
||||
label-key="buttons_select"
|
||||
type="button"
|
||||
disabled="model.selection.length === 0"
|
||||
action="vm.submit(model)">
|
||||
</umb-button>
|
||||
|
||||
</umb-editor-footer-content-right>
|
||||
</umb-editor-footer>
|
||||
|
||||
</umb-editor-view>
|
||||
</div>
|
||||
@@ -22,7 +22,16 @@ angular.module("umbraco")
|
||||
$scope.setImage = function(){
|
||||
var startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : undefined;
|
||||
var startNodeIsVirtual = startNodeId ? $scope.model.config.startNodeIsVirtual : undefined;
|
||||
|
||||
var value = $scope.control.value;
|
||||
var target = value
|
||||
? {
|
||||
udi: value.udi,
|
||||
url: value.image,
|
||||
image: value.image,
|
||||
focalPoint: value.focalPoint,
|
||||
coordinates: value.coordinates
|
||||
}
|
||||
: null;
|
||||
var mediaPicker = {
|
||||
startNodeId: startNodeId,
|
||||
startNodeIsVirtual: startNodeIsVirtual,
|
||||
@@ -31,11 +40,13 @@ angular.module("umbraco")
|
||||
disableFolderSelect: true,
|
||||
onlyImages: true,
|
||||
dataTypeKey: $scope.model.dataTypeKey,
|
||||
currentTarget: target,
|
||||
submit: function(model) {
|
||||
var selectedImage = model.selection[0];
|
||||
|
||||
$scope.control.value = {
|
||||
focalPoint: selectedImage.focalPoint,
|
||||
coordinates: selectedImage.coordinates,
|
||||
id: selectedImage.id,
|
||||
udi: selectedImage.udi,
|
||||
image: selectedImage.image,
|
||||
@@ -66,14 +77,25 @@ angular.module("umbraco")
|
||||
var url = $scope.control.value.image;
|
||||
|
||||
if($scope.control.editor.config && $scope.control.editor.config.size){
|
||||
url += "?width=" + $scope.control.editor.config.size.width;
|
||||
if ($scope.control.value.coordinates) {
|
||||
// New way, crop by percent must come before width/height.
|
||||
var coords = $scope.control.value.coordinates;
|
||||
url += "?crop=" + coords.x1 + "," + coords.y1 + "," + coords.x2 + "," + coords.y2 + "&cropmode=percentage";
|
||||
} else {
|
||||
// Here in order not to break existing content where focalPoint were used.
|
||||
// For some reason width/height have to come first when mode=crop.
|
||||
if ($scope.control.value.focalPoint) {
|
||||
url += "?center=" + $scope.control.value.focalPoint.top + "," + $scope.control.value.focalPoint.left;
|
||||
url += "&mode=crop";
|
||||
} else {
|
||||
// Prevent black padding and no crop when focal point not set / changed from default
|
||||
url += "?center=0.5,0.5&mode=crop";
|
||||
}
|
||||
}
|
||||
url += "&width=" + $scope.control.editor.config.size.width;
|
||||
url += "&height=" + $scope.control.editor.config.size.height;
|
||||
url += "&animationprocessmode=first";
|
||||
|
||||
if($scope.control.value.focalPoint){
|
||||
url += "¢er=" + $scope.control.value.focalPoint.top +"," + $scope.control.value.focalPoint.left;
|
||||
url += "&mode=crop";
|
||||
}
|
||||
}
|
||||
|
||||
// set default size if no crop present (moved from the view)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<div ng-controller="Umbraco.PropertyEditors.Grid.MediaController">
|
||||
|
||||
<div class="umb-editor-placeholder" ng-click="setImage()" ng-if="control.value === null">
|
||||
<i class="icon icon-picture"></i>
|
||||
<div class="umb-editor-placeholder" ng-click="setImage()" ng-if="control.value === null">
|
||||
<i class="icon icon-picture"></i>
|
||||
<div ng-id="!control.$inserted" class="help-text"><localize key="grid_clickToInsertImage">Click to insert image</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="thumbnailUrl !== null">
|
||||
<img
|
||||
ng-click="setImage()"
|
||||
ng-src="{{thumbnailUrl}}"
|
||||
class="fullSizeImage" />
|
||||
<div ng-if="thumbnailUrl !== null">
|
||||
<img
|
||||
ng-click="setImage()"
|
||||
ng-src="{{thumbnailUrl}}"
|
||||
class="fullSizeImage" />
|
||||
<input type="text" class="caption" ng-model="control.value.caption" localize="placeholder" placeholder="@grid_placeholderImageCaption" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user