Fixes: U4-3840 MediaPicker doesn't show images when there are also folders inside of the folder being viewed
and moves some of the util classes into their own files, adjusted media picker's icon sizes too to make it more readable.
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.iconHelper
|
||||
* @description A helper service for dealing with icons, mostly dealing with legacy tree icons
|
||||
**/
|
||||
function iconHelper($q, $timeout) {
|
||||
|
||||
var converter = [
|
||||
{ oldIcon: ".sprNew", newIcon: "add" },
|
||||
{ oldIcon: ".sprDelete", newIcon: "remove" },
|
||||
{ oldIcon: ".sprMove", newIcon: "enter" },
|
||||
{ oldIcon: ".sprCopy", newIcon: "documents" },
|
||||
{ oldIcon: ".sprSort", newIcon: "navigation-vertical" },
|
||||
{ oldIcon: ".sprPublish", newIcon: "globe" },
|
||||
{ oldIcon: ".sprRollback", newIcon: "undo" },
|
||||
{ oldIcon: ".sprProtect", newIcon: "lock" },
|
||||
{ oldIcon: ".sprAudit", newIcon: "time" },
|
||||
{ oldIcon: ".sprNotify", newIcon: "envelope" },
|
||||
{ oldIcon: ".sprDomain", newIcon: "home" },
|
||||
{ oldIcon: ".sprPermission", newIcon: "lock" },
|
||||
{ oldIcon: ".sprRefresh", newIcon: "refresh" },
|
||||
{ oldIcon: ".sprBinEmpty", newIcon: "trash" },
|
||||
{ oldIcon: ".sprExportDocumentType", newIcon: "download-alt" },
|
||||
{ oldIcon: ".sprImportDocumentType", newIcon: "page-up" },
|
||||
{ oldIcon: ".sprLiveEdit", newIcon: "edit" },
|
||||
{ oldIcon: ".sprCreateFolder", newIcon: "add" },
|
||||
{ oldIcon: ".sprPackage2", newIcon: "box" },
|
||||
{ oldIcon: ".sprLogout", newIcon: "logout" },
|
||||
{ oldIcon: ".sprSave", newIcon: "save" },
|
||||
{ oldIcon: ".sprSendToTranslate", newIcon: "envelope-alt" },
|
||||
{ oldIcon: ".sprToPublish", newIcon: "mail-forward" },
|
||||
{ oldIcon: ".sprTranslate", newIcon: "comments" },
|
||||
{ oldIcon: ".sprUpdate", newIcon: "save" },
|
||||
|
||||
{ oldIcon: ".sprTreeSettingDomain", newIcon: "icon-home" },
|
||||
{ oldIcon: ".sprTreeDoc", newIcon: "icon-document" },
|
||||
{ oldIcon: ".sprTreeDoc2", newIcon: "icon-diploma-alt" },
|
||||
{ oldIcon: ".sprTreeDoc3", newIcon: "icon-notepad" },
|
||||
{ oldIcon: ".sprTreeDoc4", newIcon: "icon-newspaper-alt" },
|
||||
{ oldIcon: ".sprTreeDoc5", newIcon: "icon-notepad-alt" },
|
||||
|
||||
{ oldIcon: ".sprTreeDocPic", newIcon: "icon-picture" },
|
||||
{ oldIcon: ".sprTreeFolder", newIcon: "icon-folder" },
|
||||
{ oldIcon: ".sprTreeFolder_o", newIcon: "icon-folder" },
|
||||
{ oldIcon: ".sprTreeMediaFile", newIcon: "icon-music" },
|
||||
{ oldIcon: ".sprTreeMediaMovie", newIcon: "icon-movie" },
|
||||
{ oldIcon: ".sprTreeMediaPhoto", newIcon: "icon-picture" },
|
||||
|
||||
{ oldIcon: ".sprTreeMember", newIcon: "icon-user" },
|
||||
{ oldIcon: ".sprTreeMemberGroup", newIcon: "icon-users" },
|
||||
{ oldIcon: ".sprTreeMemberType", newIcon: "icon-users" },
|
||||
|
||||
{ oldIcon: ".sprTreeNewsletter", newIcon: "icon-file-text-alt" },
|
||||
{ oldIcon: ".sprTreePackage", newIcon: "icon-box" },
|
||||
{ oldIcon: ".sprTreeRepository", newIcon: "icon-server-alt" },
|
||||
|
||||
{ oldIcon: ".sprTreeSettingDataType", newIcon: "icon-autofill" },
|
||||
|
||||
//TODO:
|
||||
/*
|
||||
{ oldIcon: ".sprTreeSettingAgent", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingCss", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingCssItem", newIcon: "" },
|
||||
|
||||
{ oldIcon: ".sprTreeSettingDataTypeChild", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingDomain", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingLanguage", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingScript", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingTemplate", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingXml", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeStatistik", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeUser", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeUserGroup", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeUserType", newIcon: "" },
|
||||
*/
|
||||
|
||||
{ oldIcon: "folder.png", newIcon: "icon-folder" },
|
||||
{ oldIcon: "mediaphoto.gif", newIcon: "icon-picture" },
|
||||
{ oldIcon: "mediafile.gif", newIcon: "icon-document" },
|
||||
|
||||
{ oldIcon: ".sprTreeDeveloperCacheItem", newIcon: "icon-box" },
|
||||
{ oldIcon: ".sprTreeDeveloperCacheTypes", newIcon: "icon-box" },
|
||||
{ oldIcon: ".sprTreeDeveloperMacro", newIcon: "icon-cogs" },
|
||||
{ oldIcon: ".sprTreeDeveloperRegistry", newIcon: "icon-windows" },
|
||||
{ oldIcon: ".sprTreeDeveloperPython", newIcon: "icon-linux" }
|
||||
];
|
||||
|
||||
var imageConverter = [
|
||||
{oldImage: "contour.png", newIcon: "icon-umb-contour"}
|
||||
];
|
||||
|
||||
var collectedIcons;
|
||||
|
||||
return {
|
||||
|
||||
/** Used by the create dialogs for content/media types to format the data so that the thumbnails are styled properly */
|
||||
formatContentTypeThumbnails: function (contentTypes) {
|
||||
for (var i = 0; i < contentTypes.length; i++) {
|
||||
|
||||
if (contentTypes[i].thumbnailIsClass === undefined || contentTypes[i].thumbnailIsClass) {
|
||||
contentTypes[i].cssClass = this.convertFromLegacyIcon(contentTypes[i].thumbnail);
|
||||
}else {
|
||||
contentTypes[i].style = "background-image: url('" + contentTypes[i].thumbnailFilePath + "');height:36px; background-position:4px 0px; background-repeat: no-repeat;background-size: 35px 35px;";
|
||||
//we need an 'icon-' class in there for certain styles to work so if it is image based we'll add this
|
||||
contentTypes[i].cssClass = "custom-file";
|
||||
}
|
||||
}
|
||||
return contentTypes;
|
||||
},
|
||||
formatContentTypeIcons: function (contentTypes) {
|
||||
for (var i = 0; i < contentTypes.length; i++) {
|
||||
contentTypes[i].icon = this.convertFromLegacyIcon(contentTypes[i].icon);
|
||||
|
||||
//couldnt find replacement
|
||||
if(contentTypes[i].icon.indexOf(".") > 0){
|
||||
contentTypes[i].icon = "icon-document-dashed-line";
|
||||
}
|
||||
}
|
||||
return contentTypes;
|
||||
},
|
||||
/** If the icon is file based (i.e. it has a file path) */
|
||||
isFileBasedIcon: function (icon) {
|
||||
//if it doesn't start with a '.' but contains one then we'll assume it's file based
|
||||
if (icon.startsWith('..') || (!icon.startsWith('.') && icon.indexOf('.') > 1)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/** If the icon is legacy */
|
||||
isLegacyIcon: function (icon) {
|
||||
if(icon.startsWith('..')){
|
||||
return false;
|
||||
}
|
||||
|
||||
if (icon.startsWith('.')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/** If the tree node has a legacy icon */
|
||||
isLegacyTreeNodeIcon: function(treeNode){
|
||||
if (treeNode.iconIsClass) {
|
||||
return this.isLegacyIcon(treeNode.icon);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/** Return a list of icons, optionally filter them */
|
||||
/** It fetches them directly from the active stylesheets in the browser */
|
||||
getIcons: function(){
|
||||
var deferred = $q.defer();
|
||||
$timeout(function(){
|
||||
if(collectedIcons){
|
||||
deferred.resolve(collectedIcons);
|
||||
}else{
|
||||
collectedIcons = [];
|
||||
var c = ".icon-";
|
||||
|
||||
for (var i = document.styleSheets.length - 1; i >= 0; i--) {
|
||||
var classes = document.styleSheets[i].rules || document.styleSheets[i].cssRules;
|
||||
|
||||
if (classes !== null) {
|
||||
for(var x=0;x<classes.length;x++) {
|
||||
var cur = classes[x];
|
||||
if(cur.selectorText && cur.selectorText.indexOf(c) === 0) {
|
||||
var s = cur.selectorText.substring(1);
|
||||
var hasSpace = s.indexOf(" ");
|
||||
if(hasSpace>0){
|
||||
s = s.substring(0, hasSpace);
|
||||
}
|
||||
var hasPseudo = s.indexOf(":");
|
||||
if(hasPseudo>0){
|
||||
s = s.substring(0, hasPseudo);
|
||||
}
|
||||
|
||||
if(collectedIcons.indexOf(s) < 0){
|
||||
collectedIcons.push(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
deferred.resolve(collectedIcons);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/** Converts the icon from legacy to a new one if an old one is detected */
|
||||
convertFromLegacyIcon: function (icon) {
|
||||
if (this.isLegacyIcon(icon)) {
|
||||
//its legacy so convert it if we can
|
||||
var found = _.find(converter, function (item) {
|
||||
return item.oldIcon.toLowerCase() === icon.toLowerCase();
|
||||
});
|
||||
return (found ? found.newIcon : icon);
|
||||
}
|
||||
return icon;
|
||||
},
|
||||
|
||||
convertFromLegacyImage: function (icon) {
|
||||
var found = _.find(imageConverter, function (item) {
|
||||
return item.oldImage.toLowerCase() === icon.toLowerCase();
|
||||
});
|
||||
return (found ? found.newIcon : undefined);
|
||||
},
|
||||
|
||||
/** If we detect that the tree node has legacy icons that can be converted, this will convert them */
|
||||
convertFromLegacyTreeNodeIcon: function (treeNode) {
|
||||
if (this.isLegacyTreeNodeIcon(treeNode)) {
|
||||
return this.convertFromLegacyIcon(treeNode.icon);
|
||||
}
|
||||
return treeNode.icon;
|
||||
}
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.services').factory('iconHelper', iconHelper);
|
||||
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.imageHelper
|
||||
* @description A helper object used for parsing image paths
|
||||
**/
|
||||
function imageHelper(umbRequestHelper) {
|
||||
return {
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.imageHelper#getImagePropertyValue
|
||||
* @methodOf umbraco.services.imageHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the actual image path associated with the image property if there is one
|
||||
*
|
||||
* @param {object} options Options object
|
||||
* @param {object} options.imageModel The media object to retrieve the image path from
|
||||
*/
|
||||
getImagePropertyValue: function (options) {
|
||||
if (!options && !options.imageModel) {
|
||||
throw "The options objet does not contain the required parameters: imageModel";
|
||||
}
|
||||
|
||||
|
||||
//combine all props, TODO: we really need a better way then this
|
||||
var props = [];
|
||||
if (options.imageModel.properties) {
|
||||
props = options.imageModel.properties;
|
||||
} else {
|
||||
$(options.imageModel.tabs).each(function (i, tab) {
|
||||
props = props.concat(tab.properties);
|
||||
});
|
||||
}
|
||||
|
||||
var mediaRoot = Umbraco.Sys.ServerVariables.umbracoSettings.mediaPath;
|
||||
var imageProp = _.find(props, function (item) {
|
||||
if (item.alias === "umbracoFile") {
|
||||
return true;
|
||||
}
|
||||
|
||||
//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) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!imageProp) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var imageVal;
|
||||
|
||||
//our default images might store one or many images (as csv)
|
||||
var split = imageProp.value.split(',');
|
||||
var self = this;
|
||||
imageVal = _.map(split, function (item) {
|
||||
return { file: item, isImage: self.detectIfImageByExtension(item) };
|
||||
});
|
||||
|
||||
//for now we'll just return the first image in the collection.
|
||||
//TODO: we should enable returning many to be displayed in the picker if the uploader supports many.
|
||||
if (imageVal.length && imageVal.length > 0 && imageVal[0].isImage) {
|
||||
return imageVal[0].file;
|
||||
}
|
||||
|
||||
return "";
|
||||
},
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.imageHelper#getThumbnail
|
||||
* @methodOf umbraco.services.imageHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* formats the display model used to display the content to the model used to save the content
|
||||
*
|
||||
* @param {object} options Options object
|
||||
* @param {object} options.imageModel The media object to retrieve the image path from
|
||||
*/
|
||||
getThumbnail: function (options) {
|
||||
|
||||
if (!options && !options.imageModel) {
|
||||
throw "The options objet does not contain the required parameters: imageModel";
|
||||
}
|
||||
|
||||
var imagePropVal = this.getImagePropertyValue(options);
|
||||
if (imagePropVal !== "") {
|
||||
return this.getThumbnailFromPath(imagePropVal);
|
||||
}
|
||||
return "";
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.imageHelper#scaleToMaxSize
|
||||
* @methodOf umbraco.services.imageHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Finds the corrct max width and max height, given maximum dimensions and keeping aspect ratios
|
||||
*
|
||||
* @param {number} maxSize Maximum width & height
|
||||
* @param {number} width Current width
|
||||
* @param {number} height Current height
|
||||
*/
|
||||
scaleToMaxSize: function (maxSize, width, height) {
|
||||
var retval = { width: width, height: height };
|
||||
|
||||
var maxWidth = maxSize; // Max width for the image
|
||||
var maxHeight = maxSize; // Max height for the image
|
||||
var ratio = 0; // Used for aspect ratio
|
||||
|
||||
// Check if the current width is larger than the max
|
||||
if (width > maxWidth) {
|
||||
ratio = maxWidth / width; // get ratio for scaling image
|
||||
|
||||
retval.width = maxWidth;
|
||||
retval.height = height * ratio;
|
||||
|
||||
height = height * ratio; // Reset height to match scaled image
|
||||
width = width * ratio; // Reset width to match scaled image
|
||||
}
|
||||
|
||||
// Check if current height is larger than max
|
||||
if (height > maxHeight) {
|
||||
ratio = maxHeight / height; // get ratio for scaling image
|
||||
|
||||
retval.height = maxHeight;
|
||||
retval.width = width * ratio;
|
||||
width = width * ratio; // Reset width to match scaled image
|
||||
}
|
||||
|
||||
return retval;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.imageHelper#getThumbnailFromPath
|
||||
* @methodOf umbraco.services.imageHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the path to the thumbnail version of a given media library image path
|
||||
*
|
||||
* @param {string} imagePath Image path, ex: /media/1234/my-image.jpg
|
||||
*/
|
||||
getThumbnailFromPath: function (imagePath) {
|
||||
|
||||
//get the proxy url for big thumbnails (this ensures one is always generated)
|
||||
var thumbnailUrl = umbRequestHelper.getApiUrl(
|
||||
"imagesApiBaseUrl",
|
||||
"GetBigThumbnail",
|
||||
[{ originalImagePath: imagePath }]);
|
||||
|
||||
//var ext = imagePath.substr(imagePath.lastIndexOf('.'));
|
||||
//return imagePath.substr(0, imagePath.lastIndexOf('.')) + "_big-thumb" + ".jpg";
|
||||
|
||||
return thumbnailUrl;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.imageHelper#detectIfImageByExtension
|
||||
* @methodOf umbraco.services.imageHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns true/false, indicating if the given path has an allowed image extension
|
||||
*
|
||||
* @param {string} imagePath Image path, ex: /media/1234/my-image.jpg
|
||||
*/
|
||||
detectIfImageByExtension: function (imagePath) {
|
||||
var lowered = imagePath.toLowerCase();
|
||||
var ext = lowered.substr(lowered.lastIndexOf(".") + 1);
|
||||
return ("," + Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes + ",").indexOf("," + ext + ",") !== -1;
|
||||
}
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.services').factory('imageHelper', imageHelper);
|
||||
@@ -94,15 +94,16 @@ function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, um
|
||||
// which means we'll need to remove one from the row so we can scale up to fill the row
|
||||
if (attemptedRowHeight < minDisplayHeight) {
|
||||
|
||||
if (idealImages.length > 2) {
|
||||
if (idealImages.length > 1) {
|
||||
//we'll generate a new targetHeight that is halfway between the max and the current and recurse, passing in a new targetHeight
|
||||
targetHeight += Math.floor((maxRowHeight - targetHeight) / 2);
|
||||
return this.getRowHeightForImages(imgs, maxRowHeight, minDisplayHeight, maxRowWidth, idealImgPerRow - 1, margin, targetHeight);
|
||||
}
|
||||
else {
|
||||
//well this shouldn't happen and we don't want to end up with a row of only one so we're just gonna have to return the
|
||||
//newHeight which will not actually scale to the row correctly
|
||||
return { height: minDisplayHeight, imgCount: idealImages.length };
|
||||
else {
|
||||
//this will occur when we only have one image remaining in the row but it's still going to be too wide even when
|
||||
// using the minimum display height specified. In this case we're going to have to just crop the image in it's center
|
||||
// using the minimum display height and the full row width
|
||||
return { height: minDisplayHeight, imgCount: 1 };
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -118,13 +119,18 @@ function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, um
|
||||
//we have no more remaining images to fill the space, so we'll just use the calc height
|
||||
return { height: targetHeight, imgCount: idealImages.length };
|
||||
}
|
||||
if (idealImages.length === idealImgPerRow && targetHeight < maxRowHeight) {
|
||||
else if (idealImages.length === 1) {
|
||||
//this will occur when we only have one image remaining in the row to process but it's not really going to fit ideally
|
||||
// in the row so we'll just return the minDisplayHeight and it will just get centered on the row
|
||||
return { height: minDisplayHeight, imgCount: 1 };
|
||||
}
|
||||
else if (idealImages.length === idealImgPerRow && targetHeight < maxRowHeight) {
|
||||
//if we're already dealing with the ideal images per row and it's not quite there, we can scale up a little bit so
|
||||
// long as the targetHeight is currently less than the maxRowHeight. The scale up will be half-way between our current
|
||||
// target height and the maxRowHeight
|
||||
|
||||
targetHeight += Math.floor((maxRowHeight - targetHeight) / 2);
|
||||
while (targetHeight < maxRowHeight) {
|
||||
// target height and the maxRowHeight (we won't loop forever though - if there's a difference of 5 px we'll just quit)
|
||||
|
||||
while (targetHeight < maxRowHeight && (maxRowHeight - targetHeight) > 5) {
|
||||
targetHeight += Math.floor((maxRowHeight - targetHeight) / 2);
|
||||
attemptedRowHeight = this.performGetRowHeight(idealImages, targetRowWidth, minDisplayHeight, targetHeight);
|
||||
if (attemptedRowHeight != null) {
|
||||
//success!
|
||||
@@ -175,6 +181,19 @@ function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, um
|
||||
for (var i = 0; i < imgs.length; i++) {
|
||||
//get the lower width to ensure it always fits
|
||||
var scaledWidth = Math.floor(this.getScaledWidth(imgs[i], imageRowHeight.height));
|
||||
|
||||
//in this case, a single image will not fit into the row so we need to crop/center
|
||||
// width the full width and the min display height
|
||||
if (imgs.length > 1 && imageRowHeight.imgCount === 1) {
|
||||
sizes.push({
|
||||
width: targetWidth,
|
||||
//ensure that the height is rounded
|
||||
height: Math.round(minDisplayHeight)
|
||||
});
|
||||
row.images.push(imgs[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (currRowWidth + scaledWidth <= targetWidth) {
|
||||
currRowWidth += scaledWidth;
|
||||
sizes.push({
|
||||
@@ -183,7 +202,7 @@ function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, um
|
||||
height: Math.round(imageRowHeight.height)
|
||||
});
|
||||
row.images.push(imgs[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//the max width has been reached
|
||||
break;
|
||||
@@ -409,181 +428,6 @@ function umbPropEditorHelper() {
|
||||
}
|
||||
angular.module('umbraco.services').factory('umbPropEditorHelper', umbPropEditorHelper);
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.imageHelper
|
||||
* @description A helper object used for parsing image paths
|
||||
**/
|
||||
function imageHelper() {
|
||||
return {
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.imageHelper#getImagePropertyValue
|
||||
* @methodOf umbraco.services.imageHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the actual image path associated with the image property if there is one
|
||||
*
|
||||
* @param {object} options Options object
|
||||
* @param {object} options.imageModel The media object to retrieve the image path from
|
||||
*/
|
||||
getImagePropertyValue: function(options) {
|
||||
if (!options && !options.imageModel) {
|
||||
throw "The options objet does not contain the required parameters: imageModel";
|
||||
}
|
||||
|
||||
|
||||
//combine all props, TODO: we really need a better way then this
|
||||
var props = [];
|
||||
if(options.imageModel.properties){
|
||||
props = options.imageModel.properties;
|
||||
}else{
|
||||
$(options.imageModel.tabs).each(function(i, tab){
|
||||
props = props.concat(tab.properties);
|
||||
});
|
||||
}
|
||||
|
||||
var mediaRoot = Umbraco.Sys.ServerVariables.umbracoSettings.mediaPath;
|
||||
var imageProp = _.find(props, function (item) {
|
||||
if(item.alias === "umbracoFile")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//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){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!imageProp) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var imageVal;
|
||||
|
||||
//our default images might store one or many images (as csv)
|
||||
var split = imageProp.value.split(',');
|
||||
var self = this;
|
||||
imageVal = _.map(split, function(item) {
|
||||
return { file: item, isImage: self.detectIfImageByExtension(item) };
|
||||
});
|
||||
|
||||
//for now we'll just return the first image in the collection.
|
||||
//TODO: we should enable returning many to be displayed in the picker if the uploader supports many.
|
||||
if (imageVal.length && imageVal.length > 0 && imageVal[0].isImage) {
|
||||
return imageVal[0].file;
|
||||
}
|
||||
|
||||
return "";
|
||||
},
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.imageHelper#getThumbnail
|
||||
* @methodOf umbraco.services.imageHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* formats the display model used to display the content to the model used to save the content
|
||||
*
|
||||
* @param {object} options Options object
|
||||
* @param {object} options.imageModel The media object to retrieve the image path from
|
||||
*/
|
||||
getThumbnail: function (options) {
|
||||
|
||||
if (!options && !options.imageModel) {
|
||||
throw "The options objet does not contain the required parameters: imageModel";
|
||||
}
|
||||
|
||||
var imagePropVal = this.getImagePropertyValue(options);
|
||||
if (imagePropVal !== "") {
|
||||
return this.getThumbnailFromPath(imagePropVal);
|
||||
}
|
||||
return "";
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.imageHelper#scaleToMaxSize
|
||||
* @methodOf umbraco.services.imageHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Finds the corrct max width and max height, given maximum dimensions and keeping aspect ratios
|
||||
*
|
||||
* @param {number} maxSize Maximum width & height
|
||||
* @param {number} width Current width
|
||||
* @param {number} height Current height
|
||||
*/
|
||||
scaleToMaxSize: function(maxSize, width, height){
|
||||
var retval = {width: width, height: height};
|
||||
|
||||
var maxWidth = maxSize; // Max width for the image
|
||||
var maxHeight = maxSize; // Max height for the image
|
||||
var ratio = 0; // Used for aspect ratio
|
||||
|
||||
// Check if the current width is larger than the max
|
||||
if(width > maxWidth){
|
||||
ratio = maxWidth / width; // get ratio for scaling image
|
||||
|
||||
retval.width = maxWidth;
|
||||
retval.height = height * ratio;
|
||||
|
||||
height = height * ratio; // Reset height to match scaled image
|
||||
width = width * ratio; // Reset width to match scaled image
|
||||
}
|
||||
|
||||
// Check if current height is larger than max
|
||||
if(height > maxHeight){
|
||||
ratio = maxHeight / height; // get ratio for scaling image
|
||||
|
||||
retval.height = maxHeight;
|
||||
retval.width = width * ratio;
|
||||
width = width * ratio; // Reset width to match scaled image
|
||||
}
|
||||
|
||||
return retval;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.imageHelper#getThumbnailFromPath
|
||||
* @methodOf umbraco.services.imageHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the path to the thumbnail version of a given media library image path
|
||||
*
|
||||
* @param {string} imagePath Image path, ex: /media/1234/my-image.jpg
|
||||
*/
|
||||
getThumbnailFromPath: function(imagePath) {
|
||||
var ext = imagePath.substr(imagePath.lastIndexOf('.'));
|
||||
return imagePath.substr(0, imagePath.lastIndexOf('.')) + "_big-thumb" + ".jpg";
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name umbraco.services.imageHelper#detectIfImageByExtension
|
||||
* @methodOf umbraco.services.imageHelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns true/false, indicating if the given path has an allowed image extension
|
||||
*
|
||||
* @param {string} imagePath Image path, ex: /media/1234/my-image.jpg
|
||||
*/
|
||||
detectIfImageByExtension: function(imagePath) {
|
||||
var lowered = imagePath.toLowerCase();
|
||||
var ext = lowered.substr(lowered.lastIndexOf(".") + 1);
|
||||
return ("," + Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes + ",").indexOf("," + ext + ",") !== -1;
|
||||
}
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.services').factory('imageHelper', imageHelper);
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
@@ -743,606 +587,4 @@ function umbDataFormatter() {
|
||||
}
|
||||
angular.module('umbraco.services').factory('umbDataFormatter', umbDataFormatter);
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.iconHelper
|
||||
* @description A helper service for dealing with icons, mostly dealing with legacy tree icons
|
||||
**/
|
||||
function iconHelper($q, $timeout) {
|
||||
|
||||
var converter = [
|
||||
{ oldIcon: ".sprNew", newIcon: "add" },
|
||||
{ oldIcon: ".sprDelete", newIcon: "remove" },
|
||||
{ oldIcon: ".sprMove", newIcon: "enter" },
|
||||
{ oldIcon: ".sprCopy", newIcon: "documents" },
|
||||
{ oldIcon: ".sprSort", newIcon: "navigation-vertical" },
|
||||
{ oldIcon: ".sprPublish", newIcon: "globe" },
|
||||
{ oldIcon: ".sprRollback", newIcon: "undo" },
|
||||
{ oldIcon: ".sprProtect", newIcon: "lock" },
|
||||
{ oldIcon: ".sprAudit", newIcon: "time" },
|
||||
{ oldIcon: ".sprNotify", newIcon: "envelope" },
|
||||
{ oldIcon: ".sprDomain", newIcon: "home" },
|
||||
{ oldIcon: ".sprPermission", newIcon: "lock" },
|
||||
{ oldIcon: ".sprRefresh", newIcon: "refresh" },
|
||||
{ oldIcon: ".sprBinEmpty", newIcon: "trash" },
|
||||
{ oldIcon: ".sprExportDocumentType", newIcon: "download-alt" },
|
||||
{ oldIcon: ".sprImportDocumentType", newIcon: "page-up" },
|
||||
{ oldIcon: ".sprLiveEdit", newIcon: "edit" },
|
||||
{ oldIcon: ".sprCreateFolder", newIcon: "add" },
|
||||
{ oldIcon: ".sprPackage2", newIcon: "box" },
|
||||
{ oldIcon: ".sprLogout", newIcon: "logout" },
|
||||
{ oldIcon: ".sprSave", newIcon: "save" },
|
||||
{ oldIcon: ".sprSendToTranslate", newIcon: "envelope-alt" },
|
||||
{ oldIcon: ".sprToPublish", newIcon: "mail-forward" },
|
||||
{ oldIcon: ".sprTranslate", newIcon: "comments" },
|
||||
{ oldIcon: ".sprUpdate", newIcon: "save" },
|
||||
|
||||
{ oldIcon: ".sprTreeSettingDomain", newIcon: "icon-home" },
|
||||
{ oldIcon: ".sprTreeDoc", newIcon: "icon-document" },
|
||||
{ oldIcon: ".sprTreeDoc2", newIcon: "icon-diploma-alt" },
|
||||
{ oldIcon: ".sprTreeDoc3", newIcon: "icon-notepad" },
|
||||
{ oldIcon: ".sprTreeDoc4", newIcon: "icon-newspaper-alt" },
|
||||
{ oldIcon: ".sprTreeDoc5", newIcon: "icon-notepad-alt" },
|
||||
|
||||
{ oldIcon: ".sprTreeDocPic", newIcon: "icon-picture" },
|
||||
{ oldIcon: ".sprTreeFolder", newIcon: "icon-folder" },
|
||||
{ oldIcon: ".sprTreeFolder_o", newIcon: "icon-folder" },
|
||||
{ oldIcon: ".sprTreeMediaFile", newIcon: "icon-music" },
|
||||
{ oldIcon: ".sprTreeMediaMovie", newIcon: "icon-movie" },
|
||||
{ oldIcon: ".sprTreeMediaPhoto", newIcon: "icon-picture" },
|
||||
|
||||
{ oldIcon: ".sprTreeMember", newIcon: "icon-user" },
|
||||
{ oldIcon: ".sprTreeMemberGroup", newIcon: "icon-users" },
|
||||
{ oldIcon: ".sprTreeMemberType", newIcon: "icon-users" },
|
||||
|
||||
{ oldIcon: ".sprTreeNewsletter", newIcon: "icon-file-text-alt" },
|
||||
{ oldIcon: ".sprTreePackage", newIcon: "icon-box" },
|
||||
{ oldIcon: ".sprTreeRepository", newIcon: "icon-server-alt" },
|
||||
|
||||
{ oldIcon: ".sprTreeSettingDataType", newIcon: "icon-autofill" },
|
||||
|
||||
//TODO:
|
||||
/*
|
||||
{ oldIcon: ".sprTreeSettingAgent", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingCss", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingCssItem", newIcon: "" },
|
||||
|
||||
{ oldIcon: ".sprTreeSettingDataTypeChild", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingDomain", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingLanguage", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingScript", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingTemplate", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeSettingXml", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeStatistik", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeUser", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeUserGroup", newIcon: "" },
|
||||
{ oldIcon: ".sprTreeUserType", newIcon: "" },
|
||||
*/
|
||||
|
||||
{ oldIcon: "folder.png", newIcon: "icon-folder" },
|
||||
{ oldIcon: "mediaphoto.gif", newIcon: "icon-picture" },
|
||||
{ oldIcon: "mediafile.gif", newIcon: "icon-document" },
|
||||
|
||||
{ oldIcon: ".sprTreeDeveloperCacheItem", newIcon: "icon-box" },
|
||||
{ oldIcon: ".sprTreeDeveloperCacheTypes", newIcon: "icon-box" },
|
||||
{ oldIcon: ".sprTreeDeveloperMacro", newIcon: "icon-cogs" },
|
||||
{ oldIcon: ".sprTreeDeveloperRegistry", newIcon: "icon-windows" },
|
||||
{ oldIcon: ".sprTreeDeveloperPython", newIcon: "icon-linux" }
|
||||
];
|
||||
|
||||
var imageConverter = [
|
||||
{oldImage: "contour.png", newIcon: "icon-umb-contour"}
|
||||
];
|
||||
|
||||
var collectedIcons;
|
||||
|
||||
return {
|
||||
|
||||
/** Used by the create dialogs for content/media types to format the data so that the thumbnails are styled properly */
|
||||
formatContentTypeThumbnails: function (contentTypes) {
|
||||
for (var i = 0; i < contentTypes.length; i++) {
|
||||
|
||||
if (contentTypes[i].thumbnailIsClass === undefined || contentTypes[i].thumbnailIsClass) {
|
||||
contentTypes[i].cssClass = this.convertFromLegacyIcon(contentTypes[i].thumbnail);
|
||||
}else {
|
||||
contentTypes[i].style = "background-image: url('" + contentTypes[i].thumbnailFilePath + "');height:36px; background-position:4px 0px; background-repeat: no-repeat;background-size: 35px 35px;";
|
||||
//we need an 'icon-' class in there for certain styles to work so if it is image based we'll add this
|
||||
contentTypes[i].cssClass = "custom-file";
|
||||
}
|
||||
}
|
||||
return contentTypes;
|
||||
},
|
||||
formatContentTypeIcons: function (contentTypes) {
|
||||
for (var i = 0; i < contentTypes.length; i++) {
|
||||
contentTypes[i].icon = this.convertFromLegacyIcon(contentTypes[i].icon);
|
||||
|
||||
//couldnt find replacement
|
||||
if(contentTypes[i].icon.indexOf(".") > 0){
|
||||
contentTypes[i].icon = "icon-document-dashed-line";
|
||||
}
|
||||
}
|
||||
return contentTypes;
|
||||
},
|
||||
/** If the icon is file based (i.e. it has a file path) */
|
||||
isFileBasedIcon: function (icon) {
|
||||
//if it doesn't start with a '.' but contains one then we'll assume it's file based
|
||||
if (icon.startsWith('..') || (!icon.startsWith('.') && icon.indexOf('.') > 1)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/** If the icon is legacy */
|
||||
isLegacyIcon: function (icon) {
|
||||
if(icon.startsWith('..')){
|
||||
return false;
|
||||
}
|
||||
|
||||
if (icon.startsWith('.')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/** If the tree node has a legacy icon */
|
||||
isLegacyTreeNodeIcon: function(treeNode){
|
||||
if (treeNode.iconIsClass) {
|
||||
return this.isLegacyIcon(treeNode.icon);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/** Return a list of icons, optionally filter them */
|
||||
/** It fetches them directly from the active stylesheets in the browser */
|
||||
getIcons: function(){
|
||||
var deferred = $q.defer();
|
||||
$timeout(function(){
|
||||
if(collectedIcons){
|
||||
deferred.resolve(collectedIcons);
|
||||
}else{
|
||||
collectedIcons = [];
|
||||
var c = ".icon-";
|
||||
|
||||
for (var i = document.styleSheets.length - 1; i >= 0; i--) {
|
||||
var classes = document.styleSheets[i].rules || document.styleSheets[i].cssRules;
|
||||
|
||||
if (classes !== null) {
|
||||
for(var x=0;x<classes.length;x++) {
|
||||
var cur = classes[x];
|
||||
if(cur.selectorText && cur.selectorText.indexOf(c) === 0) {
|
||||
var s = cur.selectorText.substring(1);
|
||||
var hasSpace = s.indexOf(" ");
|
||||
if(hasSpace>0){
|
||||
s = s.substring(0, hasSpace);
|
||||
}
|
||||
var hasPseudo = s.indexOf(":");
|
||||
if(hasPseudo>0){
|
||||
s = s.substring(0, hasPseudo);
|
||||
}
|
||||
|
||||
if(collectedIcons.indexOf(s) < 0){
|
||||
collectedIcons.push(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
deferred.resolve(collectedIcons);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/** Converts the icon from legacy to a new one if an old one is detected */
|
||||
convertFromLegacyIcon: function (icon) {
|
||||
if (this.isLegacyIcon(icon)) {
|
||||
//its legacy so convert it if we can
|
||||
var found = _.find(converter, function (item) {
|
||||
return item.oldIcon.toLowerCase() === icon.toLowerCase();
|
||||
});
|
||||
return (found ? found.newIcon : icon);
|
||||
}
|
||||
return icon;
|
||||
},
|
||||
|
||||
convertFromLegacyImage: function (icon) {
|
||||
var found = _.find(imageConverter, function (item) {
|
||||
return item.oldImage.toLowerCase() === icon.toLowerCase();
|
||||
});
|
||||
return (found ? found.newIcon : undefined);
|
||||
},
|
||||
|
||||
/** If we detect that the tree node has legacy icons that can be converted, this will convert them */
|
||||
convertFromLegacyTreeNodeIcon: function (treeNode) {
|
||||
if (this.isLegacyTreeNodeIcon(treeNode)) {
|
||||
return this.convertFromLegacyIcon(treeNode.icon);
|
||||
}
|
||||
return treeNode.icon;
|
||||
}
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.services').factory('iconHelper', iconHelper);
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.xmlhelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Used to convert legacy xml data to json and back again
|
||||
*/
|
||||
function xmlhelper($http) {
|
||||
/*
|
||||
Copyright 2011 Abdulla Abdurakhmanov
|
||||
Original sources are available at https://code.google.com/p/x2js/
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
function X2JS() {
|
||||
var VERSION = "1.0.11";
|
||||
var escapeMode = false;
|
||||
|
||||
var DOMNodeTypes = {
|
||||
ELEMENT_NODE : 1,
|
||||
TEXT_NODE : 3,
|
||||
CDATA_SECTION_NODE : 4,
|
||||
DOCUMENT_NODE : 9
|
||||
};
|
||||
|
||||
function getNodeLocalName( node ) {
|
||||
var nodeLocalName = node.localName;
|
||||
if(nodeLocalName == null){
|
||||
nodeLocalName = node.baseName;
|
||||
} // Yeah, this is IE!!
|
||||
|
||||
if(nodeLocalName === null || nodeLocalName===""){
|
||||
nodeLocalName = node.nodeName;
|
||||
} // =="" is IE too
|
||||
|
||||
return nodeLocalName;
|
||||
}
|
||||
|
||||
function getNodePrefix(node) {
|
||||
return node.prefix;
|
||||
}
|
||||
|
||||
function escapeXmlChars(str) {
|
||||
if(typeof(str) === "string"){
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/');
|
||||
}else{
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
function unescapeXmlChars(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(///g, '\/');
|
||||
}
|
||||
|
||||
function parseDOMChildren( node ) {
|
||||
var result,child, childName;
|
||||
|
||||
if(node.nodeType === DOMNodeTypes.DOCUMENT_NODE) {
|
||||
result = {};
|
||||
child = node.firstChild;
|
||||
childName = getNodeLocalName(child);
|
||||
result[childName] = parseDOMChildren(child);
|
||||
return result;
|
||||
}
|
||||
else{
|
||||
|
||||
if(node.nodeType === DOMNodeTypes.ELEMENT_NODE) {
|
||||
result = {};
|
||||
result.__cnt=0;
|
||||
var nodeChildren = node.childNodes;
|
||||
|
||||
// Children nodes
|
||||
for(var cidx=0; cidx <nodeChildren.length; cidx++) {
|
||||
child = nodeChildren.item(cidx); // nodeChildren[cidx];
|
||||
childName = getNodeLocalName(child);
|
||||
|
||||
result.__cnt++;
|
||||
if(result[childName] === null) {
|
||||
result[childName] = parseDOMChildren(child);
|
||||
result[childName+"_asArray"] = new Array(1);
|
||||
result[childName+"_asArray"][0] = result[childName];
|
||||
}
|
||||
else {
|
||||
if(result[childName] !== null) {
|
||||
if( !(result[childName] instanceof Array)) {
|
||||
var tmpObj = result[childName];
|
||||
result[childName] = [];
|
||||
result[childName][0] = tmpObj;
|
||||
|
||||
result[childName+"_asArray"] = result[childName];
|
||||
}
|
||||
}
|
||||
var aridx = 0;
|
||||
while(result[childName][aridx]!==null){
|
||||
aridx++;
|
||||
}
|
||||
|
||||
(result[childName])[aridx] = parseDOMChildren(child);
|
||||
}
|
||||
}
|
||||
|
||||
// Attributes
|
||||
for(var aidx=0; aidx <node.attributes.length; aidx++) {
|
||||
var attr = node.attributes.item(aidx); // [aidx];
|
||||
result.__cnt++;
|
||||
result["_"+attr.name]=attr.value;
|
||||
}
|
||||
|
||||
// Node namespace prefix
|
||||
var nodePrefix = getNodePrefix(node);
|
||||
if(nodePrefix!==null && nodePrefix!=="") {
|
||||
result.__cnt++;
|
||||
result.__prefix=nodePrefix;
|
||||
}
|
||||
|
||||
if( result.__cnt === 1 && result["#text"]!==null ) {
|
||||
result = result["#text"];
|
||||
}
|
||||
|
||||
if(result["#text"]!==null) {
|
||||
result.__text = result["#text"];
|
||||
if(escapeMode){
|
||||
result.__text = unescapeXmlChars(result.__text);
|
||||
}
|
||||
|
||||
delete result["#text"];
|
||||
delete result["#text_asArray"];
|
||||
}
|
||||
if(result["#cdata-section"]!=null) {
|
||||
result.__cdata = result["#cdata-section"];
|
||||
delete result["#cdata-section"];
|
||||
delete result["#cdata-section_asArray"];
|
||||
}
|
||||
|
||||
if(result.__text!=null || result.__cdata!=null) {
|
||||
result.toString = function() {
|
||||
return (this.__text!=null? this.__text:'')+( this.__cdata!=null ? this.__cdata:'');
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else{
|
||||
if(node.nodeType === DOMNodeTypes.TEXT_NODE || node.nodeType === DOMNodeTypes.CDATA_SECTION_NODE) {
|
||||
return node.nodeValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startTag(jsonObj, element, attrList, closed) {
|
||||
var resultStr = "<"+ ( (jsonObj!=null && jsonObj.__prefix!=null)? (jsonObj.__prefix+":"):"") + element;
|
||||
if(attrList!=null) {
|
||||
for(var aidx = 0; aidx < attrList.length; aidx++) {
|
||||
var attrName = attrList[aidx];
|
||||
var attrVal = jsonObj[attrName];
|
||||
resultStr+=" "+attrName.substr(1)+"='"+attrVal+"'";
|
||||
}
|
||||
}
|
||||
if(!closed){
|
||||
resultStr+=">";
|
||||
}else{
|
||||
resultStr+="/>";
|
||||
}
|
||||
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
function endTag(jsonObj,elementName) {
|
||||
return "</"+ (jsonObj.__prefix!==null? (jsonObj.__prefix+":"):"")+elementName+">";
|
||||
}
|
||||
|
||||
function endsWith(str, suffix) {
|
||||
return str.indexOf(suffix, str.length - suffix.length) !== -1;
|
||||
}
|
||||
|
||||
function jsonXmlSpecialElem ( jsonObj, jsonObjField ) {
|
||||
if(endsWith(jsonObjField.toString(),("_asArray")) || jsonObjField.toString().indexOf("_")===0 || (jsonObj[jsonObjField] instanceof Function) ){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function jsonXmlElemCount ( jsonObj ) {
|
||||
var elementsCnt = 0;
|
||||
if(jsonObj instanceof Object ) {
|
||||
for( var it in jsonObj ) {
|
||||
if(jsonXmlSpecialElem ( jsonObj, it) ){
|
||||
continue;
|
||||
}
|
||||
elementsCnt++;
|
||||
}
|
||||
}
|
||||
return elementsCnt;
|
||||
}
|
||||
|
||||
function parseJSONAttributes ( jsonObj ) {
|
||||
var attrList = [];
|
||||
if(jsonObj instanceof Object ) {
|
||||
for( var ait in jsonObj ) {
|
||||
if(ait.toString().indexOf("__")=== -1 && ait.toString().indexOf("_")===0) {
|
||||
attrList.push(ait);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attrList;
|
||||
}
|
||||
|
||||
function parseJSONTextAttrs ( jsonTxtObj ) {
|
||||
var result ="";
|
||||
|
||||
if(jsonTxtObj.__cdata!=null) {
|
||||
result+="<![CDATA["+jsonTxtObj.__cdata+"]]>";
|
||||
}
|
||||
|
||||
if(jsonTxtObj.__text!=null) {
|
||||
if(escapeMode){
|
||||
result+=escapeXmlChars(jsonTxtObj.__text);
|
||||
}else{
|
||||
result+=jsonTxtObj.__text;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseJSONTextObject ( jsonTxtObj ) {
|
||||
var result ="";
|
||||
|
||||
if( jsonTxtObj instanceof Object ) {
|
||||
result+=parseJSONTextAttrs ( jsonTxtObj );
|
||||
}
|
||||
else{
|
||||
if(jsonTxtObj!=null) {
|
||||
if(escapeMode){
|
||||
result+=escapeXmlChars(jsonTxtObj);
|
||||
}else{
|
||||
result+=jsonTxtObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseJSONArray ( jsonArrRoot, jsonArrObj, attrList ) {
|
||||
var result = "";
|
||||
if(jsonArrRoot.length === 0) {
|
||||
result+=startTag(jsonArrRoot, jsonArrObj, attrList, true);
|
||||
}
|
||||
else {
|
||||
for(var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) {
|
||||
result+=startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false);
|
||||
result+=parseJSONObject(jsonArrRoot[arIdx]);
|
||||
result+=endTag(jsonArrRoot[arIdx],jsonArrObj);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseJSONObject ( jsonObj ) {
|
||||
var result = "";
|
||||
|
||||
var elementsCnt = jsonXmlElemCount ( jsonObj );
|
||||
|
||||
if(elementsCnt > 0) {
|
||||
for( var it in jsonObj ) {
|
||||
if(jsonXmlSpecialElem ( jsonObj, it) ){
|
||||
continue;
|
||||
}
|
||||
|
||||
var subObj = jsonObj[it];
|
||||
var attrList = parseJSONAttributes( subObj );
|
||||
|
||||
if(subObj === null || subObj === undefined) {
|
||||
result+=startTag(subObj, it, attrList, true);
|
||||
}else{
|
||||
if(subObj instanceof Object) {
|
||||
|
||||
if(subObj instanceof Array) {
|
||||
result+=parseJSONArray( subObj, it, attrList );
|
||||
}else {
|
||||
var subObjElementsCnt = jsonXmlElemCount ( subObj );
|
||||
if(subObjElementsCnt > 0 || subObj.__text!==null || subObj.__cdata!==null) {
|
||||
result+=startTag(subObj, it, attrList, false);
|
||||
result+=parseJSONObject(subObj);
|
||||
result+=endTag(subObj,it);
|
||||
}else{
|
||||
result+=startTag(subObj, it, attrList, true);
|
||||
}
|
||||
}
|
||||
|
||||
}else {
|
||||
result+=startTag(subObj, it, attrList, false);
|
||||
result+=parseJSONTextObject(subObj);
|
||||
result+=endTag(subObj,it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result+=parseJSONTextObject(jsonObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this.parseXmlString = function(xmlDocStr) {
|
||||
var xmlDoc;
|
||||
if (window.DOMParser) {
|
||||
var parser=new window.DOMParser();
|
||||
xmlDoc = parser.parseFromString( xmlDocStr, "text/xml" );
|
||||
}
|
||||
else {
|
||||
// IE :(
|
||||
if(xmlDocStr.indexOf("<?")===0) {
|
||||
xmlDocStr = xmlDocStr.substr( xmlDocStr.indexOf("?>") + 2 );
|
||||
}
|
||||
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
|
||||
xmlDoc.async="false";
|
||||
xmlDoc.loadXML(xmlDocStr);
|
||||
}
|
||||
return xmlDoc;
|
||||
};
|
||||
|
||||
this.xml2json = function (xmlDoc) {
|
||||
return parseDOMChildren ( xmlDoc );
|
||||
};
|
||||
|
||||
this.xml_str2json = function (xmlDocStr) {
|
||||
var xmlDoc = this.parseXmlString(xmlDocStr);
|
||||
return this.xml2json(xmlDoc);
|
||||
};
|
||||
|
||||
this.json2xml_str = function (jsonObj) {
|
||||
return parseJSONObject ( jsonObj );
|
||||
};
|
||||
|
||||
this.json2xml = function (jsonObj) {
|
||||
var xmlDocStr = this.json2xml_str (jsonObj);
|
||||
return this.parseXmlString(xmlDocStr);
|
||||
};
|
||||
|
||||
this.getVersion = function () {
|
||||
return VERSION;
|
||||
};
|
||||
|
||||
this.escapeMode = function(enabled) {
|
||||
escapeMode = enabled;
|
||||
};
|
||||
}
|
||||
|
||||
var x2js = new X2JS();
|
||||
return {
|
||||
/** Called to load in the legacy tree js which is required on startup if a user is logged in or
|
||||
after login, but cannot be called until they are authenticated which is why it needs to be lazy loaded. */
|
||||
toJson: function(xml) {
|
||||
var json = x2js.xml_str2json( xml );
|
||||
return json;
|
||||
},
|
||||
fromJson: function(json) {
|
||||
var xml = x2js.json2xml_str( json );
|
||||
return xml;
|
||||
},
|
||||
parseFeed: function (url) {
|
||||
return $http.jsonp('//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=50&callback=JSON_CALLBACK&q=' + encodeURIComponent(url));
|
||||
}
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.services').factory('xmlhelper', xmlhelper);
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.xmlhelper
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Used to convert legacy xml data to json and back again
|
||||
*/
|
||||
function xmlhelper($http) {
|
||||
/*
|
||||
Copyright 2011 Abdulla Abdurakhmanov
|
||||
Original sources are available at https://code.google.com/p/x2js/
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
function X2JS() {
|
||||
var VERSION = "1.0.11";
|
||||
var escapeMode = false;
|
||||
|
||||
var DOMNodeTypes = {
|
||||
ELEMENT_NODE: 1,
|
||||
TEXT_NODE: 3,
|
||||
CDATA_SECTION_NODE: 4,
|
||||
DOCUMENT_NODE: 9
|
||||
};
|
||||
|
||||
function getNodeLocalName(node) {
|
||||
var nodeLocalName = node.localName;
|
||||
if (nodeLocalName == null) {
|
||||
nodeLocalName = node.baseName;
|
||||
} // Yeah, this is IE!!
|
||||
|
||||
if (nodeLocalName === null || nodeLocalName === "") {
|
||||
nodeLocalName = node.nodeName;
|
||||
} // =="" is IE too
|
||||
|
||||
return nodeLocalName;
|
||||
}
|
||||
|
||||
function getNodePrefix(node) {
|
||||
return node.prefix;
|
||||
}
|
||||
|
||||
function escapeXmlChars(str) {
|
||||
if (typeof (str) === "string") {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/');
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
function unescapeXmlChars(str) {
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(///g, '\/');
|
||||
}
|
||||
|
||||
function parseDOMChildren(node) {
|
||||
var result, child, childName;
|
||||
|
||||
if (node.nodeType === DOMNodeTypes.DOCUMENT_NODE) {
|
||||
result = {};
|
||||
child = node.firstChild;
|
||||
childName = getNodeLocalName(child);
|
||||
result[childName] = parseDOMChildren(child);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
|
||||
if (node.nodeType === DOMNodeTypes.ELEMENT_NODE) {
|
||||
result = {};
|
||||
result.__cnt = 0;
|
||||
var nodeChildren = node.childNodes;
|
||||
|
||||
// Children nodes
|
||||
for (var cidx = 0; cidx < nodeChildren.length; cidx++) {
|
||||
child = nodeChildren.item(cidx); // nodeChildren[cidx];
|
||||
childName = getNodeLocalName(child);
|
||||
|
||||
result.__cnt++;
|
||||
if (result[childName] === null) {
|
||||
result[childName] = parseDOMChildren(child);
|
||||
result[childName + "_asArray"] = new Array(1);
|
||||
result[childName + "_asArray"][0] = result[childName];
|
||||
}
|
||||
else {
|
||||
if (result[childName] !== null) {
|
||||
if (!(result[childName] instanceof Array)) {
|
||||
var tmpObj = result[childName];
|
||||
result[childName] = [];
|
||||
result[childName][0] = tmpObj;
|
||||
|
||||
result[childName + "_asArray"] = result[childName];
|
||||
}
|
||||
}
|
||||
var aridx = 0;
|
||||
while (result[childName][aridx] !== null) {
|
||||
aridx++;
|
||||
}
|
||||
|
||||
(result[childName])[aridx] = parseDOMChildren(child);
|
||||
}
|
||||
}
|
||||
|
||||
// Attributes
|
||||
for (var aidx = 0; aidx < node.attributes.length; aidx++) {
|
||||
var attr = node.attributes.item(aidx); // [aidx];
|
||||
result.__cnt++;
|
||||
result["_" + attr.name] = attr.value;
|
||||
}
|
||||
|
||||
// Node namespace prefix
|
||||
var nodePrefix = getNodePrefix(node);
|
||||
if (nodePrefix !== null && nodePrefix !== "") {
|
||||
result.__cnt++;
|
||||
result.__prefix = nodePrefix;
|
||||
}
|
||||
|
||||
if (result.__cnt === 1 && result["#text"] !== null) {
|
||||
result = result["#text"];
|
||||
}
|
||||
|
||||
if (result["#text"] !== null) {
|
||||
result.__text = result["#text"];
|
||||
if (escapeMode) {
|
||||
result.__text = unescapeXmlChars(result.__text);
|
||||
}
|
||||
|
||||
delete result["#text"];
|
||||
delete result["#text_asArray"];
|
||||
}
|
||||
if (result["#cdata-section"] != null) {
|
||||
result.__cdata = result["#cdata-section"];
|
||||
delete result["#cdata-section"];
|
||||
delete result["#cdata-section_asArray"];
|
||||
}
|
||||
|
||||
if (result.__text != null || result.__cdata != null) {
|
||||
result.toString = function () {
|
||||
return (this.__text != null ? this.__text : '') + (this.__cdata != null ? this.__cdata : '');
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
if (node.nodeType === DOMNodeTypes.TEXT_NODE || node.nodeType === DOMNodeTypes.CDATA_SECTION_NODE) {
|
||||
return node.nodeValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function startTag(jsonObj, element, attrList, closed) {
|
||||
var resultStr = "<" + ((jsonObj != null && jsonObj.__prefix != null) ? (jsonObj.__prefix + ":") : "") + element;
|
||||
if (attrList != null) {
|
||||
for (var aidx = 0; aidx < attrList.length; aidx++) {
|
||||
var attrName = attrList[aidx];
|
||||
var attrVal = jsonObj[attrName];
|
||||
resultStr += " " + attrName.substr(1) + "='" + attrVal + "'";
|
||||
}
|
||||
}
|
||||
if (!closed) {
|
||||
resultStr += ">";
|
||||
} else {
|
||||
resultStr += "/>";
|
||||
}
|
||||
|
||||
return resultStr;
|
||||
}
|
||||
|
||||
function endTag(jsonObj, elementName) {
|
||||
return "</" + (jsonObj.__prefix !== null ? (jsonObj.__prefix + ":") : "") + elementName + ">";
|
||||
}
|
||||
|
||||
function endsWith(str, suffix) {
|
||||
return str.indexOf(suffix, str.length - suffix.length) !== -1;
|
||||
}
|
||||
|
||||
function jsonXmlSpecialElem(jsonObj, jsonObjField) {
|
||||
if (endsWith(jsonObjField.toString(), ("_asArray")) || jsonObjField.toString().indexOf("_") === 0 || (jsonObj[jsonObjField] instanceof Function)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function jsonXmlElemCount(jsonObj) {
|
||||
var elementsCnt = 0;
|
||||
if (jsonObj instanceof Object) {
|
||||
for (var it in jsonObj) {
|
||||
if (jsonXmlSpecialElem(jsonObj, it)) {
|
||||
continue;
|
||||
}
|
||||
elementsCnt++;
|
||||
}
|
||||
}
|
||||
return elementsCnt;
|
||||
}
|
||||
|
||||
function parseJSONAttributes(jsonObj) {
|
||||
var attrList = [];
|
||||
if (jsonObj instanceof Object) {
|
||||
for (var ait in jsonObj) {
|
||||
if (ait.toString().indexOf("__") === -1 && ait.toString().indexOf("_") === 0) {
|
||||
attrList.push(ait);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attrList;
|
||||
}
|
||||
|
||||
function parseJSONTextAttrs(jsonTxtObj) {
|
||||
var result = "";
|
||||
|
||||
if (jsonTxtObj.__cdata != null) {
|
||||
result += "<![CDATA[" + jsonTxtObj.__cdata + "]]>";
|
||||
}
|
||||
|
||||
if (jsonTxtObj.__text != null) {
|
||||
if (escapeMode) {
|
||||
result += escapeXmlChars(jsonTxtObj.__text);
|
||||
} else {
|
||||
result += jsonTxtObj.__text;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseJSONTextObject(jsonTxtObj) {
|
||||
var result = "";
|
||||
|
||||
if (jsonTxtObj instanceof Object) {
|
||||
result += parseJSONTextAttrs(jsonTxtObj);
|
||||
}
|
||||
else {
|
||||
if (jsonTxtObj != null) {
|
||||
if (escapeMode) {
|
||||
result += escapeXmlChars(jsonTxtObj);
|
||||
} else {
|
||||
result += jsonTxtObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseJSONArray(jsonArrRoot, jsonArrObj, attrList) {
|
||||
var result = "";
|
||||
if (jsonArrRoot.length === 0) {
|
||||
result += startTag(jsonArrRoot, jsonArrObj, attrList, true);
|
||||
}
|
||||
else {
|
||||
for (var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) {
|
||||
result += startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false);
|
||||
result += parseJSONObject(jsonArrRoot[arIdx]);
|
||||
result += endTag(jsonArrRoot[arIdx], jsonArrObj);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseJSONObject(jsonObj) {
|
||||
var result = "";
|
||||
|
||||
var elementsCnt = jsonXmlElemCount(jsonObj);
|
||||
|
||||
if (elementsCnt > 0) {
|
||||
for (var it in jsonObj) {
|
||||
if (jsonXmlSpecialElem(jsonObj, it)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var subObj = jsonObj[it];
|
||||
var attrList = parseJSONAttributes(subObj);
|
||||
|
||||
if (subObj === null || subObj === undefined) {
|
||||
result += startTag(subObj, it, attrList, true);
|
||||
} else {
|
||||
if (subObj instanceof Object) {
|
||||
|
||||
if (subObj instanceof Array) {
|
||||
result += parseJSONArray(subObj, it, attrList);
|
||||
} else {
|
||||
var subObjElementsCnt = jsonXmlElemCount(subObj);
|
||||
if (subObjElementsCnt > 0 || subObj.__text !== null || subObj.__cdata !== null) {
|
||||
result += startTag(subObj, it, attrList, false);
|
||||
result += parseJSONObject(subObj);
|
||||
result += endTag(subObj, it);
|
||||
} else {
|
||||
result += startTag(subObj, it, attrList, true);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
result += startTag(subObj, it, attrList, false);
|
||||
result += parseJSONTextObject(subObj);
|
||||
result += endTag(subObj, it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result += parseJSONTextObject(jsonObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this.parseXmlString = function (xmlDocStr) {
|
||||
var xmlDoc;
|
||||
if (window.DOMParser) {
|
||||
var parser = new window.DOMParser();
|
||||
xmlDoc = parser.parseFromString(xmlDocStr, "text/xml");
|
||||
}
|
||||
else {
|
||||
// IE :(
|
||||
if (xmlDocStr.indexOf("<?") === 0) {
|
||||
xmlDocStr = xmlDocStr.substr(xmlDocStr.indexOf("?>") + 2);
|
||||
}
|
||||
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
|
||||
xmlDoc.async = "false";
|
||||
xmlDoc.loadXML(xmlDocStr);
|
||||
}
|
||||
return xmlDoc;
|
||||
};
|
||||
|
||||
this.xml2json = function (xmlDoc) {
|
||||
return parseDOMChildren(xmlDoc);
|
||||
};
|
||||
|
||||
this.xml_str2json = function (xmlDocStr) {
|
||||
var xmlDoc = this.parseXmlString(xmlDocStr);
|
||||
return this.xml2json(xmlDoc);
|
||||
};
|
||||
|
||||
this.json2xml_str = function (jsonObj) {
|
||||
return parseJSONObject(jsonObj);
|
||||
};
|
||||
|
||||
this.json2xml = function (jsonObj) {
|
||||
var xmlDocStr = this.json2xml_str(jsonObj);
|
||||
return this.parseXmlString(xmlDocStr);
|
||||
};
|
||||
|
||||
this.getVersion = function () {
|
||||
return VERSION;
|
||||
};
|
||||
|
||||
this.escapeMode = function (enabled) {
|
||||
escapeMode = enabled;
|
||||
};
|
||||
}
|
||||
|
||||
var x2js = new X2JS();
|
||||
return {
|
||||
/** Called to load in the legacy tree js which is required on startup if a user is logged in or
|
||||
after login, but cannot be called until they are authenticated which is why it needs to be lazy loaded. */
|
||||
toJson: function (xml) {
|
||||
var json = x2js.xml_str2json(xml);
|
||||
return json;
|
||||
},
|
||||
fromJson: function (json) {
|
||||
var xml = x2js.json2xml_str(json);
|
||||
return xml;
|
||||
},
|
||||
parseFeed: function (url) {
|
||||
return $http.jsonp('//ajax.googleapis.com/ajax/services/feed/load?v=1.0&num=50&callback=JSON_CALLBACK&q=' + encodeURIComponent(url));
|
||||
}
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.services').factory('xmlhelper', xmlhelper);
|
||||
@@ -53,8 +53,10 @@ data-file-upload="options" data-file-upload-progress="" data-ng-class="{'fileupl
|
||||
ng-hide="active() == 0"></div>
|
||||
|
||||
<umb-photo-folder
|
||||
min-height="150"
|
||||
min-height="75"
|
||||
min-width="150"
|
||||
max-height="250"
|
||||
ideal-items-per-row="3"
|
||||
on-click="clickHandler"
|
||||
ng-model="images"
|
||||
filter-by="searchTerm"/>
|
||||
|
||||
@@ -23,16 +23,5 @@ describe('image helper tests', function () {
|
||||
expect(umbImageHelper.detectIfImageByExtension(doc1)).toBe(false);
|
||||
});
|
||||
|
||||
it('gets a thumbnail path', function () {
|
||||
|
||||
var image1 = "a-jpeg.jpg";
|
||||
var image2 = "a-png.png";
|
||||
var image3 = "thisisagif.blah.gif";
|
||||
|
||||
expect(umbImageHelper.getThumbnailFromPath(image1)).toBe("a-jpeg_big-thumb.jpg");
|
||||
expect(umbImageHelper.getThumbnailFromPath(image2)).toBe("a-png_big-thumb.jpg");
|
||||
expect(umbImageHelper.getThumbnailFromPath(image3)).toBe("thisisagif.blah_big-thumb.jpg");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user