|
|
|
|
@@ -6,124 +6,171 @@
|
|
|
|
|
angular.module("umbraco.directives.html")
|
|
|
|
|
.directive('umbPhotoFolder', function ($compile, $log, $timeout, $filter, imageHelper, umbRequestHelper) {
|
|
|
|
|
|
|
|
|
|
function renderCollection(scope, photos, fixedRowWidth) {
|
|
|
|
|
/** sets the image's url - will check if it is a folder or a real image */
|
|
|
|
|
function setImageUrl(img) {
|
|
|
|
|
//get the image property (if one exists)
|
|
|
|
|
var imageProp = imageHelper.getImagePropertyValue({ imageModel: img });
|
|
|
|
|
if (!imageProp) {
|
|
|
|
|
img.thumbnail = "none";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
|
|
//get the proxy url for big thumbnails (this ensures one is always generated)
|
|
|
|
|
var thumbnailUrl = umbRequestHelper.getApiUrl(
|
|
|
|
|
"imagesApiBaseUrl",
|
|
|
|
|
"GetBigThumbnail",
|
|
|
|
|
[{ mediaId: img.id }]);
|
|
|
|
|
img.thumbnail = thumbnailUrl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** sets the images original size properties - will check if it is a folder and if so will just make it square */
|
|
|
|
|
function setOriginalSize(img, maxHeight) {
|
|
|
|
|
//set to a square by default
|
|
|
|
|
img.originalWidth = maxHeight;
|
|
|
|
|
img.originalHeight = maxHeight;
|
|
|
|
|
|
|
|
|
|
var widthProp = _.find(img.properties, function (v) { return (v.alias === "umbracoWidth"); });
|
|
|
|
|
if (widthProp && widthProp.value) {
|
|
|
|
|
img.originalWidth = parseInt(widthProp.value, 10);
|
|
|
|
|
if (isNaN(img.originalWidth)) {
|
|
|
|
|
img.originalWidth = maxHeight;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
var heightProp = _.find(img.properties, function (v) { return (v.alias === "umbracoHeight"); });
|
|
|
|
|
if (heightProp && heightProp.value) {
|
|
|
|
|
img.originalHeight = parseInt(heightProp.value, 10);
|
|
|
|
|
if (isNaN(img.originalHeight)) {
|
|
|
|
|
img.originalHeight = maxHeight;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** sets the image style which get's used in the angular markup */
|
|
|
|
|
function setImageStyle(img, width, height) {
|
|
|
|
|
img.style = { width: width, height: height };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** gets the image's scaled wdith based on the max row height and width */
|
|
|
|
|
function getScaledWidth(img, maxHeight, maxRowWidth) {
|
|
|
|
|
var scaled = img.originalWidth * maxHeight / img.originalHeight;
|
|
|
|
|
//return to 2 dec places
|
|
|
|
|
var rounded = Math.round(scaled * 100) / 100;
|
|
|
|
|
//if the max row width is smaller than the scaled width of the image then we better make
|
|
|
|
|
// the scaled width the max row width so an image can actually fit on the row
|
|
|
|
|
return Math.min(rounded, maxRowWidth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
This will determine the row/image height for the next collection of images which takes into account the
|
|
|
|
|
ideal image count per row. It will check if a row can be filled with this ideal count and if not - if there
|
|
|
|
|
are additional images available to fill the row it will keep calculating until they fit.
|
|
|
|
|
*/
|
|
|
|
|
function getRowHeight(imgs, maxRowHeight, minDisplayHeight, maxRowWidth, idealImgPerRow) {
|
|
|
|
|
|
|
|
|
|
var currRowWidth = 0;
|
|
|
|
|
|
|
|
|
|
var idealImages = imgs.slice(0, idealImgPerRow);
|
|
|
|
|
|
|
|
|
|
var maxScaleableHeight = getMaxScaleableHeight(idealImages, maxRowHeight);
|
|
|
|
|
|
|
|
|
|
var biggerHeight = Math.max(maxScaleableHeight, minDisplayHeight);
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < idealImages.length; i++) {
|
|
|
|
|
var scaledWidth = getScaledWidth(
|
|
|
|
|
idealImages[i],
|
|
|
|
|
biggerHeight,
|
|
|
|
|
maxRowWidth);
|
|
|
|
|
currRowWidth += scaledWidth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currRowWidth > maxRowWidth) {
|
|
|
|
|
//get the ratio
|
|
|
|
|
var r = maxRowWidth / currRowWidth;
|
|
|
|
|
var newHeight = biggerHeight * r;
|
|
|
|
|
//if this becomes smaller than the min display then we need to use the min display
|
|
|
|
|
if (newHeight < minDisplayHeight) {
|
|
|
|
|
newHeight = minDisplayHeight;
|
|
|
|
|
}
|
|
|
|
|
//always take the rounded down width so we know it will fit
|
|
|
|
|
return Math.floor(newHeight);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
|
|
//we know the width will fit in a row, but we now need to figure out if we can fill
|
|
|
|
|
// the entire row in the case that we have more images remaining than the idealImgPerRow.
|
|
|
|
|
|
|
|
|
|
if (idealImages.length === imgs.length) {
|
|
|
|
|
//we have no more remaining images to fill the space, so we'll just use the calc height
|
|
|
|
|
return biggerHeight;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//we have additional images so we'll recurse and add 1 to the idealImgPerRow until it fits
|
|
|
|
|
return getRowHeight(imgs, maxRowHeight, minDisplayHeight, maxRowWidth, idealImgPerRow + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** builds an image grid row */
|
|
|
|
|
function buildRow(imgs, maxRowHeight, minDisplayHeight, maxRowWidth, idealImgPerRow) {
|
|
|
|
|
var currRowWidth = 0;
|
|
|
|
|
var row = [];
|
|
|
|
|
|
|
|
|
|
var calcRowHeight = getRowHeight(imgs, maxRowHeight, minDisplayHeight, maxRowWidth, idealImgPerRow);
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < imgs.length; i++) {
|
|
|
|
|
|
|
|
|
|
var scaledWidth = getScaledWidth(imgs[i], calcRowHeight, maxRowWidth);
|
|
|
|
|
if (currRowWidth + scaledWidth <= maxRowWidth) {
|
|
|
|
|
currRowWidth += scaledWidth;
|
|
|
|
|
setImageStyle(imgs[i], scaledWidth, calcRowHeight);
|
|
|
|
|
row.push(imgs[i]);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//the max width has been reached
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Returns the maximum image scaling height for the current image collection */
|
|
|
|
|
function getMaxScaleableHeight(imgs, maxRowHeight) {
|
|
|
|
|
|
|
|
|
|
var smallestHeight = _.min(imgs, function (item) { return item.originalHeight; }).originalHeight;
|
|
|
|
|
|
|
|
|
|
//adjust the smallestHeight if it is larger than the static max row height
|
|
|
|
|
if (smallestHeight > maxRowHeight) {
|
|
|
|
|
smallestHeight = maxRowHeight;
|
|
|
|
|
}
|
|
|
|
|
return smallestHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Creates the image grid with calculated widths/heights for images to fill the grid nicely */
|
|
|
|
|
function buildGrid(images, maxRowWidth, maxRowHeight, startingIndex, minDisplayHeight, idealImgPerRow) {
|
|
|
|
|
|
|
|
|
|
var rows = [];
|
|
|
|
|
var imagesProcessed = 0;
|
|
|
|
|
|
|
|
|
|
//first fill in all of the original image sizes and URLs
|
|
|
|
|
for (var i = startingIndex; i < images.length; i++) {
|
|
|
|
|
setImageUrl(images[i]);
|
|
|
|
|
setOriginalSize(images[i], maxRowHeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// initial height - effectively the maximum height +/- 10%;
|
|
|
|
|
var initialHeight = Math.max(scope.minHeight, Math.floor(fixedRowWidth / 5));
|
|
|
|
|
|
|
|
|
|
// store relative widths of all images (scaled to match estimate height above)
|
|
|
|
|
var ws = [];
|
|
|
|
|
$.each(photos, function (key, val) {
|
|
|
|
|
|
|
|
|
|
val.width_n = $.grep(val.properties, function (v, index) { return (v.alias === "umbracoWidth"); })[0];
|
|
|
|
|
val.height_n = $.grep(val.properties, function (v, index) { return (v.alias === "umbracoHeight"); })[0];
|
|
|
|
|
|
|
|
|
|
if (val.width_n && val.height_n) {
|
|
|
|
|
var parsedWidth = parseInt(val.width_n.value, 10);
|
|
|
|
|
var parsedHeight = parseInt(val.height_n.value, 10);
|
|
|
|
|
|
|
|
|
|
//if the parsedHeight is less than the minHeight than set it to the minHeight
|
|
|
|
|
//TODO: Should we set it to it's original in this case?
|
|
|
|
|
if (parsedHeight >= scope.minHeight) {
|
|
|
|
|
if (parsedHeight !== initialHeight) {
|
|
|
|
|
parsedWidth = Math.floor(parsedWidth * (initialHeight / parsedHeight));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ws.push(parsedWidth);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ws.push(scope.minHeight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
//if its files or folders, we make them square
|
|
|
|
|
ws.push(scope.minHeight);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var rowNum = 0;
|
|
|
|
|
var limit = photos.length;
|
|
|
|
|
while (scope.baseline < limit) {
|
|
|
|
|
rowNum++;
|
|
|
|
|
// number of images appearing in this row
|
|
|
|
|
var imgsPerRow = 0;
|
|
|
|
|
// total width of images in this row - including margins
|
|
|
|
|
var totalRowWidth = 0;
|
|
|
|
|
|
|
|
|
|
// calculate width of images and number of images to view in this row.
|
|
|
|
|
while ((totalRowWidth * 1.1 < fixedRowWidth) && (scope.baseline + imgsPerRow < limit)) {
|
|
|
|
|
totalRowWidth += ws[scope.baseline + imgsPerRow++] + scope.border * 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ratio of actual width of row to total width of images to be used.
|
|
|
|
|
var r = fixedRowWidth / totalRowWidth;
|
|
|
|
|
// image number being processed
|
|
|
|
|
var i = 0;
|
|
|
|
|
// reset total width to be total width of processed images
|
|
|
|
|
totalRowWidth = 0;
|
|
|
|
|
|
|
|
|
|
// new height is not original height * ratio
|
|
|
|
|
var ht = Math.floor(initialHeight * r);
|
|
|
|
|
|
|
|
|
|
var row = {};
|
|
|
|
|
row.photos = [];
|
|
|
|
|
row.style = {};
|
|
|
|
|
row.style = { "height": ht + scope.border * 2, "width": fixedRowWidth };
|
|
|
|
|
rows.push(row);
|
|
|
|
|
|
|
|
|
|
while (i < imgsPerRow) {
|
|
|
|
|
var photo = photos[scope.baseline + i];
|
|
|
|
|
// Calculate new width based on ratio
|
|
|
|
|
var calcWidth = Math.floor(ws[scope.baseline + i] * r);
|
|
|
|
|
// add to total width with margins
|
|
|
|
|
totalRowWidth += calcWidth + scope.border * 2;
|
|
|
|
|
|
|
|
|
|
//get the image property (if one exists)
|
|
|
|
|
var imageProp = imageHelper.getImagePropertyValue({ imageModel: photo });
|
|
|
|
|
if (!imageProp) {
|
|
|
|
|
//TODO: Do something better than this!!
|
|
|
|
|
photo.thumbnail = "none";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
|
|
//get the proxy url for big thumbnails (this ensures one is always generated)
|
|
|
|
|
var thumbnailUrl = umbRequestHelper.getApiUrl(
|
|
|
|
|
"imagesApiBaseUrl",
|
|
|
|
|
"GetBigThumbnail",
|
|
|
|
|
[{ mediaId: photo.id }]);
|
|
|
|
|
photo.thumbnail = thumbnailUrl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
photo.style = { "width": calcWidth, "height": ht, "margin": scope.border + "px", "cursor": "pointer" };
|
|
|
|
|
row.photos.push(photo);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set row height to actual height + margins
|
|
|
|
|
scope.baseline += imgsPerRow;
|
|
|
|
|
|
|
|
|
|
// if total width is slightly smaller than
|
|
|
|
|
// actual div width then add 1 to each
|
|
|
|
|
// photo width till they match
|
|
|
|
|
while ((imagesProcessed + startingIndex) < images.length) {
|
|
|
|
|
//get the maxHeight for the current un-processed images
|
|
|
|
|
var currImgs = images.slice(imagesProcessed);
|
|
|
|
|
|
|
|
|
|
/*i = 0;
|
|
|
|
|
while (tw < w-1) {
|
|
|
|
|
row.photos[i].style.width++;
|
|
|
|
|
i = (i + 1) % c;
|
|
|
|
|
tw++;
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
// if total width is slightly bigger than
|
|
|
|
|
// actual div width then subtract 1 from each
|
|
|
|
|
// photo width till they match
|
|
|
|
|
i = 0;
|
|
|
|
|
while (totalRowWidth > fixedRowWidth - 1) {
|
|
|
|
|
row.photos[i].style.width--;
|
|
|
|
|
i = (i + 1) % imgsPerRow;
|
|
|
|
|
totalRowWidth--;
|
|
|
|
|
//build the row
|
|
|
|
|
var row = buildRow(currImgs, maxRowHeight, minDisplayHeight, maxRowWidth, idealImgPerRow);
|
|
|
|
|
if (row.length > 0) {
|
|
|
|
|
rows.push(row);
|
|
|
|
|
imagesProcessed += row.length;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
//if there was nothing processed, exit
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -144,28 +191,35 @@ angular.module("umbraco.directives.html")
|
|
|
|
|
$timeout(function () {
|
|
|
|
|
var photos = ngModel.$modelValue;
|
|
|
|
|
|
|
|
|
|
scope.imagesOnly = element.attr('imagesOnly');
|
|
|
|
|
scope.baseline = element.attr('baseline') ? parseInt(element.attr('baseline'), 10) : 0;
|
|
|
|
|
scope.minWidth = element.attr('min-width') ? parseInt(element.attr('min-width'), 10) : 420;
|
|
|
|
|
scope.minHeight = element.attr('min-height') ? parseInt(element.attr('min-height'), 10) : 200;
|
|
|
|
|
scope.border = element.attr('border') ? parseInt(element.attr('border'), 10) : 5;
|
|
|
|
|
scope.clickHandler = scope.$eval(element.attr('on-click'));
|
|
|
|
|
var fixedRowWidth = Math.max(element.width(), scope.minWidth);
|
|
|
|
|
|
|
|
|
|
scope.rows = renderCollection(scope, photos, fixedRowWidth);
|
|
|
|
|
//todo: this doesn't do anything
|
|
|
|
|
var imagesOnly = element.attr('imagesOnly');
|
|
|
|
|
//todo: this doesn't do anything
|
|
|
|
|
var border = element.attr('border') ? parseInt(element.attr('border'), 10) : 5;
|
|
|
|
|
|
|
|
|
|
var startingIndex = element.attr('baseline') ? parseInt(element.attr('baseline'), 10) : 0;
|
|
|
|
|
var minWidth = element.attr('min-width') ? parseInt(element.attr('min-width'), 10) : 420;
|
|
|
|
|
var minHeight = element.attr('min-height') ? parseInt(element.attr('min-height'), 10) : 100;
|
|
|
|
|
var maxHeight = element.attr('max-height') ? parseInt(element.attr('max-height'), 10) : 300;
|
|
|
|
|
var idealImgPerRow = element.attr('ideal-items-per-row') ? parseInt(element.attr('ideal-items-per-row'), 10) : 5;
|
|
|
|
|
|
|
|
|
|
var fixedRowWidth = Math.max(element.width(), minWidth);
|
|
|
|
|
|
|
|
|
|
scope.rows = buildGrid(photos, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow);
|
|
|
|
|
|
|
|
|
|
if (attrs.filterBy) {
|
|
|
|
|
scope.$watch(attrs.filterBy, function (newVal, oldVal) {
|
|
|
|
|
if (newVal !== oldVal) {
|
|
|
|
|
var p = $filter('filter')(photos, newVal, false);
|
|
|
|
|
scope.baseline = 0;
|
|
|
|
|
var m = renderCollection(scope, p);
|
|
|
|
|
var m = buildGrid(p, fixedRowWidth, 400);
|
|
|
|
|
scope.rows = m;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}, 200); //end timeout
|
|
|
|
|
}, 500); //end timeout
|
|
|
|
|
} //end if modelValue
|
|
|
|
|
|
|
|
|
|
}; //end $render
|
|
|
|
|
|