Merge remote-tracking branch 'origin/7.1.0-ImageCropper-WIP' into 7.1.0

This commit is contained in:
perploug
2014-02-11 14:56:41 +01:00
35 changed files with 826 additions and 390 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -5,21 +5,21 @@
* @function
**/
angular.module("umbraco.directives")
.directive('umbImageCrop', function ($timeout, localizationService, $log) {
.directive('umbImageCrop',
function ($timeout, localizationService, cropperHelper, $log) {
return {
restrict: 'E',
replace: true,
templateUrl: 'views/directives/imaging/umb-image-crop.html',
scope: {
src: '=',
width: '=',
height: '=',
crop: "="
width: '@',
height: '@',
crop: "=",
center: "=",
},
link: function(scope, element, attrs) {
scope.scale = 100;
//if image is over this, we re-calculate the editors global ratio
//this will not have an effect on the result, since that is returned in percentage
scope.maxHeight = 500;
@@ -32,17 +32,23 @@ angular.module("umbraco.directives")
image: {},
cropper:{},
viewport:{},
margin: 20,
ratio: 1
margin: 40,
scale: {
min: 0.3,
max: 3,
current: 1
}
};
scope.style = function () {
return {
'height': (parseInt(scope.height, 10) + 2 * scope.dimensions.margin) + 'px',
'width': (parseInt(scope.width, 10) + 2 * scope.dimensions.margin) + 'px'
};
};
//live rendering of viewport and image styles
scope.style = function () {
return {
'height': (parseInt(scope.height, 10) + 2 * scope.dimensions.margin) + 'px',
'width': (parseInt(scope.width, 10) + 2 * scope.dimensions.margin) + 'px'
};
};
//elements
var $viewport = element.find(".viewport");
@@ -51,11 +57,31 @@ angular.module("umbraco.directives")
var $container = element.find(".crop-container");
//default constraints for drag n drop
var constraints = {left: {max: 20, min: 20}, top: {max: 20, min: 20}, };
var setDimensions = function(){
scope.dimensions.image.width = $image.width();
scope.dimensions.image.height = $image.height();
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;
};
var setDimensions = function(originalImage){
originalImage.width("auto");
originalImage.height("auto");
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;
scope.dimensions.image = image;
scope.dimensions.viewport.width = $viewport.width();
scope.dimensions.viewport.height = $viewport.height();
@@ -64,39 +90,12 @@ angular.module("umbraco.directives")
scope.dimensions.cropper.height = scope.dimensions.viewport.height - 2 * scope.dimensions.margin;
};
var setImageSize = function(width, height){
$image.width(width);
$image.height(height);
scope.dimensions.image.width = width;
scope.dimensions.image.height = height;
};
//when loading an image without any crop info, we center and fit it
var fitImage = function(){
fitToViewPort($image);
centerImage($image);
syncOverLay();
setConstraints($image);
};
//utill for centering scaled image
var centerImage = function(img) {
var image_width = img.width(),
image_height = img.height(),
mask_width = $viewport.width(),
mask_height = $viewport.height();
var resizeImageToEditor = function(){
img.css({
'position': 'absolute',
'left': scope.dimensions.viewport.width / 2 - scope.dimensions.image.width / 2,
'top': scope.dimensions.viewport.height / 2 - scope.dimensions.image.height / 2
});
};
//utill for scaling image to fit viewport
var fitToViewPort = function(img) {
//returns size fitting the cropper
var size = calculateAspectRatioFit(
var size = cropperHelper.calculateAspectRatioFit(
scope.dimensions.image.width,
scope.dimensions.image.height,
scope.dimensions.cropper.width,
@@ -104,163 +103,154 @@ angular.module("umbraco.directives")
true);
//sets the image size and updates the scope
setImageSize(size.width, size.height);
scope.dimensions.image.width = size.width;
scope.dimensions.image.height = size.height;
scope.minScale = size.ratio;
scope.maxScale = size.ratio * 3;
scope.currentScale = scope.minScale;
scope.scale = scope.currentScale;
//calculate the best suited ratios
scope.dimensions.scale.min = size.ratio;
scope.dimensions.scale.max = 2;
scope.dimensions.scale.current = size.ratio;
//center the image
var position = cropperHelper.centerInsideViewPort(scope.dimensions.image, scope.dimensions.cropper);
scope.dimensions.top = position.top;
scope.dimensions.left = position.left;
setConstraints();
};
var resizeImageToScale = function(img, ratio){
//resize to a given ratio
var resizeImageToScale = function(ratio){
//do stuff
var size = calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio);
setImageSize(size.width, size.height);
centerImage(img);
scope.currentScale = scope.scale;
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;
syncOverLay();
setConstraints();
validatePosition(scope.dimensions.image.left, scope.dimensions.image.top);
};
//set constaints for cropping drag and drop
var setConstraints = function(img){
//do stuff
var w = img.width(),
h = img.height(),
crop_width = $viewport.width() - 2 * 20,
crop_height = $viewport.height() - 2 * 20;
//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);
constraints.left.min = 20 + crop_width - w;
constraints.top.min = 20 + crop_height - h;
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;
//min max based on original width/height
scope.dimensions.scale.min = ratioCalculation.ratio;
scope.dimensions.scale.max = 2;
};
//utill for getting either min/max aspect ratio to scale image after
var calculateAspectRatioFit = function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) {
var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ];
if(maximize){
ratio = Math.max(ratio[0], ratio[1]);
}else{
ratio = Math.min(ratio[0], ratio[1]);
var validatePosition = function(left, top){
if(left > constraints.left.max)
{
left = constraints.left.max;
}
return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
};
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;
}
scope.dimensions.image.left = left;
scope.dimensions.image.top = top;
};
//utill for scaling width / height given a ratio
var calculateSizeToRatio= function(srcWidth, srcHeight, ratio) {
return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
};
//sets scope.crop to the recalculated % based crop
var calculateCropBox = function(){
scope.crop.left = Math.abs($image[0].offsetLeft - scope.dimensions.margin) / scope.dimensions.image.width;
scope.crop.top = Math.abs($image[0].offsetTop - scope.dimensions.margin) / scope.dimensions.image.height;
scope.crop.right = 1 - Math.abs(scope.dimensions.cropper.width - (scope.dimensions.image.width - scope.crop.left)) / scope.dimensions.image.width;
scope.crop.bottom = 1 - Math.abs(scope.dimensions.cropper.height - (scope.dimensions.image.height - scope.crop.top)) / scope.dimensions.image.height;
};
var calculatePosition = function(crop){
var left = (crop.left * scope.dimensions.image.originalWidth);
var top = (crop.top * scope.dimensions.image.originalHeight);
var cropped_width = scope.dimensions.image.originalWidth - left;
var ratio = cropped_width / scope.dimensions.image.originalWidth;
scope.scale = ratio;
resizeImageToScale($image, ratio);
$image.css({
"top": -top,
"left": -left
});
syncOverLay();
};
var syncOverLay = function(){
$overlay.height($image.height());
$overlay.width($image.width());
$overlay.css({
"top": $image[0].offsetTop,
"left": $image[0].offsetLeft
});
calculateCropBox();
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({
start: function(event, ui) {
syncOverLay();
},
drag: function(event, ui) {
if(ui.position.left <= constraints.left.max && ui.position.left >= constraints.left.min){
$image.css({
'left': ui.position.left
});
}
if(ui.position.top <= constraints.top.max && ui.position.top >= constraints.top.min){
$image.css({
'top': ui.position.top
});
}
scope.$apply(function(){
validatePosition(ui.position.left, ui.position.top);
});
},
stop: function() {
syncOverLay();
stop: function(event, ui){
scope.$apply(function(){
calculateCropBox();
scope.dimensions.image.rnd = Math.random();
});
}
});
var init = function(image){
scope.loaded = false;
//set dimensions on image, viewport, cropper etc
setDimensions(image);
//if we have a crop already position the image
if(scope.crop){
resizeImageToCrop();
}else{
resizeImageToEditor();
}
//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();
}
});
//happens when we change the scale
scope.$watch("dimensions.scale.current", function(){
if(scope.loaded){
resizeImageToScale(scope.dimensions.scale.current);
calculateCropBox();
}
});
//// INIT /////
$image.load(function(){
$timeout(function(){
$image.width("auto");
$image.height("auto");
scope.dimensions.image.originalWidth = $image.width();
scope.dimensions.image.originalHeight = $image.height();
setDimensions();
if(scope.crop && scope.crop.top){
calculatePosition(scope.crop);
}else{
scope.crop = {};
fitImage();
}
scope.loaded = true;
init($image);
});
});
/// WATCHERS ////
scope.$watch("scale", function(){
if(scope.loaded && scope.scale !== scope.currentScale){
resizeImageToScale($image, scope.scale);
setConstraints($image);
}
});
/// WATCHERS ////
scope.$watch("crop", function(newVal, oldVal){
if(scope.loaded && newVal !== oldVal){
calculatePosition(scope.crop);
}
});
}
};
});

View File

@@ -14,104 +14,69 @@ angular.module("umbraco.directives")
templateUrl: 'views/directives/imaging/umb-image-gravity.html',
scope: {
src: '=',
width: "=",
height: "=",
gravity: "="
center: "="
},
link: function(scope, element, attrs) {
//Internal values for keeping track of the dot and the size of the editor
scope.dimensions = {
width: 0,
height: 0,
left: 0,
top: 0
};
//elements
var $viewport = element.find(".viewport");
var $image = element.find("img");
var $overlay = element.find(".overlay");
scope.style = function () {
if(scope.dimensions.width <= 0){
setDimensions();
}
return {
'top': scope.dimensions.top + 'px',
'left': scope.dimensions.left + 'px'
};
};
var setDimensions = function(){
scope.imagewidth = $image.width();
scope.imageheight = $image.height();
};
scope.dimensions.width = $image.width();
scope.dimensions.height = $image.height();
var setImageSize = function(width, height){
$image.width(width);
$image.height(height);
$viewport.width(width);
$viewport.height(height);
};
var fitImage = function(){
fitToViewPort($image);
centerImage($image);
$log.log("centered and fitted");
};
//utill for centering scaled image
var centerImage = function(img) {
img.css({
'position': 'absolute',
'left': scope.width / 2 - scope.imageWidth / 2,
'top': scope.height / 2 - scope.imageHeight / 2
});
};
//utill for scaling image to fit viewport
var fitToViewPort = function(img) {
//returns size fitting the cropper
var size = calculateAspectRatioFit(
scope.imageWidth,
scope.imageHeight,
scope.width,
scope.height,
false);
//sets the image size and updates the scope
setImageSize(size.width, size.height);
};
//utill for getting either min/max aspect ratio to scale image after
var calculateAspectRatioFit = function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) {
var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ];
if(maximize){
ratio = Math.max(ratio[0], ratio[1]);
}else{
ratio = Math.min(ratio[0], ratio[1]);
if(scope.center){
scope.dimensions.left = scope.center.left * scope.dimensions.width -10;
scope.dimensions.top = scope.center.top * scope.dimensions.height -10;
}
return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
};
var calculateGravity = function(){
scope.gravity.left = $overlay[0].offsetLeft + 10;
scope.gravity.top = $overlay[0].offsetTop + 10;
};
};
var calculateGravity = function(){
scope.dimensions.left = $overlay[0].offsetLeft;
scope.dimensions.top = $overlay[0].offsetTop;
scope.center.left = (scope.dimensions.left+10) / scope.dimensions.width;
scope.center.top = (scope.dimensions.top+10) / scope.dimensions.height;
};
//Drag and drop positioning, using jquery ui draggable
var onStartDragPosition, top, left;
//TODO ensure that the point doesnt go outside the box
$overlay.draggable({
containment: "parent",
stop: function() {
calculateGravity();
scope.$apply(function(){
calculateGravity();
});
}
});
//// INIT /////
$image.load(function(){
$timeout(function(){
$image.width("auto");
$image.height("auto");
setDimensions();
fitImage();
scope.loaded = true;
});
});
}
};
});

View File

@@ -7,7 +7,8 @@
* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
**/
angular.module("umbraco.directives")
.directive('umbImageThumbnail', function ($timeout, localizationService, $log) {
.directive('umbImageThumbnail',
function ($timeout, localizationService, cropperHelper, $log) {
return {
restrict: 'E',
replace: true,
@@ -15,17 +16,69 @@ angular.module("umbraco.directives")
scope: {
src: '=',
width: '=',
height: '=',
gravity: "=",
width: '@',
height: '@',
center: "=",
crop: "="
},
link: function(scope, element, attrs) {
scope.marginLeft = 0-Math.abs( scope.width * scope.gravity.left);
scope.marginTop = 0-Math.abs( scope.width * scope.gravity.top);
//// INIT /////
var $image = element.find("img");
$image.load(function(){
$timeout(function(){
$image.width("auto");
$image.height("auto");
scope.image = {};
scope.image.width = $image[0].width;
scope.image.height = $image[0].height;
setPreviewStyle();
});
});
/// WATCHERS ////
scope.$watchCollection('[crop, center]', function(newValues, oldValues){
//we have to reinit the whole thing if
//one of the external params changes
setPreviewStyle();
});
scope.$watch("center", function(){
setPreviewStyle();
}, true);
function setPreviewStyle(){
if(scope.crop && scope.image){
scope.preview = cropperHelper.convertToStyle(
scope.crop,
scope.image,
{width: scope.width, height: scope.height},
0);
}else if(scope.image){
//returns size fitting the cropper
var p = cropperHelper.calculateAspectRatioFit(
scope.image.width,
scope.image.height,
scope.width,
scope.height,
true);
if(scope.center){
var xy = cropperHelper.alignToCoordinates(p, scope.center, {width: scope.width, height: scope.height});
p.top = xy.top;
p.left = xy.left;
}else{
}
p.position = "absolute";
scope.preview = p;
}
}
}
};
});

View File

@@ -19,7 +19,7 @@ function mediaHelper(umbRequestHelper) {
* @param {object} options.imageOnly Optional, if true then will only return a path if the media item is an image
*/
getMediaPropertyValue: function (options) {
return "assets/img/mocks/image.jpg";
return "assets/img/mocks/big-image.jpg";
},
/**
@@ -35,7 +35,7 @@ function mediaHelper(umbRequestHelper) {
* @param {object} options.imageModel The media object to retrieve the image path from
*/
getImagePropertyValue: function (options) {
return "assets/img/mocks/image.jpg";
return "assets/img/mocks/big-image.jpg";
},
/**
* @ngdoc function

View File

@@ -90,17 +90,21 @@ angular.module('umbraco.services')
var t = timeout || 5000;
var a = attributes || undefined;
yepnope.injectCss(path, function () {
if (!scope) {
deferred.resolve(true);
}else{
angularHelper.safeApply(scope, function () {
deferred.resolve(true);
});
}
},a,t);
yepnope({
forceCSS: true,
load: path,
timeout: t,
attrs: a,
callback: function (url, result, key) {
if (!scope) {
deferred.resolve(true);
}else{
angularHelper.safeApply(scope, function () {
deferred.resolve(true);
});
}
}
});
return deferred.promise;
},
@@ -124,6 +128,26 @@ angular.module('umbraco.services')
var t = timeout || 5000;
var a = attributes || undefined;
yepnope({
forceJS: true,
load: path,
timeout: t,
attrs: a,
callback: function (url, result, key) {
if (!scope) {
deferred.resolve(true);
}else{
angularHelper.safeApply(scope, function () {
deferred.resolve(true);
});
}
}
});
/*
yepnope.injectJs(path, function () {
if (!scope) {
@@ -135,6 +159,7 @@ angular.module('umbraco.services')
}
},a,t);
*/
return deferred.promise;
},

View File

@@ -0,0 +1,130 @@
/**
* @ngdoc service
* @name umbraco.services.mediaHelper
* @description A helper object used for dealing with media items
**/
function cropperHelper(umbRequestHelper) {
var service = {
//utill for getting either min/max aspect ratio to scale image after
calculateAspectRatioFit : function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) {
var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ];
if(maximize){
ratio = Math.max(ratio[0], ratio[1]);
}else{
ratio = Math.min(ratio[0], ratio[1]);
}
return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
},
//utill for scaling width / height given a ratio
calculateSizeToRatio : function(srcWidth, srcHeight, ratio) {
return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
},
//returns a ng-style object with top,left,width,height pixel measurements
//expects {left,right,top,bottom} - {width,height}, {width,height}, int
//offset is just to push the image position a number of pixels from top,left
convertToStyle : function(coordinates, originalSize, viewPort, offset){
var coordinates_px = service.coordinatesToPixels(coordinates, originalSize, offset);
var _offset = offset || 0;
var x = coordinates.x1 + Math.abs(coordinates.x2);
var left_of_x = originalSize.width - (originalSize.width * x);
var ratio = viewPort.width / left_of_x;
var style = {
position: "absolute",
top: -(coordinates_px.y1*ratio)+ _offset,
left: -(coordinates_px.x1* ratio)+ _offset,
width: Math.floor(originalSize.width * ratio),
height: Math.floor(originalSize.height * ratio),
originalWidth: originalSize.width,
originalHeight: originalSize.height,
ratio: ratio
};
return style;
},
//returns a ng-style object with top,left,width,height pixel measurements
//expects {left,right,top,bottom} - {width,height}, {width,height}, int
//offset is just to push the image position a number of pixels from top,left
coordinatesToPixels : function(coordinates, originalSize, offset){
var coordinates_px = {
x1: Math.floor(coordinates.x1 * originalSize.width),
y1: Math.floor(coordinates.y1 * originalSize.height),
x2: Math.floor(coordinates.x2 * originalSize.width),
y2: Math.floor(coordinates.y2 * originalSize.height)
};
return coordinates_px;
},
pixelsToCoordinates : function(image, width, height, offset){
var x1_px = Math.abs(image.left-offset);
var y1_px = Math.abs(image.top-offset);
var x2_px = (x1_px + width) - image.width;
var y2_px = (y1_px + height) - image.height;
//crop coordinates in %
var crop = {};
crop.x1 = x1_px / image.width;
crop.y1 = y1_px / image.height;
crop.x2 = x2_px / image.width;
crop.y2 = y2_px / image.height;
return crop;
},
centerInsideViewPort : function(img, viewport){
var left = viewport.width/ 2 - img.width / 2,
top = viewport.height / 2 - img.height / 2;
return {left: left, top: top};
},
alignToCoordinates : function(image, center, viewport){
var min_left = (image.width) - (viewport.width);
var min_top = (image.height) - (viewport.height);
var c_top = -(center.top * image.height) + (viewport.height / 2);
var c_left = -(center.left * image.width) + (viewport.width / 2);
if(c_top < -min_top){
c_top = -min_top;
}
if(c_top > 0){
c_top = 0;
}
if(c_left < -min_left){
c_left = -min_left;
}
if(c_left > 0){
c_left = 0;
}
return {left: c_left, top: c_top};
},
syncElements : function(source, target){
target.height(source.height());
target.width(source.width());
target.css({
"top": source[0].offsetTop,
"left": source[0].offsetLeft
});
}
};
return service;
}
angular.module('umbraco.services').factory('cropperHelper', cropperHelper);

View File

@@ -41,7 +41,7 @@ function mediaHelper(umbRequestHelper) {
//this performs a simple check to see if we have a media file as value
//it doesnt catch everything, but better then nothing
if (item.value.indexOf(mediaRoot) === 0) {
if (angular.isString(item.value) && item.value.indexOf(mediaRoot) === 0) {
return true;
}

View File

@@ -1,20 +1,20 @@
/*Contains multiple services for various helper tasks */
function packageHelper(assetsService, treeService, eventsService) {
function packageHelper(assetsService, treeService, eventsService, $templateCache) {
return {
/** Called when a package is installed, this resets a bunch of data and ensures the new package assets are loaded in */
packageInstalled: function () {
//assetsService._reloadApplicationAssets().then(function() {
// treeService.clearCache();
// //send event
// //eventsService.emit("app.reInitialize");
// //TODO: This doesn't work and will end in an infinite browser load loop, we can't really
// // re-bootstrap anyways since that would be the same as loading the whole browser window.
// //angular.bootstrap(document, ['umbraco']);
//});
//clears the tree
treeService.clearCache();
//clears the template cache
$templateCache.removeAll();
//emit event to notify anything else
eventsService.emit("app.reInitialize");
}
};

View File

@@ -48,6 +48,7 @@
@import "../../lib/bootstrap/less/modals.less";
@import "../../lib/bootstrap/less/tooltip.less";
@import "../../lib/bootstrap/less/popovers.less";
@import "tipmenu.less";
// Components: Misc
@import "../../lib/bootstrap/less/thumbnails.less";

View File

@@ -130,6 +130,8 @@ ul.color-picker li a {
text-decoration: none;
}
.umb-thumbnails{
position: relative;
}
@@ -194,13 +196,17 @@ ul.color-picker li a {
// Cropper
// -------------------------------------------------
.umb-cropper{
position: relative;
padding-bottom: 30px;
}
.umb-cropper img, .umb-cropper-gravity img{
position: absolute;
top: 0;
left: 0;
}
.umb-cropper .overlay, .umb-cropper-gravity .overlay {
top: 0;
left: 0;
@@ -208,13 +214,19 @@ ul.color-picker li a {
z-index: 6001;
}
.umb-cropper .viewport, .umb-cropper-gravity .viewport {
overflow: hidden;
position: relative;
border:1px solid @grayLight;
width: 600px;
height: 480px;
.umb-cropper .viewport{
overflow: hidden;
position: relative;
border: 1px solid @grayLight;
}
.umb-cropper-gravity .viewport{
overflow: hidden;
position: relative;
width: 400px;
height: 300px
}
.umb-cropper .viewport:after {
content: "";
@@ -227,9 +239,9 @@ ul.color-picker li a {
-moz-opacity: .75;
opacity: .75;
filter: alpha(opacity=7);
-webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
-webkit-box-shadow: inset 0 0 0 40px white,inset 0 0 0 41px rgba(0,0,0,.1),inset 0 0 40px 41px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 0 0 40px white,inset 0 0 0 41px rgba(0,0,0,.1),inset 0 0 40px 41px rgba(0,0,0,.2);
box-shadow: inset 0 0 0 40px white,inset 0 0 0 41px rgba(0,0,0,.1),inset 0 0 40px 41px rgba(0,0,0,.2);
}
.umb-cropper-gravity .overlay{
@@ -240,12 +252,27 @@ ul.color-picker li a {
opacity: 0.6;
background: white;
}
.umb-cropper-gravity .overlay i{
font-size: 26px;
line-height: 26px;
opacity: 0.8 !important;
}
.umb-cropper .crop-container{
text-align: center;
}
.umb-cropper .crop-slider{
vertical-align: middle;
width: 250px;
padding-top: 12px;
}
.umb-cropper .crop-slider i{color: @gray;}
.umb-cropper .crop-slider input{
margin-top: 7px;
}
//

View File

@@ -0,0 +1,15 @@
.tipmenu .tooltip{
position:absolute;
left:0;
right:0;
margin-left:auto;
margin-right:auto;
}
.tipmenu:hover .tooltip{
opacity: 1;
}
.tipmenu:hover .tooltip a{
color: white;
}

View File

@@ -1,16 +1,24 @@
//global no-cache filter, this is enabled when we're in debug mode
//in live mode we use client dependency and don't turn this thing on
yepnope.addFilter(function (resourceObj) {
var url = resourceObj.url;
if(url.indexOf("lib/") === 0 || url.indexOf("js/umbraco.") === 0){
return resourceObj;
}
resourceObj.url = resourceObj.url + "?umb__rnd=" + (new Date).getTime();
return resourceObj;
});
yepnope({
load: [
'lib/jquery/jquery-2.0.3.min.js',
/* the jquery ui elements we need */
/* NOTE: I've opted not to use the full lib, just the parts we need to save on DL*/
'lib/jquery/jquery.ui.core.min.js',
'lib/jquery/jquery.ui.widget.min.js',
'lib/jquery/jquery.ui.mouse.min.js',
'lib/jquery/jquery.ui.sortable.min.js',
'lib/jquery/jquery-ui-1.10.3.custom.min.js',
/* 1.1.5 */
'lib/angular/1.1.5/angular.min.js',
'lib/angular/1.1.5/angular-cookies.min.js',

View File

@@ -1,32 +1,21 @@
<div class="umb-cropper" ng-show="src">
<div class="umb-cropper umb-editor" ng-show="src">
<div class="crop-container">
<div class="viewport" ng-style="style()">
<img src="{{src}}" />
<div class="overlay"></div>
<img src="{{src}}" ng-style="dimensions.image"/>
<div class="overlay" ng-style="dimensions.image"></div>
</div>
</div>
<input
type="range"
min="{{minScale}}"
max="{{maxScale}}"
step="0.01"
ng-model="scale" />
<pre>
{{dimensions | json}}
</pre>
<pre>
{{box | json}}
</pre>
<!--
<ul class="crops-preview">
<li ng-repeat="preview in previews">
<div ng-style="preview.style">
<img src="{{preview.src" ng-style="preview.imageStyle">
</div>
</li>
</ul> -->
<div class="crop-slider">
<i class="icon-picture"></i>
<input
type="range"
min="{{dimensions.scale.min}}"
max="{{dimensions.scale.max}}"
step="0.01"
ng-model="dimensions.scale.current" />
<i class="icon-picture" style="font-size: 22px"></i>
</div>
</div>

View File

@@ -1,12 +1,12 @@
<div class="umb-cropper-gravity">
<div class="gravity-container">
<div class="viewport" ng-style="style()">
<img src="{{src}}" />
<div class="viewport">
<img src="{{src}}" style="max-width: 100%; max-height: 100%" />
<div class="overlay">
<div class="overlay" ng-style="style()">
<i class="icon-crosshair"></i>
</div>
</div>
</div>
</div>

View File

@@ -1,5 +1,4 @@
<div class="umb-crop-thumbnail-container"
ng-style="{height: height, width: width, border: '1px solid red', overflow: 'hidden'}">
<img src="{{src}}"
ng-style="{'margin-left': marginLeft, 'margin-top': marginTop}" class="noScale" />
ng-style="{height: height, width: width, overflow: 'hidden', position: 'relative'}">
<img src="{{src}}" ng-style="preview" class="noScale" />
</div>

View File

@@ -0,0 +1,57 @@
//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
angular.module('umbraco').controller("Umbraco.PropertyEditors.ImageCropperController",
function($rootScope, $scope, mediaHelper, $timeout, editorState) {
//check the pre-values for multi-picker
var multiPicker = $scope.model.config.multiPicker !== '0' ? true : false;
//used to reference the part of the data we will either crop or "point"
$scope.currentCrop = undefined;
$scope.currentPoint = undefined;
var imgPath = mediaHelper.getImagePropertyValue({imageModel: editorState.current});
$scope.crop = function(crop){
$scope.currentCrop = crop;
$scope.currentPoint = undefined;
};
$scope.done = function(){
$scope.currentCrop = undefined;
$scope.currentPoint = undefined;
};
//Data sample
if(!$scope.model.value){
$scope.model.value = {
//image to crops
src: imgPath,
//global intrestpoint, used if no crop is specified
center: {left: 0.5, top: 0.4},
crops:{
thumbnail:
{
//crop dimensions
width: 100,
height: 100,
//crops in percentages
crop:{
"x1": 0.31731772342645215,
"y1": 0.17420325244997603,
"x2": -0.36246473116627076,
"y2": -0.30226197981593617
}
},
highrise:
{
width: 90,
height: 340
}
}
};
}
});

View File

@@ -0,0 +1,46 @@
<div class="umb-editor umb-mediapicker"
ng-controller="Umbraco.PropertyEditors.ImageCropperController">
<div ng-if="model.value.src">
<div style="border-bottom: 1px solid #f8f8f8; padding-bottom: 10px; margin-bottom: 15px;">
<div ng-if="currentCrop">
<div>
<umb-image-crop
height="{{currentCrop.height}}"
width= "{{currentCrop.width}}"
crop="currentCrop.crop"
center="model.value.center"
src="model.value.src" />
</div>
<a href class="btn btn-success" ng-click="done()">Done</a>
</div>
<div ng-if="!currentCrop">
<umb-image-gravity
src="model.value.src"
center="model.value.center" />
</div>
</div>
<ul class="umb-sortable-thumbnails">
<li class="tipmenu" ng-repeat="(key,value) in model.value.crops">
<a href ng-click="crop(value)">
<umb-image-thumbnail
center="model.value.center"
crop="value.crop"
src="model.value.src"
height="{{value.height}}"
width="{{value.width}}" />
</a>
<div class="tooltip bottom" ng-if="value.crop">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">
<a href ng-click="value.crop = undefined">Reset</a>
</div>
</div>
</li>
</ul>
</div>
</div>

View File

@@ -11,7 +11,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
//Data sample
var __img = {
//image to crop
src: "assets/img/mocks/image.jpg",
src: "assets/img/mocks/big-image.jpg",
//global gravity, used if not crop is specified
gravity: {left: 0.5, top: 0.4},
@@ -20,8 +20,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
thumbnail:
{
//crop dimensions
width: "30px",
height: "40px",
width: 100,
height: 100,
//crops in percentages
crop:{ "left": 0.31731772342645215,
@@ -33,8 +33,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
banner:
{
width: "200px",
height: "20px",
width: 340,
height: 90,
crop:{ "left": 0.31731772342645215,
"top": 0.17420325244997603,
@@ -45,8 +45,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
highrise:
{
width: "20px",
height: "200px"
width: 90,
height: 340
},
}
};
@@ -81,7 +81,6 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
$scope.edit = function(image){
$scope.currentImage = image;
$scope.cropper.image = __img;
}

View File

@@ -1,35 +1,38 @@
<div class="umb-editor umb-mediapicker" ng-controller="Umbraco.PropertyEditors.MediaPickerController">
<div ng-if="cropper.image">
<div ng-if="cropper.crop">
<umb-image-crop
crop="cropper.crop"
gravity="cropper.image.gravity"
<div ng-if="cropper.preset">
<umb-image-crop
height="{{cropper.preset.height}}"
width= "{{cropper.preset.width}}"
crop="cropper.preset.crop"
src="cropper.image.src" />
</div>
<div ng-if="cropper.grav">
<div ng-if="cropper.point">
<umb-image-gravity
height="'300px'"
width= "'480px'"
src="cropper.grav.src"
gravity="cropper.grav.gravity" />
height="200"
width= "380"
src="cropper.image.src"
gravity="cropper.point.gravity" />
</div>
<ul class="umb-sortable-thumbnails">
<li class="span2">
<img ng-src="{{cropper.image.src}}"
ng-click="cropper.crop = undefined; cropper.grav = cropper.image" />
ng-click="cropper.crop = undefined; cropper.point = cropper.image" />
original
</li>
<li ng-repeat="crop in cropper.image.crops">
<a href ng-click="cropper.crop = crop; cropper.grav = undefined">
<li ng-repeat="(key,value) in cropper.image.crops">
<a href ng-click="cropper.preset = value; cropper.point = undefined">
<umb-image-thumbnail
gravity="cropper.image.gravity"
crop="cropper.image.crop"
src="cropper.image.src"
height="crop.height"
width="crop.width">
height="value.height"
width="value.width">
</a>
</li>
@@ -38,7 +41,7 @@
<ul ui-sortable="sortableOptions" ng-model="images" class="umb-sortable-thumbnails">
<li style="width: 120px; height: 100px; overflow: hidden;" ng-repeat="image in images">
<img ng-src="{{image.thumbnail}}" alt="" ng-show="image.src">
<img ng-src="{{image.thumbnail}}" ng-click="edit(image)" alt="" ng-show="image.src">
<span class="icon-holder" ng-hide="image.src">
<i class="icon {{image.icon}} large" ></i>