fix media, macro and embed overlays in rte in grid

This commit is contained in:
Mads Rasmussen
2015-12-11 13:41:54 +01:00
parent fe7f73ff57
commit a08566c04c
5 changed files with 275 additions and 167 deletions

View File

@@ -7,7 +7,10 @@ angular.module("umbraco.directives")
onClick: '&',
onFocus: '&',
onBlur: '&',
configuration:"="
configuration:"=",
onMediaPickerClick: "=",
onEmbedClick: "=",
onMacroPickerClick: "="
},
template: "<textarea ng-model=\"value\" rows=\"10\" class=\"mceNoEditor\" style=\"overflow:hidden\" id=\"{{uniqueId}}\"></textarea>",
replace: true,
@@ -207,16 +210,25 @@ angular.module("umbraco.directives")
//Create the insert media plugin
tinyMceService.createMediaPicker(editor, scope);
tinyMceService.createMediaPicker(editor, scope, function(currentTarget, userData){
if(scope.onMediaPickerClick) {
scope.onMediaPickerClick(editor, currentTarget, userData);
}
});
//Create the embedded plugin
tinyMceService.createInsertEmbeddedMedia(editor, scope);
//Create the insert link plugin
//tinyMceService.createLinkPicker(editor, scope);
tinyMceService.createInsertEmbeddedMedia(editor, scope, function(){
if(scope.onEmbedClick) {
scope.onEmbedClick(editor);
}
});
//Create the insert macro plugin
tinyMceService.createInsertMacro(editor, scope);
tinyMceService.createInsertMacro(editor, scope, function(dialogData){
if(scope.onMacroPickerClick) {
scope.onMacroPickerClick(editor, dialogData);
}
});
};

View File

@@ -56,31 +56,22 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
* @param {Object} editor the TinyMCE editor instance
* @param {Object} $scope the current controller scope
*/
createInsertEmbeddedMedia: function (editor, $scope) {
createInsertEmbeddedMedia: function (editor, scope, callback) {
editor.addButton('umbembeddialog', {
icon: 'custom icon-tv',
tooltip: 'Embed',
onclick: function () {
$scope.embedOverlay = {};
$scope.embedOverlay.view = "embed";
$scope.embedOverlay.show = true;
$scope.embedOverlay.submit = function(model) {
editor.insertContent(model.embed.preview);
$scope.embedOverlay.show = true;
$scope.embedOverlay = null;
};
$scope.embedOverlay.close = function(oldModel) {
$scope.embedOverlay.show = true;
$scope.embedOverlay = null;
};
if (callback) {
callback();
}
}
});
},
insertEmbeddedMediaInEditor: function(editor, preview) {
editor.insertContent(preview);
},
/**
* @ngdoc method
* @name umbraco.services.tinyMceService#createMediaPicker
@@ -92,7 +83,7 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
* @param {Object} editor the TinyMCE editor instance
* @param {Object} $scope the current controller scope
*/
createMediaPicker: function (editor, scope) {
createMediaPicker: function (editor, scope, callback) {
editor.addButton('umbmediapicker', {
icon: 'custom icon-picture',
tooltip: 'Media Picker',
@@ -112,66 +103,48 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
}
userService.getCurrentUser().then(function(userData) {
scope.mediaPickerOverlay = {};
scope.mediaPickerOverlay.currentTarget = currentTarget;
scope.mediaPickerOverlay.onlyImages = true;
scope.mediaPickerOverlay.showDetails = true;
scope.mediaPickerOverlay.startNodeId = userData.startMediaId;
scope.mediaPickerOverlay.view = "mediapicker";
scope.mediaPickerOverlay.show = true;
scope.mediaPickerOverlay.submit = function(model) {
var img = model.selectedImages[0];
if(img) {
var data = {
alt: img.altText || "",
src: (img.url) ? img.url : "nothing.jpg",
rel: img.id,
'data-id': img.id,
id: '__mcenew'
};
editor.insertContent(editor.dom.createHTML('img', data));
$timeout(function () {
var imgElm = editor.dom.get('__mcenew');
var size = editor.dom.getSize(imgElm);
if (editor.settings.maxImageSize && editor.settings.maxImageSize !== 0) {
var newSize = imageHelper.scaleToMaxSize(editor.settings.maxImageSize, size.w, size.h);
var s = "width: " + newSize.width + "px; height:" + newSize.height + "px;";
editor.dom.setAttrib(imgElm, 'style', s);
editor.dom.setAttrib(imgElm, 'id', null);
if (img.url) {
var src = img.url + "?width=" + newSize.width + "&height=" + newSize.height;
editor.dom.setAttrib(imgElm, 'data-mce-src', src);
}
}
}, 500);
}
scope.mediaPickerOverlay.show = false;
scope.mediaPickerOverlay = null;
};
scope.mediaPickerOverlay.close = function(oldModel) {
scope.mediaPickerOverlay.show = false;
scope.mediaPickerOverlay = null;
};
if(callback) {
callback(currentTarget, userData);
}
});
}
});
},
insertMediaInEditor: function(editor, img) {
if(img) {
var data = {
alt: img.altText || "",
src: (img.url) ? img.url : "nothing.jpg",
rel: img.id,
'data-id': img.id,
id: '__mcenew'
};
editor.insertContent(editor.dom.createHTML('img', data));
$timeout(function () {
var imgElm = editor.dom.get('__mcenew');
var size = editor.dom.getSize(imgElm);
if (editor.settings.maxImageSize && editor.settings.maxImageSize !== 0) {
var newSize = imageHelper.scaleToMaxSize(editor.settings.maxImageSize, size.w, size.h);
var s = "width: " + newSize.width + "px; height:" + newSize.height + "px;";
editor.dom.setAttrib(imgElm, 'style', s);
editor.dom.setAttrib(imgElm, 'id', null);
if (img.url) {
var src = img.url + "?width=" + newSize.width + "&height=" + newSize.height;
editor.dom.setAttrib(imgElm, 'data-mce-src', src);
}
}
}, 500);
}
},
/**
* @ngdoc method
* @name umbraco.services.tinyMceService#createUmbracoMacro
@@ -183,8 +156,10 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
* @param {Object} editor the TinyMCE editor instance
* @param {Object} $scope the current controller scope
*/
createInsertMacro: function (editor, $scope) {
createInsertMacro: function (editor, $scope, callback) {
var createInsertMacroScope = this;
/** Adds custom rules for the macro plugin and custom serialization */
editor.on('preInit', function (args) {
//this is requires so that we tell the serializer that a 'div' is actually allowed in the root, otherwise the cleanup will strip it out
@@ -220,45 +195,6 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
return null;
}
/** loads in the macro content async from the server */
function loadMacroContent($macroDiv, macroData) {
//if we don't have the macroData, then we'll need to parse it from the macro div
if (!macroData) {
var contents = $macroDiv.contents();
var comment = _.find(contents, function (item) {
return item.nodeType === 8;
});
if (!comment) {
throw "Cannot parse the current macro, the syntax in the editor is invalid";
}
var syntax = comment.textContent.trim();
var parsed = macroService.parseMacroSyntax(syntax);
macroData = parsed;
}
var $ins = $macroDiv.find("ins");
//show the throbber
$macroDiv.addClass("loading");
var contentId = $routeParams.id;
//need to wrap in safe apply since this might be occuring outside of angular
angularHelper.safeApply($scope, function() {
macroResource.getMacroResultAsHtmlForEditor(macroData.macroAlias, contentId, macroData.macroParamsDictionary)
.then(function (htmlResult) {
$macroDiv.removeClass("loading");
htmlResult = htmlResult.trim();
if (htmlResult !== "") {
$ins.html(htmlResult);
}
});
});
}
/** Adds the button instance */
editor.addButton('umbmacro', {
icon: 'custom icon-settings-alt',
@@ -361,8 +297,8 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
//get all macro divs and load their content
$(editor.dom.select(".umb-macro-holder.mceNonEditable")).each(function() {
loadMacroContent($(this));
});
createInsertMacroScope.loadMacroContent($(this), null, $scope);
});
});
@@ -485,44 +421,73 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
};
}
$scope.macroPickerOverlay = {};
$scope.macroPickerOverlay.view = "macropicker";
$scope.macroPickerOverlay.dialogData = dialogData;
$scope.macroPickerOverlay.show = true;
$scope.macroPickerOverlay.submit = function(model) {
var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
//put the macro syntax in comments, we will parse this out on the server side to be used
//for persisting.
var macroSyntaxComment = "<!-- " + macroObject.syntax + " -->";
//create an id class for this element so we can re-select it after inserting
var uniqueId = "umb-macro-" + editor.dom.uniqueId();
var macroDiv = editor.dom.create('div',
{
'class': 'umb-macro-holder ' + macroObject.macroAlias + ' mceNonEditable ' + uniqueId
},
macroSyntaxComment + '<ins>Macro alias: <strong>' + macroObject.macroAlias + '</strong></ins>');
editor.selection.setNode(macroDiv);
var $macroDiv = $(editor.dom.select("div.umb-macro-holder." + uniqueId));
//async load the macro content
loadMacroContent($macroDiv, macroObject);
$scope.macroPickerOverlay.show = false;
$scope.macroPickerOverlay = null;
};
$scope.macroPickerOverlay.close = function(oldModel) {
$scope.macroPickerOverlay.show = false;
$scope.macroPickerOverlay = null;
};
if(callback) {
callback(dialogData);
}
}
});
},
insertMacroInEditor: function(editor, macroObject, $scope) {
//put the macro syntax in comments, we will parse this out on the server side to be used
//for persisting.
var macroSyntaxComment = "<!-- " + macroObject.syntax + " -->";
//create an id class for this element so we can re-select it after inserting
var uniqueId = "umb-macro-" + editor.dom.uniqueId();
var macroDiv = editor.dom.create('div',
{
'class': 'umb-macro-holder ' + macroObject.macroAlias + ' mceNonEditable ' + uniqueId
},
macroSyntaxComment + '<ins>Macro alias: <strong>' + macroObject.macroAlias + '</strong></ins>');
editor.selection.setNode(macroDiv);
var $macroDiv = $(editor.dom.select("div.umb-macro-holder." + uniqueId));
//async load the macro content
this.loadMacroContent($macroDiv, macroObject, $scope);
},
/** loads in the macro content async from the server */
loadMacroContent: function($macroDiv, macroData, $scope) {
//if we don't have the macroData, then we'll need to parse it from the macro div
if (!macroData) {
var contents = $macroDiv.contents();
var comment = _.find(contents, function (item) {
return item.nodeType === 8;
});
if (!comment) {
throw "Cannot parse the current macro, the syntax in the editor is invalid";
}
var syntax = comment.textContent.trim();
var parsed = macroService.parseMacroSyntax(syntax);
macroData = parsed;
}
var $ins = $macroDiv.find("ins");
//show the throbber
$macroDiv.addClass("loading");
var contentId = $routeParams.id;
//need to wrap in safe apply since this might be occuring outside of angular
angularHelper.safeApply($scope, function() {
macroResource.getMacroResultAsHtmlForEditor(macroData.macroAlias, contentId, macroData.macroParamsDictionary)
.then(function (htmlResult) {
$macroDiv.removeClass("loading");
htmlResult = htmlResult.trim();
if (htmlResult !== "") {
$ins.html(htmlResult);
}
});
});
}
};
}

View File

@@ -0,0 +1,60 @@
(function() {
"use strict";
function GridRichTextEditorController($scope, tinyMceService, macroService) {
var vm = this;
vm.openMediaPicker = openMediaPicker;
vm.openMacroPicker = openMacroPicker;
vm.openEmbed = openEmbed;
function openMediaPicker(editor, currentTarget, userData) {
vm.mediaPickerOverlay = {
currentTarget: currentTarget,
onlyImages: true,
showDetails: true,
startNodeId: userData.startMediaId,
view: "mediapicker",
show: true,
submit: function(model) {
tinyMceService.insertMediaInEditor(editor, model.selectedImages[0]);
vm.mediaPickerOverlay.show = false;
vm.mediaPickerOverlay = null;
}
};
}
function openEmbed(editor) {
vm.embedOverlay = {
view: "embed",
show: true,
submit: function(model) {
tinyMceService.insertEmbeddedMediaInEditor(editor, model.embed.preview);
vm.embedOverlay.show = false;
vm.embedOverlay = null;
}
};
}
function openMacroPicker(editor, dialogData) {
vm.macroPickerOverlay = {
view: "macropicker",
dialogData: dialogData,
show: true,
submit: function(model) {
var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
tinyMceService.insertMacroInEditor(editor, macroObject, $scope);
vm.macroPickerOverlay.show = false;
vm.macroPickerOverlay = null;
}
};
}
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.Grid.RichTextEditorController", GridRichTextEditorController);
})();

View File

@@ -1,5 +1,34 @@
<div
unique-id="control.$uniqueId"
value="control.value"
grid-rte configuration="model.config.rte">
<div ng-controller="Umbraco.PropertyEditors.Grid.RichTextEditorController as vm">
<div
grid-rte
configuration="model.config.rte"
value="control.value"
unique-id="control.$uniqueId"
on-media-picker-click="vm.openMediaPicker"
on-embed-click="vm.openEmbed"
on-macro-picker-click="vm.openMacroPicker">
</div>
<umb-overlay
ng-if="vm.mediaPickerOverlay.show"
model="vm.mediaPickerOverlay"
position="right"
view="vm.mediaPickerOverlay.view">
</umb-overlay>
<umb-overlay
ng-if="vm.embedOverlay.show"
model="vm.embedOverlay"
position="right"
view="vm.embedOverlay.view">
</umb-overlay>
<umb-overlay
ng-if="vm.macroPickerOverlay.show"
model="vm.macroPickerOverlay"
view="vm.macroPickerOverlay.view"
position="right">
</umb-overlay>
</div>

View File

@@ -1,7 +1,7 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.RTEController",
function ($rootScope, $scope, $q, dialogService, $log, imageHelper, assetsService, $timeout, tinyMceService, angularHelper, stylesheetResource) {
function ($rootScope, $scope, $q, dialogService, $log, imageHelper, assetsService, $timeout, tinyMceService, angularHelper, stylesheetResource, macroService) {
$scope.isLoading = true;
//To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias
@@ -229,15 +229,57 @@ angular.module("umbraco")
syncContent(editor);
});
//Create the insert media plugin
tinyMceService.createMediaPicker(editor, $scope);
tinyMceService.createMediaPicker(editor, $scope, function(currentTarget, userData){
$scope.mediaPickerOverlay = {
currentTarget: currentTarget,
onlyImages: true,
showDetails: true,
startNodeId: userData.startMediaId,
view: "mediapicker",
show: true,
submit: function(model) {
tinyMceService.insertMediaInEditor(editor, model.selectedImages[0]);
$scope.mediaPickerOverlay.show = false;
$scope.mediaPickerOverlay = null;
}
};
});
//Create the embedded plugin
tinyMceService.createInsertEmbeddedMedia(editor, $scope);
tinyMceService.createInsertEmbeddedMedia(editor, $scope, function() {
$scope.embedOverlay = {
view: "embed",
show: true,
submit: function(model) {
tinyMceService.insertEmbeddedMediaInEditor(editor, model.embed.preview);
$scope.embedOverlay.show = false;
$scope.embedOverlay = null;
}
};
});
//Create the insert macro plugin
tinyMceService.createInsertMacro(editor, $scope);
tinyMceService.createInsertMacro(editor, $scope, function(dialogData) {
$scope.macroPickerOverlay = {
view: "macropicker",
dialogData: dialogData,
show: true,
submit: function(model) {
var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
tinyMceService.insertMacroInEditor(editor, macroObject, $scope);
$scope.macroPickerOverlay.show = false;
$scope.macroPickerOverlay = null;
}
};
});
};