diff --git a/src/Umbraco.Web.UI.Client/lib/angular/angular-ui-sortable.js b/src/Umbraco.Web.UI.Client/lib/angular/angular-ui-sortable.js index 92e88f60ca..5229f5886e 100644 --- a/src/Umbraco.Web.UI.Client/lib/angular/angular-ui-sortable.js +++ b/src/Umbraco.Web.UI.Client/lib/angular/angular-ui-sortable.js @@ -1,4 +1,4 @@ -/* +/* jQuery UI Sortable plugin wrapper @param [ui-sortable] {object} Options to pass to $.fn.sortable() merged onto ui.config @@ -7,255 +7,256 @@ angular.module('ui.sortable', []) .value('uiSortableConfig',{}) .directive('uiSortable', [ 'uiSortableConfig', '$timeout', '$log', - function(uiSortableConfig, $timeout, $log) { - return { +function(uiSortableConfig, $timeout, $log) { + return { require: '?ngModel', link: function(scope, element, attrs, ngModel) { - var savedNodes; + var savedNodes; - function combineCallbacks(first,second){ - if(second && (typeof second === 'function')) { - return function(e, ui) { - first(e, ui); - second(e, ui); - }; + function combineCallbacks(first,second){ + if(second && (typeof second === 'function')) { + return function(e, ui) { + first(e, ui); + second(e, ui); + }; + } + return first; } - return first; - } - function hasSortingHelper (element, ui) { - var helperOption = element.sortable('option','helper'); - return helperOption === 'clone' || (typeof helperOption === 'function' && ui.item.sortable.isCustomHelperUsed()); - } + function hasSortingHelper (element, ui) { + var helperOption = element.sortable('option','helper'); + return helperOption === 'clone' || (typeof helperOption === 'function' && ui.item.sortable.isCustomHelperUsed()); + } - var opts = {}; + var opts = {}; - var callbacks = { - receive: null, - remove:null, - start:null, - stop:null, - update:null - }; - - var wrappers = { - helper: null - }; - - angular.extend(opts, uiSortableConfig, scope.$eval(attrs.uiSortable)); - - if (!angular.element.fn || !angular.element.fn.jquery) { - $log.error('ui.sortable: jQuery should be included before AngularJS!'); - return; - } - - if (ngModel) { - - // When we add or remove elements, we need the sortable to 'refresh' - // so it can find the new/removed elements. - scope.$watch(attrs.ngModel+'.length', function() { - // Timeout to let ng-repeat modify the DOM - $timeout(function() { - // ensure that the jquery-ui-sortable widget instance - // is still bound to the directive's element - if (!!element.data('ui-sortable')) { - element.sortable('refresh'); - } - }); - }); - - callbacks.start = function(e, ui) { - // Save the starting position of dragged item - ui.item.sortable = { - index: ui.item.index(), - cancel: function () { - ui.item.sortable._isCanceled = true; - }, - isCanceled: function () { - return ui.item.sortable._isCanceled; - }, - isCustomHelperUsed: function () { - return !!ui.item.sortable._isCustomHelperUsed; - }, - _isCanceled: false, - _isCustomHelperUsed: ui.item.sortable._isCustomHelperUsed - }; + var callbacks = { + receive: null, + remove:null, + start:null, + stop:null, + update:null }; - callbacks.activate = function(/*e, ui*/) { - // We need to make a copy of the current element's contents so - // we can restore it after sortable has messed it up. - // This is inside activate (instead of start) in order to save - // both lists when dragging between connected lists. - savedNodes = element.contents(); - - // If this list has a placeholder (the connected lists won't), - // don't inlcude it in saved nodes. - var placeholder = element.sortable('option','placeholder'); - - // placeholder.element will be a function if the placeholder, has - // been created (placeholder will be an object). If it hasn't - // been created, either placeholder will be false if no - // placeholder class was given or placeholder.element will be - // undefined if a class was given (placeholder will be a string) - if (placeholder && placeholder.element && typeof placeholder.element === 'function') { - var phElement = placeholder.element(); - // workaround for jquery ui 1.9.x, - // not returning jquery collection - phElement = angular.element(phElement); - - // exact match with the placeholder's class attribute to handle - // the case that multiple connected sortables exist and - // the placehoilder option equals the class of sortable items - var excludes = element.find('[class="' + phElement.attr('class') + '"]'); - - savedNodes = savedNodes.not(excludes); - } + var wrappers = { + helper: null }; - callbacks.update = function(e, ui) { - // Save current drop position but only if this is not a second - // update that happens when moving between lists because then - // the value will be overwritten with the old value - if(!ui.item.sortable.received) { - ui.item.sortable.dropindex = ui.item.index(); - ui.item.sortable.droptarget = ui.item.parent(); + angular.extend(opts, uiSortableConfig, scope.$eval(attrs.uiSortable)); - // Cancel the sort (let ng-repeat do the sort for us) - // Don't cancel if this is the received list because it has - // already been canceled in the other list, and trying to cancel - // here will mess up the DOM. - element.sortable('cancel'); - } + if (!angular.element.fn || !angular.element.fn.jquery) { + $log.error('ui.sortable: jQuery should be included before AngularJS!'); + return; + } - // Put the nodes back exactly the way they started (this is very - // important because ng-repeat uses comment elements to delineate - // the start and stop of repeat sections and sortable doesn't - // respect their order (even if we cancel, the order of the - // comments are still messed up). - if (hasSortingHelper(element, ui) && !ui.item.sortable.received) { - // restore all the savedNodes except .ui-sortable-helper element - // (which is placed last). That way it will be garbage collected. - savedNodes = savedNodes.not(savedNodes.last()); - } - savedNodes.appendTo(element); + if (ngModel) { - // If this is the target connected list then - // it's safe to clear the restored nodes since: - // update is currently running and - // stop is not called for the target list. - if(ui.item.sortable.received) { - savedNodes = null; - } - - // If received is true (an item was dropped in from another list) - // then we add the new item to this list otherwise wait until the - // stop event where we will know if it was a sort or item was - // moved here from another list - if(ui.item.sortable.received && !ui.item.sortable.isCanceled()) { - scope.$apply(function () { - ngModel.$modelValue.splice(ui.item.sortable.dropindex, 0, - ui.item.sortable.moved); + // When we add or remove elements, we need the sortable to 'refresh' + // so it can find the new/removed elements. + scope.$watch(attrs.ngModel+'.length', function() { + // Timeout to let ng-repeat modify the DOM + $timeout(function() { + // ensure that the jquery-ui-sortable widget instance + // is still bound to the directive's element + if (!!element.data('ui-sortable')) { + element.sortable('refresh'); + } + }); }); - } - }; - callbacks.stop = function(e, ui) { - // If the received flag hasn't be set on the item, this is a - // normal sort, if dropindex is set, the item was moved, so move - // the items in the list. - if(!ui.item.sortable.received && - ('dropindex' in ui.item.sortable) && - !ui.item.sortable.isCanceled()) { - - scope.$apply(function () { - ngModel.$modelValue.splice( - ui.item.sortable.dropindex, 0, - ngModel.$modelValue.splice(ui.item.sortable.index, 1)[0]); - }); - } else { - // if the item was not moved, then restore the elements - // so that the ngRepeat's comment are correct. - if ((!('dropindex' in ui.item.sortable) || ui.item.sortable.isCanceled()) && - !hasSortingHelper(element, ui)) { - savedNodes.appendTo(element); - } - } - - // It's now safe to clear the savedNodes - // since stop is the last callback. - savedNodes = null; - }; - - callbacks.receive = function(e, ui) { - // An item was dropped here from another list, set a flag on the - // item. - ui.item.sortable.received = true; - }; - - callbacks.remove = function(e, ui) { - // Workaround for a problem observed in nested connected lists. - // There should be an 'update' event before 'remove' when moving - // elements. If the event did not fire, cancel sorting. - if (!('dropindex' in ui.item.sortable)) { - element.sortable('cancel'); - ui.item.sortable.cancel(); - } - - // Remove the item from this list's model and copy data into item, - // so the next list can retrive it - if (!ui.item.sortable.isCanceled()) { - scope.$apply(function () { - ui.item.sortable.moved = ngModel.$modelValue.splice( - ui.item.sortable.index, 1)[0]; - }); - } - }; - - wrappers.helper = function (inner) { - if (inner && typeof inner === 'function') { - return function (e, item) { - var innerResult = inner(e, item); - item.sortable._isCustomHelperUsed = item !== innerResult; - return innerResult; + callbacks.start = function(e, ui) { + // Save the starting position of dragged item + ui.item.sortable = { + index: ui.item.index(), + cancel: function () { + ui.item.sortable._isCanceled = true; + }, + isCanceled: function () { + return ui.item.sortable._isCanceled; + }, + isCustomHelperUsed: function () { + return !!ui.item.sortable._isCustomHelperUsed; + }, + _isCanceled: false, + _isCustomHelperUsed: ui.item.sortable._isCustomHelperUsed + }; }; - } - return inner; - }; - scope.$watch(attrs.uiSortable, function(newVal /*, oldVal*/) { - // ensure that the jquery-ui-sortable widget instance - // is still bound to the directive's element - if (!!element.data('ui-sortable')) { - angular.forEach(newVal, function(value, key) { - if(callbacks[key]) { - if( key === 'stop' ){ - // call apply after stop - value = combineCallbacks( - value, function() { scope.$apply(); }); + callbacks.activate = function(/*e, ui*/) { + // We need to make a copy of the current element's contents so + // we can restore it after sortable has messed it up. + // This is inside activate (instead of start) in order to save + // both lists when dragging between connected lists. + savedNodes = element.contents(); + + // If this list has a placeholder (the connected lists won't), + // don't inlcude it in saved nodes. + var placeholder = element.sortable('option','placeholder'); + + // placeholder.element will be a function if the placeholder, has + // been created (placeholder will be an object). If it hasn't + // been created, either placeholder will be false if no + // placeholder class was given or placeholder.element will be + // undefined if a class was given (placeholder will be a string) + if (placeholder && placeholder.element && typeof placeholder.element === 'function') { + var phElement = placeholder.element(); + // workaround for jquery ui 1.9.x, + // not returning jquery collection + phElement = angular.element(phElement); + + // exact match with the placeholder's class attribute to handle + // the case that multiple connected sortables exist and + // the placehoilder option equals the class of sortable items + var excludes = element.find('[class="' + phElement.attr('class') + '"]'); + + savedNodes = savedNodes.not(excludes); } - // wrap the callback - value = combineCallbacks(callbacks[key], value); - } else if (wrappers[key]) { - value = wrappers[key](value); - } + }; + + callbacks.update = function(e, ui) { + // Save current drop position but only if this is not a second + // update that happens when moving between lists because then + // the value will be overwritten with the old value + if(!ui.item.sortable.received) { + ui.item.sortable.dropindex = ui.item.index(); + ui.item.sortable.droptarget = ui.item.parent(); + + // Cancel the sort (let ng-repeat do the sort for us) + // Don't cancel if this is the received list because it has + // already been canceled in the other list, and trying to cancel + // here will mess up the DOM. + element.sortable('cancel'); + } + + // Put the nodes back exactly the way they started (this is very + // important because ng-repeat uses comment elements to delineate + // the start and stop of repeat sections and sortable doesn't + // respect their order (even if we cancel, the order of the + // comments are still messed up). + if (hasSortingHelper(element, ui) && !ui.item.sortable.received && + element.sortable( 'option', 'appendTo' ) === 'parent') { + // restore all the savedNodes except .ui-sortable-helper element + // (which is placed last). That way it will be garbage collected. + savedNodes = savedNodes.not(savedNodes.last()); + } + savedNodes.appendTo(element); + + // If this is the target connected list then + // it's safe to clear the restored nodes since: + // update is currently running and + // stop is not called for the target list. + if(ui.item.sortable.received) { + savedNodes = null; + } + + // If received is true (an item was dropped in from another list) + // then we add the new item to this list otherwise wait until the + // stop event where we will know if it was a sort or item was + // moved here from another list + if(ui.item.sortable.received && !ui.item.sortable.isCanceled()) { + scope.$apply(function () { + ngModel.$modelValue.splice(ui.item.sortable.dropindex, 0, + ui.item.sortable.moved); + }); + } + }; + + callbacks.stop = function(e, ui) { + // If the received flag hasn't be set on the item, this is a + // normal sort, if dropindex is set, the item was moved, so move + // the items in the list. + if(!ui.item.sortable.received && + ('dropindex' in ui.item.sortable) && + !ui.item.sortable.isCanceled()) { + + scope.$apply(function () { + ngModel.$modelValue.splice( + ui.item.sortable.dropindex, 0, + ngModel.$modelValue.splice(ui.item.sortable.index, 1)[0]); + }); + } else { + // if the item was not moved, then restore the elements + // so that the ngRepeat's comment are correct. + if ((!('dropindex' in ui.item.sortable) || ui.item.sortable.isCanceled()) && + !hasSortingHelper(element, ui)) { + savedNodes.appendTo(element); + } + } + + // It's now safe to clear the savedNodes + // since stop is the last callback. + savedNodes = null; + }; + + callbacks.receive = function(e, ui) { + // An item was dropped here from another list, set a flag on the + // item. + ui.item.sortable.received = true; + }; + + callbacks.remove = function(e, ui) { + // Workaround for a problem observed in nested connected lists. + // There should be an 'update' event before 'remove' when moving + // elements. If the event did not fire, cancel sorting. + if (!('dropindex' in ui.item.sortable)) { + element.sortable('cancel'); + ui.item.sortable.cancel(); + } + + // Remove the item from this list's model and copy data into item, + // so the next list can retrive it + if (!ui.item.sortable.isCanceled()) { + scope.$apply(function () { + ui.item.sortable.moved = ngModel.$modelValue.splice( + ui.item.sortable.index, 1)[0]; + }); + } + }; + + wrappers.helper = function (inner) { + if (inner && typeof inner === 'function') { + return function (e, item) { + var innerResult = inner(e, item); + item.sortable._isCustomHelperUsed = item !== innerResult; + return innerResult; + }; + } + return inner; + }; + + scope.$watch(attrs.uiSortable, function(newVal /*, oldVal*/) { + // ensure that the jquery-ui-sortable widget instance + // is still bound to the directive's element + if (!!element.data('ui-sortable')) { + angular.forEach(newVal, function(value, key) { + if(callbacks[key]) { + if( key === 'stop' ){ + // call apply after stop + value = combineCallbacks( + value, function() { scope.$apply(); }); + } + // wrap the callback + value = combineCallbacks(callbacks[key], value); + } else if (wrappers[key]) { + value = wrappers[key](value); + } - element.sortable('option', key, value); + element.sortable('option', key, value); + }); + } + }, true); + + angular.forEach(callbacks, function(value, key) { + opts[key] = combineCallbacks(value, opts[key]); }); - } - }, true); - angular.forEach(callbacks, function(value, key) { - opts[key] = combineCallbacks(value, opts[key]); - }); + } else { + $log.info('ui.sortable: ngModel not provided!', element); + } - } else { - $log.info('ui.sortable: ngModel not provided!', element); - } - - // Create sortable - element.sortable(opts); + // Create sortable + element.sortable(opts); } - }; - } - ]); \ No newline at end of file + }; +} +]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/grid/grid.rte.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/grid/grid.rte.directive.js index e2f1076767..ff32a44976 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/grid/grid.rte.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/grid/grid.rte.directive.js @@ -9,7 +9,7 @@ angular.module("umbraco") onBlur: '&', configuration:"=" }, - template: "", + template: "", replace: true, link: function (scope, element, attrs) { @@ -96,7 +96,6 @@ angular.module("umbraco") menubar: false, statusbar: false, relative_urls: false, - autoresize_min_height: 30, toolbar: toolbar, content_css: stylesheets.join(','), style_formats: styleFormats @@ -117,6 +116,7 @@ angular.module("umbraco") //enable browser based spell checking editor.on('init', function (e) { + editor.getBody().setAttribute('spellcheck', true); //hide toolbar by default @@ -128,7 +128,7 @@ angular.module("umbraco") if(scope.value === null){ editor.focus(); } - }, 500); + }, 400); }); diff --git a/src/Umbraco.Web.UI.Client/src/less/gridview.less b/src/Umbraco.Web.UI.Client/src/less/gridview.less index 7c2666485b..5a6f89999f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/gridview.less +++ b/src/Umbraco.Web.UI.Client/src/less/gridview.less @@ -1,18 +1,24 @@ // Gridview // ------------------------- - + .mceContentBody{ + overflow-y:hidden!important; + } + + IFRAME {overflow:hidden;} // Sortabel // ------------------------- .usky-grid .ui-sortable-helper { - border: dashed 1px #000; + border: dashed 1px #000 !important; background: #ccc; opacity: 0.4; - height: 50px !important; + height: 80px !important; + width: 160px !important; overflow: hidden; padding: 5px; + border-radius:50px; } .usky-grid .ui-sortable-helper *{ diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js index 4a8d9ebec1..e80398230a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js @@ -19,6 +19,14 @@ angular.module("umbraco") cursor: "move", placeholder: 'ui-sortable-placeholder', handle: '.cell-tools-move', + forcePlaceholderSize: true, + tolerance: "pointer", + zIndex: 999999999999999999, + scrollSensitivity: 100, + cursorAt: { + top: 45, + left: 90 + }, start: function (e, ui) { ui.item.find('.mceNoEditor').each(function () { @@ -31,11 +39,13 @@ angular.module("umbraco") tinyMCE.execCommand('mceRemoveEditor', false, $(this).attr('id')); tinyMCE.execCommand('mceAddEditor', false, $(this).attr('id')); }); - } + } }; + var notIncludedRte = []; var cancelMove = false; + $scope.sortableOptionsCell = { distance: 10, @@ -44,13 +54,17 @@ angular.module("umbraco") handle: '.cell-tools-move', connectWith: ".usky-cell", forcePlaceholderSize: true, + tolerance:"pointer", + zIndex: 999999999999999999, + scrollSensitivity: 100, + cursorAt: { + top: 45, + left: 90 + }, over: function (event, ui) { - allowedEditors = $(event.target).scope().area.allowed; - console.info(allowedEditors) - if ($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) { ui.placeholder.hide(); cancelMove = true; @@ -68,45 +82,41 @@ angular.module("umbraco") ui.item.sortable.cancel(); } ui.item.parents(".usky-cell").find('.mceNoEditor').each(function () { - tinyMCE.execCommand('mceRemoveEditor', false, $(this).attr('id')); - tinyMCE.execCommand('mceAddEditor', false, $(this).attr('id')); + if ($.inArray($(this).attr('id'), notIncludedRte) < 0) { + notIncludedRte.splice(0, 0, $(this).attr('id')); + } }); } else { - - console.info("sender"); - - var notIncludedRte = []; - - ui.item.find('.mceNoEditor').each(function () { - notIncludedRte.splice(0, 0, $(this).attr('id')); - }); - $(event.target).find('.mceNoEditor').each(function () { if ($.inArray($(this).attr('id'), notIncludedRte) < 0) { - tinyMCE.execCommand('mceRemoveEditor', false, $(this).attr('id')); - tinyMCE.execCommand('mceAddEditor', false, $(this).attr('id')); + notIncludedRte.splice(0, 0, $(this).attr('id')); } }); - } }, start: function (e, ui) { ui.item.find('.mceNoEditor').each(function () { + notIncludedRte = [] tinyMCE.execCommand('mceRemoveEditor', false, $(this).attr('id')) }); }, stop: function (e, ui) { ui.item.parents(".usky-cell").find('.mceNoEditor').each(function () { - var id = $(this).attr('id') - tinyMCE.execCommand('mceRemoveEditor', false, id); - $timeout(function () { - tinyMCE.execCommand('mceAddEditor', false, id); - }, 200, false); + if ($.inArray($(this).attr('id'), notIncludedRte) < 0) { + notIncludedRte.splice(0, 0, $(this).attr('id')); + } }); + $timeout(function () { + _.forEach(notIncludedRte, function (id) { + tinyMCE.execCommand('mceRemoveEditor', false, id); + tinyMCE.execCommand('mceAddEditor', false, id); + console.info("stop " + id); + }); + }, 500, false); } } @@ -175,6 +185,8 @@ angular.module("umbraco") }; $scope.setCurrentMovedRow = function (Row) { + $scope.currentRow = null; + $scope.currentRemoveControl = null; $scope.currentMovedRow = Row; }; @@ -266,6 +278,8 @@ angular.module("umbraco") }; $scope.setCurrentMovedControl = function (Control) { + $scope.currentRow = null; + $scope.currentRemoveControl = null; $scope.currentMovedControl = Control; };