diff --git a/OurUmbraco.Site/OurUmbraco.Site.csproj b/OurUmbraco.Site/OurUmbraco.Site.csproj index 037c766a..1b094db9 100644 --- a/OurUmbraco.Site/OurUmbraco.Site.csproj +++ b/OurUmbraco.Site/OurUmbraco.Site.csproj @@ -1,5 +1,6 @@  + Debug @@ -54,7 +55,7 @@ ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\businesslogic.dll True @@ -66,11 +67,11 @@ ..\packages\ClientDependency-Mvc5.1.8.0.0\lib\net45\ClientDependency.Core.Mvc.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\cms.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\controls.dll True @@ -106,7 +107,7 @@ ..\packages\UrlTracker.3.11\lib\InfoCaster.Umbraco.UrlTracker.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\interfaces.dll True @@ -199,7 +200,7 @@ ..\packages\semver.1.1.2\lib\net451\Semver.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\SQLCE4Umbraco.dll True @@ -284,23 +285,23 @@ ..\packages\UmbracoCms.Core.7.4.2\lib\TidyNet.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\Umbraco.Core.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.DataLayer.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.editorControls.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.MacroEngines.dll True @@ -308,15 +309,15 @@ ..\packages\Umbraco.ModelsBuilder.3.0.2\lib\Umbraco.ModelsBuilder.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.providers.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\Umbraco.Web.UI.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\UmbracoExamine.dll True @@ -4469,7 +4470,10 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + - + @@ -58,7 +60,7 @@ - + diff --git a/OurUmbraco.Site/config/applications.config b/OurUmbraco.Site/config/applications.config index c35a68e9..71f0dbaa 100644 --- a/OurUmbraco.Site/config/applications.config +++ b/OurUmbraco.Site/config/applications.config @@ -1,11 +1,11 @@  - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/OurUmbraco.Site/config/trees.config b/OurUmbraco.Site/config/trees.config index 9e61db62..bc1aa98b 100644 --- a/OurUmbraco.Site/config/trees.config +++ b/OurUmbraco.Site/config/trees.config @@ -1,49 +1,75 @@  - - + + - - + + - - - - - - - - - + + + + + + + + + - - - - - - - + + + + + + + - - - + + + - - - + + + - - + + + \ No newline at end of file diff --git a/OurUmbraco.Site/config/umbracoSettings.config b/OurUmbraco.Site/config/umbracoSettings.config index 67b1fe29..119a4fb9 100644 --- a/OurUmbraco.Site/config/umbracoSettings.config +++ b/OurUmbraco.Site/config/umbracoSettings.config @@ -1,4 +1,4 @@ - + - + diff --git a/OurUmbraco.Site/packages.config b/OurUmbraco.Site/packages.config index 5059f474..d006da2e 100644 --- a/OurUmbraco.Site/packages.config +++ b/OurUmbraco.Site/packages.config @@ -52,6 +52,7 @@ + diff --git a/OurUmbraco.Site/umbraco/Views/propertyeditors/boolean/boolean.html b/OurUmbraco.Site/umbraco/Views/propertyeditors/boolean/boolean.html index fd7d04f4..2d302d0f 100644 --- a/OurUmbraco.Site/umbraco/Views/propertyeditors/boolean/boolean.html +++ b/OurUmbraco.Site/umbraco/Views/propertyeditors/boolean/boolean.html @@ -1,3 +1,3 @@
-
+ \ No newline at end of file diff --git a/OurUmbraco.Site/umbraco/assets/img/application/logo.png b/OurUmbraco.Site/umbraco/assets/img/application/logo.png index 2b2ff80a..388354e8 100644 Binary files a/OurUmbraco.Site/umbraco/assets/img/application/logo.png and b/OurUmbraco.Site/umbraco/assets/img/application/logo.png differ diff --git a/OurUmbraco.Site/umbraco/js/umbraco.controllers.js b/OurUmbraco.Site/umbraco/js/umbraco.controllers.js index 04496726..23a85a8d 100644 --- a/OurUmbraco.Site/umbraco/js/umbraco.controllers.js +++ b/OurUmbraco.Site/umbraco/js/umbraco.controllers.js @@ -6,317 +6,317 @@ (function() { - -/** - * @ngdoc controller - * @name Umbraco.MainController - * @function - * - * @description - * The main application controller - * - */ -function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale) { - - //the null is important because we do an explicit bool check on this in the view - //the avatar is by default the umbraco logo - $scope.authenticated = null; - $scope.avatar = [ - { value: "assets/img/application/logo.png" }, - { value: "assets/img/application/logo@2x.png" }, - { value: "assets/img/application/logo@3x.png" } - ]; - $scope.touchDevice = appState.getGlobalState("touchDevice"); - - - $scope.removeNotification = function (index) { - notificationsService.remove(index); - }; - - $scope.closeDialogs = function (event) { - //only close dialogs if non-link and non-buttons are clicked - var el = event.target.nodeName; - var els = ["INPUT","A","BUTTON"]; - - if(els.indexOf(el) >= 0){return;} - - var parents = $(event.target).parents("a,button"); - if(parents.length > 0){ - return; - } - - //SD: I've updated this so that we don't close the dialog when clicking inside of the dialog - var nav = $(event.target).parents("#dialog"); - if (nav.length === 1) { - return; - } - - eventsService.emit("app.closeDialogs", event); - }; - - var evts = []; - - //when a user logs out or timesout - evts.push(eventsService.on("app.notAuthenticated", function() { - $scope.authenticated = null; - $scope.user = null; - })); - - //when the app is read/user is logged in, setup the data - evts.push(eventsService.on("app.ready", function (evt, data) { - - $scope.authenticated = data.authenticated; - $scope.user = data.user; - - updateChecker.check().then(function(update){ - if(update && update !== "null"){ - if(update.type !== "None"){ - var notification = { - headline: "Update available", - message: "Click to download", - sticky: true, - type: "info", - url: update.url - }; - notificationsService.add(notification); - } - } - }) - - //if the user has changed we need to redirect to the root so they don't try to continue editing the - //last item in the URL (NOTE: the user id can equal zero, so we cannot just do !data.lastUserId since that will resolve to true) - if (data.lastUserId !== undefined && data.lastUserId !== null && data.lastUserId !== data.user.id) { - $location.path("/").search(""); - historyService.removeAll(); - treeService.clearCache(); - } - - //Load locale file - if ($scope.user.locale) { - tmhDynamicLocale.set($scope.user.locale); - } - - if ($scope.user.emailHash) { - - //let's attempt to load the avatar, it might not exist or we might not have - // internet access so we'll detect it first - $http.get("https://www.gravatar.com/avatar/" + $scope.user.emailHash + ".jpg?s=64&d=404") - .then( - function successCallback(response) { - $("#avatar-img").fadeTo(1000, 0, function () { - $scope.$apply(function () { - //this can be null if they time out - if ($scope.user && $scope.user.emailHash) { - var avatarBaseUrl = "https://www.gravatar.com/avatar/", - hash = $scope.user.emailHash; - - $scope.avatar = [ - { value: avatarBaseUrl + hash + ".jpg?s=30&d=mm" }, - { value: avatarBaseUrl + hash + ".jpg?s=60&d=mm" }, - { value: avatarBaseUrl + hash + ".jpg?s=90&d=mm" } - ]; - } - }); - $("#avatar-img").fadeTo(1000, 1); - }); - }, function errorCallback(response) { - //cannot load it from the server so we cannot do anything - }); - } - - })); - - evts.push(eventsService.on("app.ysod", function(name, error) { - $scope.ysodOverlay = { - view: "ysod", - error: error, - show: true - }; - })); - - //ensure to unregister from all events! - $scope.$on('$destroy', function () { - for (var e in evts) { - eventsService.unsubscribe(evts[e]); - } - }); - -} - - -//register it -angular.module('umbraco').controller("Umbraco.MainController", MainController). - config(function (tmhDynamicLocaleProvider) { - //Set url for locale files - tmhDynamicLocaleProvider.localeLocationPattern('lib/angular/1.1.5/i18n/angular-locale_{{locale}}.js'); + +/** + * @ngdoc controller + * @name Umbraco.MainController + * @function + * + * @description + * The main application controller + * + */ +function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale) { + + //the null is important because we do an explicit bool check on this in the view + //the avatar is by default the umbraco logo + $scope.authenticated = null; + $scope.avatar = [ + { value: "assets/img/application/logo.png" }, + { value: "assets/img/application/logo@2x.png" }, + { value: "assets/img/application/logo@3x.png" } + ]; + $scope.touchDevice = appState.getGlobalState("touchDevice"); + + + $scope.removeNotification = function (index) { + notificationsService.remove(index); + }; + + $scope.closeDialogs = function (event) { + //only close dialogs if non-link and non-buttons are clicked + var el = event.target.nodeName; + var els = ["INPUT","A","BUTTON"]; + + if(els.indexOf(el) >= 0){return;} + + var parents = $(event.target).parents("a,button"); + if(parents.length > 0){ + return; + } + + //SD: I've updated this so that we don't close the dialog when clicking inside of the dialog + var nav = $(event.target).parents("#dialog"); + if (nav.length === 1) { + return; + } + + eventsService.emit("app.closeDialogs", event); + }; + + var evts = []; + + //when a user logs out or timesout + evts.push(eventsService.on("app.notAuthenticated", function() { + $scope.authenticated = null; + $scope.user = null; + })); + + //when the app is read/user is logged in, setup the data + evts.push(eventsService.on("app.ready", function (evt, data) { + + $scope.authenticated = data.authenticated; + $scope.user = data.user; + + updateChecker.check().then(function(update){ + if(update && update !== "null"){ + if(update.type !== "None"){ + var notification = { + headline: "Update available", + message: "Click to download", + sticky: true, + type: "info", + url: update.url + }; + notificationsService.add(notification); + } + } + }) + + //if the user has changed we need to redirect to the root so they don't try to continue editing the + //last item in the URL (NOTE: the user id can equal zero, so we cannot just do !data.lastUserId since that will resolve to true) + if (data.lastUserId !== undefined && data.lastUserId !== null && data.lastUserId !== data.user.id) { + $location.path("/").search(""); + historyService.removeAll(); + treeService.clearCache(); + } + + //Load locale file + if ($scope.user.locale) { + tmhDynamicLocale.set($scope.user.locale); + } + + if ($scope.user.emailHash) { + + //let's attempt to load the avatar, it might not exist or we might not have + // internet access so we'll detect it first + $http.get("https://www.gravatar.com/avatar/" + $scope.user.emailHash + ".jpg?s=64&d=404") + .then( + function successCallback(response) { + $("#avatar-img").fadeTo(1000, 0, function () { + $scope.$apply(function () { + //this can be null if they time out + if ($scope.user && $scope.user.emailHash) { + var avatarBaseUrl = "https://www.gravatar.com/avatar/", + hash = $scope.user.emailHash; + + $scope.avatar = [ + { value: avatarBaseUrl + hash + ".jpg?s=30&d=mm" }, + { value: avatarBaseUrl + hash + ".jpg?s=60&d=mm" }, + { value: avatarBaseUrl + hash + ".jpg?s=90&d=mm" } + ]; + } + }); + $("#avatar-img").fadeTo(1000, 1); + }); + }, function errorCallback(response) { + //cannot load it from the server so we cannot do anything + }); + } + + })); + + evts.push(eventsService.on("app.ysod", function(name, error) { + $scope.ysodOverlay = { + view: "ysod", + error: error, + show: true + }; + })); + + //ensure to unregister from all events! + $scope.$on('$destroy', function () { + for (var e in evts) { + eventsService.unsubscribe(evts[e]); + } + }); + +} + + +//register it +angular.module('umbraco').controller("Umbraco.MainController", MainController). + config(function (tmhDynamicLocaleProvider) { + //Set url for locale files + tmhDynamicLocaleProvider.localeLocationPattern('lib/angular/1.1.5/i18n/angular-locale_{{locale}}.js'); }); - -/** - * @ngdoc controller - * @name Umbraco.NavigationController - * @function - * - * @description - * Handles the section area of the app - * - * @param {navigationService} navigationService A reference to the navigationService - */ -function NavigationController($scope, $rootScope, $location, $log, $routeParams, $timeout, appState, navigationService, keyboardService, dialogService, historyService, eventsService, sectionResource, angularHelper) { - - //TODO: Need to think about this and an nicer way to acheive what this is doing. - //the tree event handler i used to subscribe to the main tree click events - $scope.treeEventHandler = $({}); - navigationService.setupTreeEvents($scope.treeEventHandler); - - //Put the navigation service on this scope so we can use it's methods/properties in the view. - // IMPORTANT: all properties assigned to this scope are generally available on the scope object on dialogs since - // when we create a dialog we pass in this scope to be used for the dialog's scope instead of creating a new one. - $scope.nav = navigationService; - // TODO: Lets fix this, it is less than ideal to be passing in the navigationController scope to something else to be used as it's scope, - // this is going to lead to problems/confusion. I really don't think passing scope's around is very good practice. - $rootScope.nav = navigationService; - - //set up our scope vars - $scope.showContextMenuDialog = false; - $scope.showContextMenu = false; - $scope.showSearchResults = false; - $scope.menuDialogTitle = null; - $scope.menuActions = []; - $scope.menuNode = null; - - $scope.currentSection = appState.getSectionState("currentSection"); - $scope.showNavigation = appState.getGlobalState("showNavigation"); - - //trigger search with a hotkey: - keyboardService.bind("ctrl+shift+s", function () { - navigationService.showSearch(); - }); - - //trigger dialods with a hotkey: - keyboardService.bind("esc", function () { - eventsService.emit("app.closeDialogs"); - }); - - $scope.selectedId = navigationService.currentId; - - var evts = []; - - //Listen for global state changes - evts.push(eventsService.on("appState.globalState.changed", function(e, args) { - if (args.key === "showNavigation") { - $scope.showNavigation = args.value; - } - })); - - //Listen for menu state changes - evts.push(eventsService.on("appState.menuState.changed", function(e, args) { - if (args.key === "showMenuDialog") { - $scope.showContextMenuDialog = args.value; - } - if (args.key === "showMenu") { - $scope.showContextMenu = args.value; - } - if (args.key === "dialogTitle") { - $scope.menuDialogTitle = args.value; - } - if (args.key === "menuActions") { - $scope.menuActions = args.value; - } - if (args.key === "currentNode") { - $scope.menuNode = args.value; - } - })); - - //Listen for section state changes - evts.push(eventsService.on("appState.treeState.changed", function(e, args) { - var f = args; - if (args.value.root && args.value.root.metaData.containsTrees === false) { - $rootScope.emptySection = true; - } - else { - $rootScope.emptySection = false; - } - })); - - //Listen for section state changes - evts.push(eventsService.on("appState.sectionState.changed", function(e, args) { - //section changed - if (args.key === "currentSection") { - $scope.currentSection = args.value; - } - //show/hide search results - if (args.key === "showSearchResults") { - $scope.showSearchResults = args.value; - } - })); - - //This reacts to clicks passed to the body element which emits a global call to close all dialogs - evts.push(eventsService.on("app.closeDialogs", function(event) { - if (appState.getGlobalState("stickyNavigation")) { - navigationService.hideNavigation(); - //TODO: don't know why we need this? - we are inside of an angular event listener. - angularHelper.safeApply($scope); - } - })); - - //when a user logs out or timesout - evts.push(eventsService.on("app.notAuthenticated", function() { - $scope.authenticated = false; - })); - - //when the application is ready and the user is authorized setup the data - evts.push(eventsService.on("app.ready", function(evt, data) { - $scope.authenticated = true; - })); - - //this reacts to the options item in the tree - //todo, migrate to nav service - $scope.searchShowMenu = function (ev, args) { - //always skip default - args.skipDefault = true; - navigationService.showMenu(ev, args); - }; - - //todo, migrate to nav service - $scope.searchHide = function () { - navigationService.hideSearch(); - }; - - //the below assists with hiding/showing the tree - var treeActive = false; - - //Sets a service variable as soon as the user hovers the navigation with the mouse - //used by the leaveTree method to delay hiding - $scope.enterTree = function (event) { - treeActive = true; - }; - - // Hides navigation tree, with a short delay, is cancelled if the user moves the mouse over the tree again - $scope.leaveTree = function(event) { - //this is a hack to handle IE touch events which freaks out due to no mouse events so the tree instantly shuts down - if (!event) { - return; - } - if (!appState.getGlobalState("touchDevice")) { - treeActive = false; - $timeout(function() { - if (!treeActive) { - navigationService.hideTree(); - } - }, 300); - } - }; - - //ensure to unregister from all events! - $scope.$on('$destroy', function () { - for (var e in evts) { - eventsService.unsubscribe(evts[e]); - } - }); -} - -//register it -angular.module('umbraco').controller("Umbraco.NavigationController", NavigationController); + +/** + * @ngdoc controller + * @name Umbraco.NavigationController + * @function + * + * @description + * Handles the section area of the app + * + * @param {navigationService} navigationService A reference to the navigationService + */ +function NavigationController($scope, $rootScope, $location, $log, $routeParams, $timeout, appState, navigationService, keyboardService, dialogService, historyService, eventsService, sectionResource, angularHelper) { + + //TODO: Need to think about this and an nicer way to acheive what this is doing. + //the tree event handler i used to subscribe to the main tree click events + $scope.treeEventHandler = $({}); + navigationService.setupTreeEvents($scope.treeEventHandler); + + //Put the navigation service on this scope so we can use it's methods/properties in the view. + // IMPORTANT: all properties assigned to this scope are generally available on the scope object on dialogs since + // when we create a dialog we pass in this scope to be used for the dialog's scope instead of creating a new one. + $scope.nav = navigationService; + // TODO: Lets fix this, it is less than ideal to be passing in the navigationController scope to something else to be used as it's scope, + // this is going to lead to problems/confusion. I really don't think passing scope's around is very good practice. + $rootScope.nav = navigationService; + + //set up our scope vars + $scope.showContextMenuDialog = false; + $scope.showContextMenu = false; + $scope.showSearchResults = false; + $scope.menuDialogTitle = null; + $scope.menuActions = []; + $scope.menuNode = null; + + $scope.currentSection = appState.getSectionState("currentSection"); + $scope.showNavigation = appState.getGlobalState("showNavigation"); + + //trigger search with a hotkey: + keyboardService.bind("ctrl+shift+s", function () { + navigationService.showSearch(); + }); + + //trigger dialods with a hotkey: + keyboardService.bind("esc", function () { + eventsService.emit("app.closeDialogs"); + }); + + $scope.selectedId = navigationService.currentId; + + var evts = []; + + //Listen for global state changes + evts.push(eventsService.on("appState.globalState.changed", function(e, args) { + if (args.key === "showNavigation") { + $scope.showNavigation = args.value; + } + })); + + //Listen for menu state changes + evts.push(eventsService.on("appState.menuState.changed", function(e, args) { + if (args.key === "showMenuDialog") { + $scope.showContextMenuDialog = args.value; + } + if (args.key === "showMenu") { + $scope.showContextMenu = args.value; + } + if (args.key === "dialogTitle") { + $scope.menuDialogTitle = args.value; + } + if (args.key === "menuActions") { + $scope.menuActions = args.value; + } + if (args.key === "currentNode") { + $scope.menuNode = args.value; + } + })); + + //Listen for section state changes + evts.push(eventsService.on("appState.treeState.changed", function(e, args) { + var f = args; + if (args.value.root && args.value.root.metaData.containsTrees === false) { + $rootScope.emptySection = true; + } + else { + $rootScope.emptySection = false; + } + })); + + //Listen for section state changes + evts.push(eventsService.on("appState.sectionState.changed", function(e, args) { + //section changed + if (args.key === "currentSection") { + $scope.currentSection = args.value; + } + //show/hide search results + if (args.key === "showSearchResults") { + $scope.showSearchResults = args.value; + } + })); + + //This reacts to clicks passed to the body element which emits a global call to close all dialogs + evts.push(eventsService.on("app.closeDialogs", function(event) { + if (appState.getGlobalState("stickyNavigation")) { + navigationService.hideNavigation(); + //TODO: don't know why we need this? - we are inside of an angular event listener. + angularHelper.safeApply($scope); + } + })); + + //when a user logs out or timesout + evts.push(eventsService.on("app.notAuthenticated", function() { + $scope.authenticated = false; + })); + + //when the application is ready and the user is authorized setup the data + evts.push(eventsService.on("app.ready", function(evt, data) { + $scope.authenticated = true; + })); + + //this reacts to the options item in the tree + //todo, migrate to nav service + $scope.searchShowMenu = function (ev, args) { + //always skip default + args.skipDefault = true; + navigationService.showMenu(ev, args); + }; + + //todo, migrate to nav service + $scope.searchHide = function () { + navigationService.hideSearch(); + }; + + //the below assists with hiding/showing the tree + var treeActive = false; + + //Sets a service variable as soon as the user hovers the navigation with the mouse + //used by the leaveTree method to delay hiding + $scope.enterTree = function (event) { + treeActive = true; + }; + + // Hides navigation tree, with a short delay, is cancelled if the user moves the mouse over the tree again + $scope.leaveTree = function(event) { + //this is a hack to handle IE touch events which freaks out due to no mouse events so the tree instantly shuts down + if (!event) { + return; + } + if (!appState.getGlobalState("touchDevice")) { + treeActive = false; + $timeout(function() { + if (!treeActive) { + navigationService.hideTree(); + } + }, 300); + } + }; + + //ensure to unregister from all events! + $scope.$on('$destroy', function () { + for (var e in evts) { + eventsService.unsubscribe(evts[e]); + } + }); +} + +//register it +angular.module('umbraco').controller("Umbraco.NavigationController", NavigationController); /** * @ngdoc controller @@ -446,37 +446,37 @@ function SearchController($scope, searchService, $log, $location, navigationServ } //register it angular.module('umbraco').controller("Umbraco.SearchController", SearchController); - -/** - * @ngdoc controller - * @name Umbraco.MainController - * @function - * - * @description - * The controller for the AuthorizeUpgrade login page - * - */ -function AuthorizeUpgradeController($scope, $window) { - - //Add this method to the scope - this method will be called by the login dialog controller when the login is successful - // then we'll handle the redirect. - $scope.submit = function (event) { - - var qry = $window.location.search.trimStart("?").split("&"); - var redir = _.find(qry, function(item) { - return item.startsWith("redir="); - }); - if (redir) { - $window.location = decodeURIComponent(redir.split("=")[1]); - } - else { - $window.location = "/"; - } - - }; - -} - + +/** + * @ngdoc controller + * @name Umbraco.MainController + * @function + * + * @description + * The controller for the AuthorizeUpgrade login page + * + */ +function AuthorizeUpgradeController($scope, $window) { + + //Add this method to the scope - this method will be called by the login dialog controller when the login is successful + // then we'll handle the redirect. + $scope.submit = function (event) { + + var qry = $window.location.search.trimStart("?").split("&"); + var redir = _.find(qry, function(item) { + return item.startsWith("redir="); + }); + if (redir) { + $window.location = decodeURIComponent(redir.split("=")[1]); + } + else { + $window.location = "/"; + } + + }; + +} + angular.module('umbraco').controller("Umbraco.AuthorizeUpgradeController", AuthorizeUpgradeController); /** * @ngdoc controller @@ -509,204 +509,204 @@ function DashboardController($scope, $routeParams, dashboardResource, localizati //register it angular.module('umbraco').controller("Umbraco.DashboardController", DashboardController); -angular.module("umbraco") - .controller("Umbraco.Dialogs.ApprovedColorPickerController", function ($scope, $http, umbPropEditorHelper, assetsService) { - assetsService.loadJs("lib/cssparser/cssparser.js") - .then(function () { - - var cssPath = $scope.dialogData.cssPath; - $scope.cssClass = $scope.dialogData.cssClass; - - $scope.classes = []; - - $scope.change = function (newClass) { - $scope.model.value = newClass; - } - - $http.get(cssPath) - .success(function (data) { - var parser = new CSSParser(); - $scope.classes = parser.parse(data, false, false).cssRules; - $scope.classes.splice(0, 0, "noclass"); - }) - - assetsService.loadCss("/App_Plugins/Lecoati.uSky.Grid/lib/uSky.Grid.ApprovedColorPicker.css"); - assetsService.loadCss(cssPath); - }); +angular.module("umbraco") + .controller("Umbraco.Dialogs.ApprovedColorPickerController", function ($scope, $http, umbPropEditorHelper, assetsService) { + assetsService.loadJs("lib/cssparser/cssparser.js") + .then(function () { + + var cssPath = $scope.dialogData.cssPath; + $scope.cssClass = $scope.dialogData.cssClass; + + $scope.classes = []; + + $scope.change = function (newClass) { + $scope.model.value = newClass; + } + + $http.get(cssPath) + .success(function (data) { + var parser = new CSSParser(); + $scope.classes = parser.parse(data, false, false).cssRules; + $scope.classes.splice(0, 0, "noclass"); + }) + + assetsService.loadCss("/App_Plugins/Lecoati.uSky.Grid/lib/uSky.Grid.ApprovedColorPicker.css"); + assetsService.loadCss(cssPath); + }); }); -function ContentEditDialogController($scope, editorState, $routeParams, $q, $timeout, $window, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, umbModelMapper, $http) { - - $scope.defaultButton = null; - $scope.subButtons = []; - var dialogOptions = $scope.$parent.dialogOptions; - - // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish - function performSave(args) { - contentEditingHelper.contentEditorPerformSave({ - statusMessage: args.statusMessage, - saveMethod: args.saveMethod, - scope: $scope, - content: $scope.content - }).then(function (content) { - //success - if (dialogOptions.closeOnSave) { - $scope.submit(content); - } - - }, function(err) { - //error - }); - } - - function filterTabs(entity, blackList) { - if (blackList) { - _.each(entity.tabs, function (tab) { - tab.hide = _.contains(blackList, tab.alias); - }); - } - - return entity; - }; - - function init(content) { - var buttons = contentEditingHelper.configureContentEditorButtons({ - create: $routeParams.create, - content: content, - methods: { - saveAndPublish: $scope.saveAndPublish, - sendToPublish: $scope.sendToPublish, - save: $scope.save, - unPublish: angular.noop - } - }); - $scope.defaultButton = buttons.defaultButton; - $scope.subButtons = buttons.subButtons; - - //This is a total hack but we have really no other way of sharing data to the property editors of this - // content item, so we'll just set the property on the content item directly - $scope.content.isDialogEditor = true; - - editorState.set($scope.content); - } - - //check if the entity is being passed in, otherwise load it from the server - if (angular.isObject(dialogOptions.entity)) { - $scope.loaded = true; - $scope.content = filterTabs(dialogOptions.entity, dialogOptions.tabFilter); - init($scope.content); - } - else { - contentResource.getById(dialogOptions.id) - .then(function(data) { - $scope.loaded = true; - $scope.content = filterTabs(data, dialogOptions.tabFilter); - init($scope.content); - //in one particular special case, after we've created a new item we redirect back to the edit - // route but there might be server validation errors in the collection which we need to display - // after the redirect, so we will bind all subscriptions which will show the server validation errors - // if there are any and then clear them so the collection no longer persists them. - serverValidationManager.executeAndClearAllSubscriptions(); - }); - } - - $scope.sendToPublish = function () { - performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending..." }); - }; - - $scope.saveAndPublish = function () { - performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing..." }); - }; - - $scope.save = function () { - performSave({ saveMethod: contentResource.save, statusMessage: "Saving..." }); - }; - - // this method is called for all action buttons and then we proxy based on the btn definition - $scope.performAction = function (btn) { - - if (!btn || !angular.isFunction(btn.handler)) { - throw "btn.handler must be a function reference"; - } - - if (!$scope.busy) { - btn.handler.apply(this); - } - }; - -} - - -angular.module("umbraco") +function ContentEditDialogController($scope, editorState, $routeParams, $q, $timeout, $window, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, umbModelMapper, $http) { + + $scope.defaultButton = null; + $scope.subButtons = []; + var dialogOptions = $scope.$parent.dialogOptions; + + // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish + function performSave(args) { + contentEditingHelper.contentEditorPerformSave({ + statusMessage: args.statusMessage, + saveMethod: args.saveMethod, + scope: $scope, + content: $scope.content + }).then(function (content) { + //success + if (dialogOptions.closeOnSave) { + $scope.submit(content); + } + + }, function(err) { + //error + }); + } + + function filterTabs(entity, blackList) { + if (blackList) { + _.each(entity.tabs, function (tab) { + tab.hide = _.contains(blackList, tab.alias); + }); + } + + return entity; + }; + + function init(content) { + var buttons = contentEditingHelper.configureContentEditorButtons({ + create: $routeParams.create, + content: content, + methods: { + saveAndPublish: $scope.saveAndPublish, + sendToPublish: $scope.sendToPublish, + save: $scope.save, + unPublish: angular.noop + } + }); + $scope.defaultButton = buttons.defaultButton; + $scope.subButtons = buttons.subButtons; + + //This is a total hack but we have really no other way of sharing data to the property editors of this + // content item, so we'll just set the property on the content item directly + $scope.content.isDialogEditor = true; + + editorState.set($scope.content); + } + + //check if the entity is being passed in, otherwise load it from the server + if (angular.isObject(dialogOptions.entity)) { + $scope.loaded = true; + $scope.content = filterTabs(dialogOptions.entity, dialogOptions.tabFilter); + init($scope.content); + } + else { + contentResource.getById(dialogOptions.id) + .then(function(data) { + $scope.loaded = true; + $scope.content = filterTabs(data, dialogOptions.tabFilter); + init($scope.content); + //in one particular special case, after we've created a new item we redirect back to the edit + // route but there might be server validation errors in the collection which we need to display + // after the redirect, so we will bind all subscriptions which will show the server validation errors + // if there are any and then clear them so the collection no longer persists them. + serverValidationManager.executeAndClearAllSubscriptions(); + }); + } + + $scope.sendToPublish = function () { + performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending..." }); + }; + + $scope.saveAndPublish = function () { + performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing..." }); + }; + + $scope.save = function () { + performSave({ saveMethod: contentResource.save, statusMessage: "Saving..." }); + }; + + // this method is called for all action buttons and then we proxy based on the btn definition + $scope.performAction = function (btn) { + + if (!btn || !angular.isFunction(btn.handler)) { + throw "btn.handler must be a function reference"; + } + + if (!$scope.busy) { + btn.handler.apply(this); + } + }; + +} + + +angular.module("umbraco") .controller("Umbraco.Dialogs.Content.EditController", ContentEditDialogController); -angular.module("umbraco") - .controller("Umbraco.Dialogs.HelpController", function ($scope, $location, $routeParams, helpService, userService, localizationService) { - $scope.section = $routeParams.section; - $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion; - - if(!$scope.section){ - $scope.section = "content"; - } - - $scope.sectionName = $scope.section; - - var rq = {}; - rq.section = $scope.section; - - //translate section name - localizationService.localize("sections_" + rq.section).then(function (value) { - $scope.sectionName = value; - }); - - userService.getCurrentUser().then(function(user){ - - rq.usertype = user.userType; - rq.lang = user.locale; - - if($routeParams.url){ - rq.path = decodeURIComponent($routeParams.url); - - if(rq.path.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath) === 0){ - rq.path = rq.path.substring(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath.length); - } - - if(rq.path.indexOf(".aspx") > 0){ - rq.path = rq.path.substring(0, rq.path.indexOf(".aspx")); - } - - }else{ - rq.path = rq.section + "/" + $routeParams.tree + "/" + $routeParams.method; - } - - helpService.findHelp(rq).then(function(topics){ - $scope.topics = topics; - }); - - helpService.findVideos(rq).then(function(videos){ - $scope.videos = videos; - }); - - }); - - +angular.module("umbraco") + .controller("Umbraco.Dialogs.HelpController", function ($scope, $location, $routeParams, helpService, userService, localizationService) { + $scope.section = $routeParams.section; + $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion; + + if(!$scope.section){ + $scope.section = "content"; + } + + $scope.sectionName = $scope.section; + + var rq = {}; + rq.section = $scope.section; + + //translate section name + localizationService.localize("sections_" + rq.section).then(function (value) { + $scope.sectionName = value; + }); + + userService.getCurrentUser().then(function(user){ + + rq.usertype = user.userType; + rq.lang = user.locale; + + if($routeParams.url){ + rq.path = decodeURIComponent($routeParams.url); + + if(rq.path.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath) === 0){ + rq.path = rq.path.substring(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath.length); + } + + if(rq.path.indexOf(".aspx") > 0){ + rq.path = rq.path.substring(0, rq.path.indexOf(".aspx")); + } + + }else{ + rq.path = rq.section + "/" + $routeParams.tree + "/" + $routeParams.method; + } + + helpService.findHelp(rq).then(function(topics){ + $scope.topics = topics; + }); + + helpService.findVideos(rq).then(function(videos){ + $scope.videos = videos; + }); + + }); + + }); -//used for the icon picker dialog -angular.module("umbraco") - .controller("Umbraco.Dialogs.IconPickerController", - function ($scope, iconHelper) { - - iconHelper.getIcons().then(function(icons){ - $scope.icons = icons; - }); - - $scope.submitClass = function(icon){ - if($scope.color) - { - $scope.submit(icon + " " + $scope.color); - }else{ - $scope.submit(icon); - } - }; - } +//used for the icon picker dialog +angular.module("umbraco") + .controller("Umbraco.Dialogs.IconPickerController", + function ($scope, iconHelper) { + + iconHelper.getIcons().then(function(icons){ + $scope.icons = icons; + }); + + $scope.submitClass = function(icon){ + if($scope.color) + { + $scope.submit(icon + " " + $scope.color); + }else{ + $scope.submit(icon); + } + }; + } ); /** * @ngdoc controller @@ -1403,115 +1403,115 @@ angular.module("umbraco").controller("Umbraco.Dialogs.RteEmbedController", funct $scope.submit($scope.form.preview); }; }); -angular.module("umbraco").controller('Umbraco.Dialogs.Template.QueryBuilderController', - function($scope, $http, dialogService){ - - - $http.get("backoffice/UmbracoApi/TemplateQuery/GetAllowedProperties").then(function(response) { - $scope.properties = response.data; - }); - - $http.get("backoffice/UmbracoApi/TemplateQuery/GetContentTypes").then(function (response) { - $scope.contentTypes = response.data; - }); - - $http.get("backoffice/UmbracoApi/TemplateQuery/GetFilterConditions").then(function (response) { - $scope.conditions = response.data; - }); - - - $scope.query = { - contentType: { - name: "Everything" - }, - source:{ - name: "My website" - }, - filters:[ - { - property:undefined, - operator: undefined - } - ], - sort:{ - property:{ - alias: "", - name: "", - }, - direction: "ascending" - } - }; - - - - $scope.chooseSource = function(query){ - dialogService.contentPicker({ - callback: function (data) { - - if (data.id > 0) { - query.source = { id: data.id, name: data.name }; - } else { - query.source.name = "My website"; - delete query.source.id; - } - } - }); - }; - - var throttledFunc = _.throttle(function() { - - $http.post("backoffice/UmbracoApi/TemplateQuery/PostTemplateQuery", $scope.query).then(function (response) { - $scope.result = response.data; - }); - - }, 200); - - $scope.$watch("query", function(value) { - throttledFunc(); - }, true); - - $scope.getPropertyOperators = function (property) { - - var conditions = _.filter($scope.conditions, function(condition) { - var index = condition.appliesTo.indexOf(property.type); - return index >= 0; - }); - return conditions; - }; - - - $scope.addFilter = function(query){ - query.filters.push({}); - }; - - $scope.trashFilter = function (query) { - query.filters.splice(query,1); - }; - - $scope.changeSortOrder = function(query){ - if(query.sort.direction === "ascending"){ - query.sort.direction = "descending"; - }else{ - query.sort.direction = "ascending"; - } - }; - - $scope.setSortProperty = function(query, property){ - query.sort.property = property; - if(property.type === "datetime"){ - query.sort.direction = "descending"; - }else{ - query.sort.direction = "ascending"; - } - }; +angular.module("umbraco").controller('Umbraco.Dialogs.Template.QueryBuilderController', + function($scope, $http, dialogService){ + + + $http.get("backoffice/UmbracoApi/TemplateQuery/GetAllowedProperties").then(function(response) { + $scope.properties = response.data; + }); + + $http.get("backoffice/UmbracoApi/TemplateQuery/GetContentTypes").then(function (response) { + $scope.contentTypes = response.data; + }); + + $http.get("backoffice/UmbracoApi/TemplateQuery/GetFilterConditions").then(function (response) { + $scope.conditions = response.data; + }); + + + $scope.query = { + contentType: { + name: "Everything" + }, + source:{ + name: "My website" + }, + filters:[ + { + property:undefined, + operator: undefined + } + ], + sort:{ + property:{ + alias: "", + name: "", + }, + direction: "ascending" + } + }; + + + + $scope.chooseSource = function(query){ + dialogService.contentPicker({ + callback: function (data) { + + if (data.id > 0) { + query.source = { id: data.id, name: data.name }; + } else { + query.source.name = "My website"; + delete query.source.id; + } + } + }); + }; + + var throttledFunc = _.throttle(function() { + + $http.post("backoffice/UmbracoApi/TemplateQuery/PostTemplateQuery", $scope.query).then(function (response) { + $scope.result = response.data; + }); + + }, 200); + + $scope.$watch("query", function(value) { + throttledFunc(); + }, true); + + $scope.getPropertyOperators = function (property) { + + var conditions = _.filter($scope.conditions, function(condition) { + var index = condition.appliesTo.indexOf(property.type); + return index >= 0; + }); + return conditions; + }; + + + $scope.addFilter = function(query){ + query.filters.push({}); + }; + + $scope.trashFilter = function (query) { + query.filters.splice(query,1); + }; + + $scope.changeSortOrder = function(query){ + if(query.sort.direction === "ascending"){ + query.sort.direction = "descending"; + }else{ + query.sort.direction = "ascending"; + } + }; + + $scope.setSortProperty = function(query, property){ + query.sort.property = property; + if(property.type === "datetime"){ + query.sort.direction = "descending"; + }else{ + query.sort.direction = "ascending"; + } + }; }); -angular.module("umbraco").controller('Umbraco.Dialogs.Template.SnippetController', - function($scope) { - $scope.type = $scope.dialogOptions.type; - $scope.section = { - name: "", - required: false - }; +angular.module("umbraco").controller('Umbraco.Dialogs.Template.SnippetController', + function($scope) { + $scope.type = $scope.dialogOptions.type; + $scope.section = { + name: "", + required: false + }; }); //used for the media picker dialog angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController", @@ -1944,173 +1944,173 @@ angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController", $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); }); }); -angular.module("umbraco") - .controller("Umbraco.Dialogs.UserController", function ($scope, $location, $timeout, userService, historyService, eventsService, externalLoginInfo, authResource, currentUserResource, formHelper) { - - $scope.history = historyService.getCurrent(); - $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion; - $scope.showPasswordFields = false; - $scope.changePasswordButtonState = "init"; - - $scope.externalLoginProviders = externalLoginInfo.providers; - $scope.externalLinkLoginFormAction = Umbraco.Sys.ServerVariables.umbracoUrls.externalLinkLoginsUrl; - var evts = []; - evts.push(eventsService.on("historyService.add", function (e, args) { - $scope.history = args.all; - })); - evts.push(eventsService.on("historyService.remove", function (e, args) { - $scope.history = args.all; - })); - evts.push(eventsService.on("historyService.removeAll", function (e, args) { - $scope.history = []; - })); - - $scope.logout = function () { - - //Add event listener for when there are pending changes on an editor which means our route was not successful - var pendingChangeEvent = eventsService.on("valFormManager.pendingChanges", function (e, args) { - //one time listener, remove the event - pendingChangeEvent(); - $scope.close(); - }); - - - //perform the path change, if it is successful then the promise will resolve otherwise it will fail - $scope.close(); - $location.path("/logout"); - }; - - $scope.gotoHistory = function (link) { - $location.path(link); - $scope.close(); - }; - - //Manually update the remaining timeout seconds - function updateTimeout() { - $timeout(function () { - if ($scope.remainingAuthSeconds > 0) { - $scope.remainingAuthSeconds--; - $scope.$digest(); - //recurse - updateTimeout(); - } - - }, 1000, false); // 1 second, do NOT execute a global digest - } - - function updateUserInfo() { - //get the user - userService.getCurrentUser().then(function (user) { - $scope.user = user; - if ($scope.user) { - $scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds; - $scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1; - //set the timer - updateTimeout(); - - authResource.getCurrentUserLinkedLogins().then(function(logins) { - //reset all to be un-linked - for (var provider in $scope.externalLoginProviders) { - $scope.externalLoginProviders[provider].linkedProviderKey = undefined; - } - - //set the linked logins - for (var login in logins) { - var found = _.find($scope.externalLoginProviders, function (i) { - return i.authType == login; - }); - if (found) { - found.linkedProviderKey = logins[login]; - } - } - }); - } - }); - } - - $scope.unlink = function (e, loginProvider, providerKey) { - var result = confirm("Are you sure you want to unlink this account?"); - if (!result) { - e.preventDefault(); - return; - } - - authResource.unlinkLogin(loginProvider, providerKey).then(function (a, b, c) { - updateUserInfo(); - }); - } - - updateUserInfo(); - - //remove all event handlers - $scope.$on('$destroy', function () { - for (var e = 0; e < evts.length; e++) { - evts[e](); - } - - }); - - /* ---------- UPDATE PASSWORD ---------- */ - - //create the initial model for change password property editor - $scope.changePasswordModel = { - alias: "_umb_password", - view: "changepassword", - config: {}, - value: {} - }; - - //go get the config for the membership provider and add it to the model - currentUserResource.getMembershipProviderConfig().then(function(data) { - $scope.changePasswordModel.config = data; - //ensure the hasPassword config option is set to true (the user of course has a password already assigned) - //this will ensure the oldPassword is shown so they can change it - // disable reset password functionality beacuse it does not make sense inside the backoffice - $scope.changePasswordModel.config.hasPassword = true; - $scope.changePasswordModel.config.disableToggle = true; - $scope.changePasswordModel.config.enableReset = false; - }); - - $scope.changePassword = function() { - - if (formHelper.submitForm({ scope: $scope })) { - - $scope.changePasswordButtonState = "busy"; - - currentUserResource.changePassword($scope.changePasswordModel.value).then(function(data) { - - //if the password has been reset, then update our model - if (data.value) { - $scope.changePasswordModel.value.generatedPassword = data.value; - } - - formHelper.resetForm({ scope: $scope, notifications: data.notifications }); - - $scope.changePasswordButtonState = "success"; - - }, function (err) { - - formHelper.handleError(err); - - $scope.changePasswordButtonState = "error"; - - }); - - } - - }; - - $scope.togglePasswordFields = function() { - clearPasswordFields(); - $scope.showPasswordFields = !$scope.showPasswordFields; - } - - function clearPasswordFields() { - $scope.changePasswordModel.value.newPassword = ""; - $scope.changePasswordModel.confirm = ""; - } - - }); +angular.module("umbraco") + .controller("Umbraco.Dialogs.UserController", function ($scope, $location, $timeout, userService, historyService, eventsService, externalLoginInfo, authResource, currentUserResource, formHelper) { + + $scope.history = historyService.getCurrent(); + $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion; + $scope.showPasswordFields = false; + $scope.changePasswordButtonState = "init"; + + $scope.externalLoginProviders = externalLoginInfo.providers; + $scope.externalLinkLoginFormAction = Umbraco.Sys.ServerVariables.umbracoUrls.externalLinkLoginsUrl; + var evts = []; + evts.push(eventsService.on("historyService.add", function (e, args) { + $scope.history = args.all; + })); + evts.push(eventsService.on("historyService.remove", function (e, args) { + $scope.history = args.all; + })); + evts.push(eventsService.on("historyService.removeAll", function (e, args) { + $scope.history = []; + })); + + $scope.logout = function () { + + //Add event listener for when there are pending changes on an editor which means our route was not successful + var pendingChangeEvent = eventsService.on("valFormManager.pendingChanges", function (e, args) { + //one time listener, remove the event + pendingChangeEvent(); + $scope.close(); + }); + + + //perform the path change, if it is successful then the promise will resolve otherwise it will fail + $scope.close(); + $location.path("/logout"); + }; + + $scope.gotoHistory = function (link) { + $location.path(link); + $scope.close(); + }; + + //Manually update the remaining timeout seconds + function updateTimeout() { + $timeout(function () { + if ($scope.remainingAuthSeconds > 0) { + $scope.remainingAuthSeconds--; + $scope.$digest(); + //recurse + updateTimeout(); + } + + }, 1000, false); // 1 second, do NOT execute a global digest + } + + function updateUserInfo() { + //get the user + userService.getCurrentUser().then(function (user) { + $scope.user = user; + if ($scope.user) { + $scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds; + $scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1; + //set the timer + updateTimeout(); + + authResource.getCurrentUserLinkedLogins().then(function(logins) { + //reset all to be un-linked + for (var provider in $scope.externalLoginProviders) { + $scope.externalLoginProviders[provider].linkedProviderKey = undefined; + } + + //set the linked logins + for (var login in logins) { + var found = _.find($scope.externalLoginProviders, function (i) { + return i.authType == login; + }); + if (found) { + found.linkedProviderKey = logins[login]; + } + } + }); + } + }); + } + + $scope.unlink = function (e, loginProvider, providerKey) { + var result = confirm("Are you sure you want to unlink this account?"); + if (!result) { + e.preventDefault(); + return; + } + + authResource.unlinkLogin(loginProvider, providerKey).then(function (a, b, c) { + updateUserInfo(); + }); + } + + updateUserInfo(); + + //remove all event handlers + $scope.$on('$destroy', function () { + for (var e = 0; e < evts.length; e++) { + evts[e](); + } + + }); + + /* ---------- UPDATE PASSWORD ---------- */ + + //create the initial model for change password property editor + $scope.changePasswordModel = { + alias: "_umb_password", + view: "changepassword", + config: {}, + value: {} + }; + + //go get the config for the membership provider and add it to the model + currentUserResource.getMembershipProviderConfig().then(function(data) { + $scope.changePasswordModel.config = data; + //ensure the hasPassword config option is set to true (the user of course has a password already assigned) + //this will ensure the oldPassword is shown so they can change it + // disable reset password functionality beacuse it does not make sense inside the backoffice + $scope.changePasswordModel.config.hasPassword = true; + $scope.changePasswordModel.config.disableToggle = true; + $scope.changePasswordModel.config.enableReset = false; + }); + + $scope.changePassword = function() { + + if (formHelper.submitForm({ scope: $scope })) { + + $scope.changePasswordButtonState = "busy"; + + currentUserResource.changePassword($scope.changePasswordModel.value).then(function(data) { + + //if the password has been reset, then update our model + if (data.value) { + $scope.changePasswordModel.value.generatedPassword = data.value; + } + + formHelper.resetForm({ scope: $scope, notifications: data.notifications }); + + $scope.changePasswordButtonState = "success"; + + }, function (err) { + + formHelper.handleError(err); + + $scope.changePasswordButtonState = "error"; + + }); + + } + + }; + + $scope.togglePasswordFields = function() { + clearPasswordFields(); + $scope.showPasswordFields = !$scope.showPasswordFields; + } + + function clearPasswordFields() { + $scope.changePasswordModel.value.newPassword = ""; + $scope.changePasswordModel.confirm = ""; + } + + }); /** * @ngdoc controller @@ -2194,1073 +2194,1073 @@ angular.module('umbraco').controller("Umbraco.LoginController", function (events }); -//used for the media picker dialog -angular.module("umbraco").controller("Umbraco.Notifications.ConfirmRouteChangeController", - function ($scope, $location, $log, notificationsService) { - - $scope.discard = function(not){ - not.args.listener(); - - $location.search(""); - - //we need to break the path up into path and query - var parts = not.args.path.split("?"); - var query = {}; - if (parts.length > 1) { - _.each(parts[1].split("&"), function(q) { - var keyVal = q.split("="); - query[keyVal[0]] = keyVal[1]; - }); - } - - $location.path(parts[0]).search(query); - notificationsService.remove(not); - }; - - $scope.stay = function(not){ - notificationsService.remove(not); - }; - +//used for the media picker dialog +angular.module("umbraco").controller("Umbraco.Notifications.ConfirmRouteChangeController", + function ($scope, $location, $log, notificationsService) { + + $scope.discard = function(not){ + not.args.listener(); + + $location.search(""); + + //we need to break the path up into path and query + var parts = not.args.path.split("?"); + var query = {}; + if (parts.length > 1) { + _.each(parts[1].split("&"), function(q) { + var keyVal = q.split("="); + query[keyVal[0]] = keyVal[1]; + }); + } + + $location.path(parts[0]).search(query); + notificationsService.remove(not); + }; + + $scope.stay = function(not){ + notificationsService.remove(not); + }; + }); - (function() { - "use strict"; - - function CompositionsOverlay($scope) { - - var vm = this; - - vm.isSelected = isSelected; - - function isSelected(alias) { - if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) { - return true; - } - } - } - - angular.module("umbraco").controller("Umbraco.Overlays.CompositionsOverlay", CompositionsOverlay); - -})(); - -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.PropertyController - * @function - * - * @description - * The controller for the content type editor property dialog - */ - -(function() { - "use strict"; - - function EditorPickerOverlay($scope, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService) { - - var vm = this; - - if (!$scope.model.title) { - $scope.model.title = localizationService.localize("defaultdialogs_selectEditor"); - } - - vm.searchTerm = ""; - vm.showTabs = false; - vm.tabsLoaded = 0; - vm.typesAndEditors = []; - vm.userConfigured = []; - vm.loading = false; - vm.tabs = [{ - active: true, - id: 1, - label: localizationService.localize("contentTypeEditor_availableEditors"), - alias: "Default", - typesAndEditors: [] - }, { - active: false, - id: 2, - label: localizationService.localize("contentTypeEditor_reuse"), - alias: "Reuse", - userConfigured: [] - }]; - - vm.filterItems = filterItems; - vm.showDetailsOverlay = showDetailsOverlay; - vm.hideDetailsOverlay = hideDetailsOverlay; - vm.pickEditor = pickEditor; - vm.pickDataType = pickDataType; - - function activate() { - - getGroupedDataTypes(); - getGroupedPropertyEditors(); - - } - - function getGroupedPropertyEditors() { - - vm.loading = true; - - dataTypeResource.getGroupedPropertyEditors().then(function(data) { - vm.tabs[0].typesAndEditors = data; - vm.typesAndEditors = data; - vm.tabsLoaded = vm.tabsLoaded + 1; - checkIfTabContentIsLoaded(); - }); - - } - - function getGroupedDataTypes() { - - vm.loading = true; - - dataTypeResource.getGroupedDataTypes().then(function(data) { - vm.tabs[1].userConfigured = data; - vm.userConfigured = data; - vm.tabsLoaded = vm.tabsLoaded + 1; - checkIfTabContentIsLoaded(); - }); - - } - - function checkIfTabContentIsLoaded() { - if (vm.tabsLoaded === 2) { - vm.loading = false; - vm.showTabs = true; - } - } - - function filterItems() { - // clear item details - $scope.model.itemDetails = null; - - if (vm.searchTerm) { - vm.showFilterResult = true; - vm.showTabs = false; - } else { - vm.showFilterResult = false; - vm.showTabs = true; - } - - } - - function showDetailsOverlay(property) { - - var propertyDetails = {}; - propertyDetails.icon = property.icon; - propertyDetails.title = property.name; - - $scope.model.itemDetails = propertyDetails; - - } - - function hideDetailsOverlay() { - $scope.model.itemDetails = null; - } - - function pickEditor(editor) { - - var parentId = -1; - - dataTypeResource.getScaffold(parentId).then(function(dataType) { - - // set alias - dataType.selectedEditor = editor.alias; - - // set name - var nameArray = []; - - if ($scope.model.contentTypeName) { - nameArray.push($scope.model.contentTypeName); - } - - if ($scope.model.property.label) { - nameArray.push($scope.model.property.label); - } - - if (editor.name) { - nameArray.push(editor.name); - } - - // make name - dataType.name = nameArray.join(" - "); - - // get pre values - dataTypeResource.getPreValues(dataType.selectedEditor).then(function(preValues) { - - dataType.preValues = preValues; - - openEditorSettingsOverlay(dataType, true); - - }); - - }); - - } - - function pickDataType(selectedDataType) { - - dataTypeResource.getById(selectedDataType.id).then(function(dataType) { - contentTypeResource.getPropertyTypeScaffold(dataType.id).then(function(propertyType) { - submitOverlay(dataType, propertyType, false); - }); - }); - - } - - function openEditorSettingsOverlay(dataType, isNew) { - vm.editorSettingsOverlay = { - title: localizationService.localize("contentTypeEditor_editorSettings"), - dataType: dataType, - view: "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html", - show: true, - submit: function(model) { - var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues); - - dataTypeResource.save(model.dataType, preValues, isNew).then(function(newDataType) { - - contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function(propertyType) { - - submitOverlay(newDataType, propertyType, true); - - vm.editorSettingsOverlay.show = false; - vm.editorSettingsOverlay = null; - - }); - - }); - } - }; - - } - - function submitOverlay(dataType, propertyType, isNew) { - - // update property - $scope.model.property.config = propertyType.config; - $scope.model.property.editor = propertyType.editor; - $scope.model.property.view = propertyType.view; - $scope.model.property.dataTypeId = dataType.id; - $scope.model.property.dataTypeIcon = dataType.icon; - $scope.model.property.dataTypeName = dataType.name; - - $scope.model.updateSameDataTypes = isNew; - - $scope.model.submit($scope.model); - - } - - activate(); - - } - - angular.module("umbraco").controller("Umbraco.Overlays.EditorPickerOverlay", EditorPickerOverlay); - -})(); - -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.PropertyController - * @function - * - * @description - * The controller for the content type editor property dialog - */ - - (function() { - "use strict"; - - function PropertySettingsOverlay($scope, contentTypeResource, dataTypeResource, dataTypeHelper, localizationService) { - - var vm = this; - - vm.showValidationPattern = false; - vm.focusOnPatternField = false; - vm.focusOnMandatoryField = false; - vm.selectedValidationType = {}; - vm.validationTypes = [ - { - "name": localizationService.localize("validation_validateAsEmail"), - "key": "email", - "pattern": "[a-zA-Z0-9_\.\+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-\.]+", - "enableEditing": true - }, - { - "name": localizationService.localize("validation_validateAsNumber"), - "key": "number", - "pattern": "^[0-9]*$", - "enableEditing": true - }, - { - "name": localizationService.localize("validation_validateAsUrl"), - "key": "url", - "pattern": "https?\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}", - "enableEditing": true - }, - { - "name": localizationService.localize("validation_enterCustomValidation"), - "key": "custom", - "pattern": "", - "enableEditing": true - } - ]; - - vm.changeValidationType = changeValidationType; - vm.changeValidationPattern = changeValidationPattern; - vm.openEditorPickerOverlay = openEditorPickerOverlay; - vm.openEditorSettingsOverlay = openEditorSettingsOverlay; - - function activate() { - - matchValidationType(); - - } - - function changeValidationPattern() { - matchValidationType(); - } - - function openEditorPickerOverlay(property) { - - vm.focusOnMandatoryField = false; - - vm.editorPickerOverlay = {}; - vm.editorPickerOverlay.property = $scope.model.property; - vm.editorPickerOverlay.contentTypeName = $scope.model.contentTypeName; - vm.editorPickerOverlay.view = "views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html"; - vm.editorPickerOverlay.show = true; - - vm.editorPickerOverlay.submit = function(model) { - - $scope.model.updateSameDataTypes = model.updateSameDataTypes; - - vm.focusOnMandatoryField = true; - - // update property - property.config = model.property.config; - property.editor = model.property.editor; - property.view = model.property.view; - property.dataTypeId = model.property.dataTypeId; - property.dataTypeIcon = model.property.dataTypeIcon; - property.dataTypeName = model.property.dataTypeName; - - vm.editorPickerOverlay.show = false; - vm.editorPickerOverlay = null; - }; - - vm.editorPickerOverlay.close = function(model) { - vm.editorPickerOverlay.show = false; - vm.editorPickerOverlay = null; - }; - - } - - function openEditorSettingsOverlay(property) { - - vm.focusOnMandatoryField = false; - - // get data type - dataTypeResource.getById(property.dataTypeId).then(function(dataType) { - - vm.editorSettingsOverlay = {}; - vm.editorSettingsOverlay.title = "Editor settings"; - vm.editorSettingsOverlay.view = "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html"; - vm.editorSettingsOverlay.dataType = dataType; - vm.editorSettingsOverlay.show = true; - - vm.editorSettingsOverlay.submit = function(model) { - - var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues); - - dataTypeResource.save(model.dataType, preValues, false).then(function(newDataType) { - - contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function(propertyType) { - - // update editor - property.config = propertyType.config; - property.editor = propertyType.editor; - property.view = propertyType.view; - property.dataTypeId = newDataType.id; - property.dataTypeIcon = newDataType.icon; - property.dataTypeName = newDataType.name; - - // set flag to update same data types - $scope.model.updateSameDataTypes = true; - - vm.focusOnMandatoryField = true; - - vm.editorSettingsOverlay.show = false; - vm.editorSettingsOverlay = null; - - }); - - }); - - }; - - vm.editorSettingsOverlay.close = function(oldModel) { - vm.editorSettingsOverlay.show = false; - vm.editorSettingsOverlay = null; - }; - - }); - - } - - function matchValidationType() { - - if($scope.model.property.validation.pattern !== null && $scope.model.property.validation.pattern !== "" && $scope.model.property.validation.pattern !== undefined) { - - var match = false; - - // find and show if a match from the list has been chosen - angular.forEach(vm.validationTypes, function(validationType, index){ - if($scope.model.property.validation.pattern === validationType.pattern) { - vm.selectedValidationType = vm.validationTypes[index]; - vm.showValidationPattern = true; - match = true; - } - }); - - // if there is no match - choose the custom validation option. - if(!match) { - angular.forEach(vm.validationTypes, function(validationType){ - if(validationType.key === "custom") { - vm.selectedValidationType = validationType; - vm.showValidationPattern = true; - } - }); - } - } - - } - - function changeValidationType(selectedValidationType) { - - if(selectedValidationType) { - $scope.model.property.validation.pattern = selectedValidationType.pattern; - vm.showValidationPattern = true; - - // set focus on textarea - if(selectedValidationType.key === "custom") { - vm.focusOnPatternField = true; - } - - } else { - $scope.model.property.validation.pattern = ""; - vm.showValidationPattern = false; - } - - } - - activate(); - - } - - angular.module("umbraco").controller("Umbraco.Overlay.PropertySettingsOverlay", PropertySettingsOverlay); - -})(); - - (function() { - "use strict"; - - function CopyOverlay($scope, localizationService, eventsService) { - - var vm = this; - - if(!$scope.model.title) { - $scope.model.title = localizationService.localize("general_copy"); - } - - vm.hideSearch = hideSearch; - vm.selectResult = selectResult; - vm.onSearchResults = onSearchResults; - - var dialogOptions = $scope.model; - var searchText = "Search..."; - var node = dialogOptions.currentNode; - - localizationService.localize("general_search").then(function (value) { - searchText = value + "..."; - }); - - $scope.model.relateToOriginal = true; - $scope.dialogTreeEventHandler = $({}); - - vm.searchInfo = { - searchFromId: null, - searchFromName: null, - showSearch: false, - results: [], - selectedSearchResults: [] - }; - - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - if (args.node.metaData.listViewNode) { - //check if list view 'search' node was selected - - vm.searchInfo.showSearch = true; - vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id; - vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name; - } - else { - //eventsService.emit("editors.content.copyController.select", args); - - if ($scope.model.target) { - //un-select if there's a current one selected - $scope.model.target.selected = false; - } - - $scope.model.target = args.node; - $scope.model.target.selected = true; - } - - } - - function nodeExpandedHandler(ev, args) { - if (angular.isArray(args.children)) { - - //iterate children - _.each(args.children, function (child) { - //check if any of the items are list views, if so we need to add a custom - // child: A node to activate the search - if (child.metaData.isContainer) { - child.hasChildren = true; - child.children = [ - { - level: child.level + 1, - hasChildren: false, - name: searchText, - metaData: { - listViewNode: child, - }, - cssClass: "icon umb-tree-icon sprTree icon-search", - cssClasses: ["not-published"] - } - ]; - } - }); - } - } - - function hideSearch() { - vm.searchInfo.showSearch = false; - vm.searchInfo.searchFromId = null; - vm.searchInfo.searchFromName = null; - vm.searchInfo.results = []; - } - - // method to select a search result - function selectResult(evt, result) { - result.selected = result.selected === true ? false : true; - nodeSelectHandler(evt, { event: evt, node: result }); - } - - //callback when there are search results - function onSearchResults(results) { - vm.searchInfo.results = results; - vm.searchInfo.showSearch = true; - } - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); - }); - - - } - - angular.module("umbraco").controller("Umbraco.Overlays.CopyOverlay", CopyOverlay); - -})(); - -(function() { - "use strict"; - - function EmbedOverlay($scope, $http, umbRequestHelper, localizationService) { - - var vm = this; - var origWidth = 500; - var origHeight = 300; - - if(!$scope.model.title) { - $scope.model.title = localizationService.localize("general_embed"); - } - - $scope.model.embed = { - url: "", - width: 360, - height: 240, - constrain: true, - preview: "", - success: false, - info: "", - supportsDimensions: "" - }; - - vm.showPreview = showPreview; - vm.changeSize = changeSize; - - function showPreview() { - - if ($scope.model.embed.url) { - $scope.model.embed.show = true; - $scope.model.embed.preview = "
"; - $scope.model.embed.info = ""; - $scope.model.embed.success = false; - - $http({ - method: 'GET', - url: umbRequestHelper.getApiUrl("embedApiBaseUrl", "GetEmbed"), - params: { - url: $scope.model.embed.url, - width: $scope.model.embed.width, - height: $scope.model.embed.height - } - }) - .success(function(data) { - - $scope.model.embed.preview = ""; - - switch (data.Status) { - case 0: - //not supported - $scope.model.embed.info = "Not supported"; - break; - case 1: - //error - $scope.model.embed.info = "Computer says no"; - break; - case 2: - $scope.model.embed.preview = data.Markup; - $scope.model.embed.supportsDimensions = data.SupportsDimensions; - $scope.model.embed.success = true; - break; - } - }) - .error(function() { - $scope.model.embed.supportsDimensions = false; - $scope.model.embed.preview = ""; - $scope.model.embed.info = "Computer says no"; - }); - } else { - $scope.model.embed.supportsDimensions = false; - $scope.model.embed.preview = ""; - $scope.model.embed.info = "Please enter a URL"; - } - } - - function changeSize(type) { - - var width, height; - - if ($scope.model.embed.constrain) { - width = parseInt($scope.model.embed.width, 10); - height = parseInt($scope.model.embed.height, 10); - if (type == 'width') { - origHeight = Math.round((width / origWidth) * height); - $scope.model.embed.height = origHeight; - } else { - origWidth = Math.round((height / origHeight) * width); - $scope.model.embed.width = origWidth; - } - } - if ($scope.model.embed.url !== "") { - showPreview(); - } - - } - - } - - angular.module("umbraco").controller("Umbraco.Overlays.EmbedOverlay", EmbedOverlay); - -})(); - -angular.module("umbraco") - .controller("Umbraco.Overlays.HelpController", function ($scope, $location, $routeParams, helpService, userService, localizationService) { - $scope.section = $routeParams.section; - $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion; - $scope.model.subtitle = "Umbraco version" + " " + $scope.version; - - if(!$scope.model.title) { - $scope.model.title = localizationService.localize("general_help"); - } - - if(!$scope.section){ - $scope.section = "content"; - } - - $scope.sectionName = $scope.section; - - var rq = {}; - rq.section = $scope.section; - - //translate section name - localizationService.localize("sections_" + rq.section).then(function (value) { - $scope.sectionName = value; - }); - - userService.getCurrentUser().then(function(user){ - - rq.usertype = user.userType; - rq.lang = user.locale; - - if($routeParams.url){ - rq.path = decodeURIComponent($routeParams.url); - - if(rq.path.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath) === 0){ - rq.path = rq.path.substring(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath.length); - } - - if(rq.path.indexOf(".aspx") > 0){ - rq.path = rq.path.substring(0, rq.path.indexOf(".aspx")); - } - - }else{ - rq.path = rq.section + "/" + $routeParams.tree + "/" + $routeParams.method; - } - - helpService.findHelp(rq).then(function(topics){ - $scope.topics = topics; - }); - - helpService.findVideos(rq).then(function(videos){ - $scope.videos = videos; - }); - - }); - - - }); - -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.PropertyController - * @function - * - * @description - * The controller for the content type editor property dialog - */ -function IconPickerOverlay($scope, iconHelper, localizationService) { - - $scope.loading = true; - $scope.model.hideSubmitButton = true; - - if (!$scope.model.title) { - $scope.model.title = localizationService.localize("defaultdialogs_selectIcon"); - } - - iconHelper.getIcons().then(function(icons) { - $scope.icons = icons; - $scope.loading = false; - }); - - $scope.selectIcon = function(icon, color) { - $scope.model.icon = icon; - $scope.model.color = color; - $scope.submitForm($scope.model); - }; - -} - -angular.module("umbraco").controller("Umbraco.Overlays.IconPickerOverlay", IconPickerOverlay); - -function ItemPickerOverlay($scope, localizationService) { - - if (!$scope.model.title) { - $scope.model.title = localizationService.localize("defaultdialogs_selectItem"); - } - - $scope.model.hideSubmitButton = true; - - $scope.selectItem = function(item) { - $scope.model.selectedItem = item; - $scope.submitForm($scope.model); - }; - -} - -angular.module("umbraco").controller("Umbraco.Overlays.ItemPickerOverlay", ItemPickerOverlay); - -//used for the media picker dialog -angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", - function ($scope, eventsService, dialogService, entityResource, contentResource, mediaHelper, userService, localizationService) { - var dialogOptions = $scope.model; - - var searchText = "Search..."; - localizationService.localize("general_search").then(function (value) { - searchText = value + "..."; - }); - - if(!$scope.model.title) { - $scope.model.title = localizationService.localize("defaultdialogs_selectLink"); - } - - $scope.dialogTreeEventHandler = $({}); - $scope.model.target = {}; - $scope.searchInfo = { - searchFromId: null, - searchFromName: null, - showSearch: false, - results: [], - selectedSearchResults: [] - }; - - if (dialogOptions.currentTarget) { - $scope.model.target = dialogOptions.currentTarget; - - //if we have a node ID, we fetch the current node to build the form data - if ($scope.model.target.id) { - - if (!$scope.model.target.path) { - entityResource.getPath($scope.model.target.id, "Document").then(function (path) { - $scope.model.target.path = path; - //now sync the tree to this path - $scope.dialogTreeEventHandler.syncTree({ path: $scope.model.target.path, tree: "content" }); - }); - } - - contentResource.getNiceUrl($scope.model.target.id).then(function (url) { - $scope.model.target.url = url; - }); - } - } - - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - if (args.node.metaData.listViewNode) { - //check if list view 'search' node was selected - - $scope.searchInfo.showSearch = true; - $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id; - $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name; - } - else { - eventsService.emit("dialogs.linkPicker.select", args); - - if ($scope.currentNode) { - //un-select if there's a current one selected - $scope.currentNode.selected = false; - } - - $scope.currentNode = args.node; - $scope.currentNode.selected = true; - $scope.model.target.id = args.node.id; - $scope.model.target.name = args.node.name; - - if (args.node.id < 0) { - $scope.model.target.url = "/"; - } - else { - contentResource.getNiceUrl(args.node.id).then(function (url) { - $scope.model.target.url = url; - }); - } - - if (!angular.isUndefined($scope.model.target.isMedia)) { - delete $scope.model.target.isMedia; - } - } - } - - function nodeExpandedHandler(ev, args) { - if (angular.isArray(args.children)) { - - //iterate children - _.each(args.children, function (child) { - //check if any of the items are list views, if so we need to add a custom - // child: A node to activate the search - if (child.metaData.isContainer) { - child.hasChildren = true; - child.children = [ - { - level: child.level + 1, - hasChildren: false, - name: searchText, - metaData: { - listViewNode: child, - }, - cssClass: "icon umb-tree-icon sprTree icon-search", - cssClasses: ["not-published"] - } - ]; - } - }); - } - } - - $scope.switchToMediaPicker = function () { - userService.getCurrentUser().then(function (userData) { - $scope.mediaPickerOverlay = { - view: "mediapicker", - startNodeId: userData.startMediaId, - show: true, - submit: function(model) { - var media = model.selectedImages[0]; - - $scope.model.target.id = media.id; - $scope.model.target.isMedia = true; - $scope.model.target.name = media.name; - $scope.model.target.url = mediaHelper.resolveFile(media); - - $scope.mediaPickerOverlay.show = false; - $scope.mediaPickerOverlay = null; - } - }; - }); - }; - - $scope.hideSearch = function () { - $scope.searchInfo.showSearch = false; - $scope.searchInfo.searchFromId = null; - $scope.searchInfo.searchFromName = null; - $scope.searchInfo.results = []; - } - - // method to select a search result - $scope.selectResult = function (evt, result) { - result.selected = result.selected === true ? false : true; - nodeSelectHandler(evt, {event: evt, node: result}); - }; - - //callback when there are search results - $scope.onSearchResults = function (results) { - $scope.searchInfo.results = results; - $scope.searchInfo.showSearch = true; - }; - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); - }); - }); - -function MacroPickerController($scope, entityResource, macroResource, umbPropEditorHelper, macroService, formHelper, localizationService) { - - - if(!$scope.model.title) { - $scope.model.title = localizationService.localize("defaultdialogs_selectMacro"); - } - - $scope.macros = []; - $scope.model.selectedMacro = null; - $scope.wizardStep = "macroSelect"; - $scope.model.macroParams = []; - $scope.noMacroParams = false; - - $scope.changeMacro = function() { - if ($scope.wizardStep === "macroSelect") { - editParams(); - } else { - submitForm(); - } - }; - - /** changes the view to edit the params of the selected macro */ - function editParams() { - //get the macro params if there are any - macroResource.getMacroParameters($scope.model.selectedMacro.id) - .then(function (data) { - - //go to next page if there are params otherwise we can just exit - if (!angular.isArray(data) || data.length === 0) { - - $scope.noMacroParams = true; - - } else { - $scope.wizardStep = "paramSelect"; - $scope.model.macroParams = data; - - //fill in the data if we are editing this macro - if ($scope.model.dialogData && $scope.model.dialogData.macroData && $scope.model.dialogData.macroData.macroParamsDictionary) { - _.each($scope.model.dialogData.macroData.macroParamsDictionary, function (val, key) { - var prop = _.find($scope.model.macroParams, function (item) { - return item.alias == key; - }); - if (prop) { - - if (_.isString(val)) { - //we need to unescape values as they have most likely been escaped while inserted - val = _.unescape(val); - - //detect if it is a json string - if (val.detectIsJson()) { - try { - //Parse it to json - prop.value = angular.fromJson(val); - } - catch (e) { - // not json - prop.value = val; - } - } - else { - prop.value = val; - } - } - else { - prop.value = val; - } - } - }); - - } - } - - }); - } - - //here we check to see if we've been passed a selected macro and if so we'll set the - //editor to start with parameter editing - if ($scope.model.dialogData && $scope.model.dialogData.macroData) { - $scope.wizardStep = "paramSelect"; - } - - //get the macro list - pass in a filter if it is only for rte - entityResource.getAll("Macro", ($scope.model.dialogData && $scope.model.dialogData.richTextEditor && $scope.model.dialogData.richTextEditor === true) ? "UseInEditor=true" : null) - .then(function (data) { - - //if 'allowedMacros' is specified, we need to filter - if (angular.isArray($scope.model.dialogData.allowedMacros) && $scope.model.dialogData.allowedMacros.length > 0) { - $scope.macros = _.filter(data, function(d) { - return _.contains($scope.model.dialogData.allowedMacros, d.alias); - }); - } - else { - $scope.macros = data; - } - - - //check if there's a pre-selected macro and if it exists - if ($scope.model.dialogData && $scope.model.dialogData.macroData && $scope.model.dialogData.macroData.macroAlias) { - var found = _.find(data, function (item) { - return item.alias === $scope.model.dialogData.macroData.macroAlias; - }); - if (found) { - //select the macro and go to next screen - $scope.model.selectedMacro = found; - editParams(); - return; - } - } - //we don't have a pre-selected macro so ensure the correct step is set - $scope.wizardStep = "macroSelect"; - }); - - -} - -angular.module("umbraco").controller("Umbraco.Overlays.MacroPickerController", MacroPickerController); + (function() { + "use strict"; + + function CompositionsOverlay($scope) { + + var vm = this; + + vm.isSelected = isSelected; + + function isSelected(alias) { + if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) { + return true; + } + } + } + + angular.module("umbraco").controller("Umbraco.Overlays.CompositionsOverlay", CompositionsOverlay); + +})(); + +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.PropertyController + * @function + * + * @description + * The controller for the content type editor property dialog + */ + +(function() { + "use strict"; + + function EditorPickerOverlay($scope, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService) { + + var vm = this; + + if (!$scope.model.title) { + $scope.model.title = localizationService.localize("defaultdialogs_selectEditor"); + } + + vm.searchTerm = ""; + vm.showTabs = false; + vm.tabsLoaded = 0; + vm.typesAndEditors = []; + vm.userConfigured = []; + vm.loading = false; + vm.tabs = [{ + active: true, + id: 1, + label: localizationService.localize("contentTypeEditor_availableEditors"), + alias: "Default", + typesAndEditors: [] + }, { + active: false, + id: 2, + label: localizationService.localize("contentTypeEditor_reuse"), + alias: "Reuse", + userConfigured: [] + }]; + + vm.filterItems = filterItems; + vm.showDetailsOverlay = showDetailsOverlay; + vm.hideDetailsOverlay = hideDetailsOverlay; + vm.pickEditor = pickEditor; + vm.pickDataType = pickDataType; + + function activate() { + + getGroupedDataTypes(); + getGroupedPropertyEditors(); + + } + + function getGroupedPropertyEditors() { + + vm.loading = true; + + dataTypeResource.getGroupedPropertyEditors().then(function(data) { + vm.tabs[0].typesAndEditors = data; + vm.typesAndEditors = data; + vm.tabsLoaded = vm.tabsLoaded + 1; + checkIfTabContentIsLoaded(); + }); + + } + + function getGroupedDataTypes() { + + vm.loading = true; + + dataTypeResource.getGroupedDataTypes().then(function(data) { + vm.tabs[1].userConfigured = data; + vm.userConfigured = data; + vm.tabsLoaded = vm.tabsLoaded + 1; + checkIfTabContentIsLoaded(); + }); + + } + + function checkIfTabContentIsLoaded() { + if (vm.tabsLoaded === 2) { + vm.loading = false; + vm.showTabs = true; + } + } + + function filterItems() { + // clear item details + $scope.model.itemDetails = null; + + if (vm.searchTerm) { + vm.showFilterResult = true; + vm.showTabs = false; + } else { + vm.showFilterResult = false; + vm.showTabs = true; + } + + } + + function showDetailsOverlay(property) { + + var propertyDetails = {}; + propertyDetails.icon = property.icon; + propertyDetails.title = property.name; + + $scope.model.itemDetails = propertyDetails; + + } + + function hideDetailsOverlay() { + $scope.model.itemDetails = null; + } + + function pickEditor(editor) { + + var parentId = -1; + + dataTypeResource.getScaffold(parentId).then(function(dataType) { + + // set alias + dataType.selectedEditor = editor.alias; + + // set name + var nameArray = []; + + if ($scope.model.contentTypeName) { + nameArray.push($scope.model.contentTypeName); + } + + if ($scope.model.property.label) { + nameArray.push($scope.model.property.label); + } + + if (editor.name) { + nameArray.push(editor.name); + } + + // make name + dataType.name = nameArray.join(" - "); + + // get pre values + dataTypeResource.getPreValues(dataType.selectedEditor).then(function(preValues) { + + dataType.preValues = preValues; + + openEditorSettingsOverlay(dataType, true); + + }); + + }); + + } + + function pickDataType(selectedDataType) { + + dataTypeResource.getById(selectedDataType.id).then(function(dataType) { + contentTypeResource.getPropertyTypeScaffold(dataType.id).then(function(propertyType) { + submitOverlay(dataType, propertyType, false); + }); + }); + + } + + function openEditorSettingsOverlay(dataType, isNew) { + vm.editorSettingsOverlay = { + title: localizationService.localize("contentTypeEditor_editorSettings"), + dataType: dataType, + view: "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html", + show: true, + submit: function(model) { + var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues); + + dataTypeResource.save(model.dataType, preValues, isNew).then(function(newDataType) { + + contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function(propertyType) { + + submitOverlay(newDataType, propertyType, true); + + vm.editorSettingsOverlay.show = false; + vm.editorSettingsOverlay = null; + + }); + + }); + } + }; + + } + + function submitOverlay(dataType, propertyType, isNew) { + + // update property + $scope.model.property.config = propertyType.config; + $scope.model.property.editor = propertyType.editor; + $scope.model.property.view = propertyType.view; + $scope.model.property.dataTypeId = dataType.id; + $scope.model.property.dataTypeIcon = dataType.icon; + $scope.model.property.dataTypeName = dataType.name; + + $scope.model.updateSameDataTypes = isNew; + + $scope.model.submit($scope.model); + + } + + activate(); + + } + + angular.module("umbraco").controller("Umbraco.Overlays.EditorPickerOverlay", EditorPickerOverlay); + +})(); + +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.PropertyController + * @function + * + * @description + * The controller for the content type editor property dialog + */ + + (function() { + "use strict"; + + function PropertySettingsOverlay($scope, contentTypeResource, dataTypeResource, dataTypeHelper, localizationService) { + + var vm = this; + + vm.showValidationPattern = false; + vm.focusOnPatternField = false; + vm.focusOnMandatoryField = false; + vm.selectedValidationType = {}; + vm.validationTypes = [ + { + "name": localizationService.localize("validation_validateAsEmail"), + "key": "email", + "pattern": "[a-zA-Z0-9_\.\+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-\.]+", + "enableEditing": true + }, + { + "name": localizationService.localize("validation_validateAsNumber"), + "key": "number", + "pattern": "^[0-9]*$", + "enableEditing": true + }, + { + "name": localizationService.localize("validation_validateAsUrl"), + "key": "url", + "pattern": "https?\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}", + "enableEditing": true + }, + { + "name": localizationService.localize("validation_enterCustomValidation"), + "key": "custom", + "pattern": "", + "enableEditing": true + } + ]; + + vm.changeValidationType = changeValidationType; + vm.changeValidationPattern = changeValidationPattern; + vm.openEditorPickerOverlay = openEditorPickerOverlay; + vm.openEditorSettingsOverlay = openEditorSettingsOverlay; + + function activate() { + + matchValidationType(); + + } + + function changeValidationPattern() { + matchValidationType(); + } + + function openEditorPickerOverlay(property) { + + vm.focusOnMandatoryField = false; + + vm.editorPickerOverlay = {}; + vm.editorPickerOverlay.property = $scope.model.property; + vm.editorPickerOverlay.contentTypeName = $scope.model.contentTypeName; + vm.editorPickerOverlay.view = "views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html"; + vm.editorPickerOverlay.show = true; + + vm.editorPickerOverlay.submit = function(model) { + + $scope.model.updateSameDataTypes = model.updateSameDataTypes; + + vm.focusOnMandatoryField = true; + + // update property + property.config = model.property.config; + property.editor = model.property.editor; + property.view = model.property.view; + property.dataTypeId = model.property.dataTypeId; + property.dataTypeIcon = model.property.dataTypeIcon; + property.dataTypeName = model.property.dataTypeName; + + vm.editorPickerOverlay.show = false; + vm.editorPickerOverlay = null; + }; + + vm.editorPickerOverlay.close = function(model) { + vm.editorPickerOverlay.show = false; + vm.editorPickerOverlay = null; + }; + + } + + function openEditorSettingsOverlay(property) { + + vm.focusOnMandatoryField = false; + + // get data type + dataTypeResource.getById(property.dataTypeId).then(function(dataType) { + + vm.editorSettingsOverlay = {}; + vm.editorSettingsOverlay.title = "Editor settings"; + vm.editorSettingsOverlay.view = "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html"; + vm.editorSettingsOverlay.dataType = dataType; + vm.editorSettingsOverlay.show = true; + + vm.editorSettingsOverlay.submit = function(model) { + + var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues); + + dataTypeResource.save(model.dataType, preValues, false).then(function(newDataType) { + + contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function(propertyType) { + + // update editor + property.config = propertyType.config; + property.editor = propertyType.editor; + property.view = propertyType.view; + property.dataTypeId = newDataType.id; + property.dataTypeIcon = newDataType.icon; + property.dataTypeName = newDataType.name; + + // set flag to update same data types + $scope.model.updateSameDataTypes = true; + + vm.focusOnMandatoryField = true; + + vm.editorSettingsOverlay.show = false; + vm.editorSettingsOverlay = null; + + }); + + }); + + }; + + vm.editorSettingsOverlay.close = function(oldModel) { + vm.editorSettingsOverlay.show = false; + vm.editorSettingsOverlay = null; + }; + + }); + + } + + function matchValidationType() { + + if($scope.model.property.validation.pattern !== null && $scope.model.property.validation.pattern !== "" && $scope.model.property.validation.pattern !== undefined) { + + var match = false; + + // find and show if a match from the list has been chosen + angular.forEach(vm.validationTypes, function(validationType, index){ + if($scope.model.property.validation.pattern === validationType.pattern) { + vm.selectedValidationType = vm.validationTypes[index]; + vm.showValidationPattern = true; + match = true; + } + }); + + // if there is no match - choose the custom validation option. + if(!match) { + angular.forEach(vm.validationTypes, function(validationType){ + if(validationType.key === "custom") { + vm.selectedValidationType = validationType; + vm.showValidationPattern = true; + } + }); + } + } + + } + + function changeValidationType(selectedValidationType) { + + if(selectedValidationType) { + $scope.model.property.validation.pattern = selectedValidationType.pattern; + vm.showValidationPattern = true; + + // set focus on textarea + if(selectedValidationType.key === "custom") { + vm.focusOnPatternField = true; + } + + } else { + $scope.model.property.validation.pattern = ""; + vm.showValidationPattern = false; + } + + } + + activate(); + + } + + angular.module("umbraco").controller("Umbraco.Overlay.PropertySettingsOverlay", PropertySettingsOverlay); + +})(); + + (function() { + "use strict"; + + function CopyOverlay($scope, localizationService, eventsService) { + + var vm = this; + + if(!$scope.model.title) { + $scope.model.title = localizationService.localize("general_copy"); + } + + vm.hideSearch = hideSearch; + vm.selectResult = selectResult; + vm.onSearchResults = onSearchResults; + + var dialogOptions = $scope.model; + var searchText = "Search..."; + var node = dialogOptions.currentNode; + + localizationService.localize("general_search").then(function (value) { + searchText = value + "..."; + }); + + $scope.model.relateToOriginal = true; + $scope.dialogTreeEventHandler = $({}); + + vm.searchInfo = { + searchFromId: null, + searchFromName: null, + showSearch: false, + results: [], + selectedSearchResults: [] + }; + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if (args.node.metaData.listViewNode) { + //check if list view 'search' node was selected + + vm.searchInfo.showSearch = true; + vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id; + vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name; + } + else { + //eventsService.emit("editors.content.copyController.select", args); + + if ($scope.model.target) { + //un-select if there's a current one selected + $scope.model.target.selected = false; + } + + $scope.model.target = args.node; + $scope.model.target.selected = true; + } + + } + + function nodeExpandedHandler(ev, args) { + if (angular.isArray(args.children)) { + + //iterate children + _.each(args.children, function (child) { + //check if any of the items are list views, if so we need to add a custom + // child: A node to activate the search + if (child.metaData.isContainer) { + child.hasChildren = true; + child.children = [ + { + level: child.level + 1, + hasChildren: false, + name: searchText, + metaData: { + listViewNode: child, + }, + cssClass: "icon umb-tree-icon sprTree icon-search", + cssClasses: ["not-published"] + } + ]; + } + }); + } + } + + function hideSearch() { + vm.searchInfo.showSearch = false; + vm.searchInfo.searchFromId = null; + vm.searchInfo.searchFromName = null; + vm.searchInfo.results = []; + } + + // method to select a search result + function selectResult(evt, result) { + result.selected = result.selected === true ? false : true; + nodeSelectHandler(evt, { event: evt, node: result }); + } + + //callback when there are search results + function onSearchResults(results) { + vm.searchInfo.results = results; + vm.searchInfo.showSearch = true; + } + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); + }); + + + } + + angular.module("umbraco").controller("Umbraco.Overlays.CopyOverlay", CopyOverlay); + +})(); + +(function() { + "use strict"; + + function EmbedOverlay($scope, $http, umbRequestHelper, localizationService) { + + var vm = this; + var origWidth = 500; + var origHeight = 300; + + if(!$scope.model.title) { + $scope.model.title = localizationService.localize("general_embed"); + } + + $scope.model.embed = { + url: "", + width: 360, + height: 240, + constrain: true, + preview: "", + success: false, + info: "", + supportsDimensions: "" + }; + + vm.showPreview = showPreview; + vm.changeSize = changeSize; + + function showPreview() { + + if ($scope.model.embed.url) { + $scope.model.embed.show = true; + $scope.model.embed.preview = "
"; + $scope.model.embed.info = ""; + $scope.model.embed.success = false; + + $http({ + method: 'GET', + url: umbRequestHelper.getApiUrl("embedApiBaseUrl", "GetEmbed"), + params: { + url: $scope.model.embed.url, + width: $scope.model.embed.width, + height: $scope.model.embed.height + } + }) + .success(function(data) { + + $scope.model.embed.preview = ""; + + switch (data.Status) { + case 0: + //not supported + $scope.model.embed.info = "Not supported"; + break; + case 1: + //error + $scope.model.embed.info = "Computer says no"; + break; + case 2: + $scope.model.embed.preview = data.Markup; + $scope.model.embed.supportsDimensions = data.SupportsDimensions; + $scope.model.embed.success = true; + break; + } + }) + .error(function() { + $scope.model.embed.supportsDimensions = false; + $scope.model.embed.preview = ""; + $scope.model.embed.info = "Computer says no"; + }); + } else { + $scope.model.embed.supportsDimensions = false; + $scope.model.embed.preview = ""; + $scope.model.embed.info = "Please enter a URL"; + } + } + + function changeSize(type) { + + var width, height; + + if ($scope.model.embed.constrain) { + width = parseInt($scope.model.embed.width, 10); + height = parseInt($scope.model.embed.height, 10); + if (type == 'width') { + origHeight = Math.round((width / origWidth) * height); + $scope.model.embed.height = origHeight; + } else { + origWidth = Math.round((height / origHeight) * width); + $scope.model.embed.width = origWidth; + } + } + if ($scope.model.embed.url !== "") { + showPreview(); + } + + } + + } + + angular.module("umbraco").controller("Umbraco.Overlays.EmbedOverlay", EmbedOverlay); + +})(); + +angular.module("umbraco") + .controller("Umbraco.Overlays.HelpController", function ($scope, $location, $routeParams, helpService, userService, localizationService) { + $scope.section = $routeParams.section; + $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion; + $scope.model.subtitle = "Umbraco version" + " " + $scope.version; + + if(!$scope.model.title) { + $scope.model.title = localizationService.localize("general_help"); + } + + if(!$scope.section){ + $scope.section = "content"; + } + + $scope.sectionName = $scope.section; + + var rq = {}; + rq.section = $scope.section; + + //translate section name + localizationService.localize("sections_" + rq.section).then(function (value) { + $scope.sectionName = value; + }); + + userService.getCurrentUser().then(function(user){ + + rq.usertype = user.userType; + rq.lang = user.locale; + + if($routeParams.url){ + rq.path = decodeURIComponent($routeParams.url); + + if(rq.path.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath) === 0){ + rq.path = rq.path.substring(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath.length); + } + + if(rq.path.indexOf(".aspx") > 0){ + rq.path = rq.path.substring(0, rq.path.indexOf(".aspx")); + } + + }else{ + rq.path = rq.section + "/" + $routeParams.tree + "/" + $routeParams.method; + } + + helpService.findHelp(rq).then(function(topics){ + $scope.topics = topics; + }); + + helpService.findVideos(rq).then(function(videos){ + $scope.videos = videos; + }); + + }); + + + }); + +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.PropertyController + * @function + * + * @description + * The controller for the content type editor property dialog + */ +function IconPickerOverlay($scope, iconHelper, localizationService) { + + $scope.loading = true; + $scope.model.hideSubmitButton = true; + + if (!$scope.model.title) { + $scope.model.title = localizationService.localize("defaultdialogs_selectIcon"); + } + + iconHelper.getIcons().then(function(icons) { + $scope.icons = icons; + $scope.loading = false; + }); + + $scope.selectIcon = function(icon, color) { + $scope.model.icon = icon; + $scope.model.color = color; + $scope.submitForm($scope.model); + }; + +} + +angular.module("umbraco").controller("Umbraco.Overlays.IconPickerOverlay", IconPickerOverlay); + +function ItemPickerOverlay($scope, localizationService) { + + if (!$scope.model.title) { + $scope.model.title = localizationService.localize("defaultdialogs_selectItem"); + } + + $scope.model.hideSubmitButton = true; + + $scope.selectItem = function(item) { + $scope.model.selectedItem = item; + $scope.submitForm($scope.model); + }; + +} + +angular.module("umbraco").controller("Umbraco.Overlays.ItemPickerOverlay", ItemPickerOverlay); + +//used for the media picker dialog +angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController", + function ($scope, eventsService, dialogService, entityResource, contentResource, mediaHelper, userService, localizationService) { + var dialogOptions = $scope.model; + + var searchText = "Search..."; + localizationService.localize("general_search").then(function (value) { + searchText = value + "..."; + }); + + if(!$scope.model.title) { + $scope.model.title = localizationService.localize("defaultdialogs_selectLink"); + } + + $scope.dialogTreeEventHandler = $({}); + $scope.model.target = {}; + $scope.searchInfo = { + searchFromId: null, + searchFromName: null, + showSearch: false, + results: [], + selectedSearchResults: [] + }; + + if (dialogOptions.currentTarget) { + $scope.model.target = dialogOptions.currentTarget; + + //if we have a node ID, we fetch the current node to build the form data + if ($scope.model.target.id) { + + if (!$scope.model.target.path) { + entityResource.getPath($scope.model.target.id, "Document").then(function (path) { + $scope.model.target.path = path; + //now sync the tree to this path + $scope.dialogTreeEventHandler.syncTree({ path: $scope.model.target.path, tree: "content" }); + }); + } + + contentResource.getNiceUrl($scope.model.target.id).then(function (url) { + $scope.model.target.url = url; + }); + } + } + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if (args.node.metaData.listViewNode) { + //check if list view 'search' node was selected + + $scope.searchInfo.showSearch = true; + $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id; + $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name; + } + else { + eventsService.emit("dialogs.linkPicker.select", args); + + if ($scope.currentNode) { + //un-select if there's a current one selected + $scope.currentNode.selected = false; + } + + $scope.currentNode = args.node; + $scope.currentNode.selected = true; + $scope.model.target.id = args.node.id; + $scope.model.target.name = args.node.name; + + if (args.node.id < 0) { + $scope.model.target.url = "/"; + } + else { + contentResource.getNiceUrl(args.node.id).then(function (url) { + $scope.model.target.url = url; + }); + } + + if (!angular.isUndefined($scope.model.target.isMedia)) { + delete $scope.model.target.isMedia; + } + } + } + + function nodeExpandedHandler(ev, args) { + if (angular.isArray(args.children)) { + + //iterate children + _.each(args.children, function (child) { + //check if any of the items are list views, if so we need to add a custom + // child: A node to activate the search + if (child.metaData.isContainer) { + child.hasChildren = true; + child.children = [ + { + level: child.level + 1, + hasChildren: false, + name: searchText, + metaData: { + listViewNode: child, + }, + cssClass: "icon umb-tree-icon sprTree icon-search", + cssClasses: ["not-published"] + } + ]; + } + }); + } + } + + $scope.switchToMediaPicker = function () { + userService.getCurrentUser().then(function (userData) { + $scope.mediaPickerOverlay = { + view: "mediapicker", + startNodeId: userData.startMediaId, + show: true, + submit: function(model) { + var media = model.selectedImages[0]; + + $scope.model.target.id = media.id; + $scope.model.target.isMedia = true; + $scope.model.target.name = media.name; + $scope.model.target.url = mediaHelper.resolveFile(media); + + $scope.mediaPickerOverlay.show = false; + $scope.mediaPickerOverlay = null; + } + }; + }); + }; + + $scope.hideSearch = function () { + $scope.searchInfo.showSearch = false; + $scope.searchInfo.searchFromId = null; + $scope.searchInfo.searchFromName = null; + $scope.searchInfo.results = []; + } + + // method to select a search result + $scope.selectResult = function (evt, result) { + result.selected = result.selected === true ? false : true; + nodeSelectHandler(evt, {event: evt, node: result}); + }; + + //callback when there are search results + $scope.onSearchResults = function (results) { + $scope.searchInfo.results = results; + $scope.searchInfo.showSearch = true; + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); + }); + }); + +function MacroPickerController($scope, entityResource, macroResource, umbPropEditorHelper, macroService, formHelper, localizationService) { + + + if(!$scope.model.title) { + $scope.model.title = localizationService.localize("defaultdialogs_selectMacro"); + } + + $scope.macros = []; + $scope.model.selectedMacro = null; + $scope.wizardStep = "macroSelect"; + $scope.model.macroParams = []; + $scope.noMacroParams = false; + + $scope.changeMacro = function() { + if ($scope.wizardStep === "macroSelect") { + editParams(); + } else { + submitForm(); + } + }; + + /** changes the view to edit the params of the selected macro */ + function editParams() { + //get the macro params if there are any + macroResource.getMacroParameters($scope.model.selectedMacro.id) + .then(function (data) { + + //go to next page if there are params otherwise we can just exit + if (!angular.isArray(data) || data.length === 0) { + + $scope.noMacroParams = true; + + } else { + $scope.wizardStep = "paramSelect"; + $scope.model.macroParams = data; + + //fill in the data if we are editing this macro + if ($scope.model.dialogData && $scope.model.dialogData.macroData && $scope.model.dialogData.macroData.macroParamsDictionary) { + _.each($scope.model.dialogData.macroData.macroParamsDictionary, function (val, key) { + var prop = _.find($scope.model.macroParams, function (item) { + return item.alias == key; + }); + if (prop) { + + if (_.isString(val)) { + //we need to unescape values as they have most likely been escaped while inserted + val = _.unescape(val); + + //detect if it is a json string + if (val.detectIsJson()) { + try { + //Parse it to json + prop.value = angular.fromJson(val); + } + catch (e) { + // not json + prop.value = val; + } + } + else { + prop.value = val; + } + } + else { + prop.value = val; + } + } + }); + + } + } + + }); + } + + //here we check to see if we've been passed a selected macro and if so we'll set the + //editor to start with parameter editing + if ($scope.model.dialogData && $scope.model.dialogData.macroData) { + $scope.wizardStep = "paramSelect"; + } + + //get the macro list - pass in a filter if it is only for rte + entityResource.getAll("Macro", ($scope.model.dialogData && $scope.model.dialogData.richTextEditor && $scope.model.dialogData.richTextEditor === true) ? "UseInEditor=true" : null) + .then(function (data) { + + //if 'allowedMacros' is specified, we need to filter + if (angular.isArray($scope.model.dialogData.allowedMacros) && $scope.model.dialogData.allowedMacros.length > 0) { + $scope.macros = _.filter(data, function(d) { + return _.contains($scope.model.dialogData.allowedMacros, d.alias); + }); + } + else { + $scope.macros = data; + } + + + //check if there's a pre-selected macro and if it exists + if ($scope.model.dialogData && $scope.model.dialogData.macroData && $scope.model.dialogData.macroData.macroAlias) { + var found = _.find(data, function (item) { + return item.alias === $scope.model.dialogData.macroData.macroAlias; + }); + if (found) { + //select the macro and go to next screen + $scope.model.selectedMacro = found; + editParams(); + return; + } + } + //we don't have a pre-selected macro so ensure the correct step is set + $scope.wizardStep = "macroSelect"; + }); + + +} + +angular.module("umbraco").controller("Umbraco.Overlays.MacroPickerController", MacroPickerController); //used for the media picker dialog angular.module("umbraco") @@ -3586,124 +3586,124 @@ angular.module("umbraco").controller("Umbraco.Overlays.MemberGroupPickerControll }); - (function() { - "use strict"; - - function MoveOverlay($scope, localizationService, eventsService) { - - var vm = this; - - vm.hideSearch = hideSearch; - vm.selectResult = selectResult; - vm.onSearchResults = onSearchResults; - - var dialogOptions = $scope.model; - var searchText = "Search..."; - var node = dialogOptions.currentNode; - - localizationService.localize("general_search").then(function (value) { - searchText = value + "..."; - }); - - if(!$scope.model.title) { - $scope.model.title = localizationService.localize("actions_move"); - } - - $scope.model.relateToOriginal = true; - $scope.dialogTreeEventHandler = $({}); - - vm.searchInfo = { - searchFromId: null, - searchFromName: null, - showSearch: false, - results: [], - selectedSearchResults: [] - }; - - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - if (args.node.metaData.listViewNode) { - //check if list view 'search' node was selected - - vm.searchInfo.showSearch = true; - vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id; - vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name; - } - else { - //eventsService.emit("editors.content.copyController.select", args); - - if ($scope.model.target) { - //un-select if there's a current one selected - $scope.model.target.selected = false; - } - - $scope.model.target = args.node; - $scope.model.target.selected = true; - } - - } - - function nodeExpandedHandler(ev, args) { - if (angular.isArray(args.children)) { - - //iterate children - _.each(args.children, function (child) { - //check if any of the items are list views, if so we need to add a custom - // child: A node to activate the search - if (child.metaData.isContainer) { - child.hasChildren = true; - child.children = [ - { - level: child.level + 1, - hasChildren: false, - name: searchText, - metaData: { - listViewNode: child, - }, - cssClass: "icon umb-tree-icon sprTree icon-search", - cssClasses: ["not-published"] - } - ]; - } - }); - } - } - - function hideSearch() { - vm.searchInfo.showSearch = false; - vm.searchInfo.searchFromId = null; - vm.searchInfo.searchFromName = null; - vm.searchInfo.results = []; - } - - // method to select a search result - function selectResult(evt, result) { - result.selected = result.selected === true ? false : true; - nodeSelectHandler(evt, { event: evt, node: result }); - } - - //callback when there are search results - function onSearchResults(results) { - vm.searchInfo.results = results; - vm.searchInfo.showSearch = true; - } - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); - }); - - - } - - angular.module("umbraco").controller("Umbraco.Overlays.MoveOverlay", MoveOverlay); - -})(); + (function() { + "use strict"; + + function MoveOverlay($scope, localizationService, eventsService) { + + var vm = this; + + vm.hideSearch = hideSearch; + vm.selectResult = selectResult; + vm.onSearchResults = onSearchResults; + + var dialogOptions = $scope.model; + var searchText = "Search..."; + var node = dialogOptions.currentNode; + + localizationService.localize("general_search").then(function (value) { + searchText = value + "..."; + }); + + if(!$scope.model.title) { + $scope.model.title = localizationService.localize("actions_move"); + } + + $scope.model.relateToOriginal = true; + $scope.dialogTreeEventHandler = $({}); + + vm.searchInfo = { + searchFromId: null, + searchFromName: null, + showSearch: false, + results: [], + selectedSearchResults: [] + }; + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if (args.node.metaData.listViewNode) { + //check if list view 'search' node was selected + + vm.searchInfo.showSearch = true; + vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id; + vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name; + } + else { + //eventsService.emit("editors.content.copyController.select", args); + + if ($scope.model.target) { + //un-select if there's a current one selected + $scope.model.target.selected = false; + } + + $scope.model.target = args.node; + $scope.model.target.selected = true; + } + + } + + function nodeExpandedHandler(ev, args) { + if (angular.isArray(args.children)) { + + //iterate children + _.each(args.children, function (child) { + //check if any of the items are list views, if so we need to add a custom + // child: A node to activate the search + if (child.metaData.isContainer) { + child.hasChildren = true; + child.children = [ + { + level: child.level + 1, + hasChildren: false, + name: searchText, + metaData: { + listViewNode: child, + }, + cssClass: "icon umb-tree-icon sprTree icon-search", + cssClasses: ["not-published"] + } + ]; + } + }); + } + } + + function hideSearch() { + vm.searchInfo.showSearch = false; + vm.searchInfo.searchFromId = null; + vm.searchInfo.searchFromName = null; + vm.searchInfo.results = []; + } + + // method to select a search result + function selectResult(evt, result) { + result.selected = result.selected === true ? false : true; + nodeSelectHandler(evt, { event: evt, node: result }); + } + + //callback when there are search results + function onSearchResults(results) { + vm.searchInfo.results = results; + vm.searchInfo.showSearch = true; + } + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); + }); + + + } + + angular.module("umbraco").controller("Umbraco.Overlays.MoveOverlay", MoveOverlay); + +})(); //used for the media picker dialog angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", @@ -4204,182 +4204,182 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", }); }); -angular.module("umbraco") - .controller("Umbraco.Overlays.UserController", function ($scope, $location, $timeout, userService, historyService, eventsService, externalLoginInfo, authResource, currentUserResource, formHelper, localizationService) { - - $scope.history = historyService.getCurrent(); - $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion; - $scope.showPasswordFields = false; - $scope.changePasswordButtonState = "init"; - $scope.model.subtitle = "Umbraco version" + " " + $scope.version; - - if(!$scope.model.title) { - $scope.model.title = localizationService.localize("general_user"); - } - - $scope.externalLoginProviders = externalLoginInfo.providers; - $scope.externalLinkLoginFormAction = Umbraco.Sys.ServerVariables.umbracoUrls.externalLinkLoginsUrl; - var evts = []; - evts.push(eventsService.on("historyService.add", function (e, args) { - $scope.history = args.all; - })); - evts.push(eventsService.on("historyService.remove", function (e, args) { - $scope.history = args.all; - })); - evts.push(eventsService.on("historyService.removeAll", function (e, args) { - $scope.history = []; - })); - - $scope.logout = function () { - - //Add event listener for when there are pending changes on an editor which means our route was not successful - var pendingChangeEvent = eventsService.on("valFormManager.pendingChanges", function (e, args) { - //one time listener, remove the event - pendingChangeEvent(); - $scope.model.close(); - }); - - - //perform the path change, if it is successful then the promise will resolve otherwise it will fail - $scope.model.close(); - $location.path("/logout"); - }; - - $scope.gotoHistory = function (link) { - $location.path(link); - $scope.model.close(); - }; - - //Manually update the remaining timeout seconds - function updateTimeout() { - $timeout(function () { - if ($scope.remainingAuthSeconds > 0) { - $scope.remainingAuthSeconds--; - $scope.$digest(); - //recurse - updateTimeout(); - } - - }, 1000, false); // 1 second, do NOT execute a global digest - } - - function updateUserInfo() { - //get the user - userService.getCurrentUser().then(function (user) { - $scope.user = user; - if ($scope.user) { - $scope.model.title = user.name; - $scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds; - $scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1; - //set the timer - updateTimeout(); - - authResource.getCurrentUserLinkedLogins().then(function(logins) { - //reset all to be un-linked - for (var provider in $scope.externalLoginProviders) { - $scope.externalLoginProviders[provider].linkedProviderKey = undefined; - } - - //set the linked logins - for (var login in logins) { - var found = _.find($scope.externalLoginProviders, function (i) { - return i.authType == login; - }); - if (found) { - found.linkedProviderKey = logins[login]; - } - } - }); - } - }); - } - - $scope.unlink = function (e, loginProvider, providerKey) { - var result = confirm("Are you sure you want to unlink this account?"); - if (!result) { - e.preventDefault(); - return; - } - - authResource.unlinkLogin(loginProvider, providerKey).then(function (a, b, c) { - updateUserInfo(); - }); - } - - updateUserInfo(); - - //remove all event handlers - $scope.$on('$destroy', function () { - for (var e = 0; e < evts.length; e++) { - evts[e](); - } - - }); - - /* ---------- UPDATE PASSWORD ---------- */ - - //create the initial model for change password property editor - $scope.changePasswordModel = { - alias: "_umb_password", - view: "changepassword", - config: {}, - value: {} - }; - - //go get the config for the membership provider and add it to the model - currentUserResource.getMembershipProviderConfig().then(function(data) { - $scope.changePasswordModel.config = data; - //ensure the hasPassword config option is set to true (the user of course has a password already assigned) - //this will ensure the oldPassword is shown so they can change it - // disable reset password functionality beacuse it does not make sense inside the backoffice - $scope.changePasswordModel.config.hasPassword = true; - $scope.changePasswordModel.config.disableToggle = true; - $scope.changePasswordModel.config.enableReset = false; - }); - - $scope.changePassword = function() { - - if (formHelper.submitForm({ scope: $scope })) { - - $scope.changePasswordButtonState = "busy"; - - currentUserResource.changePassword($scope.changePasswordModel.value).then(function(data) { - - //if the password has been reset, then update our model - if (data.value) { - $scope.changePasswordModel.value.generatedPassword = data.value; - } - - formHelper.resetForm({ scope: $scope, notifications: data.notifications }); - - $scope.changePasswordButtonState = "success"; - $timeout(function() { - $scope.togglePasswordFields(); - }, 2000); - - }, function (err) { - - formHelper.handleError(err); - - $scope.changePasswordButtonState = "error"; - - }); - - } - - }; - - $scope.togglePasswordFields = function() { - clearPasswordFields(); - $scope.showPasswordFields = !$scope.showPasswordFields; - } - - function clearPasswordFields() { - $scope.changePasswordModel.value.newPassword = ""; - $scope.changePasswordModel.confirm = ""; - } - - }); +angular.module("umbraco") + .controller("Umbraco.Overlays.UserController", function ($scope, $location, $timeout, userService, historyService, eventsService, externalLoginInfo, authResource, currentUserResource, formHelper, localizationService) { + + $scope.history = historyService.getCurrent(); + $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion; + $scope.showPasswordFields = false; + $scope.changePasswordButtonState = "init"; + $scope.model.subtitle = "Umbraco version" + " " + $scope.version; + + if(!$scope.model.title) { + $scope.model.title = localizationService.localize("general_user"); + } + + $scope.externalLoginProviders = externalLoginInfo.providers; + $scope.externalLinkLoginFormAction = Umbraco.Sys.ServerVariables.umbracoUrls.externalLinkLoginsUrl; + var evts = []; + evts.push(eventsService.on("historyService.add", function (e, args) { + $scope.history = args.all; + })); + evts.push(eventsService.on("historyService.remove", function (e, args) { + $scope.history = args.all; + })); + evts.push(eventsService.on("historyService.removeAll", function (e, args) { + $scope.history = []; + })); + + $scope.logout = function () { + + //Add event listener for when there are pending changes on an editor which means our route was not successful + var pendingChangeEvent = eventsService.on("valFormManager.pendingChanges", function (e, args) { + //one time listener, remove the event + pendingChangeEvent(); + $scope.model.close(); + }); + + + //perform the path change, if it is successful then the promise will resolve otherwise it will fail + $scope.model.close(); + $location.path("/logout"); + }; + + $scope.gotoHistory = function (link) { + $location.path(link); + $scope.model.close(); + }; + + //Manually update the remaining timeout seconds + function updateTimeout() { + $timeout(function () { + if ($scope.remainingAuthSeconds > 0) { + $scope.remainingAuthSeconds--; + $scope.$digest(); + //recurse + updateTimeout(); + } + + }, 1000, false); // 1 second, do NOT execute a global digest + } + + function updateUserInfo() { + //get the user + userService.getCurrentUser().then(function (user) { + $scope.user = user; + if ($scope.user) { + $scope.model.title = user.name; + $scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds; + $scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1; + //set the timer + updateTimeout(); + + authResource.getCurrentUserLinkedLogins().then(function(logins) { + //reset all to be un-linked + for (var provider in $scope.externalLoginProviders) { + $scope.externalLoginProviders[provider].linkedProviderKey = undefined; + } + + //set the linked logins + for (var login in logins) { + var found = _.find($scope.externalLoginProviders, function (i) { + return i.authType == login; + }); + if (found) { + found.linkedProviderKey = logins[login]; + } + } + }); + } + }); + } + + $scope.unlink = function (e, loginProvider, providerKey) { + var result = confirm("Are you sure you want to unlink this account?"); + if (!result) { + e.preventDefault(); + return; + } + + authResource.unlinkLogin(loginProvider, providerKey).then(function (a, b, c) { + updateUserInfo(); + }); + } + + updateUserInfo(); + + //remove all event handlers + $scope.$on('$destroy', function () { + for (var e = 0; e < evts.length; e++) { + evts[e](); + } + + }); + + /* ---------- UPDATE PASSWORD ---------- */ + + //create the initial model for change password property editor + $scope.changePasswordModel = { + alias: "_umb_password", + view: "changepassword", + config: {}, + value: {} + }; + + //go get the config for the membership provider and add it to the model + currentUserResource.getMembershipProviderConfig().then(function(data) { + $scope.changePasswordModel.config = data; + //ensure the hasPassword config option is set to true (the user of course has a password already assigned) + //this will ensure the oldPassword is shown so they can change it + // disable reset password functionality beacuse it does not make sense inside the backoffice + $scope.changePasswordModel.config.hasPassword = true; + $scope.changePasswordModel.config.disableToggle = true; + $scope.changePasswordModel.config.enableReset = false; + }); + + $scope.changePassword = function() { + + if (formHelper.submitForm({ scope: $scope })) { + + $scope.changePasswordButtonState = "busy"; + + currentUserResource.changePassword($scope.changePasswordModel.value).then(function(data) { + + //if the password has been reset, then update our model + if (data.value) { + $scope.changePasswordModel.value.generatedPassword = data.value; + } + + formHelper.resetForm({ scope: $scope, notifications: data.notifications }); + + $scope.changePasswordButtonState = "success"; + $timeout(function() { + $scope.togglePasswordFields(); + }, 2000); + + }, function (err) { + + formHelper.handleError(err); + + $scope.changePasswordButtonState = "error"; + + }); + + } + + }; + + $scope.togglePasswordFields = function() { + clearPasswordFields(); + $scope.showPasswordFields = !$scope.showPasswordFields; + } + + function clearPasswordFields() { + $scope.changePasswordModel.value.newPassword = ""; + $scope.changePasswordModel.confirm = ""; + } + + }); angular.module("umbraco") .controller("Umbraco.Overlays.YsodController", function ($scope, legacyResource, treeService, navigationService, localizationService) { @@ -4395,145 +4395,145 @@ angular.module("umbraco") }); -angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController", - function ($scope, eventsService, contentResource, navigationService, appState, treeService, localizationService, notificationsService) { - - var dialogOptions = $scope.dialogOptions; - var searchText = "Search..."; - localizationService.localize("general_search").then(function (value) { - searchText = value + "..."; - }); - - $scope.relateToOriginal = true; - $scope.recursive = true; - $scope.dialogTreeEventHandler = $({}); - $scope.busy = false; - $scope.searchInfo = { - searchFromId: null, - searchFromName: null, - showSearch: false, - results: [], - selectedSearchResults: [] - } - - var node = dialogOptions.currentNode; - - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - if (args.node.metaData.listViewNode) { - //check if list view 'search' node was selected - - $scope.searchInfo.showSearch = true; - $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id; - $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name; - } - else { - eventsService.emit("editors.content.copyController.select", args); - - if ($scope.target) { - //un-select if there's a current one selected - $scope.target.selected = false; - } - - $scope.target = args.node; - $scope.target.selected = true; - } - - } - - function nodeExpandedHandler(ev, args) { - if (angular.isArray(args.children)) { - - //iterate children - _.each(args.children, function (child) { - //check if any of the items are list views, if so we need to add a custom - // child: A node to activate the search - if (child.metaData.isContainer) { - child.hasChildren = true; - child.children = [ - { - level: child.level + 1, - hasChildren: false, - name: searchText, - metaData: { - listViewNode: child, - }, - cssClass: "icon umb-tree-icon sprTree icon-search", - cssClasses: ["not-published"] - } - ]; - } - }); - } - } - - $scope.hideSearch = function () { - $scope.searchInfo.showSearch = false; - $scope.searchInfo.searchFromId = null; - $scope.searchInfo.searchFromName = null; - $scope.searchInfo.results = []; - } - - // method to select a search result - $scope.selectResult = function (evt, result) { - result.selected = result.selected === true ? false : true; - nodeSelectHandler(evt, { event: evt, node: result }); - }; - - //callback when there are search results - $scope.onSearchResults = function (results) { - $scope.searchInfo.results = results; - $scope.searchInfo.showSearch = true; - }; - - $scope.copy = function () { - - $scope.busy = true; - $scope.error = false; - - contentResource.copy({ parentId: $scope.target.id, id: node.id, relateToOriginal: $scope.relateToOriginal, recursive: $scope.recursive }) - .then(function (path) { - $scope.error = false; - $scope.success = true; - $scope.busy = false; - - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - - //we need to do a double sync here: first sync to the copied content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was copied!!) - - navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true }); - } - }); - - }, function (err) { - $scope.success = false; - $scope.error = err; - $scope.busy = false; - //show any notifications - if (angular.isArray(err.data.notifications)) { - for (var i = 0; i < err.data.notifications.length; i++) { - notificationsService.showNotification(err.data.notifications[i]); - } - } - }); - }; - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); - }); - }); +angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController", + function ($scope, eventsService, contentResource, navigationService, appState, treeService, localizationService, notificationsService) { + + var dialogOptions = $scope.dialogOptions; + var searchText = "Search..."; + localizationService.localize("general_search").then(function (value) { + searchText = value + "..."; + }); + + $scope.relateToOriginal = true; + $scope.recursive = true; + $scope.dialogTreeEventHandler = $({}); + $scope.busy = false; + $scope.searchInfo = { + searchFromId: null, + searchFromName: null, + showSearch: false, + results: [], + selectedSearchResults: [] + } + + var node = dialogOptions.currentNode; + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if (args.node.metaData.listViewNode) { + //check if list view 'search' node was selected + + $scope.searchInfo.showSearch = true; + $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id; + $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name; + } + else { + eventsService.emit("editors.content.copyController.select", args); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + } + + function nodeExpandedHandler(ev, args) { + if (angular.isArray(args.children)) { + + //iterate children + _.each(args.children, function (child) { + //check if any of the items are list views, if so we need to add a custom + // child: A node to activate the search + if (child.metaData.isContainer) { + child.hasChildren = true; + child.children = [ + { + level: child.level + 1, + hasChildren: false, + name: searchText, + metaData: { + listViewNode: child, + }, + cssClass: "icon umb-tree-icon sprTree icon-search", + cssClasses: ["not-published"] + } + ]; + } + }); + } + } + + $scope.hideSearch = function () { + $scope.searchInfo.showSearch = false; + $scope.searchInfo.searchFromId = null; + $scope.searchInfo.searchFromName = null; + $scope.searchInfo.results = []; + } + + // method to select a search result + $scope.selectResult = function (evt, result) { + result.selected = result.selected === true ? false : true; + nodeSelectHandler(evt, { event: evt, node: result }); + }; + + //callback when there are search results + $scope.onSearchResults = function (results) { + $scope.searchInfo.results = results; + $scope.searchInfo.showSearch = true; + }; + + $scope.copy = function () { + + $scope.busy = true; + $scope.error = false; + + contentResource.copy({ parentId: $scope.target.id, id: node.id, relateToOriginal: $scope.relateToOriginal, recursive: $scope.recursive }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the copied content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was copied!!) + + navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); + }); + }); /** * @ngdoc controller @@ -4619,238 +4619,238 @@ function ContentDeleteController($scope, contentResource, treeService, navigatio angular.module("umbraco").controller("Umbraco.Editors.Content.DeleteController", ContentDeleteController); -/** - * @ngdoc controller - * @name Umbraco.Editors.Content.EditController - * @function - * - * @description - * The controller for the content editor - */ -function ContentEditController($scope, $rootScope, $routeParams, $q, $timeout, $window, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, keyboardService, umbModelMapper, editorState, $http) { - - //setup scope vars - $scope.defaultButton = null; - $scope.subButtons = []; - - $scope.page = {}; - $scope.page.loading = false; - $scope.page.menu = {}; - $scope.page.menu.currentNode = null; - $scope.page.menu.currentSection = appState.getSectionState("currentSection"); - $scope.page.listViewPath = null; - $scope.page.isNew = $routeParams.create; - $scope.page.buttonGroupState = "init"; - - function init(content) { - - var buttons = contentEditingHelper.configureContentEditorButtons({ - create: $routeParams.create, - content: content, - methods: { - saveAndPublish: $scope.saveAndPublish, - sendToPublish: $scope.sendToPublish, - save: $scope.save, - unPublish: $scope.unPublish - } - }); - $scope.defaultButton = buttons.defaultButton; - $scope.subButtons = buttons.subButtons; - - editorState.set($scope.content); - - //We fetch all ancestors of the node to generate the footer breadcrumb navigation - if (!$routeParams.create) { - if (content.parentId && content.parentId != -1) { - entityResource.getAncestors(content.id, "document") - .then(function (anc) { - $scope.ancestors = anc; - }); - } - } - } - - /** Syncs the content item to it's tree node - this occurs on first load and after saving */ - function syncTreeNode(content, path, initialLoad) { - - if (!$scope.content.isChildOfListView) { - navigationService.syncTree({ tree: "content", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) { - $scope.page.menu.currentNode = syncArgs.node; - }); - } - else if (initialLoad === true) { - - //it's a child item, just sync the ui node to the parent - navigationService.syncTree({ tree: "content", path: path.substring(0, path.lastIndexOf(",")).split(","), forceReload: initialLoad !== true }); - - //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node - // from the server so that we can load in the actions menu. - umbRequestHelper.resourcePromise( - $http.get(content.treeNodeUrl), - 'Failed to retrieve data for child node ' + content.id).then(function (node) { - $scope.page.menu.currentNode = node; - }); - } - } - - // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish - function performSave(args) { - var deferred = $q.defer(); - - $scope.page.buttonGroupState = "busy"; - - contentEditingHelper.contentEditorPerformSave({ - statusMessage: args.statusMessage, - saveMethod: args.saveMethod, - scope: $scope, - content: $scope.content, - action: args.action - }).then(function (data) { - //success - init($scope.content); - syncTreeNode($scope.content, data.path); - - $scope.page.buttonGroupState = "success"; - - deferred.resolve(data); - }, function (err) { - //error - if (err) { - editorState.set($scope.content); - } - - $scope.page.buttonGroupState = "error"; - - deferred.reject(err); - }); - - return deferred.promise; - } - - function resetLastListPageNumber(content) { - // We're using rootScope to store the page number for list views, so if returning to the list - // we can restore the page. If we've moved on to edit a piece of content that's not the list or it's children - // we should remove this so as not to confuse if navigating to a different list - if (!content.isChildOfListView && !content.isContainer) { - $rootScope.lastListViewPageViewed = null; - } - } - - if ($routeParams.create) { - - $scope.page.loading = true; - - //we are creating so get an empty content item - contentResource.getScaffold($routeParams.id, $routeParams.doctype) - .then(function (data) { - - $scope.content = data; - - init($scope.content); - - resetLastListPageNumber($scope.content); - - $scope.page.loading = false; - - }); - } - else { - - $scope.page.loading = true; - - //we are editing so get the content item from the server - contentResource.getById($routeParams.id) - .then(function (data) { - - $scope.content = data; - - if (data.isChildOfListView && data.trashed === false) { - $scope.page.listViewPath = ($routeParams.page) - ? "/content/content/edit/" + data.parentId + "?page=" + $routeParams.page - : "/content/content/edit/" + data.parentId; - } - - init($scope.content); - - //in one particular special case, after we've created a new item we redirect back to the edit - // route but there might be server validation errors in the collection which we need to display - // after the redirect, so we will bind all subscriptions which will show the server validation errors - // if there are any and then clear them so the collection no longer persists them. - serverValidationManager.executeAndClearAllSubscriptions(); - - syncTreeNode($scope.content, data.path, true); - - resetLastListPageNumber($scope.content); - - $scope.page.loading = false; - - }); - } - - - $scope.unPublish = function () { - - if (formHelper.submitForm({ scope: $scope, statusMessage: "Unpublishing...", skipValidation: true })) { - - $scope.page.buttonGroupState = "busy"; - - contentResource.unPublish($scope.content.id) - .then(function (data) { - - formHelper.resetForm({ scope: $scope, notifications: data.notifications }); - - contentEditingHelper.handleSuccessfulSave({ - scope: $scope, - savedContent: data, - rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) - }); - - init($scope.content); - - syncTreeNode($scope.content, data.path); - - $scope.page.buttonGroupState = "success"; - - }); - } - - }; - - $scope.sendToPublish = function () { - return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending...", action: "sendToPublish" }); - }; - - $scope.saveAndPublish = function () { - return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing...", action: "publish" }); - }; - - $scope.save = function () { - return performSave({ saveMethod: contentResource.save, statusMessage: "Saving...", action: "save" }); - }; - - $scope.preview = function (content) { - - - if (!$scope.busy) { - - // Chromes popup blocker will kick in if a window is opened - // outwith the initial scoped request. This trick will fix that. - // - var previewWindow = $window.open('preview/?id=' + content.id, 'umbpreview'); - $scope.save().then(function (data) { - // Build the correct path so both /#/ and #/ work. - var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?id=' + data.id; - previewWindow.location.href = redirect; - }); - - - } - - }; - -} - -angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController); +/** + * @ngdoc controller + * @name Umbraco.Editors.Content.EditController + * @function + * + * @description + * The controller for the content editor + */ +function ContentEditController($scope, $rootScope, $routeParams, $q, $timeout, $window, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, keyboardService, umbModelMapper, editorState, $http) { + + //setup scope vars + $scope.defaultButton = null; + $scope.subButtons = []; + + $scope.page = {}; + $scope.page.loading = false; + $scope.page.menu = {}; + $scope.page.menu.currentNode = null; + $scope.page.menu.currentSection = appState.getSectionState("currentSection"); + $scope.page.listViewPath = null; + $scope.page.isNew = $routeParams.create; + $scope.page.buttonGroupState = "init"; + + function init(content) { + + var buttons = contentEditingHelper.configureContentEditorButtons({ + create: $routeParams.create, + content: content, + methods: { + saveAndPublish: $scope.saveAndPublish, + sendToPublish: $scope.sendToPublish, + save: $scope.save, + unPublish: $scope.unPublish + } + }); + $scope.defaultButton = buttons.defaultButton; + $scope.subButtons = buttons.subButtons; + + editorState.set($scope.content); + + //We fetch all ancestors of the node to generate the footer breadcrumb navigation + if (!$routeParams.create) { + if (content.parentId && content.parentId != -1) { + entityResource.getAncestors(content.id, "document") + .then(function (anc) { + $scope.ancestors = anc; + }); + } + } + } + + /** Syncs the content item to it's tree node - this occurs on first load and after saving */ + function syncTreeNode(content, path, initialLoad) { + + if (!$scope.content.isChildOfListView) { + navigationService.syncTree({ tree: "content", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) { + $scope.page.menu.currentNode = syncArgs.node; + }); + } + else if (initialLoad === true) { + + //it's a child item, just sync the ui node to the parent + navigationService.syncTree({ tree: "content", path: path.substring(0, path.lastIndexOf(",")).split(","), forceReload: initialLoad !== true }); + + //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node + // from the server so that we can load in the actions menu. + umbRequestHelper.resourcePromise( + $http.get(content.treeNodeUrl), + 'Failed to retrieve data for child node ' + content.id).then(function (node) { + $scope.page.menu.currentNode = node; + }); + } + } + + // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish + function performSave(args) { + var deferred = $q.defer(); + + $scope.page.buttonGroupState = "busy"; + + contentEditingHelper.contentEditorPerformSave({ + statusMessage: args.statusMessage, + saveMethod: args.saveMethod, + scope: $scope, + content: $scope.content, + action: args.action + }).then(function (data) { + //success + init($scope.content); + syncTreeNode($scope.content, data.path); + + $scope.page.buttonGroupState = "success"; + + deferred.resolve(data); + }, function (err) { + //error + if (err) { + editorState.set($scope.content); + } + + $scope.page.buttonGroupState = "error"; + + deferred.reject(err); + }); + + return deferred.promise; + } + + function resetLastListPageNumber(content) { + // We're using rootScope to store the page number for list views, so if returning to the list + // we can restore the page. If we've moved on to edit a piece of content that's not the list or it's children + // we should remove this so as not to confuse if navigating to a different list + if (!content.isChildOfListView && !content.isContainer) { + $rootScope.lastListViewPageViewed = null; + } + } + + if ($routeParams.create) { + + $scope.page.loading = true; + + //we are creating so get an empty content item + contentResource.getScaffold($routeParams.id, $routeParams.doctype) + .then(function (data) { + + $scope.content = data; + + init($scope.content); + + resetLastListPageNumber($scope.content); + + $scope.page.loading = false; + + }); + } + else { + + $scope.page.loading = true; + + //we are editing so get the content item from the server + contentResource.getById($routeParams.id) + .then(function (data) { + + $scope.content = data; + + if (data.isChildOfListView && data.trashed === false) { + $scope.page.listViewPath = ($routeParams.page) + ? "/content/content/edit/" + data.parentId + "?page=" + $routeParams.page + : "/content/content/edit/" + data.parentId; + } + + init($scope.content); + + //in one particular special case, after we've created a new item we redirect back to the edit + // route but there might be server validation errors in the collection which we need to display + // after the redirect, so we will bind all subscriptions which will show the server validation errors + // if there are any and then clear them so the collection no longer persists them. + serverValidationManager.executeAndClearAllSubscriptions(); + + syncTreeNode($scope.content, data.path, true); + + resetLastListPageNumber($scope.content); + + $scope.page.loading = false; + + }); + } + + + $scope.unPublish = function () { + + if (formHelper.submitForm({ scope: $scope, statusMessage: "Unpublishing...", skipValidation: true })) { + + $scope.page.buttonGroupState = "busy"; + + contentResource.unPublish($scope.content.id) + .then(function (data) { + + formHelper.resetForm({ scope: $scope, notifications: data.notifications }); + + contentEditingHelper.handleSuccessfulSave({ + scope: $scope, + savedContent: data, + rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) + }); + + init($scope.content); + + syncTreeNode($scope.content, data.path); + + $scope.page.buttonGroupState = "success"; + + }); + } + + }; + + $scope.sendToPublish = function () { + return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending...", action: "sendToPublish" }); + }; + + $scope.saveAndPublish = function () { + return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing...", action: "publish" }); + }; + + $scope.save = function () { + return performSave({ saveMethod: contentResource.save, statusMessage: "Saving...", action: "save" }); + }; + + $scope.preview = function (content) { + + + if (!$scope.busy) { + + // Chromes popup blocker will kick in if a window is opened + // outwith the initial scoped request. This trick will fix that. + // + var previewWindow = $window.open('preview/?id=' + content.id, 'umbpreview'); + $scope.save().then(function (data) { + // Build the correct path so both /#/ and #/ work. + var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?id=' + data.id; + previewWindow.location.href = redirect; + }); + + + } + + }; + +} + +angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController); /** * @ngdoc controller @@ -4883,270 +4883,270 @@ function ContentEmptyRecycleBinController($scope, contentResource, treeService, angular.module("umbraco").controller("Umbraco.Editors.Content.EmptyRecycleBinController", ContentEmptyRecycleBinController); -angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController", - function ($scope, eventsService, contentResource, navigationService, appState, treeService, localizationService, notificationsService) { - - var dialogOptions = $scope.dialogOptions; - var searchText = "Search..."; - localizationService.localize("general_search").then(function (value) { - searchText = value + "..."; - }); - - $scope.dialogTreeEventHandler = $({}); - $scope.busy = false; - $scope.searchInfo = { - searchFromId: null, - searchFromName: null, - showSearch: false, - results: [], - selectedSearchResults: [] - } - - var node = dialogOptions.currentNode; - - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - if (args.node.metaData.listViewNode) { - //check if list view 'search' node was selected - - $scope.searchInfo.showSearch = true; - $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id; - $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name; - } - else { - eventsService.emit("editors.content.moveController.select", args); - - if ($scope.target) { - //un-select if there's a current one selected - $scope.target.selected = false; - } - - $scope.target = args.node; - $scope.target.selected = true; - } - } - - function nodeExpandedHandler(ev, args) { - if (angular.isArray(args.children)) { - - //iterate children - _.each(args.children, function (child) { - //check if any of the items are list views, if so we need to add a custom - // child: A node to activate the search - if (child.metaData.isContainer) { - child.hasChildren = true; - child.children = [ - { - level: child.level + 1, - hasChildren: false, - name: searchText, - metaData: { - listViewNode: child, - }, - cssClass: "icon umb-tree-icon sprTree icon-search", - cssClasses: ["not-published"] - } - ]; - } - }); - } - } - - $scope.hideSearch = function () { - $scope.searchInfo.showSearch = false; - $scope.searchInfo.searchFromId = null; - $scope.searchInfo.searchFromName = null; - $scope.searchInfo.results = []; - } - - // method to select a search result - $scope.selectResult = function (evt, result) { - result.selected = result.selected === true ? false : true; - nodeSelectHandler(evt, { event: evt, node: result }); - }; - - //callback when there are search results - $scope.onSearchResults = function (results) { - $scope.searchInfo.results = results; - $scope.searchInfo.showSearch = true; - }; - - $scope.move = function () { - - $scope.busy = true; - $scope.error = false; - - contentResource.move({ parentId: $scope.target.id, id: node.id }) - .then(function (path) { - $scope.error = false; - $scope.success = true; - $scope.busy = false; - - //first we need to remove the node that launched the dialog - treeService.removeNode($scope.currentNode); - - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - - //we need to do a double sync here: first sync to the moved content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was moved!!) - - navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true }); - } - }); - - }, function (err) { - $scope.success = false; - $scope.error = err; - $scope.busy = false; - //show any notifications - if (angular.isArray(err.data.notifications)) { - for (var i = 0; i < err.data.notifications.length; i++) { - notificationsService.showNotification(err.data.notifications[i]); - } - } - }); - }; - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); - }); +angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController", + function ($scope, eventsService, contentResource, navigationService, appState, treeService, localizationService, notificationsService) { + + var dialogOptions = $scope.dialogOptions; + var searchText = "Search..."; + localizationService.localize("general_search").then(function (value) { + searchText = value + "..."; + }); + + $scope.dialogTreeEventHandler = $({}); + $scope.busy = false; + $scope.searchInfo = { + searchFromId: null, + searchFromName: null, + showSearch: false, + results: [], + selectedSearchResults: [] + } + + var node = dialogOptions.currentNode; + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if (args.node.metaData.listViewNode) { + //check if list view 'search' node was selected + + $scope.searchInfo.showSearch = true; + $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id; + $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name; + } + else { + eventsService.emit("editors.content.moveController.select", args); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + } + + function nodeExpandedHandler(ev, args) { + if (angular.isArray(args.children)) { + + //iterate children + _.each(args.children, function (child) { + //check if any of the items are list views, if so we need to add a custom + // child: A node to activate the search + if (child.metaData.isContainer) { + child.hasChildren = true; + child.children = [ + { + level: child.level + 1, + hasChildren: false, + name: searchText, + metaData: { + listViewNode: child, + }, + cssClass: "icon umb-tree-icon sprTree icon-search", + cssClasses: ["not-published"] + } + ]; + } + }); + } + } + + $scope.hideSearch = function () { + $scope.searchInfo.showSearch = false; + $scope.searchInfo.searchFromId = null; + $scope.searchInfo.searchFromName = null; + $scope.searchInfo.results = []; + } + + // method to select a search result + $scope.selectResult = function (evt, result) { + result.selected = result.selected === true ? false : true; + nodeSelectHandler(evt, { event: evt, node: result }); + }; + + //callback when there are search results + $scope.onSearchResults = function (results) { + $scope.searchInfo.results = results; + $scope.searchInfo.showSearch = true; + }; + + $scope.move = function () { + + $scope.busy = true; + $scope.error = false; + + contentResource.move({ parentId: $scope.target.id, id: node.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //first we need to remove the node that launched the dialog + treeService.removeNode($scope.currentNode); + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the moved content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was moved!!) + + navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); + }); }); -/** - * @ngdoc controller - * @name Umbraco.Editors.Content.RecycleBinController - * @function - * - * @description - * Controls the recycle bin for content - * - */ +/** + * @ngdoc controller + * @name Umbraco.Editors.Content.RecycleBinController + * @function + * + * @description + * Controls the recycle bin for content + * + */ + +function ContentRecycleBinController($scope, $routeParams, contentResource, navigationService, localizationService) { + + //ensures the list view doesn't actually load until we query for the list view config + // for the section + $scope.page = {}; + $scope.page.name = "Recycle Bin"; + $scope.page.nameLocked = true; + + //ensures the list view doesn't actually load until we query for the list view config + // for the section + $scope.listViewPath = null; + + $routeParams.id = "-20"; + contentResource.getRecycleBin().then(function (result) { + //we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a + // single property, so we'll extract that property (list view) and use it's data. + var listproperty = result.tabs[0].properties[0]; + + _.each(listproperty.config, function (val, key) { + $scope.model.config[key] = val; + }); + $scope.listViewPath = 'views/propertyeditors/listview/listview.html'; + }); + + $scope.model = { config: { entityType: $routeParams.section, layouts: [] } }; + + // sync tree node + navigationService.syncTree({ tree: "content", path: ["-1", $routeParams.id], forceReload: false }); + + localizePageName(); + + function localizePageName() { + + var pageName = "general_recycleBin"; + + localizationService.localize(pageName).then(function (value) { + $scope.page.name = value; + }); + + } +} + +angular.module('umbraco').controller("Umbraco.Editors.Content.RecycleBinController", ContentRecycleBinController); -function ContentRecycleBinController($scope, $routeParams, contentResource, navigationService, localizationService) { - - //ensures the list view doesn't actually load until we query for the list view config - // for the section - $scope.page = {}; - $scope.page.name = "Recycle Bin"; - $scope.page.nameLocked = true; - - //ensures the list view doesn't actually load until we query for the list view config - // for the section - $scope.listViewPath = null; - - $routeParams.id = "-20"; - contentResource.getRecycleBin().then(function (result) { - //we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a - // single property, so we'll extract that property (list view) and use it's data. - var listproperty = result.tabs[0].properties[0]; - - _.each(listproperty.config, function (val, key) { - $scope.model.config[key] = val; - }); - $scope.listViewPath = 'views/propertyeditors/listview/listview.html'; - }); - - $scope.model = { config: { entityType: $routeParams.section, layouts: [] } }; - - // sync tree node - navigationService.syncTree({ tree: "content", path: ["-1", $routeParams.id], forceReload: false }); - - localizePageName(); - - function localizePageName() { - - var pageName = "general_recycleBin"; - - localizationService.localize(pageName).then(function (value) { - $scope.page.name = value; - }); - - } -} - -angular.module('umbraco').controller("Umbraco.Editors.Content.RecycleBinController", ContentRecycleBinController); - -angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController", - function ($scope, relationResource, contentResource, navigationService, appState, treeService) { - var dialogOptions = $scope.dialogOptions; - - var node = dialogOptions.currentNode; - - $scope.error = null; - $scope.success = false; - - relationResource.getByChildId(node.id, "relateParentDocumentOnDelete").then(function (data) { - - if (data.length == 0) { - $scope.success = false; - $scope.error = { - errorMsg: "Cannot automatically restore this item", - data: { - Message: "There is no 'restore' relation found for this node. Use the Move menu item to move it manually." - } - } - return; - } - - $scope.relation = data[0]; - - if ($scope.relation.parentId == -1) { - $scope.target = { id: -1, name: "Root" }; - - } else { - contentResource.getById($scope.relation.parentId).then(function (data) { - $scope.target = data; - - }, function (err) { - $scope.success = false; - $scope.error = err; - }); - } - - }, function (err) { - $scope.success = false; - $scope.error = err; - }); - - $scope.restore = function () { - // this code was copied from `content.move.controller.js` - contentResource.move({ parentId: $scope.target.id, id: node.id }) - .then(function (path) { - - $scope.success = true; - - //first we need to remove the node that launched the dialog - treeService.removeNode($scope.currentNode); - - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - - //we need to do a double sync here: first sync to the moved content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was moved!!) - - navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true }); - } - }); - - }, function (err) { - $scope.success = false; - $scope.error = err; - }); - }; +angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController", + function ($scope, relationResource, contentResource, navigationService, appState, treeService) { + var dialogOptions = $scope.dialogOptions; + + var node = dialogOptions.currentNode; + + $scope.error = null; + $scope.success = false; + + relationResource.getByChildId(node.id, "relateParentDocumentOnDelete").then(function (data) { + + if (data.length == 0) { + $scope.success = false; + $scope.error = { + errorMsg: "Cannot automatically restore this item", + data: { + Message: "There is no 'restore' relation found for this node. Use the Move menu item to move it manually." + } + } + return; + } + + $scope.relation = data[0]; + + if ($scope.relation.parentId == -1) { + $scope.target = { id: -1, name: "Root" }; + + } else { + contentResource.getById($scope.relation.parentId).then(function (data) { + $scope.target = data; + + }, function (err) { + $scope.success = false; + $scope.error = err; + }); + } + + }, function (err) { + $scope.success = false; + $scope.error = err; + }); + + $scope.restore = function () { + // this code was copied from `content.move.controller.js` + contentResource.move({ parentId: $scope.target.id, id: node.id }) + .then(function (path) { + + $scope.success = true; + + //first we need to remove the node that launched the dialog + treeService.removeNode($scope.currentNode); + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the moved content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was moved!!) + + navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + }); + }; }); function startUpVideosDashboardController($scope, xmlhelper, $log, $http) { $scope.videos = []; @@ -5363,313 +5363,313 @@ function MediaFolderBrowserDashboardController($rootScope, $scope, contentTypeRe } angular.module("umbraco").controller("Umbraco.Dashboard.MediaFolderBrowserDashboardController", MediaFolderBrowserDashboardController); -function examineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeout) { - - $scope.indexerDetails = []; - $scope.searcherDetails = []; - $scope.loading = true; - - function checkProcessing(indexer, checkActionName) { - umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", checkActionName, { indexerName: indexer.name })), - 'Failed to check index processing') - .then(function(data) { - - if (data !== null && data !== "null") { - - //copy all resulting properties - for (var k in data) { - indexer[k] = data[k]; - } - indexer.isProcessing = false; - } - else { - $timeout(function () { - //don't continue if we've tried 100 times - if (indexer.processingAttempts < 100) { - checkProcessing(indexer, checkActionName); - //add an attempt - indexer.processingAttempts++; - } - else { - //we've exceeded 100 attempts, stop processing - indexer.isProcessing = false; - } - }, 1000); - } - }); - } - - $scope.search = function (searcher, e) { - if (e && e.keyCode !== 13) { - return; - } - - umbRequestHelper.resourcePromise( - $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearchResults", { - searcherName: searcher.name, - query: encodeURIComponent(searcher.searchText), - queryType: searcher.searchType - })), - 'Failed to search') - .then(function(searchResults) { - searcher.isSearching = true; - searcher.searchResults = searchResults; - }); - } - - $scope.toggle = function(provider, propName) { - if (provider[propName] !== undefined) { - provider[propName] = !provider[propName]; - } - else { - provider[propName] = true; - } - } - - $scope.rebuildIndex = function(indexer) { - if (confirm("This will cause the index to be rebuilt. " + - "Depending on how much content there is in your site this could take a while. " + - "It is not recommended to rebuild an index during times of high website traffic " + - "or when editors are editing content.")) { - - indexer.isProcessing = true; - indexer.processingAttempts = 0; - - umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "PostRebuildIndex", { indexerName: indexer.name })), - 'Failed to rebuild index') - .then(function () { - - //rebuilding has started, nothing is returned accept a 200 status code. - //lets poll to see if it is done. - $timeout(function () { - checkProcessing(indexer, "PostCheckRebuildIndex"); - }, 1000); - - }); - } - } - - $scope.optimizeIndex = function(indexer) { - if (confirm("This will cause the index to be optimized which will improve its performance. " + - "It is not recommended to optimize an index during times of high website traffic " + - "or when editors are editing content.")) { - indexer.isProcessing = true; - - umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "PostOptimizeIndex", { indexerName: indexer.name })), - 'Failed to optimize index') - .then(function () { - - //optimizing has started, nothing is returned accept a 200 status code. - //lets poll to see if it is done. - $timeout(function () { - checkProcessing(indexer, "PostCheckOptimizeIndex"); - }, 1000); - - }); - } - } - - $scope.closeSearch = function(searcher) { - searcher.isSearching = true; - } - - - //go get the data - - //combine two promises and execute when they are both done - $q.all([ - - //get the indexer details - umbRequestHelper.resourcePromise( - $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetIndexerDetails")), - 'Failed to retrieve indexer details') - .then(function(data) { - $scope.indexerDetails = data; - }), - - //get the searcher details - umbRequestHelper.resourcePromise( - $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearcherDetails")), - 'Failed to retrieve searcher details') - .then(function(data) { - $scope.searcherDetails = data; - for (var s in $scope.searcherDetails) { - $scope.searcherDetails[s].searchType = "text"; - } - }) - - ]).then(function () { - //all init loading is complete - $scope.loading = false; - }); - - -} +function examineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeout) { + + $scope.indexerDetails = []; + $scope.searcherDetails = []; + $scope.loading = true; + + function checkProcessing(indexer, checkActionName) { + umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", checkActionName, { indexerName: indexer.name })), + 'Failed to check index processing') + .then(function(data) { + + if (data !== null && data !== "null") { + + //copy all resulting properties + for (var k in data) { + indexer[k] = data[k]; + } + indexer.isProcessing = false; + } + else { + $timeout(function () { + //don't continue if we've tried 100 times + if (indexer.processingAttempts < 100) { + checkProcessing(indexer, checkActionName); + //add an attempt + indexer.processingAttempts++; + } + else { + //we've exceeded 100 attempts, stop processing + indexer.isProcessing = false; + } + }, 1000); + } + }); + } + + $scope.search = function (searcher, e) { + if (e && e.keyCode !== 13) { + return; + } + + umbRequestHelper.resourcePromise( + $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearchResults", { + searcherName: searcher.name, + query: encodeURIComponent(searcher.searchText), + queryType: searcher.searchType + })), + 'Failed to search') + .then(function(searchResults) { + searcher.isSearching = true; + searcher.searchResults = searchResults; + }); + } + + $scope.toggle = function(provider, propName) { + if (provider[propName] !== undefined) { + provider[propName] = !provider[propName]; + } + else { + provider[propName] = true; + } + } + + $scope.rebuildIndex = function(indexer) { + if (confirm("This will cause the index to be rebuilt. " + + "Depending on how much content there is in your site this could take a while. " + + "It is not recommended to rebuild an index during times of high website traffic " + + "or when editors are editing content.")) { + + indexer.isProcessing = true; + indexer.processingAttempts = 0; + + umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "PostRebuildIndex", { indexerName: indexer.name })), + 'Failed to rebuild index') + .then(function () { + + //rebuilding has started, nothing is returned accept a 200 status code. + //lets poll to see if it is done. + $timeout(function () { + checkProcessing(indexer, "PostCheckRebuildIndex"); + }, 1000); + + }); + } + } + + $scope.optimizeIndex = function(indexer) { + if (confirm("This will cause the index to be optimized which will improve its performance. " + + "It is not recommended to optimize an index during times of high website traffic " + + "or when editors are editing content.")) { + indexer.isProcessing = true; + + umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "PostOptimizeIndex", { indexerName: indexer.name })), + 'Failed to optimize index') + .then(function () { + + //optimizing has started, nothing is returned accept a 200 status code. + //lets poll to see if it is done. + $timeout(function () { + checkProcessing(indexer, "PostCheckOptimizeIndex"); + }, 1000); + + }); + } + } + + $scope.closeSearch = function(searcher) { + searcher.isSearching = true; + } + + + //go get the data + + //combine two promises and execute when they are both done + $q.all([ + + //get the indexer details + umbRequestHelper.resourcePromise( + $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetIndexerDetails")), + 'Failed to retrieve indexer details') + .then(function(data) { + $scope.indexerDetails = data; + }), + + //get the searcher details + umbRequestHelper.resourcePromise( + $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearcherDetails")), + 'Failed to retrieve searcher details') + .then(function(data) { + $scope.searcherDetails = data; + for (var s in $scope.searcherDetails) { + $scope.searcherDetails[s].searchType = "text"; + } + }) + + ]).then(function () { + //all init loading is complete + $scope.loading = false; + }); + + +} angular.module("umbraco").controller("Umbraco.Dashboard.ExamineMgmtController", examineMgmtController); -function xmlDataIntegrityReportController($scope, umbRequestHelper, $log, $http, $q, $timeout) { - - function check(item) { - var action = item.check; - umbRequestHelper.resourcePromise( - $http.get(umbRequestHelper.getApiUrl("xmlDataIntegrityBaseUrl", action)), - 'Failed to retrieve data integrity status') - .then(function(result) { - item.checking = false; - item.invalid = result === "false"; - }); - } - - $scope.fix = function(item) { - var action = item.fix; - if (item.fix) { - if (confirm("This will cause all xml structures for this type to be rebuilt. " + - "Depending on how much content there is in your site this could take a while. " + - "It is not recommended to rebuild xml structures if they are not out of sync, during times of high website traffic " + - "or when editors are editing content.")) { - item.fixing = true; - umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("xmlDataIntegrityBaseUrl", action)), - 'Failed to retrieve data integrity status') - .then(function (result) { - item.fixing = false; - item.invalid = result === "false"; - }); - } - } - } - - $scope.items = { - "contentXml": { - label: "Content in the cmsContentXml table", - checking: true, - fixing: false, - fix: "FixContentXmlTable", - check: "CheckContentXmlTable" - }, - "mediaXml": { - label: "Media in the cmsContentXml table", - checking: true, - fixing: false, - fix: "FixMediaXmlTable", - check: "CheckMediaXmlTable" - }, - "memberXml": { - label: "Members in the cmsContentXml table", - checking: true, - fixing: false, - fix: "FixMembersXmlTable", - check: "CheckMembersXmlTable" - } - }; - - for (var i in $scope.items) { - check($scope.items[i]); - } - -} +function xmlDataIntegrityReportController($scope, umbRequestHelper, $log, $http, $q, $timeout) { + + function check(item) { + var action = item.check; + umbRequestHelper.resourcePromise( + $http.get(umbRequestHelper.getApiUrl("xmlDataIntegrityBaseUrl", action)), + 'Failed to retrieve data integrity status') + .then(function(result) { + item.checking = false; + item.invalid = result === "false"; + }); + } + + $scope.fix = function(item) { + var action = item.fix; + if (item.fix) { + if (confirm("This will cause all xml structures for this type to be rebuilt. " + + "Depending on how much content there is in your site this could take a while. " + + "It is not recommended to rebuild xml structures if they are not out of sync, during times of high website traffic " + + "or when editors are editing content.")) { + item.fixing = true; + umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("xmlDataIntegrityBaseUrl", action)), + 'Failed to retrieve data integrity status') + .then(function (result) { + item.fixing = false; + item.invalid = result === "false"; + }); + } + } + } + + $scope.items = { + "contentXml": { + label: "Content in the cmsContentXml table", + checking: true, + fixing: false, + fix: "FixContentXmlTable", + check: "CheckContentXmlTable" + }, + "mediaXml": { + label: "Media in the cmsContentXml table", + checking: true, + fixing: false, + fix: "FixMediaXmlTable", + check: "CheckMediaXmlTable" + }, + "memberXml": { + label: "Members in the cmsContentXml table", + checking: true, + fixing: false, + fix: "FixMembersXmlTable", + check: "CheckMembersXmlTable" + } + }; + + for (var i in $scope.items) { + check($scope.items[i]); + } + +} angular.module("umbraco").controller("Umbraco.Dashboard.XmlDataIntegrityReportController", xmlDataIntegrityReportController); -/** - * @ngdoc controller - * @name Umbraco.Editors.DataType.CreateController - * @function - * - * @description - * The controller for the data type creation dialog - */ -function DataTypeCreateController($scope, $location, navigationService, dataTypeResource, formHelper, appState) { +/** + * @ngdoc controller + * @name Umbraco.Editors.DataType.CreateController + * @function + * + * @description + * The controller for the data type creation dialog + */ +function DataTypeCreateController($scope, $location, navigationService, dataTypeResource, formHelper, appState) { + + $scope.model = { + folderName: "", + creatingFolder: false + }; + + var node = $scope.dialogOptions.currentNode; + + $scope.showCreateFolder = function() { + $scope.model.creatingFolder = true; + } + + $scope.createContainer = function () { + if (formHelper.submitForm({ scope: $scope, formCtrl: this.createFolderForm, statusMessage: "Creating folder..." })) { + dataTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) { + + navigationService.hideMenu(); + var currPath = node.path ? node.path : "-1"; + navigationService.syncTree({ tree: "datatypes", path: currPath + "," + folderId, forceReload: true, activate: true }); + + formHelper.resetForm({ scope: $scope }); + + var section = appState.getSectionState("currentSection"); + + }, function(err) { + + //TODO: Handle errors + }); + }; + } + + $scope.createDataType = function() { + $location.search('create', null); + $location.path("/developer/datatypes/edit/" + node.id).search("create", "true"); + navigationService.hideMenu(); + } +} + +angular.module('umbraco').controller("Umbraco.Editors.DataType.CreateController", DataTypeCreateController); - $scope.model = { - folderName: "", - creatingFolder: false - }; - - var node = $scope.dialogOptions.currentNode; - - $scope.showCreateFolder = function() { - $scope.model.creatingFolder = true; - } - - $scope.createContainer = function () { - if (formHelper.submitForm({ scope: $scope, formCtrl: this.createFolderForm, statusMessage: "Creating folder..." })) { - dataTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) { - - navigationService.hideMenu(); - var currPath = node.path ? node.path : "-1"; - navigationService.syncTree({ tree: "datatypes", path: currPath + "," + folderId, forceReload: true, activate: true }); - - formHelper.resetForm({ scope: $scope }); - - var section = appState.getSectionState("currentSection"); - - }, function(err) { - - //TODO: Handle errors - }); - }; - } - - $scope.createDataType = function() { - $location.search('create', null); - $location.path("/developer/datatypes/edit/" + node.id).search("create", "true"); - navigationService.hideMenu(); - } -} - -angular.module('umbraco').controller("Umbraco.Editors.DataType.CreateController", DataTypeCreateController); - -/** - * @ngdoc controller - * @name Umbraco.Editors.ContentDeleteController - * @function - * - * @description - * The controller for deleting content - */ -function DataTypeDeleteController($scope, dataTypeResource, treeService, navigationService) { - - $scope.performDelete = function() { - - //mark it for deletion (used in the UI) - $scope.currentNode.loading = true; - dataTypeResource.deleteById($scope.currentNode.id).then(function () { - $scope.currentNode.loading = false; - - //get the root node before we remove it - var rootNode = treeService.getTreeRoot($scope.currentNode); - - //TODO: Need to sync tree, etc... - treeService.removeNode($scope.currentNode); - navigationService.hideMenu(); - }); - - }; - - $scope.performContainerDelete = function () { - - //mark it for deletion (used in the UI) - $scope.currentNode.loading = true; - dataTypeResource.deleteContainerById($scope.currentNode.id).then(function () { - $scope.currentNode.loading = false; - - //get the root node before we remove it - var rootNode = treeService.getTreeRoot($scope.currentNode); - - //TODO: Need to sync tree, etc... - treeService.removeNode($scope.currentNode); - navigationService.hideMenu(); - }); - - }; - - $scope.cancel = function() { - navigationService.hideDialog(); - }; -} - -angular.module("umbraco").controller("Umbraco.Editors.DataType.DeleteController", DataTypeDeleteController); +/** + * @ngdoc controller + * @name Umbraco.Editors.ContentDeleteController + * @function + * + * @description + * The controller for deleting content + */ +function DataTypeDeleteController($scope, dataTypeResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + dataTypeResource.deleteById($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + navigationService.hideMenu(); + }); + + }; + + $scope.performContainerDelete = function () { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + dataTypeResource.deleteContainerById($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + navigationService.hideMenu(); + }); + + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.DataType.DeleteController", DataTypeDeleteController); /** * @ngdoc controller @@ -5873,266 +5873,266 @@ function DataTypeEditController($scope, $routeParams, $location, appState, navig angular.module("umbraco").controller("Umbraco.Editors.DataType.EditController", DataTypeEditController); -angular.module("umbraco") -.controller("Umbraco.Editors.DataType.MoveController", - function ($scope, dataTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { - var dialogOptions = $scope.dialogOptions; - $scope.dialogTreeEventHandler = $({}); +angular.module("umbraco") +.controller("Umbraco.Editors.DataType.MoveController", + function ($scope, dataTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { + var dialogOptions = $scope.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + $scope.move = function () { + + $scope.busy = true; + $scope.error = false; + + dataTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //first we need to remove the node that launched the dialog + treeService.removeNode($scope.currentNode); + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the moved content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was moved!!) + + navigationService.syncTree({ tree: "dataTypes", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "dataTypes", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + eventsService.emit('app.refreshEditor'); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + }); + }); - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); +angular.module("umbraco") +.controller("Umbraco.Editors.DocumentTypes.CopyController", + function ($scope, contentTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { + var dialogOptions = $scope.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + $scope.copy = function () { + + $scope.busy = true; + $scope.error = false; + + contentTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the copied content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was copied!!) + + navigationService.syncTree({ tree: "documentTypes", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "documentTypes", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + }); + }); - if ($scope.target) { - //un-select if there's a current one selected - $scope.target.selected = false; - } +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.CreateController + * @function + * + * @description + * The controller for the doc type creation dialog + */ +function DocumentTypesCreateController($scope, $location, navigationService, contentTypeResource, formHelper, appState, notificationsService, localizationService) { + + $scope.model = { + allowCreateFolder: $scope.dialogOptions.currentNode.parentId === null || $scope.dialogOptions.currentNode.nodeType === "container", + folderName: "", + creatingFolder: false, + }; + + var node = $scope.dialogOptions.currentNode, + localizeCreateFolder = localizationService.localize("defaultdialog_createFolder"); + + $scope.showCreateFolder = function() { + $scope.model.creatingFolder = true; + }; + + $scope.createContainer = function() { + + if (formHelper.submitForm({scope: $scope, formCtrl: this.createFolderForm, statusMessage: localizeCreateFolder})) { + + contentTypeResource.createContainer(node.id, $scope.model.folderName).then(function(folderId) { + + navigationService.hideMenu(); + + var currPath = node.path ? node.path : "-1"; + + navigationService.syncTree({ + tree: "documenttypes", + path: currPath + "," + folderId, + forceReload: true, + activate: true + }); + + formHelper.resetForm({ + scope: $scope + }); + + var section = appState.getSectionState("currentSection"); + + }, function(err) { + + $scope.error = err; + + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + } + }; + + $scope.createDocType = function() { + $location.search('create', null); + $location.search('notemplate', null); + $location.path("/settings/documenttypes/edit/" + node.id).search("create", "true"); + navigationService.hideMenu(); + }; + + $scope.createComponent = function() { + $location.search('create', null); + $location.search('notemplate', null); + $location.path("/settings/documenttypes/edit/" + node.id).search("create", "true").search("notemplate", "true"); + navigationService.hideMenu(); + }; +} + +angular.module('umbraco').controller("Umbraco.Editors.DocumentTypes.CreateController", DocumentTypesCreateController); - $scope.target = args.node; - $scope.target.selected = true; - } - - $scope.move = function () { - - $scope.busy = true; - $scope.error = false; - - dataTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) - .then(function (path) { - $scope.error = false; - $scope.success = true; - $scope.busy = false; - - //first we need to remove the node that launched the dialog - treeService.removeNode($scope.currentNode); - - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - - //we need to do a double sync here: first sync to the moved content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was moved!!) - - navigationService.syncTree({ tree: "dataTypes", path: path, forceReload: true, activate: false }).then(function (args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "dataTypes", path: activeNodePath, forceReload: false, activate: true }); - } - }); - - eventsService.emit('app.refreshEditor'); - - }, function (err) { - $scope.success = false; - $scope.error = err; - $scope.busy = false; - //show any notifications - if (angular.isArray(err.data.notifications)) { - for (var i = 0; i < err.data.notifications.length; i++) { - notificationsService.showNotification(err.data.notifications[i]); - } - } - }); - }; - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - }); - }); - -angular.module("umbraco") -.controller("Umbraco.Editors.DocumentTypes.CopyController", - function ($scope, contentTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { - var dialogOptions = $scope.dialogOptions; - $scope.dialogTreeEventHandler = $({}); - - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - if ($scope.target) { - //un-select if there's a current one selected - $scope.target.selected = false; - } - - $scope.target = args.node; - $scope.target.selected = true; - } - - $scope.copy = function () { - - $scope.busy = true; - $scope.error = false; - - contentTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) - .then(function (path) { - $scope.error = false; - $scope.success = true; - $scope.busy = false; - - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - - //we need to do a double sync here: first sync to the copied content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was copied!!) - - navigationService.syncTree({ tree: "documentTypes", path: path, forceReload: true, activate: false }).then(function (args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "documentTypes", path: activeNodePath, forceReload: false, activate: true }); - } - }); - - }, function (err) { - $scope.success = false; - $scope.error = err; - $scope.busy = false; - //show any notifications - if (angular.isArray(err.data.notifications)) { - for (var i = 0; i < err.data.notifications.length; i++) { - notificationsService.showNotification(err.data.notifications[i]); - } - } - }); - }; - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - }); - }); - -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.CreateController - * @function - * - * @description - * The controller for the doc type creation dialog - */ -function DocumentTypesCreateController($scope, $location, navigationService, contentTypeResource, formHelper, appState, notificationsService, localizationService) { - - $scope.model = { - allowCreateFolder: $scope.dialogOptions.currentNode.parentId === null || $scope.dialogOptions.currentNode.nodeType === "container", - folderName: "", - creatingFolder: false, - }; - - var node = $scope.dialogOptions.currentNode, - localizeCreateFolder = localizationService.localize("defaultdialog_createFolder"); - - $scope.showCreateFolder = function() { - $scope.model.creatingFolder = true; - }; - - $scope.createContainer = function() { - - if (formHelper.submitForm({scope: $scope, formCtrl: this.createFolderForm, statusMessage: localizeCreateFolder})) { - - contentTypeResource.createContainer(node.id, $scope.model.folderName).then(function(folderId) { - - navigationService.hideMenu(); - - var currPath = node.path ? node.path : "-1"; - - navigationService.syncTree({ - tree: "documenttypes", - path: currPath + "," + folderId, - forceReload: true, - activate: true - }); - - formHelper.resetForm({ - scope: $scope - }); - - var section = appState.getSectionState("currentSection"); - - }, function(err) { - - $scope.error = err; - - //show any notifications - if (angular.isArray(err.data.notifications)) { - for (var i = 0; i < err.data.notifications.length; i++) { - notificationsService.showNotification(err.data.notifications[i]); - } - } - }); - } - }; - - $scope.createDocType = function() { - $location.search('create', null); - $location.search('notemplate', null); - $location.path("/settings/documenttypes/edit/" + node.id).search("create", "true"); - navigationService.hideMenu(); - }; - - $scope.createComponent = function() { - $location.search('create', null); - $location.search('notemplate', null); - $location.path("/settings/documenttypes/edit/" + node.id).search("create", "true").search("notemplate", "true"); - navigationService.hideMenu(); - }; -} - -angular.module('umbraco').controller("Umbraco.Editors.DocumentTypes.CreateController", DocumentTypesCreateController); - -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.DeleteController - * @function - * - * @description - * The controller for deleting content - */ -function DocumentTypesDeleteController($scope, dataTypeResource, contentTypeResource, treeService, navigationService) { - - $scope.performDelete = function() { - - //mark it for deletion (used in the UI) - $scope.currentNode.loading = true; - contentTypeResource.deleteById($scope.currentNode.id).then(function () { - $scope.currentNode.loading = false; - - //get the root node before we remove it - var rootNode = treeService.getTreeRoot($scope.currentNode); - - //TODO: Need to sync tree, etc... - treeService.removeNode($scope.currentNode); - navigationService.hideMenu(); - }); - - }; - - $scope.performContainerDelete = function() { - - //mark it for deletion (used in the UI) - $scope.currentNode.loading = true; - contentTypeResource.deleteContainerById($scope.currentNode.id).then(function () { - $scope.currentNode.loading = false; - - //get the root node before we remove it - var rootNode = treeService.getTreeRoot($scope.currentNode); - - //TODO: Need to sync tree, etc... - treeService.removeNode($scope.currentNode); - navigationService.hideMenu(); - }); - - }; - - $scope.cancel = function() { - navigationService.hideDialog(); - }; -} - -angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.DeleteController", DocumentTypesDeleteController); +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.DeleteController + * @function + * + * @description + * The controller for deleting content + */ +function DocumentTypesDeleteController($scope, dataTypeResource, contentTypeResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + contentTypeResource.deleteById($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + navigationService.hideMenu(); + }); + + }; + + $scope.performContainerDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + contentTypeResource.deleteContainerById($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + navigationService.hideMenu(); + }); + + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.DeleteController", DocumentTypesDeleteController); /** * @ngdoc controller @@ -6510,202 +6510,202 @@ angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.DeleteContro angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.EditController", DocumentTypesEditController); })(); -angular.module("umbraco") -.controller("Umbraco.Editors.DocumentTypes.MoveController", - function ($scope, contentTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { - var dialogOptions = $scope.dialogOptions; - $scope.dialogTreeEventHandler = $({}); +angular.module("umbraco") +.controller("Umbraco.Editors.DocumentTypes.MoveController", + function ($scope, contentTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { + var dialogOptions = $scope.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + $scope.move = function () { + + $scope.busy = true; + $scope.error = false; + + contentTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //first we need to remove the node that launched the dialog + treeService.removeNode($scope.currentNode); + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the moved content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was moved!!) + + navigationService.syncTree({ tree: "documentTypes", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "documentTypes", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + eventsService.emit('app.refreshEditor'); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + }); + }); - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.PropertyController + * @function + * + * @description + * The controller for the content type editor property dialog + */ +(function() { + 'use strict'; + + function PermissionsController($scope, contentTypeResource, iconHelper, contentTypeHelper, localizationService) { + + /* ----------- SCOPE VARIABLES ----------- */ + + var vm = this; + var childNodeSelectorOverlayTitle = ""; + + vm.contentTypes = []; + vm.selectedChildren = []; + + vm.overlayTitle = ""; + + vm.addChild = addChild; + vm.removeChild = removeChild; + + /* ---------- INIT ---------- */ + + init(); + + function init() { + + childNodeSelectorOverlayTitle = localizationService.localize("contentTypeEditor_chooseChildNode"); + + contentTypeResource.getAll().then(function(contentTypes){ + + vm.contentTypes = contentTypes; + + // convert legacy icons + iconHelper.formatContentTypeIcons(vm.contentTypes); + + vm.selectedChildren = contentTypeHelper.makeObjectArrayFromId($scope.model.allowedContentTypes, vm.contentTypes); + + if($scope.model.id === 0) { + contentTypeHelper.insertChildNodePlaceholder(vm.contentTypes, $scope.model.name, $scope.model.icon, $scope.model.id); + } + + }); + + } + + function addChild($event) { + vm.childNodeSelectorOverlay = { + view: "itempicker", + title: childNodeSelectorOverlayTitle, + availableItems: vm.contentTypes, + selectedItems: vm.selectedChildren, + event: $event, + show: true, + submit: function(model) { + vm.selectedChildren.push(model.selectedItem); + $scope.model.allowedContentTypes.push(model.selectedItem.id); + vm.childNodeSelectorOverlay.show = false; + vm.childNodeSelectorOverlay = null; + } + }; + } + + function removeChild(selectedChild, index) { + // remove from vm + vm.selectedChildren.splice(index, 1); + + // remove from content type model + var selectedChildIndex = $scope.model.allowedContentTypes.indexOf(selectedChild.id); + $scope.model.allowedContentTypes.splice(selectedChildIndex, 1); + } + + } + + angular.module("umbraco").controller("Umbraco.Editors.DocumentType.PermissionsController", PermissionsController); +})(); - if ($scope.target) { - //un-select if there's a current one selected - $scope.target.selected = false; - } - - $scope.target = args.node; - $scope.target.selected = true; - } - - $scope.move = function () { - - $scope.busy = true; - $scope.error = false; - - contentTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) - .then(function (path) { - $scope.error = false; - $scope.success = true; - $scope.busy = false; - - //first we need to remove the node that launched the dialog - treeService.removeNode($scope.currentNode); - - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - - //we need to do a double sync here: first sync to the moved content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was moved!!) - - navigationService.syncTree({ tree: "documentTypes", path: path, forceReload: true, activate: false }).then(function (args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "documentTypes", path: activeNodePath, forceReload: false, activate: true }); - } - }); - - eventsService.emit('app.refreshEditor'); - - }, function (err) { - $scope.success = false; - $scope.error = err; - $scope.busy = false; - //show any notifications - if (angular.isArray(err.data.notifications)) { - for (var i = 0; i < err.data.notifications.length; i++) { - notificationsService.showNotification(err.data.notifications[i]); - } - } - }); - }; - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - }); - }); - -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.PropertyController - * @function - * - * @description - * The controller for the content type editor property dialog - */ -(function() { - 'use strict'; - - function PermissionsController($scope, contentTypeResource, iconHelper, contentTypeHelper, localizationService) { - - /* ----------- SCOPE VARIABLES ----------- */ - - var vm = this; - var childNodeSelectorOverlayTitle = ""; - - vm.contentTypes = []; - vm.selectedChildren = []; - - vm.overlayTitle = ""; - - vm.addChild = addChild; - vm.removeChild = removeChild; - - /* ---------- INIT ---------- */ - - init(); - - function init() { - - childNodeSelectorOverlayTitle = localizationService.localize("contentTypeEditor_chooseChildNode"); - - contentTypeResource.getAll().then(function(contentTypes){ - - vm.contentTypes = contentTypes; - - // convert legacy icons - iconHelper.formatContentTypeIcons(vm.contentTypes); - - vm.selectedChildren = contentTypeHelper.makeObjectArrayFromId($scope.model.allowedContentTypes, vm.contentTypes); - - if($scope.model.id === 0) { - contentTypeHelper.insertChildNodePlaceholder(vm.contentTypes, $scope.model.name, $scope.model.icon, $scope.model.id); - } - - }); - - } - - function addChild($event) { - vm.childNodeSelectorOverlay = { - view: "itempicker", - title: childNodeSelectorOverlayTitle, - availableItems: vm.contentTypes, - selectedItems: vm.selectedChildren, - event: $event, - show: true, - submit: function(model) { - vm.selectedChildren.push(model.selectedItem); - $scope.model.allowedContentTypes.push(model.selectedItem.id); - vm.childNodeSelectorOverlay.show = false; - vm.childNodeSelectorOverlay = null; - } - }; - } - - function removeChild(selectedChild, index) { - // remove from vm - vm.selectedChildren.splice(index, 1); - - // remove from content type model - var selectedChildIndex = $scope.model.allowedContentTypes.indexOf(selectedChild.id); - $scope.model.allowedContentTypes.splice(selectedChildIndex, 1); - } - - } - - angular.module("umbraco").controller("Umbraco.Editors.DocumentType.PermissionsController", PermissionsController); -})(); - -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.TemplatesController - * @function - * - * @description - * The controller for the content type editor templates sub view - */ -(function() { - 'use strict'; - - function TemplatesController($scope, entityResource, contentTypeHelper, $routeParams) { - - /* ----------- SCOPE VARIABLES ----------- */ - - var vm = this; - - vm.availableTemplates = []; - vm.updateTemplatePlaceholder = false; - - - /* ---------- INIT ---------- */ - - init(); - - function init() { - - entityResource.getAll("Template").then(function(templates){ - - vm.availableTemplates = templates; - - // update placeholder template information on new doc types - if (!$routeParams.notemplate && $scope.model.id === 0) { - vm.updateTemplatePlaceholder = true; - vm.availableTemplates = contentTypeHelper.insertTemplatePlaceholder(vm.availableTemplates); - } - - }); - - } - - } - - angular.module("umbraco").controller("Umbraco.Editors.DocumentType.TemplatesController", TemplatesController); -})(); +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.TemplatesController + * @function + * + * @description + * The controller for the content type editor templates sub view + */ +(function() { + 'use strict'; + + function TemplatesController($scope, entityResource, contentTypeHelper, $routeParams) { + + /* ----------- SCOPE VARIABLES ----------- */ + + var vm = this; + + vm.availableTemplates = []; + vm.updateTemplatePlaceholder = false; + + + /* ---------- INIT ---------- */ + + init(); + + function init() { + + entityResource.getAll("Template").then(function(templates){ + + vm.availableTemplates = templates; + + // update placeholder template information on new doc types + if (!$routeParams.notemplate && $scope.model.id === 0) { + vm.updateTemplatePlaceholder = true; + vm.availableTemplates = contentTypeHelper.insertTemplatePlaceholder(vm.availableTemplates); + } + + }); + + } + + } + + angular.module("umbraco").controller("Umbraco.Editors.DocumentType.TemplatesController", TemplatesController); +})(); /** * @ngdoc controller @@ -6724,73 +6724,73 @@ function mediaCreateController($scope, $routeParams, mediaTypeResource, iconHelp } angular.module('umbraco').controller("Umbraco.Editors.Media.CreateController", mediaCreateController); -/** - * @ngdoc controller - * @name Umbraco.Editors.ContentDeleteController - * @function - * - * @description - * The controller for deleting content - */ -function MediaDeleteController($scope, mediaResource, treeService, navigationService, editorState, $location, dialogService, notificationsService) { - - $scope.performDelete = function() { - - //mark it for deletion (used in the UI) - $scope.currentNode.loading = true; - - mediaResource.deleteById($scope.currentNode.id).then(function () { - $scope.currentNode.loading = false; - - //get the root node before we remove it - var rootNode = treeService.getTreeRoot($scope.currentNode); - - treeService.removeNode($scope.currentNode); - - if (rootNode) { - //ensure the recycle bin has child nodes now - var recycleBin = treeService.getDescendantNode(rootNode, -21); - if (recycleBin) { - recycleBin.hasChildren = true; - } - } - - //if the current edited item is the same one as we're deleting, we need to navigate elsewhere - if (editorState.current && editorState.current.id == $scope.currentNode.id) { - - //If the deleted item lived at the root then just redirect back to the root, otherwise redirect to the item's parent - var location = "/media"; - if ($scope.currentNode.parentId.toString() !== "-1") - location = "/media/media/edit/" + $scope.currentNode.parentId; - - $location.path(location); - } - - navigationService.hideMenu(); - - }, function (err) { - - $scope.currentNode.loading = false; - - //check if response is ysod - if (err.status && err.status >= 500) { - dialogService.ysodDialog(err); - } - - if (err.data && angular.isArray(err.data.notifications)) { - for (var i = 0; i < err.data.notifications.length; i++) { - notificationsService.showNotification(err.data.notifications[i]); - } - } - }); - }; - - $scope.cancel = function() { - navigationService.hideDialog(); - }; -} - -angular.module("umbraco").controller("Umbraco.Editors.Media.DeleteController", MediaDeleteController); +/** + * @ngdoc controller + * @name Umbraco.Editors.ContentDeleteController + * @function + * + * @description + * The controller for deleting content + */ +function MediaDeleteController($scope, mediaResource, treeService, navigationService, editorState, $location, dialogService, notificationsService) { + + $scope.performDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + + mediaResource.deleteById($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + + treeService.removeNode($scope.currentNode); + + if (rootNode) { + //ensure the recycle bin has child nodes now + var recycleBin = treeService.getDescendantNode(rootNode, -21); + if (recycleBin) { + recycleBin.hasChildren = true; + } + } + + //if the current edited item is the same one as we're deleting, we need to navigate elsewhere + if (editorState.current && editorState.current.id == $scope.currentNode.id) { + + //If the deleted item lived at the root then just redirect back to the root, otherwise redirect to the item's parent + var location = "/media"; + if ($scope.currentNode.parentId.toString() !== "-1") + location = "/media/media/edit/" + $scope.currentNode.parentId; + + $location.path(location); + } + + navigationService.hideMenu(); + + }, function (err) { + + $scope.currentNode.loading = false; + + //check if response is ysod + if (err.status && err.status >= 500) { + dialogService.ysodDialog(err); + } + + if (err.data && angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.Media.DeleteController", MediaDeleteController); /** * @ngdoc controller @@ -6944,318 +6944,318 @@ function mediaEditController($scope, $routeParams, appState, mediaResource, enti angular.module("umbraco") .controller("Umbraco.Editors.Media.EditController", mediaEditController); -/** - * @ngdoc controller - * @name Umbraco.Editors.Media.EmptyRecycleBinController - * @function - * - * @description - * The controller for deleting media - */ -function MediaEmptyRecycleBinController($scope, mediaResource, treeService, navigationService) { +/** + * @ngdoc controller + * @name Umbraco.Editors.Media.EmptyRecycleBinController + * @function + * + * @description + * The controller for deleting media + */ +function MediaEmptyRecycleBinController($scope, mediaResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //(used in the UI) + $scope.currentNode.loading = true; + + mediaResource.emptyRecycleBin($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + //TODO: Need to sync tree, etc... + treeService.removeChildNodes($scope.currentNode); + navigationService.hideMenu(); + }); + + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.Media.EmptyRecycleBinController", MediaEmptyRecycleBinController); - $scope.performDelete = function() { - - //(used in the UI) - $scope.currentNode.loading = true; - - mediaResource.emptyRecycleBin($scope.currentNode.id).then(function () { - $scope.currentNode.loading = false; - //TODO: Need to sync tree, etc... - treeService.removeChildNodes($scope.currentNode); - navigationService.hideMenu(); - }); - - }; - - $scope.cancel = function() { - navigationService.hideDialog(); - }; -} - -angular.module("umbraco").controller("Umbraco.Editors.Media.EmptyRecycleBinController", MediaEmptyRecycleBinController); - -//used for the media picker dialog -angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController", - function ($scope, eventsService, mediaResource, appState, treeService, navigationService) { - var dialogOptions = $scope.dialogOptions; - - $scope.dialogTreeEventHandler = $({}); - var node = dialogOptions.currentNode; - - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - eventsService.emit("editors.media.moveController.select", args); - - if ($scope.target) { - //un-select if there's a current one selected - $scope.target.selected = false; - } - - $scope.target = args.node; - $scope.target.selected = true; - } - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - - - $scope.move = function () { - mediaResource.move({ parentId: $scope.target.id, id: node.id }) - .then(function (path) { - $scope.error = false; - $scope.success = true; - - //first we need to remove the node that launched the dialog - treeService.removeNode($scope.currentNode); - - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - - //we need to do a double sync here: first sync to the moved content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was moved!!) - - navigationService.syncTree({ tree: "media", path: path, forceReload: true, activate: false }).then(function (args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "media", path: activeNodePath, forceReload: false, activate: true }); - } - }); - - }, function (err) { - $scope.success = false; - $scope.error = err; - }); - }; - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - }); +//used for the media picker dialog +angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController", + function ($scope, eventsService, mediaResource, appState, treeService, navigationService) { + var dialogOptions = $scope.dialogOptions; + + $scope.dialogTreeEventHandler = $({}); + var node = dialogOptions.currentNode; + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + eventsService.emit("editors.media.moveController.select", args); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + + + $scope.move = function () { + mediaResource.move({ parentId: $scope.target.id, id: node.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + + //first we need to remove the node that launched the dialog + treeService.removeNode($scope.currentNode); + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the moved content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was moved!!) + + navigationService.syncTree({ tree: "media", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "media", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + }); + }; + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + }); }); -/** - * @ngdoc controller - * @name Umbraco.Editors.Content.MediaRecycleBinController - * @function - * - * @description - * Controls the recycle bin for media - * - */ +/** + * @ngdoc controller + * @name Umbraco.Editors.Content.MediaRecycleBinController + * @function + * + * @description + * Controls the recycle bin for media + * + */ + +function MediaRecycleBinController($scope, $routeParams, mediaResource, navigationService, localizationService) { + + //ensures the list view doesn't actually load until we query for the list view config + // for the section + $scope.page = {}; + $scope.page.name = "Recycle Bin"; + $scope.page.nameLocked = true; + + //ensures the list view doesn't actually load until we query for the list view config + // for the section + $scope.listViewPath = null; + + $routeParams.id = "-21"; + mediaResource.getRecycleBin().then(function (result) { + //we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a + // single property, so we'll extract that property (list view) and use it's data. + var listproperty = result.tabs[0].properties[0]; + + _.each(listproperty.config, function (val, key) { + $scope.model.config[key] = val; + }); + $scope.listViewPath = 'views/propertyeditors/listview/listview.html'; + }); + + $scope.model = { config: { entityType: $routeParams.section, layouts: [] } }; + + // sync tree node + navigationService.syncTree({ tree: "media", path: ["-1", $routeParams.id], forceReload: false }); + + localizePageName(); + + function localizePageName() { + + var pageName = "general_recycleBin"; + + localizationService.localize(pageName).then(function (value) { + $scope.page.name = value; + }); + + } +} + +angular.module('umbraco').controller("Umbraco.Editors.Media.RecycleBinController", MediaRecycleBinController); -function MediaRecycleBinController($scope, $routeParams, mediaResource, navigationService, localizationService) { +angular.module("umbraco") +.controller("Umbraco.Editors.MediaTypes.CopyController", + function ($scope, mediaTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { + var dialogOptions = $scope.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + $scope.copy = function () { + + $scope.busy = true; + $scope.error = false; + + mediaTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the copied content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was copied!!) + + navigationService.syncTree({ tree: "mediaTypes", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "mediaTypes", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + }); + }); - //ensures the list view doesn't actually load until we query for the list view config - // for the section - $scope.page = {}; - $scope.page.name = "Recycle Bin"; - $scope.page.nameLocked = true; +/** + * @ngdoc controller + * @name Umbraco.Editors.MediaType.CreateController + * @function + * + * @description + * The controller for the media type creation dialog + */ +function MediaTypesCreateController($scope, $location, navigationService, mediaTypeResource, formHelper, appState, localizationService) { + + $scope.model = { + folderName: "", + creatingFolder: false + }; + + var node = $scope.dialogOptions.currentNode, + localizeCreateFolder = localizationService.localize("defaultdialog_createFolder"); + + $scope.showCreateFolder = function() { + $scope.model.creatingFolder = true; + } + + $scope.createContainer = function () { + if (formHelper.submitForm({ + scope: $scope, + formCtrl: this.createFolderForm, + statusMessage: localizeCreateFolder + })) { + mediaTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) { + + navigationService.hideMenu(); + var currPath = node.path ? node.path : "-1"; + navigationService.syncTree({ tree: "mediatypes", path: currPath + "," + folderId, forceReload: true, activate: true }); + + formHelper.resetForm({ scope: $scope }); + + var section = appState.getSectionState("currentSection"); + + }, function(err) { + + //TODO: Handle errors + }); + }; + } + + $scope.createMediaType = function() { + $location.search('create', null); + $location.path("/settings/mediatypes/edit/" + node.id).search("create", "true"); + navigationService.hideMenu(); + } +} + +angular.module('umbraco').controller("Umbraco.Editors.MediaTypes.CreateController", MediaTypesCreateController); - //ensures the list view doesn't actually load until we query for the list view config - // for the section - $scope.listViewPath = null; - - $routeParams.id = "-21"; - mediaResource.getRecycleBin().then(function (result) { - //we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a - // single property, so we'll extract that property (list view) and use it's data. - var listproperty = result.tabs[0].properties[0]; - - _.each(listproperty.config, function (val, key) { - $scope.model.config[key] = val; - }); - $scope.listViewPath = 'views/propertyeditors/listview/listview.html'; - }); - - $scope.model = { config: { entityType: $routeParams.section, layouts: [] } }; - - // sync tree node - navigationService.syncTree({ tree: "media", path: ["-1", $routeParams.id], forceReload: false }); - - localizePageName(); - - function localizePageName() { - - var pageName = "general_recycleBin"; - - localizationService.localize(pageName).then(function (value) { - $scope.page.name = value; - }); - - } -} - -angular.module('umbraco').controller("Umbraco.Editors.Media.RecycleBinController", MediaRecycleBinController); - -angular.module("umbraco") -.controller("Umbraco.Editors.MediaTypes.CopyController", - function ($scope, mediaTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { - var dialogOptions = $scope.dialogOptions; - $scope.dialogTreeEventHandler = $({}); - - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - if ($scope.target) { - //un-select if there's a current one selected - $scope.target.selected = false; - } - - $scope.target = args.node; - $scope.target.selected = true; - } - - $scope.copy = function () { - - $scope.busy = true; - $scope.error = false; - - mediaTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) - .then(function (path) { - $scope.error = false; - $scope.success = true; - $scope.busy = false; - - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - - //we need to do a double sync here: first sync to the copied content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was copied!!) - - navigationService.syncTree({ tree: "mediaTypes", path: path, forceReload: true, activate: false }).then(function (args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "mediaTypes", path: activeNodePath, forceReload: false, activate: true }); - } - }); - - }, function (err) { - $scope.success = false; - $scope.error = err; - $scope.busy = false; - //show any notifications - if (angular.isArray(err.data.notifications)) { - for (var i = 0; i < err.data.notifications.length; i++) { - notificationsService.showNotification(err.data.notifications[i]); - } - } - }); - }; - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - }); - }); - -/** - * @ngdoc controller - * @name Umbraco.Editors.MediaType.CreateController - * @function - * - * @description - * The controller for the media type creation dialog - */ -function MediaTypesCreateController($scope, $location, navigationService, mediaTypeResource, formHelper, appState, localizationService) { - - $scope.model = { - folderName: "", - creatingFolder: false - }; - - var node = $scope.dialogOptions.currentNode, - localizeCreateFolder = localizationService.localize("defaultdialog_createFolder"); - - $scope.showCreateFolder = function() { - $scope.model.creatingFolder = true; - } - - $scope.createContainer = function () { - if (formHelper.submitForm({ - scope: $scope, - formCtrl: this.createFolderForm, - statusMessage: localizeCreateFolder - })) { - mediaTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) { - - navigationService.hideMenu(); - var currPath = node.path ? node.path : "-1"; - navigationService.syncTree({ tree: "mediatypes", path: currPath + "," + folderId, forceReload: true, activate: true }); - - formHelper.resetForm({ scope: $scope }); - - var section = appState.getSectionState("currentSection"); - - }, function(err) { - - //TODO: Handle errors - }); - }; - } - - $scope.createMediaType = function() { - $location.search('create', null); - $location.path("/settings/mediatypes/edit/" + node.id).search("create", "true"); - navigationService.hideMenu(); - } -} - -angular.module('umbraco').controller("Umbraco.Editors.MediaTypes.CreateController", MediaTypesCreateController); - -/** - * @ngdoc controller - * @name Umbraco.Editors.MediaType.DeleteController - * @function - * - * @description - * The controller for the media type delete dialog - */ -function MediaTypesDeleteController($scope, dataTypeResource, mediaTypeResource, treeService, navigationService) { - - $scope.performDelete = function() { - - //mark it for deletion (used in the UI) - $scope.currentNode.loading = true; - mediaTypeResource.deleteById($scope.currentNode.id).then(function () { - $scope.currentNode.loading = false; - - //get the root node before we remove it - var rootNode = treeService.getTreeRoot($scope.currentNode); - - //TODO: Need to sync tree, etc... - treeService.removeNode($scope.currentNode); - navigationService.hideMenu(); - }); - - }; - - $scope.performContainerDelete = function() { - - //mark it for deletion (used in the UI) - $scope.currentNode.loading = true; - mediaTypeResource.deleteContainerById($scope.currentNode.id).then(function () { - $scope.currentNode.loading = false; - - //get the root node before we remove it - var rootNode = treeService.getTreeRoot($scope.currentNode); - - //TODO: Need to sync tree, etc... - treeService.removeNode($scope.currentNode); - navigationService.hideMenu(); - }); - - }; - - $scope.cancel = function() { - navigationService.hideDialog(); - }; -} - -angular.module("umbraco").controller("Umbraco.Editors.MediaTypes.DeleteController", MediaTypesDeleteController); +/** + * @ngdoc controller + * @name Umbraco.Editors.MediaType.DeleteController + * @function + * + * @description + * The controller for the media type delete dialog + */ +function MediaTypesDeleteController($scope, dataTypeResource, mediaTypeResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + mediaTypeResource.deleteById($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + navigationService.hideMenu(); + }); + + }; + + $scope.performContainerDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + mediaTypeResource.deleteContainerById($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + navigationService.hideMenu(); + }); + + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.MediaTypes.DeleteController", MediaTypesDeleteController); /** * @ngdoc controller @@ -7606,147 +7606,147 @@ angular.module("umbraco").controller("Umbraco.Editors.MediaTypes.DeleteControlle angular.module("umbraco").controller("Umbraco.Editors.MediaTypes.EditController", MediaTypesEditController); })(); -angular.module("umbraco") -.controller("Umbraco.Editors.MediaTypes.MoveController", - function ($scope, mediaTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { +angular.module("umbraco") +.controller("Umbraco.Editors.MediaTypes.MoveController", + function ($scope, mediaTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { + + var dialogOptions = $scope.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + $scope.move = function () { + + $scope.busy = true; + $scope.error = false; + + mediaTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //first we need to remove the node that launched the dialog + treeService.removeNode($scope.currentNode); + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the moved content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was moved!!) + + navigationService.syncTree({ tree: "mediaTypes", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "mediaTypes", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + eventsService.emit('app.refreshEditor'); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + }); + }); - var dialogOptions = $scope.dialogOptions; - $scope.dialogTreeEventHandler = $({}); - - function nodeSelectHandler(ev, args) { - args.event.preventDefault(); - args.event.stopPropagation(); - - if ($scope.target) { - //un-select if there's a current one selected - $scope.target.selected = false; - } - - $scope.target = args.node; - $scope.target.selected = true; - } - - $scope.move = function () { - - $scope.busy = true; - $scope.error = false; - - mediaTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) - .then(function (path) { - $scope.error = false; - $scope.success = true; - $scope.busy = false; - - //first we need to remove the node that launched the dialog - treeService.removeNode($scope.currentNode); - - //get the currently edited node (if any) - var activeNode = appState.getTreeState("selectedNode"); - - //we need to do a double sync here: first sync to the moved content - but don't activate the node, - //then sync to the currenlty edited content (note: this might not be the content that was moved!!) - - navigationService.syncTree({ tree: "mediaTypes", path: path, forceReload: true, activate: false }).then(function (args) { - if (activeNode) { - var activeNodePath = treeService.getPath(activeNode).join(); - //sync to this node now - depending on what was copied this might already be synced but might not be - navigationService.syncTree({ tree: "mediaTypes", path: activeNodePath, forceReload: false, activate: true }); - } - }); - - eventsService.emit('app.refreshEditor'); - - }, function (err) { - $scope.success = false; - $scope.error = err; - $scope.busy = false; - //show any notifications - if (angular.isArray(err.data.notifications)) { - for (var i = 0; i < err.data.notifications.length; i++) { - notificationsService.showNotification(err.data.notifications[i]); - } - } - }); - }; - - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - - $scope.$on('$destroy', function () { - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - }); - }); - -(function() { - 'use strict'; - - function PermissionsController($scope, mediaTypeResource, iconHelper, contentTypeHelper, localizationService) { - - /* ----------- SCOPE VARIABLES ----------- */ - - var vm = this; - var childNodeSelectorOverlayTitle = ""; - - vm.mediaTypes = []; - vm.selectedChildren = []; - - vm.addChild = addChild; - vm.removeChild = removeChild; - - /* ---------- INIT ---------- */ - - init(); - - function init() { - - childNodeSelectorOverlayTitle = localizationService.localize("contentTypeEditor_chooseChildNode"); - - mediaTypeResource.getAll().then(function(mediaTypes){ - - vm.mediaTypes = mediaTypes; - - // convert legacy icons - iconHelper.formatContentTypeIcons(vm.mediaTypes); - - vm.selectedChildren = contentTypeHelper.makeObjectArrayFromId($scope.model.allowedContentTypes, vm.mediaTypes); - - if($scope.model.id === 0) { - contentTypeHelper.insertChildNodePlaceholder(vm.mediaTypes, $scope.model.name, $scope.model.icon, $scope.model.id); - } - - }); - - } - - function addChild($event) { - vm.childNodeSelectorOverlay = { - view: "itempicker", - title: childNodeSelectorOverlayTitle, - availableItems: vm.mediaTypes, - selectedItems: vm.selectedChildren, - event: $event, - show: true, - submit: function(model) { - vm.selectedChildren.push(model.selectedItem); - $scope.model.allowedContentTypes.push(model.selectedItem.id); - vm.childNodeSelectorOverlay.show = false; - vm.childNodeSelectorOverlay = null; - } - }; - } - - function removeChild(selectedChild, index) { - // remove from vm - vm.selectedChildren.splice(index, 1); - - // remove from content type model - var selectedChildIndex = $scope.model.allowedContentTypes.indexOf(selectedChild.id); - $scope.model.allowedContentTypes.splice(selectedChildIndex, 1); - } - - } - - angular.module("umbraco").controller("Umbraco.Editors.MediaType.PermissionsController", PermissionsController); -})(); +(function() { + 'use strict'; + + function PermissionsController($scope, mediaTypeResource, iconHelper, contentTypeHelper, localizationService) { + + /* ----------- SCOPE VARIABLES ----------- */ + + var vm = this; + var childNodeSelectorOverlayTitle = ""; + + vm.mediaTypes = []; + vm.selectedChildren = []; + + vm.addChild = addChild; + vm.removeChild = removeChild; + + /* ---------- INIT ---------- */ + + init(); + + function init() { + + childNodeSelectorOverlayTitle = localizationService.localize("contentTypeEditor_chooseChildNode"); + + mediaTypeResource.getAll().then(function(mediaTypes){ + + vm.mediaTypes = mediaTypes; + + // convert legacy icons + iconHelper.formatContentTypeIcons(vm.mediaTypes); + + vm.selectedChildren = contentTypeHelper.makeObjectArrayFromId($scope.model.allowedContentTypes, vm.mediaTypes); + + if($scope.model.id === 0) { + contentTypeHelper.insertChildNodePlaceholder(vm.mediaTypes, $scope.model.name, $scope.model.icon, $scope.model.id); + } + + }); + + } + + function addChild($event) { + vm.childNodeSelectorOverlay = { + view: "itempicker", + title: childNodeSelectorOverlayTitle, + availableItems: vm.mediaTypes, + selectedItems: vm.selectedChildren, + event: $event, + show: true, + submit: function(model) { + vm.selectedChildren.push(model.selectedItem); + $scope.model.allowedContentTypes.push(model.selectedItem.id); + vm.childNodeSelectorOverlay.show = false; + vm.childNodeSelectorOverlay = null; + } + }; + } + + function removeChild(selectedChild, index) { + // remove from vm + vm.selectedChildren.splice(index, 1); + + // remove from content type model + var selectedChildIndex = $scope.model.allowedContentTypes.indexOf(selectedChild.id); + $scope.model.allowedContentTypes.splice(selectedChildIndex, 1); + } + + } + + angular.module("umbraco").controller("Umbraco.Editors.MediaType.PermissionsController", PermissionsController); +})(); /** * @ngdoc controller @@ -7979,558 +7979,558 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS angular.module("umbraco").controller("Umbraco.Editors.Member.EditController", MemberEditController); -/** - * @ngdoc controller - * @name Umbraco.Editors.Member.ListController - * @function - * - * @description - * The controller for the member list view - */ -function MemberListController($scope, $routeParams, $location, $q, $window, appState, memberResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, fileManager, formHelper, umbModelMapper, editorState, localizationService) { - - //setup scope vars - $scope.currentSection = appState.getSectionState("currentSection"); - $scope.currentNode = null; //the editors affiliated node - - $scope.page = {}; - $scope.page.lockedName = true; - $scope.page.loading = true; - - //we are editing so get the content item from the server - memberResource.getListNode($routeParams.id) - .then(function (data) { - - $scope.content = data; - - //translate "All Members" - if ($scope.content != null && $scope.content.name != null && $scope.content.name.replace(" ", "").toLowerCase() == "allmembers") { - localizationService.localize("member_allMembers").then(function (value) { - $scope.content.name = value; - }); - } - - editorState.set($scope.content); - - navigationService.syncTree({ tree: "member", path: data.path.split(",") }).then(function (syncArgs) { - $scope.currentNode = syncArgs.node; - }); - - //in one particular special case, after we've created a new item we redirect back to the edit - // route but there might be server validation errors in the collection which we need to display - // after the redirect, so we will bind all subscriptions which will show the server validation errors - // if there are any and then clear them so the collection no longer persists them. - serverValidationManager.executeAndClearAllSubscriptions(); - - $scope.page.loading = false; - - }); -} - -angular.module("umbraco").controller("Umbraco.Editors.Member.ListController", MemberListController); - -/** - * @ngdoc controller - * @name Umbraco.Editors.MemberType.CreateController - * @function - * - * @description - * The controller for the member type creation dialog - */ -function MemberTypesCreateController($scope, $location, navigationService, memberTypeResource, formHelper, appState, localizationService) { - - $scope.model = { - folderName: "", - creatingFolder: false - }; - - var node = $scope.dialogOptions.currentNode, - localizeCreateFolder = localizationService.localize("defaultdialog_createFolder"); - - - $scope.showCreateFolder = function() { - $scope.model.creatingFolder = true; - } - - $scope.createContainer = function () { - if (formHelper.submitForm({ - scope: $scope, - formCtrl: this.createFolderForm, - statusMessage: localizeCreateFolder - })) { - memberTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) { - - navigationService.hideMenu(); - var currPath = node.path ? node.path : "-1"; - navigationService.syncTree({ tree: "membertypes", path: currPath + "," + folderId, forceReload: true, activate: true }); - - formHelper.resetForm({ scope: $scope }); - - var section = appState.getSectionState("currentSection"); - - }, function(err) { - - //TODO: Handle errors - }); - }; - } - - $scope.createMemberType = function() { - $location.search('create', null); - $location.path("/settings/membertypes/edit/" + node.id).search("create", "true"); - navigationService.hideMenu(); - } -} - -angular.module('umbraco').controller("Umbraco.Editors.MemberTypes.CreateController", MemberTypesCreateController); - -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.DeleteController - * @function - * - * @description - * The controller for deleting content - */ -function MemberTypesDeleteController($scope, memberTypeResource, treeService, navigationService) { - - $scope.performDelete = function() { - - //mark it for deletion (used in the UI) - $scope.currentNode.loading = true; - memberTypeResource.deleteById($scope.currentNode.id).then(function () { - $scope.currentNode.loading = false; - - //get the root node before we remove it - var rootNode = treeService.getTreeRoot($scope.currentNode); - - //TODO: Need to sync tree, etc... - treeService.removeNode($scope.currentNode); - navigationService.hideMenu(); - }); - - }; - - $scope.cancel = function() { - navigationService.hideDialog(); - }; -} - -angular.module("umbraco").controller("Umbraco.Editors.MemberTypes.DeleteController", MemberTypesDeleteController); - -/** - * @ngdoc controller - * @name Umbraco.Editors.MemberType.EditController - * @function - * - * @description - * The controller for the member type editor - */ -(function () { - "use strict"; - - function MemberTypesEditController($scope, $rootScope, $routeParams, $log, $filter, memberTypeResource, dataTypeResource, editorState, iconHelper, formHelper, navigationService, contentEditingHelper, notificationsService, $q, localizationService, overlayHelper, contentTypeHelper) { - - var vm = this; - var localizeSaving = localizationService.localize("general_saving"); - - vm.save = save; - - vm.currentNode = null; - vm.contentType = {}; - vm.page = {}; - vm.page.loading = false; - vm.page.saveButtonState = "init"; - vm.page.navigation = [ - { - "name": localizationService.localize("general_design"), - "icon": "icon-document-dashed-line", - "view": "views/membertypes/views/design/design.html", - "active": true - } - ]; - - vm.page.keyboardShortcutsOverview = [ - { - "name": localizationService.localize("shortcuts_shortcut"), - "shortcuts": [ - { - "description": localizationService.localize("shortcuts_addTab"), - "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }] - }, - { - "description": localizationService.localize("shortcuts_addProperty"), - "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }] - }, - { - "description": localizationService.localize("shortcuts_addEditor"), - "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }] - }, - { - "description": localizationService.localize("shortcuts_editDataType"), - "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }] - } - ] - } - ]; - - contentTypeHelper.checkModelsBuilderStatus().then(function (result) { - vm.page.modelsBuilder = result; - if (result) { - //Models builder mode: - vm.page.defaultButton = { - hotKey: "ctrl+s", - hotKeyWhenHidden: true, - labelKey: "buttons_save", - letter: "S", - type: "submit", - handler: function () { vm.save(); } - }; - vm.page.subButtons = [{ - hotKey: "ctrl+g", - hotKeyWhenHidden: true, - labelKey: "buttons_saveAndGenerateModels", - letter: "G", - handler: function () { - - vm.page.saveButtonState = "busy"; - - vm.save().then(function (result) { - - vm.page.saveButtonState = "busy"; - - localizationService.localize("modelsBuilder_buildingModels").then(function (headerValue) { - localizationService.localize("modelsBuilder_waitingMessage").then(function(msgValue) { - notificationsService.info(headerValue, msgValue); - }); - }); - - contentTypeHelper.generateModels().then(function (result) { - - if (result.success) { - - //re-check model status - contentTypeHelper.checkModelsBuilderStatus().then(function (statusResult) { - vm.page.modelsBuilder = statusResult; - }); - - //clear and add success - vm.page.saveButtonState = "init"; - localizationService.localize("modelsBuilder_modelsGenerated").then(function(value) { - notificationsService.success(value); - }); - - } else { - vm.page.saveButtonState = "error"; - localizationService.localize("modelsBuilder_modelsExceptionInUlog").then(function(value) { - notificationsService.error(value); - }); - } - - }, function () { - vm.page.saveButtonState = "error"; - localizationService.localize("modelsBuilder_modelsGeneratedError").then(function(value) { - notificationsService.error(value); - }); - }); - - - }); - - } - }]; - } - }); - - if ($routeParams.create) { - - vm.page.loading = true; - - //we are creating so get an empty data type item - memberTypeResource.getScaffold($routeParams.id) - .then(function (dt) { - init(dt); - - vm.page.loading = false; - }); - } - else { - - vm.page.loading = true; - - memberTypeResource.getById($routeParams.id).then(function (dt) { - init(dt); - - syncTreeNode(vm.contentType, dt.path, true); - - vm.page.loading = false; - }); - } - - function save() { - // only save if there is no overlays open - if(overlayHelper.getNumberOfOverlays() === 0) { - - var deferred = $q.defer(); - - vm.page.saveButtonState = "busy"; - - contentEditingHelper.contentEditorPerformSave({ - statusMessage: localizeSaving, - saveMethod: memberTypeResource.save, - scope: $scope, - content: vm.contentType, - //We do not redirect on failure for doc types - this is because it is not possible to actually save the doc - // type when server side validation fails - as opposed to content where we are capable of saving the content - // item if server side validation fails - redirectOnFailure: false, - // we need to rebind... the IDs that have been created! - rebindCallback: function (origContentType, savedContentType) { - vm.contentType.id = savedContentType.id; - vm.contentType.groups.forEach(function (group) { - if (!group.name) return; - - var k = 0; - while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name) - k++; - if (k == savedContentType.groups.length) { - group.id = 0; - return; - } - - var savedGroup = savedContentType.groups[k]; - if (!group.id) group.id = savedGroup.id; - - group.properties.forEach(function (property) { - if (property.id || !property.alias) return; - - k = 0; - while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias) - k++; - if (k == savedGroup.properties.length) { - property.id = 0; - return; - } - - var savedProperty = savedGroup.properties[k]; - property.id = savedProperty.id; - }); - }); - } - }).then(function (data) { - //success - syncTreeNode(vm.contentType, data.path); - - vm.page.saveButtonState = "success"; - - deferred.resolve(data); - }, function (err) { - //error - if (err) { - editorState.set($scope.content); - } - else { - localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { - localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) { - notificationsService.error(headerValue, msgValue); - }); - }); - } - - vm.page.saveButtonState = "error"; - - deferred.reject(err); - }); - - return deferred.promise; - } - - } - - function init(contentType) { - - // set all tab to inactive - if (contentType.groups.length !== 0) { - angular.forEach(contentType.groups, function (group) { - - angular.forEach(group.properties, function (property) { - // get data type details for each property - getDataTypeDetails(property); - }); - - }); - } - - // convert legacy icons - convertLegacyIcons(contentType); - - //set a shared state - editorState.set(contentType); - - vm.contentType = contentType; - - } - - function convertLegacyIcons(contentType) { - - // make array to store contentType icon - var contentTypeArray = []; - - // push icon to array - contentTypeArray.push({ "icon": contentType.icon }); - - // run through icon method - iconHelper.formatContentTypeIcons(contentTypeArray); - - // set icon back on contentType - contentType.icon = contentTypeArray[0].icon; - - } - - function getDataTypeDetails(property) { - - if (property.propertyState !== "init") { - - dataTypeResource.getById(property.dataTypeId) - .then(function (dataType) { - property.dataTypeIcon = dataType.icon; - property.dataTypeName = dataType.name; - }); - } - } - - /** Syncs the content type to it's tree node - this occurs on first load and after saving */ - function syncTreeNode(dt, path, initialLoad) { - - navigationService.syncTree({ tree: "membertypes", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) { - vm.currentNode = syncArgs.node; - }); - - } - - - } - - angular.module("umbraco").controller("Umbraco.Editors.MemberTypes.EditController", MemberTypesEditController); - -})(); - -angular.module("umbraco") -.controller("Umbraco.Editors.MemberTypes.MoveController", - function($scope){ - - }); - -function imageFilePickerController($scope) { - - $scope.pick = function() { - $scope.mediaPickerDialog = {}; - $scope.mediaPickerDialog.view = "mediapicker"; - $scope.mediaPickerDialog.show = true; - - $scope.mediaPickerDialog.submit = function(model) { - $scope.model.value = model.selectedImages[0].image; - $scope.mediaPickerDialog.show = false; - $scope.mediaPickerDialog = null; - }; - - $scope.mediaPickerDialog.close = function(oldModel) { - $scope.mediaPickerDialog.show = false; - $scope.mediaPickerDialog = null; - }; - }; - -} - -angular.module('umbraco').controller("Umbraco.PrevalueEditors.ImageFilePickerController", imageFilePickerController); - -//this controller simply tells the dialogs service to open a mediaPicker window -//with a specified callback, this callback will receive an object with a selection on it -function mediaPickerController($scope, dialogService, entityResource, $log, iconHelper) { - - function trim(str, chr) { - var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); - return str.replace(rgxtrim, ''); - } - - $scope.renderModel = []; - - var dialogOptions = { - multiPicker: false, - entityType: "Media", - section: "media", - treeAlias: "media" - }; - - $scope.openContentPicker = function() { - $scope.contentPickerOverlay = dialogOptions; - $scope.contentPickerOverlay.view = "treePicker"; - $scope.contentPickerOverlay.show = true; - - $scope.contentPickerOverlay.submit = function(model) { - - if ($scope.contentPickerOverlay.multiPicker) { - _.each(model.selection, function (item, i) { - $scope.add(item); - }); - } - else { - $scope.clear(); - $scope.add(model.selection[0]); - } - - $scope.contentPickerOverlay.show = false; - $scope.contentPickerOverlay = null; - }; - - $scope.contentPickerOverlay.close = function(oldModel) { - $scope.contentPickerOverlay.show = false; - $scope.contentPickerOverlay = null; - }; - } - - $scope.remove =function(index, event){ - event.preventDefault(); - $scope.renderModel.splice(index, 1); - }; - - $scope.clear = function() { - $scope.renderModel = []; - }; - - $scope.add = function (item) { - var currIds = _.map($scope.renderModel, function (i) { - return i.id; - }); - if (currIds.indexOf(item.id) < 0) { - item.icon = iconHelper.convertFromLegacyIcon(item.icon); - $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); - } - }; - - var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { - var currIds = _.map($scope.renderModel, function (i) { - return i.id; - }); - $scope.model.value = trim(currIds.join(), ","); - }); - - //when the scope is destroyed we need to unsubscribe - $scope.$on('$destroy', function () { - unsubscribe(); - }); - - //load media data - var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; - entityResource.getByIds(modelIds, dialogOptions.entityType).then(function (data) { - _.each(data, function (item, i) { - item.icon = iconHelper.convertFromLegacyIcon(item.icon); - $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon }); - }); - }); - -} - +/** + * @ngdoc controller + * @name Umbraco.Editors.Member.ListController + * @function + * + * @description + * The controller for the member list view + */ +function MemberListController($scope, $routeParams, $location, $q, $window, appState, memberResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, fileManager, formHelper, umbModelMapper, editorState, localizationService) { + + //setup scope vars + $scope.currentSection = appState.getSectionState("currentSection"); + $scope.currentNode = null; //the editors affiliated node + + $scope.page = {}; + $scope.page.lockedName = true; + $scope.page.loading = true; + + //we are editing so get the content item from the server + memberResource.getListNode($routeParams.id) + .then(function (data) { + + $scope.content = data; + + //translate "All Members" + if ($scope.content != null && $scope.content.name != null && $scope.content.name.replace(" ", "").toLowerCase() == "allmembers") { + localizationService.localize("member_allMembers").then(function (value) { + $scope.content.name = value; + }); + } + + editorState.set($scope.content); + + navigationService.syncTree({ tree: "member", path: data.path.split(",") }).then(function (syncArgs) { + $scope.currentNode = syncArgs.node; + }); + + //in one particular special case, after we've created a new item we redirect back to the edit + // route but there might be server validation errors in the collection which we need to display + // after the redirect, so we will bind all subscriptions which will show the server validation errors + // if there are any and then clear them so the collection no longer persists them. + serverValidationManager.executeAndClearAllSubscriptions(); + + $scope.page.loading = false; + + }); +} + +angular.module("umbraco").controller("Umbraco.Editors.Member.ListController", MemberListController); + +/** + * @ngdoc controller + * @name Umbraco.Editors.MemberType.CreateController + * @function + * + * @description + * The controller for the member type creation dialog + */ +function MemberTypesCreateController($scope, $location, navigationService, memberTypeResource, formHelper, appState, localizationService) { + + $scope.model = { + folderName: "", + creatingFolder: false + }; + + var node = $scope.dialogOptions.currentNode, + localizeCreateFolder = localizationService.localize("defaultdialog_createFolder"); + + + $scope.showCreateFolder = function() { + $scope.model.creatingFolder = true; + } + + $scope.createContainer = function () { + if (formHelper.submitForm({ + scope: $scope, + formCtrl: this.createFolderForm, + statusMessage: localizeCreateFolder + })) { + memberTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) { + + navigationService.hideMenu(); + var currPath = node.path ? node.path : "-1"; + navigationService.syncTree({ tree: "membertypes", path: currPath + "," + folderId, forceReload: true, activate: true }); + + formHelper.resetForm({ scope: $scope }); + + var section = appState.getSectionState("currentSection"); + + }, function(err) { + + //TODO: Handle errors + }); + }; + } + + $scope.createMemberType = function() { + $location.search('create', null); + $location.path("/settings/membertypes/edit/" + node.id).search("create", "true"); + navigationService.hideMenu(); + } +} + +angular.module('umbraco').controller("Umbraco.Editors.MemberTypes.CreateController", MemberTypesCreateController); + +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.DeleteController + * @function + * + * @description + * The controller for deleting content + */ +function MemberTypesDeleteController($scope, memberTypeResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + memberTypeResource.deleteById($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + navigationService.hideMenu(); + }); + + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.MemberTypes.DeleteController", MemberTypesDeleteController); + +/** + * @ngdoc controller + * @name Umbraco.Editors.MemberType.EditController + * @function + * + * @description + * The controller for the member type editor + */ +(function () { + "use strict"; + + function MemberTypesEditController($scope, $rootScope, $routeParams, $log, $filter, memberTypeResource, dataTypeResource, editorState, iconHelper, formHelper, navigationService, contentEditingHelper, notificationsService, $q, localizationService, overlayHelper, contentTypeHelper) { + + var vm = this; + var localizeSaving = localizationService.localize("general_saving"); + + vm.save = save; + + vm.currentNode = null; + vm.contentType = {}; + vm.page = {}; + vm.page.loading = false; + vm.page.saveButtonState = "init"; + vm.page.navigation = [ + { + "name": localizationService.localize("general_design"), + "icon": "icon-document-dashed-line", + "view": "views/membertypes/views/design/design.html", + "active": true + } + ]; + + vm.page.keyboardShortcutsOverview = [ + { + "name": localizationService.localize("shortcuts_shortcut"), + "shortcuts": [ + { + "description": localizationService.localize("shortcuts_addTab"), + "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }] + }, + { + "description": localizationService.localize("shortcuts_addProperty"), + "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }] + }, + { + "description": localizationService.localize("shortcuts_addEditor"), + "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }] + }, + { + "description": localizationService.localize("shortcuts_editDataType"), + "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }] + } + ] + } + ]; + + contentTypeHelper.checkModelsBuilderStatus().then(function (result) { + vm.page.modelsBuilder = result; + if (result) { + //Models builder mode: + vm.page.defaultButton = { + hotKey: "ctrl+s", + hotKeyWhenHidden: true, + labelKey: "buttons_save", + letter: "S", + type: "submit", + handler: function () { vm.save(); } + }; + vm.page.subButtons = [{ + hotKey: "ctrl+g", + hotKeyWhenHidden: true, + labelKey: "buttons_saveAndGenerateModels", + letter: "G", + handler: function () { + + vm.page.saveButtonState = "busy"; + + vm.save().then(function (result) { + + vm.page.saveButtonState = "busy"; + + localizationService.localize("modelsBuilder_buildingModels").then(function (headerValue) { + localizationService.localize("modelsBuilder_waitingMessage").then(function(msgValue) { + notificationsService.info(headerValue, msgValue); + }); + }); + + contentTypeHelper.generateModels().then(function (result) { + + if (result.success) { + + //re-check model status + contentTypeHelper.checkModelsBuilderStatus().then(function (statusResult) { + vm.page.modelsBuilder = statusResult; + }); + + //clear and add success + vm.page.saveButtonState = "init"; + localizationService.localize("modelsBuilder_modelsGenerated").then(function(value) { + notificationsService.success(value); + }); + + } else { + vm.page.saveButtonState = "error"; + localizationService.localize("modelsBuilder_modelsExceptionInUlog").then(function(value) { + notificationsService.error(value); + }); + } + + }, function () { + vm.page.saveButtonState = "error"; + localizationService.localize("modelsBuilder_modelsGeneratedError").then(function(value) { + notificationsService.error(value); + }); + }); + + + }); + + } + }]; + } + }); + + if ($routeParams.create) { + + vm.page.loading = true; + + //we are creating so get an empty data type item + memberTypeResource.getScaffold($routeParams.id) + .then(function (dt) { + init(dt); + + vm.page.loading = false; + }); + } + else { + + vm.page.loading = true; + + memberTypeResource.getById($routeParams.id).then(function (dt) { + init(dt); + + syncTreeNode(vm.contentType, dt.path, true); + + vm.page.loading = false; + }); + } + + function save() { + // only save if there is no overlays open + if(overlayHelper.getNumberOfOverlays() === 0) { + + var deferred = $q.defer(); + + vm.page.saveButtonState = "busy"; + + contentEditingHelper.contentEditorPerformSave({ + statusMessage: localizeSaving, + saveMethod: memberTypeResource.save, + scope: $scope, + content: vm.contentType, + //We do not redirect on failure for doc types - this is because it is not possible to actually save the doc + // type when server side validation fails - as opposed to content where we are capable of saving the content + // item if server side validation fails + redirectOnFailure: false, + // we need to rebind... the IDs that have been created! + rebindCallback: function (origContentType, savedContentType) { + vm.contentType.id = savedContentType.id; + vm.contentType.groups.forEach(function (group) { + if (!group.name) return; + + var k = 0; + while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name) + k++; + if (k == savedContentType.groups.length) { + group.id = 0; + return; + } + + var savedGroup = savedContentType.groups[k]; + if (!group.id) group.id = savedGroup.id; + + group.properties.forEach(function (property) { + if (property.id || !property.alias) return; + + k = 0; + while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias) + k++; + if (k == savedGroup.properties.length) { + property.id = 0; + return; + } + + var savedProperty = savedGroup.properties[k]; + property.id = savedProperty.id; + }); + }); + } + }).then(function (data) { + //success + syncTreeNode(vm.contentType, data.path); + + vm.page.saveButtonState = "success"; + + deferred.resolve(data); + }, function (err) { + //error + if (err) { + editorState.set($scope.content); + } + else { + localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) { + localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) { + notificationsService.error(headerValue, msgValue); + }); + }); + } + + vm.page.saveButtonState = "error"; + + deferred.reject(err); + }); + + return deferred.promise; + } + + } + + function init(contentType) { + + // set all tab to inactive + if (contentType.groups.length !== 0) { + angular.forEach(contentType.groups, function (group) { + + angular.forEach(group.properties, function (property) { + // get data type details for each property + getDataTypeDetails(property); + }); + + }); + } + + // convert legacy icons + convertLegacyIcons(contentType); + + //set a shared state + editorState.set(contentType); + + vm.contentType = contentType; + + } + + function convertLegacyIcons(contentType) { + + // make array to store contentType icon + var contentTypeArray = []; + + // push icon to array + contentTypeArray.push({ "icon": contentType.icon }); + + // run through icon method + iconHelper.formatContentTypeIcons(contentTypeArray); + + // set icon back on contentType + contentType.icon = contentTypeArray[0].icon; + + } + + function getDataTypeDetails(property) { + + if (property.propertyState !== "init") { + + dataTypeResource.getById(property.dataTypeId) + .then(function (dataType) { + property.dataTypeIcon = dataType.icon; + property.dataTypeName = dataType.name; + }); + } + } + + /** Syncs the content type to it's tree node - this occurs on first load and after saving */ + function syncTreeNode(dt, path, initialLoad) { + + navigationService.syncTree({ tree: "membertypes", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) { + vm.currentNode = syncArgs.node; + }); + + } + + + } + + angular.module("umbraco").controller("Umbraco.Editors.MemberTypes.EditController", MemberTypesEditController); + +})(); + +angular.module("umbraco") +.controller("Umbraco.Editors.MemberTypes.MoveController", + function($scope){ + + }); + +function imageFilePickerController($scope) { + + $scope.pick = function() { + $scope.mediaPickerDialog = {}; + $scope.mediaPickerDialog.view = "mediapicker"; + $scope.mediaPickerDialog.show = true; + + $scope.mediaPickerDialog.submit = function(model) { + $scope.model.value = model.selectedImages[0].image; + $scope.mediaPickerDialog.show = false; + $scope.mediaPickerDialog = null; + }; + + $scope.mediaPickerDialog.close = function(oldModel) { + $scope.mediaPickerDialog.show = false; + $scope.mediaPickerDialog = null; + }; + }; + +} + +angular.module('umbraco').controller("Umbraco.PrevalueEditors.ImageFilePickerController", imageFilePickerController); + +//this controller simply tells the dialogs service to open a mediaPicker window +//with a specified callback, this callback will receive an object with a selection on it +function mediaPickerController($scope, dialogService, entityResource, $log, iconHelper) { + + function trim(str, chr) { + var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); + return str.replace(rgxtrim, ''); + } + + $scope.renderModel = []; + + var dialogOptions = { + multiPicker: false, + entityType: "Media", + section: "media", + treeAlias: "media" + }; + + $scope.openContentPicker = function() { + $scope.contentPickerOverlay = dialogOptions; + $scope.contentPickerOverlay.view = "treePicker"; + $scope.contentPickerOverlay.show = true; + + $scope.contentPickerOverlay.submit = function(model) { + + if ($scope.contentPickerOverlay.multiPicker) { + _.each(model.selection, function (item, i) { + $scope.add(item); + }); + } + else { + $scope.clear(); + $scope.add(model.selection[0]); + } + + $scope.contentPickerOverlay.show = false; + $scope.contentPickerOverlay = null; + }; + + $scope.contentPickerOverlay.close = function(oldModel) { + $scope.contentPickerOverlay.show = false; + $scope.contentPickerOverlay = null; + }; + } + + $scope.remove =function(index, event){ + event.preventDefault(); + $scope.renderModel.splice(index, 1); + }; + + $scope.clear = function() { + $scope.renderModel = []; + }; + + $scope.add = function (item) { + var currIds = _.map($scope.renderModel, function (i) { + return i.id; + }); + if (currIds.indexOf(item.id) < 0) { + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon}); + } + }; + + var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { + var currIds = _.map($scope.renderModel, function (i) { + return i.id; + }); + $scope.model.value = trim(currIds.join(), ","); + }); + + //when the scope is destroyed we need to unsubscribe + $scope.$on('$destroy', function () { + unsubscribe(); + }); + + //load media data + var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; + entityResource.getByIds(modelIds, dialogOptions.entityType).then(function (data) { + _.each(data, function (item, i) { + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon }); + }); + }); + +} + angular.module('umbraco').controller("Umbraco.PrevalueEditors.MediaPickerController",mediaPickerController); angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiValuesController", function ($scope, $timeout) { @@ -8722,77 +8722,77 @@ angular.module('umbraco') } }); -//this controller simply tells the dialogs service to open a mediaPicker window -//with a specified callback, this callback will receive an object with a selection on it -angular.module('umbraco') -.controller("Umbraco.PrevalueEditors.TreeSourceController", - - function($scope, dialogService, entityResource, $log, iconHelper){ - - if (!$scope.model) { - $scope.model = {}; - } - if (!$scope.model.value) { - $scope.model.value = { - type: "content" - }; - } - - if($scope.model.value.id && $scope.model.value.type !== "member"){ - var ent = "Document"; - if($scope.model.value.type === "media"){ - ent = "Media"; - } - - entityResource.getById($scope.model.value.id, ent).then(function(item){ - item.icon = iconHelper.convertFromLegacyIcon(item.icon); - $scope.node = item; - }); - } - - - $scope.openContentPicker =function(){ - $scope.treePickerOverlay = { - view: "treepicker", - section: $scope.model.value.type, - treeAlias: $scope.model.value.type, - multiPicker: false, - show: true, - submit: function(model) { - var item = model.selection[0]; - populate(item); - $scope.treePickerOverlay.show = false; - $scope.treePickerOverlay = null; - } - }; - }; - - $scope.clear = function() { - $scope.model.value.id = undefined; - $scope.node = undefined; - $scope.model.value.query = undefined; - }; - - - //we always need to ensure we dont submit anything broken - var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { - if($scope.model.value.type === "member"){ - $scope.model.value.id = -1; - $scope.model.value.query = ""; - } - }); - - //when the scope is destroyed we need to unsubscribe - $scope.$on('$destroy', function () { - unsubscribe(); - }); - - function populate(item){ - $scope.clear(); - item.icon = iconHelper.convertFromLegacyIcon(item.icon); - $scope.node = item; - $scope.model.value.id = item.id; - } +//this controller simply tells the dialogs service to open a mediaPicker window +//with a specified callback, this callback will receive an object with a selection on it +angular.module('umbraco') +.controller("Umbraco.PrevalueEditors.TreeSourceController", + + function($scope, dialogService, entityResource, $log, iconHelper){ + + if (!$scope.model) { + $scope.model = {}; + } + if (!$scope.model.value) { + $scope.model.value = { + type: "content" + }; + } + + if($scope.model.value.id && $scope.model.value.type !== "member"){ + var ent = "Document"; + if($scope.model.value.type === "media"){ + ent = "Media"; + } + + entityResource.getById($scope.model.value.id, ent).then(function(item){ + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + $scope.node = item; + }); + } + + + $scope.openContentPicker =function(){ + $scope.treePickerOverlay = { + view: "treepicker", + section: $scope.model.value.type, + treeAlias: $scope.model.value.type, + multiPicker: false, + show: true, + submit: function(model) { + var item = model.selection[0]; + populate(item); + $scope.treePickerOverlay.show = false; + $scope.treePickerOverlay = null; + } + }; + }; + + $scope.clear = function() { + $scope.model.value.id = undefined; + $scope.node = undefined; + $scope.model.value.query = undefined; + }; + + + //we always need to ensure we dont submit anything broken + var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { + if($scope.model.value.type === "member"){ + $scope.model.value.id = -1; + $scope.model.value.query = ""; + } + }); + + //when the scope is destroyed we need to unsubscribe + $scope.$on('$destroy', function () { + unsubscribe(); + }); + + function populate(item){ + $scope.clear(); + item.icon = iconHelper.convertFromLegacyIcon(item.icon); + $scope.node = item; + $scope.model.value.id = item.id; + } }); function booleanEditorController($scope, $rootScope, assetsService) { @@ -9062,93 +9062,93 @@ function ColorPickerController($scope) { angular.module("umbraco").controller("Umbraco.PropertyEditors.ColorPickerController", ColorPickerController); -angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiColorPickerController", - function ($scope, $timeout, assetsService, angularHelper, $element) { - //NOTE: We need to make each color an object, not just a string because you cannot 2-way bind to a primitive. - var defaultColor = "000000"; - - $scope.newColor = defaultColor; - $scope.hasError = false; - - assetsService.load([ - //"lib/spectrum/tinycolor.js", - "lib/spectrum/spectrum.js" - ], $scope).then(function () { - var elem = $element.find("input"); - elem.spectrum({ - color: null, - showInitial: false, - chooseText: "choose", // TODO: These can be localised - cancelText: "cancel", // TODO: These can be localised - preferredFormat: "hex", - showInput: true, - clickoutFiresChange: true, - hide: function (color) { - //show the add butotn - $element.find(".btn.add").show(); - }, - change: function (color) { - angularHelper.safeApply($scope, function () { - $scope.newColor = color.toHexString().trimStart("#"); // #ff0000 - }); - }, - show: function() { - //hide the add butotn - $element.find(".btn.add").hide(); - } - }); - }); - - if (!angular.isArray($scope.model.value)) { - //make an array from the dictionary - var items = []; - for (var i in $scope.model.value) { - items.push({ - value: $scope.model.value[i], - id: i - }); - } - //now make the editor model the array - $scope.model.value = items; - } - - $scope.remove = function (item, evt) { - - evt.preventDefault(); - - $scope.model.value = _.reject($scope.model.value, function (x) { - return x.value === item.value; - }); - - }; - - $scope.add = function (evt) { - - evt.preventDefault(); - - if ($scope.newColor) { - var exists = _.find($scope.model.value, function(item) { - return item.value.toUpperCase() == $scope.newColor.toUpperCase(); - }); - if (!exists) { - $scope.model.value.push({ value: $scope.newColor }); - //$scope.newColor = defaultColor; - // set colorpicker to default color - //var elem = $element.find("input"); - //elem.spectrum("set", $scope.newColor); - $scope.hasError = false; - return; - } - - //there was an error, do the highlight (will be set back by the directive) - $scope.hasError = true; - } - - }; - - //load the separate css for the editor to avoid it blocking our js loading - assetsService.loadCss("lib/spectrum/spectrum.css"); - }); +angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiColorPickerController", + function ($scope, $timeout, assetsService, angularHelper, $element) { + //NOTE: We need to make each color an object, not just a string because you cannot 2-way bind to a primitive. + var defaultColor = "000000"; + + $scope.newColor = defaultColor; + $scope.hasError = false; + + assetsService.load([ + //"lib/spectrum/tinycolor.js", + "lib/spectrum/spectrum.js" + ], $scope).then(function () { + var elem = $element.find("input"); + elem.spectrum({ + color: null, + showInitial: false, + chooseText: "choose", // TODO: These can be localised + cancelText: "cancel", // TODO: These can be localised + preferredFormat: "hex", + showInput: true, + clickoutFiresChange: true, + hide: function (color) { + //show the add butotn + $element.find(".btn.add").show(); + }, + change: function (color) { + angularHelper.safeApply($scope, function () { + $scope.newColor = color.toHexString().trimStart("#"); // #ff0000 + }); + }, + show: function() { + //hide the add butotn + $element.find(".btn.add").hide(); + } + }); + }); + + if (!angular.isArray($scope.model.value)) { + //make an array from the dictionary + var items = []; + for (var i in $scope.model.value) { + items.push({ + value: $scope.model.value[i], + id: i + }); + } + //now make the editor model the array + $scope.model.value = items; + } + + $scope.remove = function (item, evt) { + + evt.preventDefault(); + + $scope.model.value = _.reject($scope.model.value, function (x) { + return x.value === item.value; + }); + + }; + + $scope.add = function (evt) { + + evt.preventDefault(); + + if ($scope.newColor) { + var exists = _.find($scope.model.value, function(item) { + return item.value.toUpperCase() == $scope.newColor.toUpperCase(); + }); + if (!exists) { + $scope.model.value.push({ value: $scope.newColor }); + //$scope.newColor = defaultColor; + // set colorpicker to default color + //var elem = $element.find("input"); + //elem.spectrum("set", $scope.newColor); + $scope.hasError = false; + return; + } + + //there was an error, do the highlight (will be set back by the directive) + $scope.hasError = true; + } + + }; + + //load the separate css for the editor to avoid it blocking our js loading + assetsService.loadCss("lib/spectrum/spectrum.css"); + }); //this controller simply tells the dialogs service to open a mediaPicker window //with a specified callback, this callback will receive an object with a selection on it @@ -9847,35 +9847,35 @@ angular.module("umbraco") } }); -angular.module("umbraco") - -//this controller is obsolete and should not be used anymore -//it proxies everything to the system media list view which has overtaken -//all the work this property editor used to perform -.controller("Umbraco.PropertyEditors.FolderBrowserController", - function ($rootScope, $scope, contentTypeResource) { - //get the system media listview - contentTypeResource.getPropertyTypeScaffold(-96) - .then(function(dt) { - - $scope.fakeProperty = { - alias: "contents", - config: dt.config, - description: "", - editor: dt.editor, - hideLabel: true, - id: 1, - label: "Contents:", - validation: { - mandatory: false, - pattern: null - }, - value: "", - view: dt.view - }; - - }); -}); +angular.module("umbraco") + +//this controller is obsolete and should not be used anymore +//it proxies everything to the system media list view which has overtaken +//all the work this property editor used to perform +.controller("Umbraco.PropertyEditors.FolderBrowserController", + function ($rootScope, $scope, contentTypeResource) { + //get the system media listview + contentTypeResource.getPropertyTypeScaffold(-96) + .then(function(dt) { + + $scope.fakeProperty = { + alias: "contents", + config: dt.config, + description: "", + editor: dt.editor, + hideLabel: true, + id: 1, + label: "Contents:", + validation: { + mandatory: false, + pattern: null + }, + value: "", + view: dt.view + }; + + }); +}); angular.module("umbraco") .controller("Umbraco.PropertyEditors.GoogleMapsController", @@ -9974,2235 +9974,2235 @@ angular.module("umbraco") initMap(); }; }); -angular.module("umbraco") - .controller("Umbraco.PropertyEditors.GridPrevalueEditor.LayoutConfigController", - function ($scope) { - - $scope.currentLayout = $scope.model.currentLayout; - $scope.columns = $scope.model.columns; - $scope.rows = $scope.model.rows; - - $scope.scaleUp = function(section, max, overflow){ - var add = 1; - if(overflow !== true){ - add = (max > 1) ? 1 : max; - } - //var add = (max > 1) ? 1 : max; - section.grid = section.grid+add; - }; - - $scope.scaleDown = function(section){ - var remove = (section.grid > 1) ? 1 : section.grid; - section.grid = section.grid-remove; - }; - - $scope.percentage = function(spans){ - return ((spans / $scope.columns) * 100).toFixed(8); - }; - - $scope.toggleCollection = function(collection, toggle){ - if(toggle){ - collection = []; - }else{ - delete collection; - } - }; - - - - /**************** - Section - *****************/ - $scope.configureSection = function(section, template){ - if(section === undefined){ - var space = ($scope.availableLayoutSpace > 4) ? 4 : $scope.availableLayoutSpace; - section = { - grid: space - }; - template.sections.push(section); - } - - $scope.currentSection = section; - }; - - - $scope.deleteSection = function(index){ - $scope.currentTemplate.sections.splice(index, 1); - }; - - $scope.closeSection = function(){ - $scope.currentSection = undefined; - }; - - $scope.$watch("currentLayout", function(layout){ - if(layout){ - var total = 0; - _.forEach(layout.sections, function(section){ - total = (total + section.grid); - }); - - $scope.availableLayoutSpace = $scope.columns - total; - } - }, true); - }); - -function RowConfigController($scope) { - - $scope.currentRow = $scope.model.currentRow; - $scope.editors = $scope.model.editors; - $scope.columns = $scope.model.columns; - - $scope.scaleUp = function(section, max, overflow) { - var add = 1; - if (overflow !== true) { - add = (max > 1) ? 1 : max; - } - //var add = (max > 1) ? 1 : max; - section.grid = section.grid + add; - }; - - $scope.scaleDown = function(section) { - var remove = (section.grid > 1) ? 1 : section.grid; - section.grid = section.grid - remove; - }; - - $scope.percentage = function(spans) { - return ((spans / $scope.columns) * 100).toFixed(8); - }; - - $scope.toggleCollection = function(collection, toggle) { - if (toggle) { - collection = []; - } - else { - delete collection; - } - }; - - - /**************** - area - *****************/ - $scope.configureCell = function(cell, row) { - if ($scope.currentCell && $scope.currentCell === cell) { - delete $scope.currentCell; - } - else { - if (cell === undefined) { - var available = $scope.availableRowSpace; - var space = 4; - - if (available < 4 && available > 0) { - space = available; - } - - cell = { - grid: space - }; - - row.areas.push(cell); - } - $scope.currentCell = cell; - } - }; - - $scope.deleteArea = function(index) { - $scope.currentRow.areas.splice(index, 1); - }; - $scope.closeArea = function() { - $scope.currentCell = undefined; - }; - - $scope.nameChanged = false; - var originalName = $scope.currentRow.name; - $scope.$watch("currentRow", function(row) { - if (row) { - - var total = 0; - _.forEach(row.areas, function(area) { - total = (total + area.grid); - }); - - $scope.availableRowSpace = $scope.columns - total; - - if (originalName) { - if (originalName != row.name) { - $scope.nameChanged = true; - } - else { - $scope.nameChanged = false; - } - } - } - }, true); - -} - -angular.module("umbraco").controller("Umbraco.PropertyEditors.GridPrevalueEditor.RowConfigController", RowConfigController); - -angular.module("umbraco") - .controller("Umbraco.PropertyEditors.Grid.EmbedController", - function ($scope, $rootScope, $timeout) { - - $scope.setEmbed = function(){ - $scope.embedDialog = {}; - $scope.embedDialog.view = "embed"; - $scope.embedDialog.show = true; - - $scope.embedDialog.submit = function(model) { - $scope.control.value = model.embed.preview; - $scope.embedDialog.show = false; - $scope.embedDialog = null; - }; - - $scope.embedDialog.close = function(oldModel) { - $scope.embedDialog.show = false; - $scope.embedDialog = null; - }; - - }; - - $timeout(function(){ - if($scope.control.$initializing){ - $scope.setEmbed(); - } - }, 200); -}); - -angular.module("umbraco") - .controller("Umbraco.PropertyEditors.Grid.MacroController", - function ($scope, $rootScope, $timeout, dialogService, macroResource, macroService, $routeParams) { - - $scope.title = "Click to insert macro"; - - $scope.setMacro = function(){ - - var dialogData = { - richTextEditor: true, - macroData: $scope.control.value || { - macroAlias: $scope.control.editor.config && $scope.control.editor.config.macroAlias - ? $scope.control.editor.config.macroAlias : "" - } - }; - - $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); - - $scope.control.value = { - macroAlias: macroObject.macroAlias, - macroParamsDictionary: macroObject.macroParamsDictionary - }; - - $scope.setPreview($scope.control.value ); - - $scope.macroPickerOverlay.show = false; - $scope.macroPickerOverlay = null; - }; - - $scope.macroPickerOverlay.close = function(oldModel) { - $scope.macroPickerOverlay.show = false; - $scope.macroPickerOverlay = null; - }; - - }; - - $scope.setPreview = function(macro){ - var contentId = $routeParams.id; - - macroResource.getMacroResultAsHtmlForEditor(macro.macroAlias, contentId, macro.macroParamsDictionary) - .then(function (htmlResult) { - $scope.title = macro.macroAlias; - if(htmlResult.trim().length > 0 && htmlResult.indexOf("Macro:") < 0){ - $scope.preview = htmlResult; - } - }); - - }; - - $timeout(function(){ - if($scope.control.$initializing){ - $scope.setMacro(); - }else if($scope.control.value){ - $scope.setPreview($scope.control.value); - } - }, 200); -}); - -angular.module("umbraco") - .controller("Umbraco.PropertyEditors.Grid.MediaController", - function ($scope, $rootScope, $timeout) { - - $scope.setImage = function(){ - $scope.mediaPickerOverlay = {}; - $scope.mediaPickerOverlay.view = "mediapicker"; - $scope.mediaPickerOverlay.cropSize = $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : undefined; - $scope.mediaPickerOverlay.showDetails = true; - $scope.mediaPickerOverlay.disableFolderSelect = true; - $scope.mediaPickerOverlay.onlyImages = true; - $scope.mediaPickerOverlay.show = true; - - $scope.mediaPickerOverlay.submit = function(model) { - var selectedImage = model.selectedImages[0]; - - $scope.control.value = { - focalPoint: selectedImage.focalPoint, - id: selectedImage.id, - image: selectedImage.image, - altText: selectedImage.altText - }; - - $scope.setUrl(); - - $scope.mediaPickerOverlay.show = false; - $scope.mediaPickerOverlay = null; - }; - - $scope.mediaPickerOverlay.close = function(oldModel) { - $scope.mediaPickerOverlay.show = false; - $scope.mediaPickerOverlay = null; - }; - }; - - $scope.setUrl = function(){ - - if($scope.control.value.image){ - var url = $scope.control.value.image; - - if($scope.control.editor.config && $scope.control.editor.config.size){ - url += "?width=" + $scope.control.editor.config.size.width; - url += "&height=" + $scope.control.editor.config.size.height; - url += "&animationprocessmode=first"; - - if($scope.control.value.focalPoint){ - url += "¢er=" + $scope.control.value.focalPoint.top +"," + $scope.control.value.focalPoint.left; - url += "&mode=crop"; - } - } - - // set default size if no crop present (moved from the view) - if (url.indexOf('?') == -1) - { - url += "?width=800&upscale=false&animationprocessmode=false" - } - $scope.url = url; - } - }; - - $timeout(function(){ - if($scope.control.$initializing){ - $scope.setImage(); - }else if($scope.control.value){ - $scope.setUrl(); - } - }, 200); -}); - -(function() { - "use strict"; - - function GridRichTextEditorController($scope, tinyMceService, macroService) { - - var vm = this; - - vm.openLinkPicker = openLinkPicker; - vm.openMediaPicker = openMediaPicker; - vm.openMacroPicker = openMacroPicker; - vm.openEmbed = openEmbed; - - function openLinkPicker(editor, currentTarget, anchorElement) { - vm.linkPickerOverlay = { - view: "linkpicker", - currentTarget: currentTarget, - show: true, - submit: function(model) { - tinyMceService.insertLinkInEditor(editor, model.target, anchorElement); - vm.linkPickerOverlay.show = false; - vm.linkPickerOverlay = null; - } - }; - } - - 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); - -})(); - -angular.module("umbraco") - .controller("Umbraco.PropertyEditors.Grid.TextStringController", - function ($scope, $rootScope, $timeout, dialogService) { - - - - }); - - -angular.module("umbraco") - .controller("Umbraco.PropertyEditors.GridController", - function ($scope, $http, assetsService, localizationService, $rootScope, dialogService, gridService, mediaResource, imageHelper, $timeout, umbRequestHelper, angularHelper) { - - // Grid status variables - var placeHolder = ""; - var currentForm = angularHelper.getCurrentForm($scope); - - $scope.currentRow = null; - $scope.currentCell = null; - $scope.currentToolsControl = null; - $scope.currentControl = null; - $scope.openRTEToolbarId = null; - $scope.hasSettings = false; - $scope.showRowConfigurations = true; - $scope.sortMode = false; - $scope.reorderKey = "general_reorder"; - - // ********************************************* - // Sortable options - // ********************************************* - - var draggedRteSettings; - - $scope.sortableOptionsRow = { - distance: 10, - cursor: "move", - placeholder: "ui-sortable-placeholder", - handle: ".umb-row-title-bar", - helper: "clone", - forcePlaceholderSize: true, - tolerance: "pointer", - zIndex: 999999999999999999, - scrollSensitivity: 100, - cursorAt: { - top: 40, - left: 60 - }, - - sort: function (event, ui) { - /* prevent vertical scroll out of the screen */ - var max = $(".umb-grid").width() - 150; - if (parseInt(ui.helper.css("left")) > max) { - ui.helper.css({ "left": max + "px" }); - } - if (parseInt(ui.helper.css("left")) < 20) { - ui.helper.css({ "left": 20 }); - } - }, - - start: function (e, ui) { - - // Fade out row when sorting - ui.item.context.style.display = "block"; - ui.item.context.style.opacity = "0.5"; - - draggedRteSettings = {}; - ui.item.find(".mceNoEditor").each(function () { - // remove all RTEs in the dragged row and save their settings - var id = $(this).attr("id"); - draggedRteSettings[id] = _.findWhere(tinyMCE.editors, { id: id }).settings; - // tinyMCE.execCommand("mceRemoveEditor", false, id); - }); - }, - - stop: function (e, ui) { - - // Fade in row when sorting stops - ui.item.context.style.opacity = "1"; - - // reset all RTEs affected by the dragging - ui.item.parents(".umb-column").find(".mceNoEditor").each(function () { - var id = $(this).attr("id"); - draggedRteSettings[id] = draggedRteSettings[id] || _.findWhere(tinyMCE.editors, { id: id }).settings; - tinyMCE.execCommand("mceRemoveEditor", false, id); - tinyMCE.init(draggedRteSettings[id]); - }); - currentForm.$setDirty(); - } - }; - - var notIncludedRte = []; - var cancelMove = false; - - $scope.sortableOptionsCell = { - distance: 10, - cursor: "move", - placeholder: "ui-sortable-placeholder", - handle: ".umb-control-handle", - helper: "clone", - connectWith: ".umb-cell-inner", - forcePlaceholderSize: true, - tolerance: "pointer", - zIndex: 999999999999999999, - scrollSensitivity: 100, - cursorAt: { - top: 45, - left: 90 - }, - - sort: function (event, ui) { - - /* prevent vertical scroll out of the screen */ - var position = parseInt(ui.item.parent().offset().left) + parseInt(ui.helper.css("left")) - parseInt($(".umb-grid").offset().left); - var max = $(".umb-grid").width() - 220; - if (position > max) { - ui.helper.css({ "left": max - parseInt(ui.item.parent().offset().left) + parseInt($(".umb-grid").offset().left) + "px" }); - } - if (position < 0) { - ui.helper.css({ "left": 0 - parseInt(ui.item.parent().offset().left) + parseInt($(".umb-grid").offset().left) + "px" }); - } - }, - - over: function (event, ui) { - var allowedEditors = $(event.target).scope().area.allowed; - - if ($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) { - - $scope.$apply(function () { - $(event.target).scope().area.dropNotAllowed = true; - }); - - ui.placeholder.hide(); - cancelMove = true; - } - else { - if ($(event.target).scope().area.controls.length == 0){ - - $scope.$apply(function () { - $(event.target).scope().area.dropOnEmpty = true; - }); - ui.placeholder.hide(); - } else { - ui.placeholder.show(); - } - cancelMove = false; - } - }, - - out: function(event, ui) { - $scope.$apply(function () { - $(event.target).scope().area.dropNotAllowed = false; - $(event.target).scope().area.dropOnEmpty = false; - }); - }, - - update: function (event, ui) { - /* add all RTEs which are affected by the dragging */ - if (!ui.sender) { - if (cancelMove) { - ui.item.sortable.cancel(); - } - ui.item.parents(".umb-cell.content").find(".mceNoEditor").each(function () { - if ($.inArray($(this).attr("id"), notIncludedRte) < 0) { - notIncludedRte.splice(0, 0, $(this).attr("id")); - } - }); - } - else { - $(event.target).find(".mceNoEditor").each(function () { - if ($.inArray($(this).attr("id"), notIncludedRte) < 0) { - notIncludedRte.splice(0, 0, $(this).attr("id")); - } - }); - } - currentForm.$setDirty(); - }, - - start: function (e, ui) { - - // fade out control when sorting - ui.item.context.style.display = "block"; - ui.item.context.style.opacity = "0.5"; - - // reset dragged RTE settings in case a RTE isn't dragged - draggedRteSettings = undefined; - ui.item.context.style.display = "block"; - ui.item.find(".mceNoEditor").each(function () { - notIncludedRte = []; - var editors = _.findWhere(tinyMCE.editors, { id: $(this).attr("id") }); - - // save the dragged RTE settings - if(editors) { - draggedRteSettings = editors.settings; - - // remove the dragged RTE - tinyMCE.execCommand("mceRemoveEditor", false, $(this).attr("id")); - - } - - }); - }, - - stop: function (e, ui) { - - // Fade in control when sorting stops - ui.item.context.style.opacity = "1"; - - ui.item.parents(".umb-cell-content").find(".mceNoEditor").each(function () { - if ($.inArray($(this).attr("id"), notIncludedRte) < 0) { - // add all dragged's neighbouring RTEs in the new cell - notIncludedRte.splice(0, 0, $(this).attr("id")); - } - }); - $timeout(function () { - // reconstruct the dragged RTE (could be undefined when dragging something else than RTE) - if (draggedRteSettings !== undefined) { - tinyMCE.init(draggedRteSettings); - } - - _.forEach(notIncludedRte, function (id) { - // reset all the other RTEs - if (draggedRteSettings === undefined || id !== draggedRteSettings.id) { - var rteSettings = _.findWhere(tinyMCE.editors, { id: id }).settings; - tinyMCE.execCommand("mceRemoveEditor", false, id); - tinyMCE.init(rteSettings); - } - }); - }, 500, false); - - $scope.$apply(function () { - - var cell = $(e.target).scope().area; - cell.hasActiveChild = hasActiveChild(cell, cell.controls); - cell.active = false; - }); - } - - }; - - $scope.toggleSortMode = function() { - $scope.sortMode = !$scope.sortMode; - if($scope.sortMode) { - $scope.reorderKey = "general_reorderDone"; - } else { - $scope.reorderKey = "general_reorder"; - } - }; - - $scope.showReorderButton = function() { - if($scope.model.value && $scope.model.value.sections) { - for(var i = 0; $scope.model.value.sections.length > i; i++) { - var section = $scope.model.value.sections[i]; - if(section.rows && section.rows.length > 0) { - return true; - } - } - } - }; - - // ********************************************* - // Add items overlay menu - // ********************************************* - $scope.openEditorOverlay = function(event, area, index, key) { - $scope.editorOverlay = { - view: "itempicker", - filter: false, - title: localizationService.localize("grid_insertControl"), - availableItems: area.$allowedEditors, - event: event, - show: true, - submit: function(model) { - $scope.addControl(model.selectedItem, area, index); - $scope.editorOverlay.show = false; - $scope.editorOverlay = null; - } - }; - }; - - // ********************************************* - // Template management functions - // ********************************************* - - $scope.addTemplate = function (template) { - $scope.model.value = angular.copy(template); - - //default row data - _.forEach($scope.model.value.sections, function (section) { - $scope.initSection(section); - }); - }; - - - // ********************************************* - // Row management function - // ********************************************* - - $scope.clickRow = function(index, rows) { - rows[index].active = true; - }; - - $scope.clickOutsideRow = function(index, rows) { - rows[index].active = false; - }; - - function getAllowedLayouts(section) { - - var layouts = $scope.model.config.items.layouts; - - //This will occur if it is a new section which has been - // created from a 'template' - if (section.allowed && section.allowed.length > 0) { - return _.filter(layouts, function (layout) { - return _.indexOf(section.allowed, layout.name) >= 0; - }); - } - else { - - - return layouts; - } - } - - $scope.addRow = function (section, layout) { - - //copy the selected layout into the rows collection - var row = angular.copy(layout); - - // Init row value - row = $scope.initRow(row); - - // Push the new row - if (row) { - section.rows.push(row); - } - - currentForm.$setDirty(); - - $scope.showRowConfigurations = false; - - }; - - $scope.removeRow = function (section, $index) { - if (section.rows.length > 0) { - section.rows.splice($index, 1); - $scope.currentRow = null; - $scope.openRTEToolbarId = null; - currentForm.$setDirty(); - } - - if(section.rows.length === 0) { - $scope.showRowConfigurations = true; - } - }; - - $scope.editGridItemSettings = function (gridItem, itemType) { - - placeHolder = "{0}"; - - var styles, config; - if (itemType === 'control') { - styles = null; - config = angular.copy(gridItem.editor.config.settings); - } else { - styles = _.filter(angular.copy($scope.model.config.items.styles), function (item) { return (item.applyTo === undefined || item.applyTo === itemType); }); - config = _.filter(angular.copy($scope.model.config.items.config), function (item) { return (item.applyTo === undefined || item.applyTo === itemType); }); - } - - if(angular.isObject(gridItem.config)){ - _.each(config, function(cfg){ - var val = gridItem.config[cfg.key]; - if(val){ - cfg.value = stripModifier(val, cfg.modifier); - } - }); - } - - if(angular.isObject(gridItem.styles)){ - _.each(styles, function(style){ - var val = gridItem.styles[style.key]; - if(val){ - style.value = stripModifier(val, style.modifier); - } - }); - } - - $scope.gridItemSettingsDialog = {}; - $scope.gridItemSettingsDialog.view = "views/propertyeditors/grid/dialogs/config.html"; - $scope.gridItemSettingsDialog.title = "Settings"; - $scope.gridItemSettingsDialog.styles = styles; - $scope.gridItemSettingsDialog.config = config; - - $scope.gridItemSettingsDialog.show = true; - - $scope.gridItemSettingsDialog.submit = function(model) { - - var styleObject = {}; - var configObject = {}; - - _.each(model.styles, function(style){ - if(style.value){ - styleObject[style.key] = addModifier(style.value, style.modifier); - } - }); - _.each(model.config, function (cfg) { - if (cfg.value) { - configObject[cfg.key] = addModifier(cfg.value, cfg.modifier); - } - }); - - gridItem.styles = styleObject; - gridItem.config = configObject; - gridItem.hasConfig = gridItemHasConfig(styleObject, configObject); - - currentForm.$setDirty(); - - $scope.gridItemSettingsDialog.show = false; - $scope.gridItemSettingsDialog = null; - }; - - $scope.gridItemSettingsDialog.close = function(oldModel) { - $scope.gridItemSettingsDialog.show = false; - $scope.gridItemSettingsDialog = null; - }; - - }; - - function stripModifier(val, modifier) { - if (!val || !modifier || modifier.indexOf(placeHolder) < 0) { - return val; - } else { - var paddArray = modifier.split(placeHolder); - if(paddArray.length == 1){ - if (modifier.indexOf(placeHolder) === 0) { - return val.slice(0, -paddArray[0].length); - } else { - return val.slice(paddArray[0].length, 0); - } - } else { - if (paddArray[1].length === 0) { - return val.slice(paddArray[0].length); - } - return val.slice(paddArray[0].length, -paddArray[1].length); - } - } - } - - var addModifier = function(val, modifier){ - if (!modifier || modifier.indexOf(placeHolder) < 0) { - return val; - } else { - return modifier.replace(placeHolder, val); - } - }; - - function gridItemHasConfig(styles, config) { - - if(_.isEmpty(styles) && _.isEmpty(config)) { - return false; - } else { - return true; - } - - } - - // ********************************************* - // Area management functions - // ********************************************* - - $scope.clickCell = function(index, cells, row) { - cells[index].active = true; - row.hasActiveChild = true; - }; - - $scope.clickOutsideCell = function(index, cells, row) { - cells[index].active = false; - row.hasActiveChild = hasActiveChild(row, cells); - }; - - $scope.cellPreview = function (cell) { - if (cell && cell.$allowedEditors) { - var editor = cell.$allowedEditors[0]; - return editor.icon; - } else { - return "icon-layout"; - } - }; - - - // ********************************************* - // Control management functions - // ********************************************* - $scope.clickControl = function (index, controls, cell) { - controls[index].active = true; - cell.hasActiveChild = true; - }; - - $scope.clickOutsideControl = function (index, controls, cell) { - controls[index].active = false; - cell.hasActiveChild = hasActiveChild(cell, controls); - }; - - function hasActiveChild(item, children) { - - var activeChild = false; - - for(var i = 0; children.length > i; i++) { - var child = children[i]; - - if(child.active) { - activeChild = true; - } - } - - if(activeChild) { - return true; - } - - } - - - var guid = (function () { - function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - return function () { - return s4() + s4() + "-" + s4() + "-" + s4() + "-" + - s4() + "-" + s4() + s4() + s4(); - }; - })(); - - $scope.setUniqueId = function (cell, index) { - return guid(); - }; - - $scope.addControl = function (editor, cell, index, initialize) { - - initialize = (initialize !== false); - - var newControl = { - value: null, - editor: editor, - $initializing: initialize - }; - - if (index === undefined) { - index = cell.controls.length; - } - - newControl.active = true; - - //populate control - $scope.initControl(newControl, index + 1); - - cell.controls.push(newControl); - - }; - - $scope.addTinyMce = function (cell) { - var rte = $scope.getEditor("rte"); - $scope.addControl(rte, cell); - }; - - $scope.getEditor = function (alias) { - return _.find($scope.availableEditors, function (editor) { return editor.alias === alias; }); - }; - - $scope.removeControl = function (cell, $index) { - $scope.currentControl = null; - cell.controls.splice($index, 1); - }; - - $scope.percentage = function (spans) { - return ((spans / $scope.model.config.items.columns) * 100).toFixed(8); - }; - - - $scope.clearPrompt = function (scopedObject, e) { - scopedObject.deletePrompt = false; - e.preventDefault(); - e.stopPropagation(); - }; - - $scope.togglePrompt = function (scopedObject) { - scopedObject.deletePrompt = !scopedObject.deletePrompt; - }; - - $scope.hidePrompt = function (scopedObject) { - scopedObject.deletePrompt = false; - }; - - $scope.toggleAddRow = function() { - $scope.showRowConfigurations = !$scope.showRowConfigurations; - }; - - - // ********************************************* - // Initialization - // these methods are called from ng-init on the template - // so we can controll their first load data - // - // intialization sets non-saved data like percentage sizing, allowed editors and - // other data that should all be pre-fixed with $ to strip it out on save - // ********************************************* - - // ********************************************* - // Init template + sections - // ********************************************* - $scope.initContent = function () { - var clear = true; - - //settings indicator shortcut - if ( ($scope.model.config.items.config && $scope.model.config.items.config.length > 0) || ($scope.model.config.items.styles && $scope.model.config.items.styles.length > 0)) { - $scope.hasSettings = true; - } - - //ensure the grid has a column value set, - //if nothing is found, set it to 12 - if ($scope.model.config.items.columns && angular.isString($scope.model.config.items.columns)) { - $scope.model.config.items.columns = parseInt($scope.model.config.items.columns); - } else { - $scope.model.config.items.columns = 12; - } - - if ($scope.model.value && $scope.model.value.sections && $scope.model.value.sections.length > 0 && $scope.model.value.sections[0].rows && $scope.model.value.sections[0].rows.length > 0) { - - if ($scope.model.value.name && angular.isArray($scope.model.config.items.templates)) { - - //This will occur if it is an existing value, in which case - // we need to determine which layout was applied by looking up - // the name - // TODO: We need to change this to an immutable ID!! - - var found = _.find($scope.model.config.items.templates, function (t) { - return t.name === $scope.model.value.name; - }); - - if (found && angular.isArray(found.sections) && found.sections.length === $scope.model.value.sections.length) { - - //Cool, we've found the template associated with our current value with matching sections counts, now we need to - // merge this template data on to our current value (as if it was new) so that we can preserve what is and isn't - // allowed for this template based on the current config. - - _.each(found.sections, function (templateSection, index) { - angular.extend($scope.model.value.sections[index], angular.copy(templateSection)); - }); - - } - } - - _.forEach($scope.model.value.sections, function (section, index) { - - if (section.grid > 0) { - $scope.initSection(section); - - //we do this to ensure that the grid can be reset by deleting the last row - if (section.rows.length > 0) { - clear = false; - } - } else { - $scope.model.value.sections.splice(index, 1); - } - }); - } else if ($scope.model.config.items.templates && $scope.model.config.items.templates.length === 1) { - $scope.addTemplate($scope.model.config.items.templates[0]); - clear = false; - } - - if (clear) { - $scope.model.value = undefined; - } - }; - - $scope.initSection = function (section) { - section.$percentage = $scope.percentage(section.grid); - - section.$allowedLayouts = getAllowedLayouts(section); - - if (!section.rows || section.rows.length === 0) { - section.rows = []; - if(section.$allowedLayouts.length === 1){ - $scope.addRow(section, section.$allowedLayouts[0]); - } - } else { - _.forEach(section.rows, function (row, index) { - if (!row.$initialized) { - var initd = $scope.initRow(row); - - //if init fails, remove - if (!initd) { - section.rows.splice(index, 1); - } else { - section.rows[index] = initd; - } - } - }); - - // if there is more than one row added - hide row add tools - $scope.showRowConfigurations = false; - } - }; - - - // ********************************************* - // Init layout / row - // ********************************************* - $scope.initRow = function (row) { - - //merge the layout data with the original config data - //if there are no config info on this, splice it out - var original = _.find($scope.model.config.items.layouts, function (o) { return o.name === row.name; }); - - if (!original) { - return null; - } else { - //make a copy to not touch the original config - original = angular.copy(original); - original.styles = row.styles; - original.config = row.config; - original.hasConfig = gridItemHasConfig(row.styles, row.config); - - - //sync area configuration - _.each(original.areas, function (area, areaIndex) { - - - if (area.grid > 0) { - var currentArea = row.areas[areaIndex]; - - if (currentArea) { - area.config = currentArea.config; - area.styles = currentArea.styles; - area.hasConfig = gridItemHasConfig(currentArea.styles, currentArea.config); - } - - //set editor permissions - if (!area.allowed || area.allowAll === true) { - area.$allowedEditors = $scope.availableEditors; - area.$allowsRTE = true; - } else { - area.$allowedEditors = _.filter($scope.availableEditors, function (editor) { - return _.indexOf(area.allowed, editor.alias) >= 0; - }); - - if (_.indexOf(area.allowed, "rte") >= 0) { - area.$allowsRTE = true; - } - } - - //copy over existing controls into the new areas - if (row.areas.length > areaIndex && row.areas[areaIndex].controls) { - area.controls = currentArea.controls; - - _.forEach(area.controls, function (control, controlIndex) { - $scope.initControl(control, controlIndex); - }); - - } else { - //if empty - area.controls = []; - - //if only one allowed editor - if(area.$allowedEditors.length === 1){ - $scope.addControl(area.$allowedEditors[0], area, 0, false); - } - } - - //set width - area.$percentage = $scope.percentage(area.grid); - area.$uniqueId = $scope.setUniqueId(); - - } else { - original.areas.splice(areaIndex, 1); - } - }); - - //replace the old row - original.$initialized = true; - - //set a disposable unique ID - original.$uniqueId = $scope.setUniqueId(); - - //set a no disposable unique ID (util for row styling) - original.id = !row.id ? $scope.setUniqueId() : row.id; - - return original; - } - - }; - - - // ********************************************* - // Init control - // ********************************************* - - $scope.initControl = function (control, index) { - control.$index = index; - control.$uniqueId = $scope.setUniqueId(); - - //error handling in case of missing editor.. - //should only happen if stripped earlier - if (!control.editor) { - control.$editorPath = "views/propertyeditors/grid/editors/error.html"; - } - - if (!control.$editorPath) { - var editorConfig = $scope.getEditor(control.editor.alias); - - if (editorConfig) { - control.editor = editorConfig; - - //if its an absolute path - if (control.editor.view.startsWith("/") || control.editor.view.startsWith("~/")) { - control.$editorPath = umbRequestHelper.convertVirtualToAbsolutePath(control.editor.view); - } - else { - //use convention - control.$editorPath = "views/propertyeditors/grid/editors/" + control.editor.view + ".html"; - } - } - else { - control.$editorPath = "views/propertyeditors/grid/editors/error.html"; - } - } - - - }; - - - gridService.getGridEditors().then(function (response) { - $scope.availableEditors = response.data; - - $scope.contentReady = true; - - // ********************************************* - // Init grid - // ********************************************* - $scope.initContent(); - - }); - - //Clean the grid value before submitting to the server, we don't need - // all of that grid configuration in the value to be stored!! All of that - // needs to be merged in at runtime to ensure that the real config values are used - // if they are ever updated. - - var unsubscribe = $scope.$on("formSubmitting", function () { - - if ($scope.model.value && $scope.model.value.sections) { - _.each($scope.model.value.sections, function(section) { - if (section.rows) { - _.each(section.rows, function (row) { - if (row.areas) { - _.each(row.areas, function (area) { - - //Remove the 'editors' - these are the allowed editors, these will - // be injected at runtime to this editor, it should not be persisted - - if (area.editors) { - delete area.editors; - } - - if (area.controls) { - _.each(area.controls, function (control) { - if (control.editor) { - //replace - var alias = control.editor.alias; - control.editor = { - alias: alias - }; - } - }); - } - }); - } - }); - } - }); - } - }); - - //when the scope is destroyed we need to unsubscribe - $scope.$on("$destroy", function () { - unsubscribe(); - }); - - }); - -angular.module("umbraco") - .controller("Umbraco.PropertyEditors.GridPrevalueEditorController", - function ($scope, $http, assetsService, $rootScope, dialogService, mediaResource, gridService, imageHelper, $timeout) { - - var emptyModel = { - styles:[ - { - label: "Set a background image", - description: "Set a row background", - key: "background-image", - view: "imagepicker", - modifier: "url({0})" - } - ], - - config:[ - { - label: "Class", - description: "Set a css class", - key: "class", - view: "textstring" - } - ], - - columns: 12, - templates:[ - { - name: "1 column layout", - sections: [ - { - grid: 12, - } - ] - }, - { - name: "2 column layout", - sections: [ - { - grid: 4, - }, - { - grid: 8 - } - ] - } - ], - - - layouts:[ - { - name: "Headline", - areas: [ - { - grid: 12, - editors: ["headline"] - } - ] - }, - { - name: "Article", - areas: [ - { - grid: 4 - }, - { - grid: 8 - } - ] - } - ] - }; - - /**************** - template - *****************/ - - $scope.configureTemplate = function(template) { - - var templatesCopy = angular.copy($scope.model.value.templates); - - if (template === undefined) { - template = { - name: "", - sections: [ - - ] - }; - $scope.model.value.templates.push(template); - } - - $scope.layoutConfigOverlay = {}; - $scope.layoutConfigOverlay.view = "views/propertyEditors/grid/dialogs/layoutconfig.html"; - $scope.layoutConfigOverlay.currentLayout = template; - $scope.layoutConfigOverlay.rows = $scope.model.value.layouts; - $scope.layoutConfigOverlay.columns = $scope.model.value.columns; - $scope.layoutConfigOverlay.show = true; - - $scope.layoutConfigOverlay.submit = function(model) { - $scope.layoutConfigOverlay.show = false; - $scope.layoutConfigOverlay = null; - }; - - $scope.layoutConfigOverlay.close = function(oldModel) { - - //reset templates - $scope.model.value.templates = templatesCopy; - - $scope.layoutConfigOverlay.show = false; - $scope.layoutConfigOverlay = null; - } - - }; - - $scope.deleteTemplate = function(index){ - $scope.model.value.templates.splice(index, 1); - }; - - - /**************** - Row - *****************/ - - $scope.configureLayout = function(layout) { - - var layoutsCopy = angular.copy($scope.model.value.layouts); - - if(layout === undefined){ - layout = { - name: "", - areas:[ - - ] - }; - $scope.model.value.layouts.push(layout); - } - - $scope.rowConfigOverlay = {}; - $scope.rowConfigOverlay.view = "views/propertyEditors/grid/dialogs/rowconfig.html"; - $scope.rowConfigOverlay.currentRow = layout; - $scope.rowConfigOverlay.editors = $scope.editors; - $scope.rowConfigOverlay.columns = $scope.model.value.columns; - $scope.rowConfigOverlay.show = true; - - $scope.rowConfigOverlay.submit = function(model) { - $scope.rowConfigOverlay.show = false; - $scope.rowConfigOverlay = null; - }; - - $scope.rowConfigOverlay.close = function(oldModel) { - $scope.model.value.layouts = layoutsCopy; - $scope.rowConfigOverlay.show = false; - $scope.rowConfigOverlay = null; - }; - - }; - - //var rowDeletesPending = false; - $scope.deleteLayout = function(index) { - - $scope.rowDeleteOverlay = {}; - $scope.rowDeleteOverlay.view = "views/propertyEditors/grid/dialogs/rowdeleteconfirm.html"; - $scope.rowDeleteOverlay.dialogData = { - rowName: $scope.model.value.layouts[index].name - }; - $scope.rowDeleteOverlay.show = true; - - $scope.rowDeleteOverlay.submit = function(model) { - - $scope.model.value.layouts.splice(index, 1); - - $scope.rowDeleteOverlay.show = false; - $scope.rowDeleteOverlay = null; - }; - - $scope.rowDeleteOverlay.close = function(oldModel) { - $scope.rowDeleteOverlay.show = false; - $scope.rowDeleteOverlay = null; - }; - - }; - - - /**************** - utillities - *****************/ - $scope.toggleCollection = function(collection, toggle){ - if(toggle){ - collection = []; - }else{ - delete collection; - } - }; - - $scope.percentage = function(spans){ - return ((spans / $scope.model.value.columns) * 100).toFixed(8); - }; - - $scope.zeroWidthFilter = function (cell) { - return cell.grid > 0; - }; - - /**************** - Config - *****************/ - - $scope.removeConfigValue = function(collection, index){ - collection.splice(index, 1); - }; - - var editConfigCollection = function(configValues, title, callback) { - - $scope.editConfigCollectionOverlay = {}; - $scope.editConfigCollectionOverlay.view = "views/propertyeditors/grid/dialogs/editconfig.html"; - $scope.editConfigCollectionOverlay.config = configValues; - $scope.editConfigCollectionOverlay.title = title; - $scope.editConfigCollectionOverlay.show = true; - - $scope.editConfigCollectionOverlay.submit = function(model) { - - callback(model.config) - - $scope.editConfigCollectionOverlay.show = false; - $scope.editConfigCollectionOverlay = null; - }; - - $scope.editConfigCollectionOverlay.close = function(oldModel) { - $scope.editConfigCollectionOverlay.show = false; - $scope.editConfigCollectionOverlay = null; - }; - - }; - - $scope.editConfig = function() { - editConfigCollection($scope.model.value.config, "Settings", function(data) { - $scope.model.value.config = data; - }); - }; - - $scope.editStyles = function() { - editConfigCollection($scope.model.value.styles, "Styling", function(data){ - $scope.model.value.styles = data; - }); - }; - - /**************** - editors - *****************/ - gridService.getGridEditors().then(function(response){ - $scope.editors = response.data; - }); - - - /* init grid data */ - if (!$scope.model.value || $scope.model.value === "" || !$scope.model.value.templates) { - $scope.model.value = emptyModel; - } else { - - if (!$scope.model.value.columns) { - $scope.model.value.columns = emptyModel.columns; - } - - - if (!$scope.model.value.config) { - $scope.model.value.config = []; - } - - if (!$scope.model.value.styles) { - $scope.model.value.styles = []; - } - } - - /**************** - Clean up - *****************/ - var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { - var ts = $scope.model.value.templates; - var ls = $scope.model.value.layouts; - - _.each(ts, function(t){ - _.each(t.sections, function(section, index){ - if(section.grid === 0){ - t.sections.splice(index, 1); - } - }); - }); - - _.each(ls, function(l){ - _.each(l.areas, function(area, index){ - if(area.grid === 0){ - l.areas.splice(index, 1); - } - }); - }); - }); - - //when the scope is destroyed we need to unsubscribe - $scope.$on('$destroy', function () { - unsubscribe(); - }); - - }); - -//this controller simply tells the dialogs service to open a mediaPicker window -//with a specified callback, this callback will receive an object with a selection on it -angular.module('umbraco') - .controller("Umbraco.PropertyEditors.ImageCropperController", - function ($rootScope, $routeParams, $scope, $log, mediaHelper, cropperHelper, $timeout, editorState, umbRequestHelper, fileManager, angularHelper) { - - var config = angular.copy($scope.model.config); - $scope.imageIsLoaded = false; - - //move previously saved value to the editor - if ($scope.model.value) { - //backwards compat with the old file upload (incase some-one swaps them..) - if (angular.isString($scope.model.value)) { - config.src = $scope.model.value; - $scope.model.value = config; - } else if ($scope.model.value.crops) { - //sync any config changes with the editor and drop outdated crops - _.each($scope.model.value.crops, function (saved) { - var configured = _.find(config.crops, function (item) { return item.alias === saved.alias }); - - if (configured && configured.height === saved.height && configured.width === saved.width) { - configured.coordinates = saved.coordinates; - } - }); - $scope.model.value.crops = config.crops; - - //restore focalpoint if missing - if (!$scope.model.value.focalPoint) { - $scope.model.value.focalPoint = { left: 0.5, top: 0.5 }; - } - } - - $scope.imageSrc = $scope.model.value.src; - } - - - //crop a specific crop - $scope.crop = function (crop) { - $scope.currentCrop = crop; - $scope.currentPoint = undefined; - }; - - //done cropping - $scope.done = function () { - $scope.currentCrop = undefined; - $scope.currentPoint = undefined; - }; - - //crop a specific crop - $scope.clear = function (crop) { - //clear current uploaded files - fileManager.setFiles($scope.model.alias, []); - - //clear the ui - $scope.imageSrc = undefined; - if ($scope.model.value) { - delete $scope.model.value; - } - - // set form to dirty to tricker discard changes dialog - var currForm = angularHelper.getCurrentForm($scope); - currForm.$setDirty(); - }; - - //show previews - $scope.togglePreviews = function () { - if ($scope.showPreviews) { - $scope.showPreviews = false; - $scope.tempShowPreviews = false; - } else { - $scope.showPreviews = true; - } - }; - - $scope.imageLoaded = function() { - $scope.imageIsLoaded = true; - }; - - //on image selected, update the cropper - $scope.$on("filesSelected", function (ev, args) { - $scope.model.value = config; - - if (args.files && args.files[0]) { - - fileManager.setFiles($scope.model.alias, args.files); - - var reader = new FileReader(); - reader.onload = function (e) { - - $scope.$apply(function () { - $scope.imageSrc = e.target.result; - }); - - }; - - reader.readAsDataURL(args.files[0]); - } - }); - - - //here we declare a special method which will be called whenever the value has changed from the server - $scope.model.onValueChanged = function (newVal, oldVal) { - //clear current uploaded files - fileManager.setFiles($scope.model.alias, []); - }; - - var unsubscribe = $scope.$on("formSubmitting", function () { - $scope.done(); - }); - - $scope.$on('$destroy', function () { - unsubscribe(); - }); - }) - .run(function (mediaHelper, umbRequestHelper) { - if (mediaHelper && mediaHelper.registerFileResolver) { - - //NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource - // they contain different data structures so if we need to query against it we need to be aware of this. - mediaHelper.registerFileResolver("Umbraco.ImageCropper", function (property, entity, thumbnail) { - if (property.value.src) { - - if (thumbnail === true) { - return property.value.src + "?width=500&mode=max&animationprocessmode=first"; - } - else { - return property.value.src; - } - - //this is a fallback in case the cropper has been asssigned a upload field - } - else if (angular.isString(property.value)) { - if (thumbnail) { - - if (mediaHelper.detectIfImageByExtension(property.value)) { - - var thumbnailUrl = umbRequestHelper.getApiUrl( - "imagesApiBaseUrl", - "GetBigThumbnail", - [{ originalImagePath: property.value }]); - - return thumbnailUrl; - } - else { - return null; - } - - } - else { - return property.value; - } - } - - return null; - }); - } - }); - -angular.module("umbraco").controller("Umbraco.PrevalueEditors.CropSizesController", - function ($scope, $timeout) { - - if (!$scope.model.value) { - $scope.model.value = []; - } - - $scope.remove = function (item, evt) { - evt.preventDefault(); - $scope.model.value = _.reject($scope.model.value, function (x) { - return x.alias === item.alias; - }); - }; - - $scope.edit = function (item, evt) { - evt.preventDefault(); - $scope.newItem = item; - }; - - $scope.cancel = function (evt) { - evt.preventDefault(); - $scope.newItem = null; - }; - - $scope.add = function (evt) { - evt.preventDefault(); - - if ($scope.newItem && $scope.newItem.alias && - angular.isNumber($scope.newItem.width) && angular.isNumber($scope.newItem.height) && - $scope.newItem.width > 0 && $scope.newItem.height > 0) { - - var exists = _.find($scope.model.value, function (item) { return $scope.newItem.alias === item.alias; }); - if (!exists) { - $scope.model.value.push($scope.newItem); - $scope.newItem = {}; - $scope.hasError = false; - return; - } - } - - //there was an error, do the highlight (will be set back by the directive) - $scope.hasError = true; - }; +angular.module("umbraco") + .controller("Umbraco.PropertyEditors.GridPrevalueEditor.LayoutConfigController", + function ($scope) { + + $scope.currentLayout = $scope.model.currentLayout; + $scope.columns = $scope.model.columns; + $scope.rows = $scope.model.rows; + + $scope.scaleUp = function(section, max, overflow){ + var add = 1; + if(overflow !== true){ + add = (max > 1) ? 1 : max; + } + //var add = (max > 1) ? 1 : max; + section.grid = section.grid+add; + }; + + $scope.scaleDown = function(section){ + var remove = (section.grid > 1) ? 1 : section.grid; + section.grid = section.grid-remove; + }; + + $scope.percentage = function(spans){ + return ((spans / $scope.columns) * 100).toFixed(8); + }; + + $scope.toggleCollection = function(collection, toggle){ + if(toggle){ + collection = []; + }else{ + delete collection; + } + }; + + + + /**************** + Section + *****************/ + $scope.configureSection = function(section, template){ + if(section === undefined){ + var space = ($scope.availableLayoutSpace > 4) ? 4 : $scope.availableLayoutSpace; + section = { + grid: space + }; + template.sections.push(section); + } + + $scope.currentSection = section; + }; + + + $scope.deleteSection = function(index){ + $scope.currentTemplate.sections.splice(index, 1); + }; + + $scope.closeSection = function(){ + $scope.currentSection = undefined; + }; + + $scope.$watch("currentLayout", function(layout){ + if(layout){ + var total = 0; + _.forEach(layout.sections, function(section){ + total = (total + section.grid); + }); + + $scope.availableLayoutSpace = $scope.columns - total; + } + }, true); + }); + +function RowConfigController($scope) { + + $scope.currentRow = $scope.model.currentRow; + $scope.editors = $scope.model.editors; + $scope.columns = $scope.model.columns; + + $scope.scaleUp = function(section, max, overflow) { + var add = 1; + if (overflow !== true) { + add = (max > 1) ? 1 : max; + } + //var add = (max > 1) ? 1 : max; + section.grid = section.grid + add; + }; + + $scope.scaleDown = function(section) { + var remove = (section.grid > 1) ? 1 : section.grid; + section.grid = section.grid - remove; + }; + + $scope.percentage = function(spans) { + return ((spans / $scope.columns) * 100).toFixed(8); + }; + + $scope.toggleCollection = function(collection, toggle) { + if (toggle) { + collection = []; + } + else { + delete collection; + } + }; + + + /**************** + area + *****************/ + $scope.configureCell = function(cell, row) { + if ($scope.currentCell && $scope.currentCell === cell) { + delete $scope.currentCell; + } + else { + if (cell === undefined) { + var available = $scope.availableRowSpace; + var space = 4; + + if (available < 4 && available > 0) { + space = available; + } + + cell = { + grid: space + }; + + row.areas.push(cell); + } + $scope.currentCell = cell; + } + }; + + $scope.deleteArea = function(index) { + $scope.currentRow.areas.splice(index, 1); + }; + $scope.closeArea = function() { + $scope.currentCell = undefined; + }; + + $scope.nameChanged = false; + var originalName = $scope.currentRow.name; + $scope.$watch("currentRow", function(row) { + if (row) { + + var total = 0; + _.forEach(row.areas, function(area) { + total = (total + area.grid); + }); + + $scope.availableRowSpace = $scope.columns - total; + + if (originalName) { + if (originalName != row.name) { + $scope.nameChanged = true; + } + else { + $scope.nameChanged = false; + } + } + } + }, true); + +} + +angular.module("umbraco").controller("Umbraco.PropertyEditors.GridPrevalueEditor.RowConfigController", RowConfigController); + +angular.module("umbraco") + .controller("Umbraco.PropertyEditors.Grid.EmbedController", + function ($scope, $rootScope, $timeout) { + + $scope.setEmbed = function(){ + $scope.embedDialog = {}; + $scope.embedDialog.view = "embed"; + $scope.embedDialog.show = true; + + $scope.embedDialog.submit = function(model) { + $scope.control.value = model.embed.preview; + $scope.embedDialog.show = false; + $scope.embedDialog = null; + }; + + $scope.embedDialog.close = function(oldModel) { + $scope.embedDialog.show = false; + $scope.embedDialog = null; + }; + + }; + + $timeout(function(){ + if($scope.control.$initializing){ + $scope.setEmbed(); + } + }, 200); +}); + +angular.module("umbraco") + .controller("Umbraco.PropertyEditors.Grid.MacroController", + function ($scope, $rootScope, $timeout, dialogService, macroResource, macroService, $routeParams) { + + $scope.title = "Click to insert macro"; + + $scope.setMacro = function(){ + + var dialogData = { + richTextEditor: true, + macroData: $scope.control.value || { + macroAlias: $scope.control.editor.config && $scope.control.editor.config.macroAlias + ? $scope.control.editor.config.macroAlias : "" + } + }; + + $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); + + $scope.control.value = { + macroAlias: macroObject.macroAlias, + macroParamsDictionary: macroObject.macroParamsDictionary + }; + + $scope.setPreview($scope.control.value ); + + $scope.macroPickerOverlay.show = false; + $scope.macroPickerOverlay = null; + }; + + $scope.macroPickerOverlay.close = function(oldModel) { + $scope.macroPickerOverlay.show = false; + $scope.macroPickerOverlay = null; + }; + + }; + + $scope.setPreview = function(macro){ + var contentId = $routeParams.id; + + macroResource.getMacroResultAsHtmlForEditor(macro.macroAlias, contentId, macro.macroParamsDictionary) + .then(function (htmlResult) { + $scope.title = macro.macroAlias; + if(htmlResult.trim().length > 0 && htmlResult.indexOf("Macro:") < 0){ + $scope.preview = htmlResult; + } + }); + + }; + + $timeout(function(){ + if($scope.control.$initializing){ + $scope.setMacro(); + }else if($scope.control.value){ + $scope.setPreview($scope.control.value); + } + }, 200); +}); + +angular.module("umbraco") + .controller("Umbraco.PropertyEditors.Grid.MediaController", + function ($scope, $rootScope, $timeout) { + + $scope.setImage = function(){ + $scope.mediaPickerOverlay = {}; + $scope.mediaPickerOverlay.view = "mediapicker"; + $scope.mediaPickerOverlay.cropSize = $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : undefined; + $scope.mediaPickerOverlay.showDetails = true; + $scope.mediaPickerOverlay.disableFolderSelect = true; + $scope.mediaPickerOverlay.onlyImages = true; + $scope.mediaPickerOverlay.show = true; + + $scope.mediaPickerOverlay.submit = function(model) { + var selectedImage = model.selectedImages[0]; + + $scope.control.value = { + focalPoint: selectedImage.focalPoint, + id: selectedImage.id, + image: selectedImage.image, + altText: selectedImage.altText + }; + + $scope.setUrl(); + + $scope.mediaPickerOverlay.show = false; + $scope.mediaPickerOverlay = null; + }; + + $scope.mediaPickerOverlay.close = function(oldModel) { + $scope.mediaPickerOverlay.show = false; + $scope.mediaPickerOverlay = null; + }; + }; + + $scope.setUrl = function(){ + + if($scope.control.value.image){ + var url = $scope.control.value.image; + + if($scope.control.editor.config && $scope.control.editor.config.size){ + url += "?width=" + $scope.control.editor.config.size.width; + url += "&height=" + $scope.control.editor.config.size.height; + url += "&animationprocessmode=first"; + + if($scope.control.value.focalPoint){ + url += "¢er=" + $scope.control.value.focalPoint.top +"," + $scope.control.value.focalPoint.left; + url += "&mode=crop"; + } + } + + // set default size if no crop present (moved from the view) + if (url.indexOf('?') == -1) + { + url += "?width=800&upscale=false&animationprocessmode=false" + } + $scope.url = url; + } + }; + + $timeout(function(){ + if($scope.control.$initializing){ + $scope.setImage(); + }else if($scope.control.value){ + $scope.setUrl(); + } + }, 200); +}); + +(function() { + "use strict"; + + function GridRichTextEditorController($scope, tinyMceService, macroService) { + + var vm = this; + + vm.openLinkPicker = openLinkPicker; + vm.openMediaPicker = openMediaPicker; + vm.openMacroPicker = openMacroPicker; + vm.openEmbed = openEmbed; + + function openLinkPicker(editor, currentTarget, anchorElement) { + vm.linkPickerOverlay = { + view: "linkpicker", + currentTarget: currentTarget, + show: true, + submit: function(model) { + tinyMceService.insertLinkInEditor(editor, model.target, anchorElement); + vm.linkPickerOverlay.show = false; + vm.linkPickerOverlay = null; + } + }; + } + + 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); + +})(); + +angular.module("umbraco") + .controller("Umbraco.PropertyEditors.Grid.TextStringController", + function ($scope, $rootScope, $timeout, dialogService) { + + + + }); + + +angular.module("umbraco") + .controller("Umbraco.PropertyEditors.GridController", + function ($scope, $http, assetsService, localizationService, $rootScope, dialogService, gridService, mediaResource, imageHelper, $timeout, umbRequestHelper, angularHelper) { + + // Grid status variables + var placeHolder = ""; + var currentForm = angularHelper.getCurrentForm($scope); + + $scope.currentRow = null; + $scope.currentCell = null; + $scope.currentToolsControl = null; + $scope.currentControl = null; + $scope.openRTEToolbarId = null; + $scope.hasSettings = false; + $scope.showRowConfigurations = true; + $scope.sortMode = false; + $scope.reorderKey = "general_reorder"; + + // ********************************************* + // Sortable options + // ********************************************* + + var draggedRteSettings; + + $scope.sortableOptionsRow = { + distance: 10, + cursor: "move", + placeholder: "ui-sortable-placeholder", + handle: ".umb-row-title-bar", + helper: "clone", + forcePlaceholderSize: true, + tolerance: "pointer", + zIndex: 999999999999999999, + scrollSensitivity: 100, + cursorAt: { + top: 40, + left: 60 + }, + + sort: function (event, ui) { + /* prevent vertical scroll out of the screen */ + var max = $(".umb-grid").width() - 150; + if (parseInt(ui.helper.css("left")) > max) { + ui.helper.css({ "left": max + "px" }); + } + if (parseInt(ui.helper.css("left")) < 20) { + ui.helper.css({ "left": 20 }); + } + }, + + start: function (e, ui) { + + // Fade out row when sorting + ui.item.context.style.display = "block"; + ui.item.context.style.opacity = "0.5"; + + draggedRteSettings = {}; + ui.item.find(".mceNoEditor").each(function () { + // remove all RTEs in the dragged row and save their settings + var id = $(this).attr("id"); + draggedRteSettings[id] = _.findWhere(tinyMCE.editors, { id: id }).settings; + // tinyMCE.execCommand("mceRemoveEditor", false, id); + }); + }, + + stop: function (e, ui) { + + // Fade in row when sorting stops + ui.item.context.style.opacity = "1"; + + // reset all RTEs affected by the dragging + ui.item.parents(".umb-column").find(".mceNoEditor").each(function () { + var id = $(this).attr("id"); + draggedRteSettings[id] = draggedRteSettings[id] || _.findWhere(tinyMCE.editors, { id: id }).settings; + tinyMCE.execCommand("mceRemoveEditor", false, id); + tinyMCE.init(draggedRteSettings[id]); + }); + currentForm.$setDirty(); + } + }; + + var notIncludedRte = []; + var cancelMove = false; + + $scope.sortableOptionsCell = { + distance: 10, + cursor: "move", + placeholder: "ui-sortable-placeholder", + handle: ".umb-control-handle", + helper: "clone", + connectWith: ".umb-cell-inner", + forcePlaceholderSize: true, + tolerance: "pointer", + zIndex: 999999999999999999, + scrollSensitivity: 100, + cursorAt: { + top: 45, + left: 90 + }, + + sort: function (event, ui) { + + /* prevent vertical scroll out of the screen */ + var position = parseInt(ui.item.parent().offset().left) + parseInt(ui.helper.css("left")) - parseInt($(".umb-grid").offset().left); + var max = $(".umb-grid").width() - 220; + if (position > max) { + ui.helper.css({ "left": max - parseInt(ui.item.parent().offset().left) + parseInt($(".umb-grid").offset().left) + "px" }); + } + if (position < 0) { + ui.helper.css({ "left": 0 - parseInt(ui.item.parent().offset().left) + parseInt($(".umb-grid").offset().left) + "px" }); + } + }, + + over: function (event, ui) { + var allowedEditors = $(event.target).scope().area.allowed; + + if ($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) { + + $scope.$apply(function () { + $(event.target).scope().area.dropNotAllowed = true; + }); + + ui.placeholder.hide(); + cancelMove = true; + } + else { + if ($(event.target).scope().area.controls.length == 0){ + + $scope.$apply(function () { + $(event.target).scope().area.dropOnEmpty = true; + }); + ui.placeholder.hide(); + } else { + ui.placeholder.show(); + } + cancelMove = false; + } + }, + + out: function(event, ui) { + $scope.$apply(function () { + $(event.target).scope().area.dropNotAllowed = false; + $(event.target).scope().area.dropOnEmpty = false; + }); + }, + + update: function (event, ui) { + /* add all RTEs which are affected by the dragging */ + if (!ui.sender) { + if (cancelMove) { + ui.item.sortable.cancel(); + } + ui.item.parents(".umb-cell.content").find(".mceNoEditor").each(function () { + if ($.inArray($(this).attr("id"), notIncludedRte) < 0) { + notIncludedRte.splice(0, 0, $(this).attr("id")); + } + }); + } + else { + $(event.target).find(".mceNoEditor").each(function () { + if ($.inArray($(this).attr("id"), notIncludedRte) < 0) { + notIncludedRte.splice(0, 0, $(this).attr("id")); + } + }); + } + currentForm.$setDirty(); + }, + + start: function (e, ui) { + + // fade out control when sorting + ui.item.context.style.display = "block"; + ui.item.context.style.opacity = "0.5"; + + // reset dragged RTE settings in case a RTE isn't dragged + draggedRteSettings = undefined; + ui.item.context.style.display = "block"; + ui.item.find(".mceNoEditor").each(function () { + notIncludedRte = []; + var editors = _.findWhere(tinyMCE.editors, { id: $(this).attr("id") }); + + // save the dragged RTE settings + if(editors) { + draggedRteSettings = editors.settings; + + // remove the dragged RTE + tinyMCE.execCommand("mceRemoveEditor", false, $(this).attr("id")); + + } + + }); + }, + + stop: function (e, ui) { + + // Fade in control when sorting stops + ui.item.context.style.opacity = "1"; + + ui.item.parents(".umb-cell-content").find(".mceNoEditor").each(function () { + if ($.inArray($(this).attr("id"), notIncludedRte) < 0) { + // add all dragged's neighbouring RTEs in the new cell + notIncludedRte.splice(0, 0, $(this).attr("id")); + } + }); + $timeout(function () { + // reconstruct the dragged RTE (could be undefined when dragging something else than RTE) + if (draggedRteSettings !== undefined) { + tinyMCE.init(draggedRteSettings); + } + + _.forEach(notIncludedRte, function (id) { + // reset all the other RTEs + if (draggedRteSettings === undefined || id !== draggedRteSettings.id) { + var rteSettings = _.findWhere(tinyMCE.editors, { id: id }).settings; + tinyMCE.execCommand("mceRemoveEditor", false, id); + tinyMCE.init(rteSettings); + } + }); + }, 500, false); + + $scope.$apply(function () { + + var cell = $(e.target).scope().area; + cell.hasActiveChild = hasActiveChild(cell, cell.controls); + cell.active = false; + }); + } + + }; + + $scope.toggleSortMode = function() { + $scope.sortMode = !$scope.sortMode; + if($scope.sortMode) { + $scope.reorderKey = "general_reorderDone"; + } else { + $scope.reorderKey = "general_reorder"; + } + }; + + $scope.showReorderButton = function() { + if($scope.model.value && $scope.model.value.sections) { + for(var i = 0; $scope.model.value.sections.length > i; i++) { + var section = $scope.model.value.sections[i]; + if(section.rows && section.rows.length > 0) { + return true; + } + } + } + }; + + // ********************************************* + // Add items overlay menu + // ********************************************* + $scope.openEditorOverlay = function(event, area, index, key) { + $scope.editorOverlay = { + view: "itempicker", + filter: false, + title: localizationService.localize("grid_insertControl"), + availableItems: area.$allowedEditors, + event: event, + show: true, + submit: function(model) { + $scope.addControl(model.selectedItem, area, index); + $scope.editorOverlay.show = false; + $scope.editorOverlay = null; + } + }; + }; + + // ********************************************* + // Template management functions + // ********************************************* + + $scope.addTemplate = function (template) { + $scope.model.value = angular.copy(template); + + //default row data + _.forEach($scope.model.value.sections, function (section) { + $scope.initSection(section); + }); + }; + + + // ********************************************* + // Row management function + // ********************************************* + + $scope.clickRow = function(index, rows) { + rows[index].active = true; + }; + + $scope.clickOutsideRow = function(index, rows) { + rows[index].active = false; + }; + + function getAllowedLayouts(section) { + + var layouts = $scope.model.config.items.layouts; + + //This will occur if it is a new section which has been + // created from a 'template' + if (section.allowed && section.allowed.length > 0) { + return _.filter(layouts, function (layout) { + return _.indexOf(section.allowed, layout.name) >= 0; + }); + } + else { + + + return layouts; + } + } + + $scope.addRow = function (section, layout) { + + //copy the selected layout into the rows collection + var row = angular.copy(layout); + + // Init row value + row = $scope.initRow(row); + + // Push the new row + if (row) { + section.rows.push(row); + } + + currentForm.$setDirty(); + + $scope.showRowConfigurations = false; + + }; + + $scope.removeRow = function (section, $index) { + if (section.rows.length > 0) { + section.rows.splice($index, 1); + $scope.currentRow = null; + $scope.openRTEToolbarId = null; + currentForm.$setDirty(); + } + + if(section.rows.length === 0) { + $scope.showRowConfigurations = true; + } + }; + + $scope.editGridItemSettings = function (gridItem, itemType) { + + placeHolder = "{0}"; + + var styles, config; + if (itemType === 'control') { + styles = null; + config = angular.copy(gridItem.editor.config.settings); + } else { + styles = _.filter(angular.copy($scope.model.config.items.styles), function (item) { return (item.applyTo === undefined || item.applyTo === itemType); }); + config = _.filter(angular.copy($scope.model.config.items.config), function (item) { return (item.applyTo === undefined || item.applyTo === itemType); }); + } + + if(angular.isObject(gridItem.config)){ + _.each(config, function(cfg){ + var val = gridItem.config[cfg.key]; + if(val){ + cfg.value = stripModifier(val, cfg.modifier); + } + }); + } + + if(angular.isObject(gridItem.styles)){ + _.each(styles, function(style){ + var val = gridItem.styles[style.key]; + if(val){ + style.value = stripModifier(val, style.modifier); + } + }); + } + + $scope.gridItemSettingsDialog = {}; + $scope.gridItemSettingsDialog.view = "views/propertyeditors/grid/dialogs/config.html"; + $scope.gridItemSettingsDialog.title = "Settings"; + $scope.gridItemSettingsDialog.styles = styles; + $scope.gridItemSettingsDialog.config = config; + + $scope.gridItemSettingsDialog.show = true; + + $scope.gridItemSettingsDialog.submit = function(model) { + + var styleObject = {}; + var configObject = {}; + + _.each(model.styles, function(style){ + if(style.value){ + styleObject[style.key] = addModifier(style.value, style.modifier); + } + }); + _.each(model.config, function (cfg) { + if (cfg.value) { + configObject[cfg.key] = addModifier(cfg.value, cfg.modifier); + } + }); + + gridItem.styles = styleObject; + gridItem.config = configObject; + gridItem.hasConfig = gridItemHasConfig(styleObject, configObject); + + currentForm.$setDirty(); + + $scope.gridItemSettingsDialog.show = false; + $scope.gridItemSettingsDialog = null; + }; + + $scope.gridItemSettingsDialog.close = function(oldModel) { + $scope.gridItemSettingsDialog.show = false; + $scope.gridItemSettingsDialog = null; + }; + + }; + + function stripModifier(val, modifier) { + if (!val || !modifier || modifier.indexOf(placeHolder) < 0) { + return val; + } else { + var paddArray = modifier.split(placeHolder); + if(paddArray.length == 1){ + if (modifier.indexOf(placeHolder) === 0) { + return val.slice(0, -paddArray[0].length); + } else { + return val.slice(paddArray[0].length, 0); + } + } else { + if (paddArray[1].length === 0) { + return val.slice(paddArray[0].length); + } + return val.slice(paddArray[0].length, -paddArray[1].length); + } + } + } + + var addModifier = function(val, modifier){ + if (!modifier || modifier.indexOf(placeHolder) < 0) { + return val; + } else { + return modifier.replace(placeHolder, val); + } + }; + + function gridItemHasConfig(styles, config) { + + if(_.isEmpty(styles) && _.isEmpty(config)) { + return false; + } else { + return true; + } + + } + + // ********************************************* + // Area management functions + // ********************************************* + + $scope.clickCell = function(index, cells, row) { + cells[index].active = true; + row.hasActiveChild = true; + }; + + $scope.clickOutsideCell = function(index, cells, row) { + cells[index].active = false; + row.hasActiveChild = hasActiveChild(row, cells); + }; + + $scope.cellPreview = function (cell) { + if (cell && cell.$allowedEditors) { + var editor = cell.$allowedEditors[0]; + return editor.icon; + } else { + return "icon-layout"; + } + }; + + + // ********************************************* + // Control management functions + // ********************************************* + $scope.clickControl = function (index, controls, cell) { + controls[index].active = true; + cell.hasActiveChild = true; + }; + + $scope.clickOutsideControl = function (index, controls, cell) { + controls[index].active = false; + cell.hasActiveChild = hasActiveChild(cell, controls); + }; + + function hasActiveChild(item, children) { + + var activeChild = false; + + for(var i = 0; children.length > i; i++) { + var child = children[i]; + + if(child.active) { + activeChild = true; + } + } + + if(activeChild) { + return true; + } + + } + + + var guid = (function () { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return function () { + return s4() + s4() + "-" + s4() + "-" + s4() + "-" + + s4() + "-" + s4() + s4() + s4(); + }; + })(); + + $scope.setUniqueId = function (cell, index) { + return guid(); + }; + + $scope.addControl = function (editor, cell, index, initialize) { + + initialize = (initialize !== false); + + var newControl = { + value: null, + editor: editor, + $initializing: initialize + }; + + if (index === undefined) { + index = cell.controls.length; + } + + newControl.active = true; + + //populate control + $scope.initControl(newControl, index + 1); + + cell.controls.push(newControl); + + }; + + $scope.addTinyMce = function (cell) { + var rte = $scope.getEditor("rte"); + $scope.addControl(rte, cell); + }; + + $scope.getEditor = function (alias) { + return _.find($scope.availableEditors, function (editor) { return editor.alias === alias; }); + }; + + $scope.removeControl = function (cell, $index) { + $scope.currentControl = null; + cell.controls.splice($index, 1); + }; + + $scope.percentage = function (spans) { + return ((spans / $scope.model.config.items.columns) * 100).toFixed(8); + }; + + + $scope.clearPrompt = function (scopedObject, e) { + scopedObject.deletePrompt = false; + e.preventDefault(); + e.stopPropagation(); + }; + + $scope.togglePrompt = function (scopedObject) { + scopedObject.deletePrompt = !scopedObject.deletePrompt; + }; + + $scope.hidePrompt = function (scopedObject) { + scopedObject.deletePrompt = false; + }; + + $scope.toggleAddRow = function() { + $scope.showRowConfigurations = !$scope.showRowConfigurations; + }; + + + // ********************************************* + // Initialization + // these methods are called from ng-init on the template + // so we can controll their first load data + // + // intialization sets non-saved data like percentage sizing, allowed editors and + // other data that should all be pre-fixed with $ to strip it out on save + // ********************************************* + + // ********************************************* + // Init template + sections + // ********************************************* + $scope.initContent = function () { + var clear = true; + + //settings indicator shortcut + if ( ($scope.model.config.items.config && $scope.model.config.items.config.length > 0) || ($scope.model.config.items.styles && $scope.model.config.items.styles.length > 0)) { + $scope.hasSettings = true; + } + + //ensure the grid has a column value set, + //if nothing is found, set it to 12 + if ($scope.model.config.items.columns && angular.isString($scope.model.config.items.columns)) { + $scope.model.config.items.columns = parseInt($scope.model.config.items.columns); + } else { + $scope.model.config.items.columns = 12; + } + + if ($scope.model.value && $scope.model.value.sections && $scope.model.value.sections.length > 0 && $scope.model.value.sections[0].rows && $scope.model.value.sections[0].rows.length > 0) { + + if ($scope.model.value.name && angular.isArray($scope.model.config.items.templates)) { + + //This will occur if it is an existing value, in which case + // we need to determine which layout was applied by looking up + // the name + // TODO: We need to change this to an immutable ID!! + + var found = _.find($scope.model.config.items.templates, function (t) { + return t.name === $scope.model.value.name; + }); + + if (found && angular.isArray(found.sections) && found.sections.length === $scope.model.value.sections.length) { + + //Cool, we've found the template associated with our current value with matching sections counts, now we need to + // merge this template data on to our current value (as if it was new) so that we can preserve what is and isn't + // allowed for this template based on the current config. + + _.each(found.sections, function (templateSection, index) { + angular.extend($scope.model.value.sections[index], angular.copy(templateSection)); + }); + + } + } + + _.forEach($scope.model.value.sections, function (section, index) { + + if (section.grid > 0) { + $scope.initSection(section); + + //we do this to ensure that the grid can be reset by deleting the last row + if (section.rows.length > 0) { + clear = false; + } + } else { + $scope.model.value.sections.splice(index, 1); + } + }); + } else if ($scope.model.config.items.templates && $scope.model.config.items.templates.length === 1) { + $scope.addTemplate($scope.model.config.items.templates[0]); + clear = false; + } + + if (clear) { + $scope.model.value = undefined; + } + }; + + $scope.initSection = function (section) { + section.$percentage = $scope.percentage(section.grid); + + section.$allowedLayouts = getAllowedLayouts(section); + + if (!section.rows || section.rows.length === 0) { + section.rows = []; + if(section.$allowedLayouts.length === 1){ + $scope.addRow(section, section.$allowedLayouts[0]); + } + } else { + _.forEach(section.rows, function (row, index) { + if (!row.$initialized) { + var initd = $scope.initRow(row); + + //if init fails, remove + if (!initd) { + section.rows.splice(index, 1); + } else { + section.rows[index] = initd; + } + } + }); + + // if there is more than one row added - hide row add tools + $scope.showRowConfigurations = false; + } + }; + + + // ********************************************* + // Init layout / row + // ********************************************* + $scope.initRow = function (row) { + + //merge the layout data with the original config data + //if there are no config info on this, splice it out + var original = _.find($scope.model.config.items.layouts, function (o) { return o.name === row.name; }); + + if (!original) { + return null; + } else { + //make a copy to not touch the original config + original = angular.copy(original); + original.styles = row.styles; + original.config = row.config; + original.hasConfig = gridItemHasConfig(row.styles, row.config); + + + //sync area configuration + _.each(original.areas, function (area, areaIndex) { + + + if (area.grid > 0) { + var currentArea = row.areas[areaIndex]; + + if (currentArea) { + area.config = currentArea.config; + area.styles = currentArea.styles; + area.hasConfig = gridItemHasConfig(currentArea.styles, currentArea.config); + } + + //set editor permissions + if (!area.allowed || area.allowAll === true) { + area.$allowedEditors = $scope.availableEditors; + area.$allowsRTE = true; + } else { + area.$allowedEditors = _.filter($scope.availableEditors, function (editor) { + return _.indexOf(area.allowed, editor.alias) >= 0; + }); + + if (_.indexOf(area.allowed, "rte") >= 0) { + area.$allowsRTE = true; + } + } + + //copy over existing controls into the new areas + if (row.areas.length > areaIndex && row.areas[areaIndex].controls) { + area.controls = currentArea.controls; + + _.forEach(area.controls, function (control, controlIndex) { + $scope.initControl(control, controlIndex); + }); + + } else { + //if empty + area.controls = []; + + //if only one allowed editor + if(area.$allowedEditors.length === 1){ + $scope.addControl(area.$allowedEditors[0], area, 0, false); + } + } + + //set width + area.$percentage = $scope.percentage(area.grid); + area.$uniqueId = $scope.setUniqueId(); + + } else { + original.areas.splice(areaIndex, 1); + } + }); + + //replace the old row + original.$initialized = true; + + //set a disposable unique ID + original.$uniqueId = $scope.setUniqueId(); + + //set a no disposable unique ID (util for row styling) + original.id = !row.id ? $scope.setUniqueId() : row.id; + + return original; + } + + }; + + + // ********************************************* + // Init control + // ********************************************* + + $scope.initControl = function (control, index) { + control.$index = index; + control.$uniqueId = $scope.setUniqueId(); + + //error handling in case of missing editor.. + //should only happen if stripped earlier + if (!control.editor) { + control.$editorPath = "views/propertyeditors/grid/editors/error.html"; + } + + if (!control.$editorPath) { + var editorConfig = $scope.getEditor(control.editor.alias); + + if (editorConfig) { + control.editor = editorConfig; + + //if its an absolute path + if (control.editor.view.startsWith("/") || control.editor.view.startsWith("~/")) { + control.$editorPath = umbRequestHelper.convertVirtualToAbsolutePath(control.editor.view); + } + else { + //use convention + control.$editorPath = "views/propertyeditors/grid/editors/" + control.editor.view + ".html"; + } + } + else { + control.$editorPath = "views/propertyeditors/grid/editors/error.html"; + } + } + + + }; + + + gridService.getGridEditors().then(function (response) { + $scope.availableEditors = response.data; + + $scope.contentReady = true; + + // ********************************************* + // Init grid + // ********************************************* + $scope.initContent(); + + }); + + //Clean the grid value before submitting to the server, we don't need + // all of that grid configuration in the value to be stored!! All of that + // needs to be merged in at runtime to ensure that the real config values are used + // if they are ever updated. + + var unsubscribe = $scope.$on("formSubmitting", function () { + + if ($scope.model.value && $scope.model.value.sections) { + _.each($scope.model.value.sections, function(section) { + if (section.rows) { + _.each(section.rows, function (row) { + if (row.areas) { + _.each(row.areas, function (area) { + + //Remove the 'editors' - these are the allowed editors, these will + // be injected at runtime to this editor, it should not be persisted + + if (area.editors) { + delete area.editors; + } + + if (area.controls) { + _.each(area.controls, function (control) { + if (control.editor) { + //replace + var alias = control.editor.alias; + control.editor = { + alias: alias + }; + } + }); + } + }); + } + }); + } + }); + } + }); + + //when the scope is destroyed we need to unsubscribe + $scope.$on("$destroy", function () { + unsubscribe(); + }); + + }); + +angular.module("umbraco") + .controller("Umbraco.PropertyEditors.GridPrevalueEditorController", + function ($scope, $http, assetsService, $rootScope, dialogService, mediaResource, gridService, imageHelper, $timeout) { + + var emptyModel = { + styles:[ + { + label: "Set a background image", + description: "Set a row background", + key: "background-image", + view: "imagepicker", + modifier: "url({0})" + } + ], + + config:[ + { + label: "Class", + description: "Set a css class", + key: "class", + view: "textstring" + } + ], + + columns: 12, + templates:[ + { + name: "1 column layout", + sections: [ + { + grid: 12, + } + ] + }, + { + name: "2 column layout", + sections: [ + { + grid: 4, + }, + { + grid: 8 + } + ] + } + ], + + + layouts:[ + { + name: "Headline", + areas: [ + { + grid: 12, + editors: ["headline"] + } + ] + }, + { + name: "Article", + areas: [ + { + grid: 4 + }, + { + grid: 8 + } + ] + } + ] + }; + + /**************** + template + *****************/ + + $scope.configureTemplate = function(template) { + + var templatesCopy = angular.copy($scope.model.value.templates); + + if (template === undefined) { + template = { + name: "", + sections: [ + + ] + }; + $scope.model.value.templates.push(template); + } + + $scope.layoutConfigOverlay = {}; + $scope.layoutConfigOverlay.view = "views/propertyEditors/grid/dialogs/layoutconfig.html"; + $scope.layoutConfigOverlay.currentLayout = template; + $scope.layoutConfigOverlay.rows = $scope.model.value.layouts; + $scope.layoutConfigOverlay.columns = $scope.model.value.columns; + $scope.layoutConfigOverlay.show = true; + + $scope.layoutConfigOverlay.submit = function(model) { + $scope.layoutConfigOverlay.show = false; + $scope.layoutConfigOverlay = null; + }; + + $scope.layoutConfigOverlay.close = function(oldModel) { + + //reset templates + $scope.model.value.templates = templatesCopy; + + $scope.layoutConfigOverlay.show = false; + $scope.layoutConfigOverlay = null; + } + + }; + + $scope.deleteTemplate = function(index){ + $scope.model.value.templates.splice(index, 1); + }; + + + /**************** + Row + *****************/ + + $scope.configureLayout = function(layout) { + + var layoutsCopy = angular.copy($scope.model.value.layouts); + + if(layout === undefined){ + layout = { + name: "", + areas:[ + + ] + }; + $scope.model.value.layouts.push(layout); + } + + $scope.rowConfigOverlay = {}; + $scope.rowConfigOverlay.view = "views/propertyEditors/grid/dialogs/rowconfig.html"; + $scope.rowConfigOverlay.currentRow = layout; + $scope.rowConfigOverlay.editors = $scope.editors; + $scope.rowConfigOverlay.columns = $scope.model.value.columns; + $scope.rowConfigOverlay.show = true; + + $scope.rowConfigOverlay.submit = function(model) { + $scope.rowConfigOverlay.show = false; + $scope.rowConfigOverlay = null; + }; + + $scope.rowConfigOverlay.close = function(oldModel) { + $scope.model.value.layouts = layoutsCopy; + $scope.rowConfigOverlay.show = false; + $scope.rowConfigOverlay = null; + }; + + }; + + //var rowDeletesPending = false; + $scope.deleteLayout = function(index) { + + $scope.rowDeleteOverlay = {}; + $scope.rowDeleteOverlay.view = "views/propertyEditors/grid/dialogs/rowdeleteconfirm.html"; + $scope.rowDeleteOverlay.dialogData = { + rowName: $scope.model.value.layouts[index].name + }; + $scope.rowDeleteOverlay.show = true; + + $scope.rowDeleteOverlay.submit = function(model) { + + $scope.model.value.layouts.splice(index, 1); + + $scope.rowDeleteOverlay.show = false; + $scope.rowDeleteOverlay = null; + }; + + $scope.rowDeleteOverlay.close = function(oldModel) { + $scope.rowDeleteOverlay.show = false; + $scope.rowDeleteOverlay = null; + }; + + }; + + + /**************** + utillities + *****************/ + $scope.toggleCollection = function(collection, toggle){ + if(toggle){ + collection = []; + }else{ + delete collection; + } + }; + + $scope.percentage = function(spans){ + return ((spans / $scope.model.value.columns) * 100).toFixed(8); + }; + + $scope.zeroWidthFilter = function (cell) { + return cell.grid > 0; + }; + + /**************** + Config + *****************/ + + $scope.removeConfigValue = function(collection, index){ + collection.splice(index, 1); + }; + + var editConfigCollection = function(configValues, title, callback) { + + $scope.editConfigCollectionOverlay = {}; + $scope.editConfigCollectionOverlay.view = "views/propertyeditors/grid/dialogs/editconfig.html"; + $scope.editConfigCollectionOverlay.config = configValues; + $scope.editConfigCollectionOverlay.title = title; + $scope.editConfigCollectionOverlay.show = true; + + $scope.editConfigCollectionOverlay.submit = function(model) { + + callback(model.config) + + $scope.editConfigCollectionOverlay.show = false; + $scope.editConfigCollectionOverlay = null; + }; + + $scope.editConfigCollectionOverlay.close = function(oldModel) { + $scope.editConfigCollectionOverlay.show = false; + $scope.editConfigCollectionOverlay = null; + }; + + }; + + $scope.editConfig = function() { + editConfigCollection($scope.model.value.config, "Settings", function(data) { + $scope.model.value.config = data; + }); + }; + + $scope.editStyles = function() { + editConfigCollection($scope.model.value.styles, "Styling", function(data){ + $scope.model.value.styles = data; + }); + }; + + /**************** + editors + *****************/ + gridService.getGridEditors().then(function(response){ + $scope.editors = response.data; + }); + + + /* init grid data */ + if (!$scope.model.value || $scope.model.value === "" || !$scope.model.value.templates) { + $scope.model.value = emptyModel; + } else { + + if (!$scope.model.value.columns) { + $scope.model.value.columns = emptyModel.columns; + } + + + if (!$scope.model.value.config) { + $scope.model.value.config = []; + } + + if (!$scope.model.value.styles) { + $scope.model.value.styles = []; + } + } + + /**************** + Clean up + *****************/ + var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { + var ts = $scope.model.value.templates; + var ls = $scope.model.value.layouts; + + _.each(ts, function(t){ + _.each(t.sections, function(section, index){ + if(section.grid === 0){ + t.sections.splice(index, 1); + } + }); + }); + + _.each(ls, function(l){ + _.each(l.areas, function(area, index){ + if(area.grid === 0){ + l.areas.splice(index, 1); + } + }); + }); + }); + + //when the scope is destroyed we need to unsubscribe + $scope.$on('$destroy', function () { + unsubscribe(); + }); + + }); + +//this controller simply tells the dialogs service to open a mediaPicker window +//with a specified callback, this callback will receive an object with a selection on it +angular.module('umbraco') + .controller("Umbraco.PropertyEditors.ImageCropperController", + function ($rootScope, $routeParams, $scope, $log, mediaHelper, cropperHelper, $timeout, editorState, umbRequestHelper, fileManager, angularHelper) { + + var config = angular.copy($scope.model.config); + $scope.imageIsLoaded = false; + + //move previously saved value to the editor + if ($scope.model.value) { + //backwards compat with the old file upload (incase some-one swaps them..) + if (angular.isString($scope.model.value)) { + config.src = $scope.model.value; + $scope.model.value = config; + } else if ($scope.model.value.crops) { + //sync any config changes with the editor and drop outdated crops + _.each($scope.model.value.crops, function (saved) { + var configured = _.find(config.crops, function (item) { return item.alias === saved.alias }); + + if (configured && configured.height === saved.height && configured.width === saved.width) { + configured.coordinates = saved.coordinates; + } + }); + $scope.model.value.crops = config.crops; + + //restore focalpoint if missing + if (!$scope.model.value.focalPoint) { + $scope.model.value.focalPoint = { left: 0.5, top: 0.5 }; + } + } + + $scope.imageSrc = $scope.model.value.src; + } + + + //crop a specific crop + $scope.crop = function (crop) { + $scope.currentCrop = crop; + $scope.currentPoint = undefined; + }; + + //done cropping + $scope.done = function () { + $scope.currentCrop = undefined; + $scope.currentPoint = undefined; + }; + + //crop a specific crop + $scope.clear = function (crop) { + //clear current uploaded files + fileManager.setFiles($scope.model.alias, []); + + //clear the ui + $scope.imageSrc = undefined; + if ($scope.model.value) { + delete $scope.model.value; + } + + // set form to dirty to tricker discard changes dialog + var currForm = angularHelper.getCurrentForm($scope); + currForm.$setDirty(); + }; + + //show previews + $scope.togglePreviews = function () { + if ($scope.showPreviews) { + $scope.showPreviews = false; + $scope.tempShowPreviews = false; + } else { + $scope.showPreviews = true; + } + }; + + $scope.imageLoaded = function() { + $scope.imageIsLoaded = true; + }; + + //on image selected, update the cropper + $scope.$on("filesSelected", function (ev, args) { + $scope.model.value = config; + + if (args.files && args.files[0]) { + + fileManager.setFiles($scope.model.alias, args.files); + + var reader = new FileReader(); + reader.onload = function (e) { + + $scope.$apply(function () { + $scope.imageSrc = e.target.result; + }); + + }; + + reader.readAsDataURL(args.files[0]); + } + }); + + + //here we declare a special method which will be called whenever the value has changed from the server + $scope.model.onValueChanged = function (newVal, oldVal) { + //clear current uploaded files + fileManager.setFiles($scope.model.alias, []); + }; + + var unsubscribe = $scope.$on("formSubmitting", function () { + $scope.done(); + }); + + $scope.$on('$destroy', function () { + unsubscribe(); + }); + }) + .run(function (mediaHelper, umbRequestHelper) { + if (mediaHelper && mediaHelper.registerFileResolver) { + + //NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource + // they contain different data structures so if we need to query against it we need to be aware of this. + mediaHelper.registerFileResolver("Umbraco.ImageCropper", function (property, entity, thumbnail) { + if (property.value.src) { + + if (thumbnail === true) { + return property.value.src + "?width=500&mode=max&animationprocessmode=first"; + } + else { + return property.value.src; + } + + //this is a fallback in case the cropper has been asssigned a upload field + } + else if (angular.isString(property.value)) { + if (thumbnail) { + + if (mediaHelper.detectIfImageByExtension(property.value)) { + + var thumbnailUrl = umbRequestHelper.getApiUrl( + "imagesApiBaseUrl", + "GetBigThumbnail", + [{ originalImagePath: property.value }]); + + return thumbnailUrl; + } + else { + return null; + } + + } + else { + return property.value; + } + } + + return null; + }); + } + }); + +angular.module("umbraco").controller("Umbraco.PrevalueEditors.CropSizesController", + function ($scope, $timeout) { + + if (!$scope.model.value) { + $scope.model.value = []; + } + + $scope.remove = function (item, evt) { + evt.preventDefault(); + $scope.model.value = _.reject($scope.model.value, function (x) { + return x.alias === item.alias; + }); + }; + + $scope.edit = function (item, evt) { + evt.preventDefault(); + $scope.newItem = item; + }; + + $scope.cancel = function (evt) { + evt.preventDefault(); + $scope.newItem = null; + }; + + $scope.add = function (evt) { + evt.preventDefault(); + + if ($scope.newItem && $scope.newItem.alias && + angular.isNumber($scope.newItem.width) && angular.isNumber($scope.newItem.height) && + $scope.newItem.width > 0 && $scope.newItem.height > 0) { + + var exists = _.find($scope.model.value, function (item) { return $scope.newItem.alias === item.alias; }); + if (!exists) { + $scope.model.value.push($scope.newItem); + $scope.newItem = {}; + $scope.hasError = false; + return; + } + } + + //there was an error, do the highlight (will be set back by the directive) + $scope.hasError = true; + }; }); -function includePropsPreValsController($rootScope, $scope, localizationService, contentTypeResource) { - - if (!$scope.model.value) { - $scope.model.value = []; - } - - $scope.propertyAliases = []; - $scope.selectedField = null; - $scope.systemFields = [ - { value: "sortOrder" }, - { value: "updateDate" }, - { value: "updater" }, - { value: "createDate" }, - { value: "owner" }, - { value: "published"}, - { value: "contentTypeAlias" }, - { value: "email" }, - { value: "username" } - ]; - - $scope.getLocalizedKey = function(alias) { - switch (alias) { - case "name": - return "general_name"; - case "sortOrder": - return "general_sort"; - case "updateDate": - return "content_updateDate"; - case "updater": - return "content_updatedBy"; - case "createDate": - return "content_createDate"; - case "owner": - return "content_createBy"; - case "published": - return "content_isPublished"; - case "contentTypeAlias": - //NOTE: This will just be 'Document' type even if it's for media/members since this is just a pre-val editor and we don't have a key for 'Content Type Alias' - return "content_documentType"; - case "email": - return "general_email"; - case "username": - return "general_username"; - } - return alias; - } - - $scope.removeField = function(e) { - $scope.model.value = _.reject($scope.model.value, function (x) { - return x.alias === e.alias; - }); - } - - //now we'll localize these strings, for some reason the directive doesn't work inside of the select group with an ng-model declared - _.each($scope.systemFields, function (e, i) { - var key = $scope.getLocalizedKey(e.value); - localizationService.localize(key).then(function (v) { - e.name = v; - - switch (e.value) { - case "updater": - e.name += " (Content only)"; - break; - case "published": - e.name += " (Content only)"; - break; - case "email": - e.name += " (Members only)"; - break; - case "username": - e.name += " (Members only)"; - break; - } - - }); - }); - - // Return a helper with preserved width of cells - var fixHelper = function (e, ui) { - var h = ui.clone(); - - h.children().each(function () { - $(this).width($(this).width()); - }); - h.css("background-color", "lightgray"); - - return h; - }; - - $scope.sortableOptions = { - helper: fixHelper, - handle: ".handle", - opacity: 0.5, - axis: 'y', - containment: 'parent', - cursor: 'move', - items: '> tr', - tolerance: 'pointer', - update: function (e, ui) { - - // Get the new and old index for the moved element (using the text as the identifier) - var newIndex = ui.item.index(); - var movedAlias = $('.alias-value', ui.item).text().trim(); - var originalIndex = getAliasIndexByText(movedAlias); - - // Move the element in the model - if (originalIndex > -1) { - var movedElement = $scope.model.value[originalIndex]; - $scope.model.value.splice(originalIndex, 1); - $scope.model.value.splice(newIndex, 0, movedElement); - } - } - }; - - contentTypeResource.getAllPropertyTypeAliases().then(function(data) { - $scope.propertyAliases = data; - }); - - $scope.addField = function () { - - var val = $scope.selectedField; - var isSystem = val.startsWith("_system_"); - if (isSystem) { - val = val.trimStart("_system_"); - } - - var exists = _.find($scope.model.value, function (i) { - return i.alias === val; - }); - if (!exists) { - $scope.model.value.push({ - alias: val, - isSystem: isSystem ? 1 : 0 - }); - } - } - - function getAliasIndexByText(value) { - for (var i = 0; i < $scope.model.value.length; i++) { - if ($scope.model.value[i].alias === value) { - return i; - } - } - - return -1; - } - -} - - +function includePropsPreValsController($rootScope, $scope, localizationService, contentTypeResource) { + + if (!$scope.model.value) { + $scope.model.value = []; + } + + $scope.propertyAliases = []; + $scope.selectedField = null; + $scope.systemFields = [ + { value: "sortOrder" }, + { value: "updateDate" }, + { value: "updater" }, + { value: "createDate" }, + { value: "owner" }, + { value: "published"}, + { value: "contentTypeAlias" }, + { value: "email" }, + { value: "username" } + ]; + + $scope.getLocalizedKey = function(alias) { + switch (alias) { + case "name": + return "general_name"; + case "sortOrder": + return "general_sort"; + case "updateDate": + return "content_updateDate"; + case "updater": + return "content_updatedBy"; + case "createDate": + return "content_createDate"; + case "owner": + return "content_createBy"; + case "published": + return "content_isPublished"; + case "contentTypeAlias": + //NOTE: This will just be 'Document' type even if it's for media/members since this is just a pre-val editor and we don't have a key for 'Content Type Alias' + return "content_documentType"; + case "email": + return "general_email"; + case "username": + return "general_username"; + } + return alias; + } + + $scope.removeField = function(e) { + $scope.model.value = _.reject($scope.model.value, function (x) { + return x.alias === e.alias; + }); + } + + //now we'll localize these strings, for some reason the directive doesn't work inside of the select group with an ng-model declared + _.each($scope.systemFields, function (e, i) { + var key = $scope.getLocalizedKey(e.value); + localizationService.localize(key).then(function (v) { + e.name = v; + + switch (e.value) { + case "updater": + e.name += " (Content only)"; + break; + case "published": + e.name += " (Content only)"; + break; + case "email": + e.name += " (Members only)"; + break; + case "username": + e.name += " (Members only)"; + break; + } + + }); + }); + + // Return a helper with preserved width of cells + var fixHelper = function (e, ui) { + var h = ui.clone(); + + h.children().each(function () { + $(this).width($(this).width()); + }); + h.css("background-color", "lightgray"); + + return h; + }; + + $scope.sortableOptions = { + helper: fixHelper, + handle: ".handle", + opacity: 0.5, + axis: 'y', + containment: 'parent', + cursor: 'move', + items: '> tr', + tolerance: 'pointer', + update: function (e, ui) { + + // Get the new and old index for the moved element (using the text as the identifier) + var newIndex = ui.item.index(); + var movedAlias = $('.alias-value', ui.item).text().trim(); + var originalIndex = getAliasIndexByText(movedAlias); + + // Move the element in the model + if (originalIndex > -1) { + var movedElement = $scope.model.value[originalIndex]; + $scope.model.value.splice(originalIndex, 1); + $scope.model.value.splice(newIndex, 0, movedElement); + } + } + }; + + contentTypeResource.getAllPropertyTypeAliases().then(function(data) { + $scope.propertyAliases = data; + }); + + $scope.addField = function () { + + var val = $scope.selectedField; + var isSystem = val.startsWith("_system_"); + if (isSystem) { + val = val.trimStart("_system_"); + } + + var exists = _.find($scope.model.value, function (i) { + return i.alias === val; + }); + if (!exists) { + $scope.model.value.push({ + alias: val, + isSystem: isSystem ? 1 : 0 + }); + } + } + + function getAliasIndexByText(value) { + for (var i = 0; i < $scope.model.value.length; i++) { + if ($scope.model.value[i].alias === value) { + return i; + } + } + + return -1; + } + +} + + angular.module("umbraco").controller("Umbraco.PrevalueEditors.IncludePropertiesListViewController", includePropsPreValsController); -/** - * @ngdoc controller - * @name Umbraco.PrevalueEditors.ListViewLayoutsPreValsController - * @function - * - * @description - * The controller for configuring layouts for list views - */ -(function() { - "use strict"; - - function ListViewLayoutsPreValsController($scope) { - - var vm = this; - vm.focusLayoutName = false; - - vm.layoutsSortableOptions = { - distance: 10, - tolerance: "pointer", - opacity: 0.7, - scroll: true, - cursor: "move", - handle: ".list-view-layout__sort-handle" - }; - - vm.addLayout = addLayout; - vm.showPrompt = showPrompt; - vm.hidePrompt = hidePrompt; - vm.removeLayout = removeLayout; - vm.openIconPicker = openIconPicker; - - function activate() { - - - - } - - function addLayout() { - - vm.focusLayoutName = false; - - var layout = { - "name": "", - "path": "", - "icon": "icon-stop", - "selected": true - }; - - $scope.model.value.push(layout); - - } - - function showPrompt(layout) { - layout.deletePrompt = true; - } - - function hidePrompt(layout) { - layout.deletePrompt = false; - } - - function removeLayout($index, layout) { - $scope.model.value.splice($index, 1); - } - - function openIconPicker(layout) { - vm.iconPickerDialog = { - view: "iconpicker", - show: true, - submit: function(model) { - if (model.color) { - layout.icon = model.icon + " " + model.color; - } else { - layout.icon = model.icon; - } - vm.focusLayoutName = true; - vm.iconPickerDialog.show = false; - vm.iconPickerDialog = null; - } - }; - } - - activate(); - - } - - angular.module("umbraco").controller("Umbraco.PrevalueEditors.ListViewLayoutsPreValsController", ListViewLayoutsPreValsController); - -})(); - -/** - * @ngdoc controller - * @name Umbraco.Editors.DocumentType.EditController - * @function - * - * @description - * The controller for the content type editor - */ -(function() { - "use strict"; - - function ListViewGridLayoutController($scope, $routeParams, mediaHelper, mediaResource, $location, listViewHelper) { - - var vm = this; - - vm.nodeId = $scope.contentId; - //vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes); - //instead of passing in a whitelist, we pass in a blacklist by adding ! to the ext - vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.disallowedUploadFiles).replace(/\./g, "!."); - - vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB"; - vm.activeDrag = false; - vm.mediaDetailsTooltip = {}; - vm.itemsWithoutFolders = []; - vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20'; - - vm.dragEnter = dragEnter; - vm.dragLeave = dragLeave; - vm.onFilesQueue = onFilesQueue; - vm.onUploadComplete = onUploadComplete; - - vm.hoverMediaItemDetails = hoverMediaItemDetails; - vm.selectContentItem = selectContentItem; - vm.selectItem = selectItem; - vm.selectFolder = selectFolder; - vm.goToItem = goToItem; - - function activate() { - vm.itemsWithoutFolders = filterOutFolders($scope.items); - } - - function filterOutFolders(items) { - - var newArray = []; - - if(items && items.length ) { - - for (var i = 0; items.length > i; i++) { - var item = items[i]; - var isFolder = !mediaHelper.hasFilePropertyType(item); - - if (!isFolder) { - newArray.push(item); - } - } - - } - - return newArray; - } - - function dragEnter(el, event) { - vm.activeDrag = true; - } - - function dragLeave(el, event) { - vm.activeDrag = false; - } - - function onFilesQueue() { - vm.activeDrag = false; - } - - function onUploadComplete() { - $scope.getContent($scope.contentId); - } - - function hoverMediaItemDetails(item, event, hover) { - - if (hover && !vm.mediaDetailsTooltip.show) { - - vm.mediaDetailsTooltip.event = event; - vm.mediaDetailsTooltip.item = item; - vm.mediaDetailsTooltip.show = true; - - } else if (!hover && vm.mediaDetailsTooltip.show) { - - vm.mediaDetailsTooltip.show = false; - - } - - } - - function selectContentItem(item, $event, $index) { - listViewHelper.selectHandler(item, $index, $scope.items, $scope.selection, $event); - } - - function selectItem(item, $event, $index) { - listViewHelper.selectHandler(item, $index, vm.itemsWithoutFolders, $scope.selection, $event); - } - - function selectFolder(folder, $event, $index) { - listViewHelper.selectHandler(folder, $index, $scope.folders, $scope.selection, $event); - } - - function goToItem(item, $event, $index) { - $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id); - } - - activate(); - - } - - angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.GridLayoutController", ListViewGridLayoutController); - -})(); - -(function() { - "use strict"; - - function ListViewListLayoutController($scope, listViewHelper, $location, mediaHelper) { - - var vm = this; - - vm.nodeId = $scope.contentId; - //vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes); - //instead of passing in a whitelist, we pass in a blacklist by adding ! to the ext - vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.disallowedUploadFiles).replace(/./g, "!."); - vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB"; - vm.activeDrag = false; - vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20'; - - vm.selectItem = selectItem; - vm.clickItem = clickItem; - vm.selectAll = selectAll; - vm.isSelectedAll = isSelectedAll; - vm.isSortDirection = isSortDirection; - vm.sort = sort; - vm.dragEnter = dragEnter; - vm.dragLeave = dragLeave; - vm.onFilesQueue = onFilesQueue; - vm.onUploadComplete = onUploadComplete; - - function selectAll($event) { - listViewHelper.selectAllItems($scope.items, $scope.selection, $event); - } - - function isSelectedAll() { - return listViewHelper.isSelectedAll($scope.items, $scope.selection); - } - - function selectItem(selectedItem, $index, $event) { - listViewHelper.selectHandler(selectedItem, $index, $scope.items, $scope.selection, $event); - } - - function clickItem(item) { - $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id); - } - - function isSortDirection(col, direction) { - return listViewHelper.setSortingDirection(col, direction, $scope.options); - } - - function sort(field, allow) { - if (allow) { - listViewHelper.setSorting(field, allow, $scope.options); - $scope.getContent($scope.contentId); - } - } - - // Dropzone upload functions - function dragEnter(el, event) { - vm.activeDrag = true; - } - - function dragLeave(el, event) { - vm.activeDrag = false; - } - - function onFilesQueue() { - vm.activeDrag = false; - } - - function onUploadComplete() { - $scope.getContent($scope.contentId); - } - - } - - angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.ListLayoutController", ListViewListLayoutController); - -})(); +/** + * @ngdoc controller + * @name Umbraco.PrevalueEditors.ListViewLayoutsPreValsController + * @function + * + * @description + * The controller for configuring layouts for list views + */ +(function() { + "use strict"; + + function ListViewLayoutsPreValsController($scope) { + + var vm = this; + vm.focusLayoutName = false; + + vm.layoutsSortableOptions = { + distance: 10, + tolerance: "pointer", + opacity: 0.7, + scroll: true, + cursor: "move", + handle: ".list-view-layout__sort-handle" + }; + + vm.addLayout = addLayout; + vm.showPrompt = showPrompt; + vm.hidePrompt = hidePrompt; + vm.removeLayout = removeLayout; + vm.openIconPicker = openIconPicker; + + function activate() { + + + + } + + function addLayout() { + + vm.focusLayoutName = false; + + var layout = { + "name": "", + "path": "", + "icon": "icon-stop", + "selected": true + }; + + $scope.model.value.push(layout); + + } + + function showPrompt(layout) { + layout.deletePrompt = true; + } + + function hidePrompt(layout) { + layout.deletePrompt = false; + } + + function removeLayout($index, layout) { + $scope.model.value.splice($index, 1); + } + + function openIconPicker(layout) { + vm.iconPickerDialog = { + view: "iconpicker", + show: true, + submit: function(model) { + if (model.color) { + layout.icon = model.icon + " " + model.color; + } else { + layout.icon = model.icon; + } + vm.focusLayoutName = true; + vm.iconPickerDialog.show = false; + vm.iconPickerDialog = null; + } + }; + } + + activate(); + + } + + angular.module("umbraco").controller("Umbraco.PrevalueEditors.ListViewLayoutsPreValsController", ListViewLayoutsPreValsController); + +})(); + +/** + * @ngdoc controller + * @name Umbraco.Editors.DocumentType.EditController + * @function + * + * @description + * The controller for the content type editor + */ +(function() { + "use strict"; + + function ListViewGridLayoutController($scope, $routeParams, mediaHelper, mediaResource, $location, listViewHelper) { + + var vm = this; + + vm.nodeId = $scope.contentId; + //vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes); + //instead of passing in a whitelist, we pass in a blacklist by adding ! to the ext + vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.disallowedUploadFiles).replace(/\./g, "!."); + + vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB"; + vm.activeDrag = false; + vm.mediaDetailsTooltip = {}; + vm.itemsWithoutFolders = []; + vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20'; + + vm.dragEnter = dragEnter; + vm.dragLeave = dragLeave; + vm.onFilesQueue = onFilesQueue; + vm.onUploadComplete = onUploadComplete; + + vm.hoverMediaItemDetails = hoverMediaItemDetails; + vm.selectContentItem = selectContentItem; + vm.selectItem = selectItem; + vm.selectFolder = selectFolder; + vm.goToItem = goToItem; + + function activate() { + vm.itemsWithoutFolders = filterOutFolders($scope.items); + } + + function filterOutFolders(items) { + + var newArray = []; + + if(items && items.length ) { + + for (var i = 0; items.length > i; i++) { + var item = items[i]; + var isFolder = !mediaHelper.hasFilePropertyType(item); + + if (!isFolder) { + newArray.push(item); + } + } + + } + + return newArray; + } + + function dragEnter(el, event) { + vm.activeDrag = true; + } + + function dragLeave(el, event) { + vm.activeDrag = false; + } + + function onFilesQueue() { + vm.activeDrag = false; + } + + function onUploadComplete() { + $scope.getContent($scope.contentId); + } + + function hoverMediaItemDetails(item, event, hover) { + + if (hover && !vm.mediaDetailsTooltip.show) { + + vm.mediaDetailsTooltip.event = event; + vm.mediaDetailsTooltip.item = item; + vm.mediaDetailsTooltip.show = true; + + } else if (!hover && vm.mediaDetailsTooltip.show) { + + vm.mediaDetailsTooltip.show = false; + + } + + } + + function selectContentItem(item, $event, $index) { + listViewHelper.selectHandler(item, $index, $scope.items, $scope.selection, $event); + } + + function selectItem(item, $event, $index) { + listViewHelper.selectHandler(item, $index, vm.itemsWithoutFolders, $scope.selection, $event); + } + + function selectFolder(folder, $event, $index) { + listViewHelper.selectHandler(folder, $index, $scope.folders, $scope.selection, $event); + } + + function goToItem(item, $event, $index) { + $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id); + } + + activate(); + + } + + angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.GridLayoutController", ListViewGridLayoutController); + +})(); + +(function() { + "use strict"; + + function ListViewListLayoutController($scope, listViewHelper, $location, mediaHelper) { + + var vm = this; + + vm.nodeId = $scope.contentId; + //vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes); + //instead of passing in a whitelist, we pass in a blacklist by adding ! to the ext + vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.disallowedUploadFiles).replace(/./g, "!."); + vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB"; + vm.activeDrag = false; + vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20'; + + vm.selectItem = selectItem; + vm.clickItem = clickItem; + vm.selectAll = selectAll; + vm.isSelectedAll = isSelectedAll; + vm.isSortDirection = isSortDirection; + vm.sort = sort; + vm.dragEnter = dragEnter; + vm.dragLeave = dragLeave; + vm.onFilesQueue = onFilesQueue; + vm.onUploadComplete = onUploadComplete; + + function selectAll($event) { + listViewHelper.selectAllItems($scope.items, $scope.selection, $event); + } + + function isSelectedAll() { + return listViewHelper.isSelectedAll($scope.items, $scope.selection); + } + + function selectItem(selectedItem, $index, $event) { + listViewHelper.selectHandler(selectedItem, $index, $scope.items, $scope.selection, $event); + } + + function clickItem(item) { + $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id); + } + + function isSortDirection(col, direction) { + return listViewHelper.setSortingDirection(col, direction, $scope.options); + } + + function sort(field, allow) { + if (allow) { + listViewHelper.setSorting(field, allow, $scope.options); + $scope.getContent($scope.contentId); + } + } + + // Dropzone upload functions + function dragEnter(el, event) { + vm.activeDrag = true; + } + + function dragLeave(el, event) { + vm.activeDrag = false; + } + + function onFilesQueue() { + vm.activeDrag = false; + } + + function onUploadComplete() { + $scope.getContent($scope.contentId); + } + + } + + angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.ListLayoutController", ListViewListLayoutController); + +})(); function listViewController($rootScope, $scope, $routeParams, $injector, $cookieStore, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper, userService) { @@ -12773,183 +12773,183 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie angular.module("umbraco").controller("Umbraco.PropertyEditors.ListViewController", listViewController); -function sortByPreValsController($rootScope, $scope, localizationService) { - - $scope.sortByFields = [ - { value: "SortOrder", key: "general_sort" }, - { value: "Name", key: "general_name" }, - { value: "VersionDate", key: "content_updateDate" }, - { value: "Updater", key: "content_updatedBy" }, - { value: "CreateDate", key: "content_createDate" }, - { value: "Owner", key: "content_createBy" }, - { value: "ContentTypeAlias", key: "content_documentType" }, - { value: "Published", key: "content_isPublished" }, - { value: "Email", key: "general_email" }, - { value: "Username", key: "general_username" } - ]; - - //now we'll localize these strings, for some reason the directive doesn't work inside of the select group with an ng-model declared - _.each($scope.sortByFields, function (e, i) { - localizationService.localize(e.key).then(function (v) { - e.name = v; - - switch (e.value) { - case "Updater": - e.name += " (Content only)"; - break; - case "Published": - e.name += " (Content only)"; - break; - case "Email": - e.name += " (Members only)"; - break; - case "Username": - e.name += " (Members only)"; - break; - } - }); - }); - -} - - +function sortByPreValsController($rootScope, $scope, localizationService) { + + $scope.sortByFields = [ + { value: "SortOrder", key: "general_sort" }, + { value: "Name", key: "general_name" }, + { value: "VersionDate", key: "content_updateDate" }, + { value: "Updater", key: "content_updatedBy" }, + { value: "CreateDate", key: "content_createDate" }, + { value: "Owner", key: "content_createBy" }, + { value: "ContentTypeAlias", key: "content_documentType" }, + { value: "Published", key: "content_isPublished" }, + { value: "Email", key: "general_email" }, + { value: "Username", key: "general_username" } + ]; + + //now we'll localize these strings, for some reason the directive doesn't work inside of the select group with an ng-model declared + _.each($scope.sortByFields, function (e, i) { + localizationService.localize(e.key).then(function (v) { + e.name = v; + + switch (e.value) { + case "Updater": + e.name += " (Content only)"; + break; + case "Published": + e.name += " (Content only)"; + break; + case "Email": + e.name += " (Members only)"; + break; + case "Username": + e.name += " (Members only)"; + break; + } + }); + }); + +} + + angular.module("umbraco").controller("Umbraco.PrevalueEditors.SortByListViewController", sortByPreValsController); -//DO NOT DELETE THIS, this is in use... -angular.module('umbraco') -.controller("Umbraco.PropertyEditors.MacroContainerController", - - function($scope, dialogService, entityResource, macroService){ - $scope.renderModel = []; - - if($scope.model.value){ - var macros = $scope.model.value.split('>'); +//DO NOT DELETE THIS, this is in use... +angular.module('umbraco') +.controller("Umbraco.PropertyEditors.MacroContainerController", + + function($scope, dialogService, entityResource, macroService){ + $scope.renderModel = []; + + if($scope.model.value){ + var macros = $scope.model.value.split('>'); + + angular.forEach(macros, function(syntax, key){ + if(syntax && syntax.length > 10){ + //re-add the char we split on + syntax = syntax + ">"; + var parsed = macroService.parseMacroSyntax(syntax); + if(!parsed){ + parsed = {}; + } + + parsed.syntax = syntax; + collectDetails(parsed); + $scope.renderModel.push(parsed); + } + }); + } + + + function collectDetails(macro){ + macro.details = ""; + if(macro.macroParamsDictionary){ + angular.forEach((macro.macroParamsDictionary), function(value, key){ + macro.details += key + ": " + value + " "; + }); + } + } + + function openDialog(index){ + var dialogData = { + allowedMacros: $scope.model.config.allowed + }; + + if(index !== null && $scope.renderModel[index]) { + var macro = $scope.renderModel[index]; + dialogData["macroData"] = 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); + collectDetails(macroObject); + + //update the raw syntax and the list... + if(index !== null && $scope.renderModel[index]) { + $scope.renderModel[index] = macroObject; + } else { + $scope.renderModel.push(macroObject); + } + + $scope.macroPickerOverlay.show = false; + $scope.macroPickerOverlay = null; + }; + + $scope.macroPickerOverlay.close = function(oldModel) { + $scope.macroPickerOverlay.show = false; + $scope.macroPickerOverlay = null; + }; + + } + + + + $scope.edit =function(index){ + openDialog(index); + }; + + $scope.add = function () { + + if ($scope.model.config.max && $scope.model.config.max > 0 && $scope.renderModel.length >= $scope.model.config.max) { + //cannot add more than the max + return; + } + + openDialog(); + }; + + $scope.remove =function(index){ + $scope.renderModel.splice(index, 1); + }; + + $scope.clear = function() { + $scope.model.value = ""; + $scope.renderModel = []; + }; + + var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { + var syntax = []; + angular.forEach($scope.renderModel, function(value, key){ + syntax.push(value.syntax); + }); + + $scope.model.value = syntax.join(""); + }); + + //when the scope is destroyed we need to unsubscribe + $scope.$on('$destroy', function () { + unsubscribe(); + }); + + + function trim(str, chr) { + var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); + return str.replace(rgxtrim, ''); + } + +}); - angular.forEach(macros, function(syntax, key){ - if(syntax && syntax.length > 10){ - //re-add the char we split on - syntax = syntax + ">"; - var parsed = macroService.parseMacroSyntax(syntax); - if(!parsed){ - parsed = {}; - } - - parsed.syntax = syntax; - collectDetails(parsed); - $scope.renderModel.push(parsed); - } - }); - } - - - function collectDetails(macro){ - macro.details = ""; - if(macro.macroParamsDictionary){ - angular.forEach((macro.macroParamsDictionary), function(value, key){ - macro.details += key + ": " + value + " "; - }); - } - } - - function openDialog(index){ - var dialogData = { - allowedMacros: $scope.model.config.allowed - }; - - if(index !== null && $scope.renderModel[index]) { - var macro = $scope.renderModel[index]; - dialogData["macroData"] = 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); - collectDetails(macroObject); - - //update the raw syntax and the list... - if(index !== null && $scope.renderModel[index]) { - $scope.renderModel[index] = macroObject; - } else { - $scope.renderModel.push(macroObject); - } - - $scope.macroPickerOverlay.show = false; - $scope.macroPickerOverlay = null; - }; - - $scope.macroPickerOverlay.close = function(oldModel) { - $scope.macroPickerOverlay.show = false; - $scope.macroPickerOverlay = null; - }; - - } - - - - $scope.edit =function(index){ - openDialog(index); - }; - - $scope.add = function () { - - if ($scope.model.config.max && $scope.model.config.max > 0 && $scope.renderModel.length >= $scope.model.config.max) { - //cannot add more than the max - return; - } - - openDialog(); - }; - - $scope.remove =function(index){ - $scope.renderModel.splice(index, 1); - }; - - $scope.clear = function() { - $scope.model.value = ""; - $scope.renderModel = []; - }; - - var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { - var syntax = []; - angular.forEach($scope.renderModel, function(value, key){ - syntax.push(value.syntax); - }); - - $scope.model.value = syntax.join(""); - }); - - //when the scope is destroyed we need to unsubscribe - $scope.$on('$destroy', function () { - unsubscribe(); - }); - - - function trim(str, chr) { - var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g'); - return str.replace(rgxtrim, ''); - } - -}); - -function MacroListController($scope, entityResource) { - - $scope.items = []; - - entityResource.getAll("Macro").then(function(items) { - _.each(items, function(i) { - $scope.items.push({ name: i.name, alias: i.alias }); - }); - - }); - - -} - -angular.module("umbraco").controller("Umbraco.PrevalueEditors.MacroList", MacroListController); +function MacroListController($scope, entityResource) { + + $scope.items = []; + + entityResource.getAll("Macro").then(function(items) { + _.each(items, function(i) { + $scope.items.push({ name: i.name, alias: i.alias }); + }); + + }); + + +} + +angular.module("umbraco").controller("Umbraco.PrevalueEditors.MacroList", MacroListController); //inject umbracos assetsServce and dialog service function MarkdownEditorController($scope, $element, assetsService, dialogService, angularHelper, $timeout) { @@ -13425,28 +13425,28 @@ function MultipleTextBoxController($scope) { angular.module("umbraco").controller("Umbraco.PropertyEditors.MultipleTextBoxController", MultipleTextBoxController); -angular.module("umbraco").controller("Umbraco.PropertyEditors.RadioButtonsController", - function($scope) { - - if (angular.isObject($scope.model.config.items)) { - - //now we need to format the items in the dictionary because we always want to have an array - var newItems = []; - var vals = _.values($scope.model.config.items); - var keys = _.keys($scope.model.config.items); - for (var i = 0; i < vals.length; i++) { - newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: vals[i].value }); - } - - //ensure the items are sorted by the provided sort order - newItems.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); }); - - //re-assign - $scope.model.config.items = newItems; - - } - - }); +angular.module("umbraco").controller("Umbraco.PropertyEditors.RadioButtonsController", + function($scope) { + + if (angular.isObject($scope.model.config.items)) { + + //now we need to format the items in the dictionary because we always want to have an array + var newItems = []; + var vals = _.values($scope.model.config.items); + var keys = _.keys($scope.model.config.items); + for (var i = 0; i < vals.length; i++) { + newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: vals[i].value }); + } + + //ensure the items are sorted by the provided sort order + newItems.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); }); + + //re-assign + $scope.model.config.items = newItems; + + } + + }); /** * @ngdoc controller @@ -14026,125 +14026,125 @@ angular.module("umbraco") }); -angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", - function ($scope, $timeout, $log, tinyMceService, stylesheetResource, assetsService) { - var cfg = tinyMceService.defaultPrevalues(); - - if($scope.model.value){ - if(angular.isString($scope.model.value)){ - $scope.model.value = cfg; - } - }else{ - $scope.model.value = cfg; - } - - if (!$scope.model.value.stylesheets) { - $scope.model.value.stylesheets = []; - } - if (!$scope.model.value.toolbar) { - $scope.model.value.toolbar = []; - } - if (!$scope.model.value.maxImageSize && $scope.model.value.maxImageSize != 0) { - $scope.model.value.maxImageSize = cfg.maxImageSize; - } - - tinyMceService.configuration().then(function(config){ - $scope.tinyMceConfig = config; - - // extend commands with properties for font-icon and if it is a custom command - $scope.tinyMceConfig.commands = _.map($scope.tinyMceConfig.commands, function (obj) { - var icon = getFontIcon(obj.frontEndCommand); - return angular.extend(obj, { - fontIcon: icon.name, - isCustom: icon.isCustom - }); - }); - }); - - stylesheetResource.getAll().then(function(stylesheets){ - $scope.stylesheets = stylesheets; - }); - - $scope.selected = function(cmd, alias, lookup){ - if (lookup && angular.isArray(lookup)) { - cmd.selected = lookup.indexOf(alias) >= 0; - return cmd.selected; - } - return false; - }; - - $scope.selectCommand = function(command){ - var index = $scope.model.value.toolbar.indexOf(command.frontEndCommand); - - if(command.selected && index === -1){ - $scope.model.value.toolbar.push(command.frontEndCommand); - }else if(index >= 0){ - $scope.model.value.toolbar.splice(index, 1); - } - }; - - $scope.selectStylesheet = function (css) { - - var index = $scope.model.value.stylesheets.indexOf(css.name); - - if(css.selected && index === -1){ - $scope.model.value.stylesheets.push(css.name); - }else if(index >= 0){ - $scope.model.value.stylesheets.splice(index, 1); - } - }; - - // map properties for specific commands - function getFontIcon(alias) { - var icon = { name: alias, isCustom: false }; - - switch (alias) { - case "codemirror": - icon.name = "code"; - icon.isCustom = false; - break; - case "styleselect": - icon.name = "icon-list"; - icon.isCustom = true; - break; - case "umbembeddialog": - icon.name = "icon-tv"; - icon.isCustom = true; - break; - case "umbmediapicker": - icon.name = "icon-picture"; - icon.isCustom = true; - break; - case "umbmacro": - icon.name = "icon-settings-alt"; - icon.isCustom = true; - break; - case "umbmacro": - icon.name = "icon-settings-alt"; - icon.isCustom = true; - break; - default: - icon.name = alias; - icon.isCustom = false; - } - - return icon; - } - - var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { - - var commands = _.where($scope.tinyMceConfig.commands, {selected: true}); - $scope.model.value.toolbar = _.pluck(commands, "frontEndCommand"); - - }); - - // when the scope is destroyed we need to unsubscribe - $scope.$on('$destroy', function () { - unsubscribe(); - }); - - // load TinyMCE skin which contains css for font-icons - assetsService.loadCss("lib/tinymce/skins/umbraco/skin.min.css"); +angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", + function ($scope, $timeout, $log, tinyMceService, stylesheetResource, assetsService) { + var cfg = tinyMceService.defaultPrevalues(); + + if($scope.model.value){ + if(angular.isString($scope.model.value)){ + $scope.model.value = cfg; + } + }else{ + $scope.model.value = cfg; + } + + if (!$scope.model.value.stylesheets) { + $scope.model.value.stylesheets = []; + } + if (!$scope.model.value.toolbar) { + $scope.model.value.toolbar = []; + } + if (!$scope.model.value.maxImageSize && $scope.model.value.maxImageSize != 0) { + $scope.model.value.maxImageSize = cfg.maxImageSize; + } + + tinyMceService.configuration().then(function(config){ + $scope.tinyMceConfig = config; + + // extend commands with properties for font-icon and if it is a custom command + $scope.tinyMceConfig.commands = _.map($scope.tinyMceConfig.commands, function (obj) { + var icon = getFontIcon(obj.frontEndCommand); + return angular.extend(obj, { + fontIcon: icon.name, + isCustom: icon.isCustom + }); + }); + }); + + stylesheetResource.getAll().then(function(stylesheets){ + $scope.stylesheets = stylesheets; + }); + + $scope.selected = function(cmd, alias, lookup){ + if (lookup && angular.isArray(lookup)) { + cmd.selected = lookup.indexOf(alias) >= 0; + return cmd.selected; + } + return false; + }; + + $scope.selectCommand = function(command){ + var index = $scope.model.value.toolbar.indexOf(command.frontEndCommand); + + if(command.selected && index === -1){ + $scope.model.value.toolbar.push(command.frontEndCommand); + }else if(index >= 0){ + $scope.model.value.toolbar.splice(index, 1); + } + }; + + $scope.selectStylesheet = function (css) { + + var index = $scope.model.value.stylesheets.indexOf(css.name); + + if(css.selected && index === -1){ + $scope.model.value.stylesheets.push(css.name); + }else if(index >= 0){ + $scope.model.value.stylesheets.splice(index, 1); + } + }; + + // map properties for specific commands + function getFontIcon(alias) { + var icon = { name: alias, isCustom: false }; + + switch (alias) { + case "codemirror": + icon.name = "code"; + icon.isCustom = false; + break; + case "styleselect": + icon.name = "icon-list"; + icon.isCustom = true; + break; + case "umbembeddialog": + icon.name = "icon-tv"; + icon.isCustom = true; + break; + case "umbmediapicker": + icon.name = "icon-picture"; + icon.isCustom = true; + break; + case "umbmacro": + icon.name = "icon-settings-alt"; + icon.isCustom = true; + break; + case "umbmacro": + icon.name = "icon-settings-alt"; + icon.isCustom = true; + break; + default: + icon.name = alias; + icon.isCustom = false; + } + + return icon; + } + + var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { + + var commands = _.where($scope.tinyMceConfig.commands, {selected: true}); + $scope.model.value.toolbar = _.pluck(commands, "frontEndCommand"); + + }); + + // when the scope is destroyed we need to unsubscribe + $scope.$on('$destroy', function () { + unsubscribe(); + }); + + // load TinyMCE skin which contains css for font-icons + assetsService.loadCss("lib/tinymce/skins/umbraco/skin.min.css"); }); function sliderController($scope, $log, $element, assetsService, angularHelper) { diff --git a/OurUmbraco.Site/umbraco/js/umbraco.directives.js b/OurUmbraco.Site/umbraco/js/umbraco.directives.js index d3b5b1bc..5d8f6871 100644 --- a/OurUmbraco.Site/umbraco/js/umbraco.directives.js +++ b/OurUmbraco.Site/umbraco/js/umbraco.directives.js @@ -10,129 +10,129 @@ angular.module("umbraco.directives", ["umbraco.directives.editors", "umbraco.dir angular.module("umbraco.directives.editors", []); angular.module("umbraco.directives.html", []); angular.module("umbraco.directives.validation", []); -/** -* @ngdoc directive -* @name umbraco.directives.directive:autoScale -* @element div -* @deprecated -* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. -* @function -* @description -* Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set -* So if you only want to scale the div to 70 pixels from the bottom you pass "70" +/** +* @ngdoc directive +* @name umbraco.directives.directive:autoScale +* @element div +* @deprecated +* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. +* @function +* @description +* Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set +* So if you only want to scale the div to 70 pixels from the bottom you pass "70" + +* @example +* +* +*
+*
+*
+**/ + +angular.module("umbraco.directives") + .directive('autoScale', function ($window) { + return function (scope, el, attrs) { + + var totalOffset = 0; + var offsety = parseInt(attrs.autoScale, 10); + var window = angular.element($window); + if (offsety !== undefined){ + totalOffset += offsety; + } + + setTimeout(function () { + el.height(window.height() - (el.offset().top + totalOffset)); + }, 500); + + window.bind("resize", function () { + el.height(window.height() - (el.offset().top + totalOffset)); + }); + + }; + }); -* @example -* -* -*
-*
-*
-**/ - -angular.module("umbraco.directives") - .directive('autoScale', function ($window) { - return function (scope, el, attrs) { - - var totalOffset = 0; - var offsety = parseInt(attrs.autoScale, 10); - var window = angular.element($window); - if (offsety !== undefined){ - totalOffset += offsety; - } - - setTimeout(function () { - el.height(window.height() - (el.offset().top + totalOffset)); - }, 500); - - window.bind("resize", function () { - el.height(window.height() - (el.offset().top + totalOffset)); - }); - - }; - }); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:detectFold -* @deprecated -* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. -* @description This is used for the editor buttons to ensure they are displayed correctly if the horizontal overflow of the editor -* exceeds the height of the window -**/ - -angular.module("umbraco.directives.html") - .directive('detectFold', function ($timeout, $log, windowResizeListener) { - return { - require: "^?umbTabs", - restrict: 'A', - link: function (scope, el, attrs, tabsCtrl) { - - var firstRun = false; - var parent = $(".umb-panel-body"); - var winHeight = $(window).height(); - var calculate = function () { - if (el && el.is(":visible") && !el.hasClass("umb-bottom-bar")) { - - //now that the element is visible, set the flag in a couple of seconds, - // this will ensure that loading time of a current tab get's completed and that - // we eventually stop watching to save on CPU time - $timeout(function() { - firstRun = true; - }, 4000); - - //var parent = el.parent(); - var hasOverflow = parent.innerHeight() < parent[0].scrollHeight; - //var belowFold = (el.offset().top + el.height()) > winHeight; - if (hasOverflow) { - el.addClass("umb-bottom-bar"); - - //I wish we didn't have to put this logic here but unfortunately we - // do. This needs to calculate the left offest to place the bottom bar - // depending on if the left column splitter has been moved by the user - // (based on the nav-resize directive) - var wrapper = $("#mainwrapper"); - var contentPanel = $("#leftcolumn").next(); - var contentPanelLeftPx = contentPanel.css("left"); - - el.css({ left: contentPanelLeftPx }); - } - } - return firstRun; - }; - - var resizeCallback = function(size) { - winHeight = size.height; - el.removeClass("umb-bottom-bar"); - calculate(); - }; - - windowResizeListener.register(resizeCallback); - - //Only execute the watcher if this tab is the active (first) tab on load, otherwise there's no reason to execute - // the watcher since it will be recalculated when the tab changes! - if (el.closest(".umb-tab-pane").index() === 0) { - //run a watcher to ensure that the calculation occurs until it's firstRun but ensure - // the calculations are throttled to save a bit of CPU - var listener = scope.$watch(_.throttle(calculate, 1000), function (newVal, oldVal) { - if (newVal !== oldVal) { - listener(); - } - }); - } - - //listen for tab changes - if (tabsCtrl != null) { - tabsCtrl.onTabShown(function (args) { - calculate(); - }); - } - - //ensure to unregister - scope.$on('$destroy', function() { - windowResizeListener.unregister(resizeCallback); - }); - } - }; +/** +* @ngdoc directive +* @name umbraco.directives.directive:detectFold +* @deprecated +* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. +* @description This is used for the editor buttons to ensure they are displayed correctly if the horizontal overflow of the editor +* exceeds the height of the window +**/ + +angular.module("umbraco.directives.html") + .directive('detectFold', function ($timeout, $log, windowResizeListener) { + return { + require: "^?umbTabs", + restrict: 'A', + link: function (scope, el, attrs, tabsCtrl) { + + var firstRun = false; + var parent = $(".umb-panel-body"); + var winHeight = $(window).height(); + var calculate = function () { + if (el && el.is(":visible") && !el.hasClass("umb-bottom-bar")) { + + //now that the element is visible, set the flag in a couple of seconds, + // this will ensure that loading time of a current tab get's completed and that + // we eventually stop watching to save on CPU time + $timeout(function() { + firstRun = true; + }, 4000); + + //var parent = el.parent(); + var hasOverflow = parent.innerHeight() < parent[0].scrollHeight; + //var belowFold = (el.offset().top + el.height()) > winHeight; + if (hasOverflow) { + el.addClass("umb-bottom-bar"); + + //I wish we didn't have to put this logic here but unfortunately we + // do. This needs to calculate the left offest to place the bottom bar + // depending on if the left column splitter has been moved by the user + // (based on the nav-resize directive) + var wrapper = $("#mainwrapper"); + var contentPanel = $("#leftcolumn").next(); + var contentPanelLeftPx = contentPanel.css("left"); + + el.css({ left: contentPanelLeftPx }); + } + } + return firstRun; + }; + + var resizeCallback = function(size) { + winHeight = size.height; + el.removeClass("umb-bottom-bar"); + calculate(); + }; + + windowResizeListener.register(resizeCallback); + + //Only execute the watcher if this tab is the active (first) tab on load, otherwise there's no reason to execute + // the watcher since it will be recalculated when the tab changes! + if (el.closest(".umb-tab-pane").index() === 0) { + //run a watcher to ensure that the calculation occurs until it's firstRun but ensure + // the calculations are throttled to save a bit of CPU + var listener = scope.$watch(_.throttle(calculate, 1000), function (newVal, oldVal) { + if (newVal !== oldVal) { + listener(); + } + }); + } + + //listen for tab changes + if (tabsCtrl != null) { + tabsCtrl.onTabShown(function (args) { + calculate(); + }); + } + + //ensure to unregister + scope.$on('$destroy', function() { + windowResizeListener.unregister(resizeCallback); + }); + } + }; }); /** * @ngdoc directive @@ -297,70 +297,70 @@ angular.module("umbraco.directives") }; }); -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbHeader -* @deprecated -* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. -* @restrict E -* @function -* @description -* The header on an editor that contains tabs using bootstrap tabs - THIS IS OBSOLETE, use umbTabHeader instead -**/ - -angular.module("umbraco.directives") -.directive('umbHeader', function ($parse, $timeout) { - return { - restrict: 'E', - replace: true, - transclude: 'true', - templateUrl: 'views/directives/_obsolete/umb-header.html', - //create a new isolated scope assigning a tabs property from the attribute 'tabs' - //which is bound to the parent scope property passed in - scope: { - tabs: "=" - }, - link: function (scope, iElement, iAttrs) { - - scope.showTabs = iAttrs.tabs ? true : false; - scope.visibleTabs = []; - - //since tabs are loaded async, we need to put a watch on them to determine - // when they are loaded, then we can close the watch - var tabWatch = scope.$watch("tabs", function (newValue, oldValue) { - - angular.forEach(newValue, function(val, index){ - var tab = {id: val.id, label: val.label}; - scope.visibleTabs.push(tab); - }); - - //don't process if we cannot or have already done so - if (!newValue) {return;} - if (!newValue.length || newValue.length === 0){return;} - - //we need to do a timeout here so that the current sync operation can complete - // and update the UI, then this will fire and the UI elements will be available. - $timeout(function () { - - //use bootstrap tabs API to show the first one - iElement.find(".nav-tabs a:first").tab('show'); - - //enable the tab drop - iElement.find('.nav-pills, .nav-tabs').tabdrop(); - - //ensure to destroy tabdrop (unbinds window resize listeners) - scope.$on('$destroy', function () { - iElement.find('.nav-pills, .nav-tabs').tabdrop("destroy"); - }); - - //stop watching now - tabWatch(); - }, 200); - - }); - } - }; -}); +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbHeader +* @deprecated +* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. +* @restrict E +* @function +* @description +* The header on an editor that contains tabs using bootstrap tabs - THIS IS OBSOLETE, use umbTabHeader instead +**/ + +angular.module("umbraco.directives") +.directive('umbHeader', function ($parse, $timeout) { + return { + restrict: 'E', + replace: true, + transclude: 'true', + templateUrl: 'views/directives/_obsolete/umb-header.html', + //create a new isolated scope assigning a tabs property from the attribute 'tabs' + //which is bound to the parent scope property passed in + scope: { + tabs: "=" + }, + link: function (scope, iElement, iAttrs) { + + scope.showTabs = iAttrs.tabs ? true : false; + scope.visibleTabs = []; + + //since tabs are loaded async, we need to put a watch on them to determine + // when they are loaded, then we can close the watch + var tabWatch = scope.$watch("tabs", function (newValue, oldValue) { + + angular.forEach(newValue, function(val, index){ + var tab = {id: val.id, label: val.label}; + scope.visibleTabs.push(tab); + }); + + //don't process if we cannot or have already done so + if (!newValue) {return;} + if (!newValue.length || newValue.length === 0){return;} + + //we need to do a timeout here so that the current sync operation can complete + // and update the UI, then this will fire and the UI elements will be available. + $timeout(function () { + + //use bootstrap tabs API to show the first one + iElement.find(".nav-tabs a:first").tab('show'); + + //enable the tab drop + iElement.find('.nav-pills, .nav-tabs').tabdrop(); + + //ensure to destroy tabdrop (unbinds window resize listeners) + scope.$on('$destroy', function () { + iElement.find('.nav-pills, .nav-tabs').tabdrop("destroy"); + }); + + //stop watching now + tabWatch(); + }, 200); + + }); + } + }; +}); /** * @ngdoc directive @@ -382,55 +382,55 @@ function loginDirective() { angular.module('umbraco.directives').directive("umbLogin", loginDirective); -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbOptionsMenu -* @deprecated -* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. -* @function -* @element ANY -* @restrict E -**/ - -angular.module("umbraco.directives") -.directive('umbOptionsMenu', function ($injector, treeService, navigationService, umbModelMapper, appState) { - return { - scope: { - currentSection: "@", - currentNode: "=" - }, - restrict: 'E', - replace: true, - templateUrl: 'views/directives/_obsolete/umb-optionsmenu.html', - link: function (scope, element, attrs, ctrl) { - - //adds a handler to the context menu item click, we need to handle this differently - //depending on what the menu item is supposed to do. - scope.executeMenuItem = function (action) { - navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection); - }; - - //callback method to go and get the options async - scope.getOptions = function () { - - if (!scope.currentNode) { - return; - } - - //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu) - appState.setMenuState("currentNode", scope.currentNode); - - if (!scope.actions) { - treeService.getMenu({ treeNode: scope.currentNode }) - .then(function (data) { - scope.actions = data.menuItems; - }); - } - }; - - } - }; -}); +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbOptionsMenu +* @deprecated +* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. +* @function +* @element ANY +* @restrict E +**/ + +angular.module("umbraco.directives") +.directive('umbOptionsMenu', function ($injector, treeService, navigationService, umbModelMapper, appState) { + return { + scope: { + currentSection: "@", + currentNode: "=" + }, + restrict: 'E', + replace: true, + templateUrl: 'views/directives/_obsolete/umb-optionsmenu.html', + link: function (scope, element, attrs, ctrl) { + + //adds a handler to the context menu item click, we need to handle this differently + //depending on what the menu item is supposed to do. + scope.executeMenuItem = function (action) { + navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection); + }; + + //callback method to go and get the options async + scope.getOptions = function () { + + if (!scope.currentNode) { + return; + } + + //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu) + appState.setMenuState("currentNode", scope.currentNode); + + if (!scope.actions) { + treeService.getMenu({ treeNode: scope.currentNode }) + .then(function (data) { + scope.actions = data.menuItems; + }); + } + }; + + } + }; +}); /** * @ngdoc directive @@ -675,184 +675,184 @@ angular.module("umbraco.directives") }; }); -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbTabView -* @deprecated -* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. -* -* @restrict E -**/ +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbTabView +* @deprecated +* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. +* +* @restrict E +**/ + +angular.module("umbraco.directives") +.directive('umbTabView', function($timeout, $log){ + return { + restrict: 'E', + replace: true, + transclude: 'true', + templateUrl: 'views/directives/_obsolete/umb-tab-view.html' + }; +}); -angular.module("umbraco.directives") -.directive('umbTabView', function($timeout, $log){ - return { - restrict: 'E', - replace: true, - transclude: 'true', - templateUrl: 'views/directives/_obsolete/umb-tab-view.html' - }; -}); +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbUploadDropzone +* @deprecated +* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. +* +* @restrict E +**/ + +angular.module("umbraco.directives.html") + .directive('umbUploadDropzone', function(){ + return { + restrict: 'E', + replace: true, + templateUrl: 'views/directives/_obsolete/umb-upload-dropzone.html' + }; + }); -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbUploadDropzone -* @deprecated -* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use. -* -* @restrict E -**/ +/** +* @ngdoc directive +* @name umbraco.directives.directive:navResize +* @restrict A + * + * @description + * Handles how the navigation responds to window resizing and controls how the draggable resize panel works +**/ +angular.module("umbraco.directives") + .directive('navResize', function (appState, eventsService, windowResizeListener) { + return { + restrict: 'A', + link: function (scope, element, attrs, ctrl) { + + var minScreenSize = 1100; + var resizeEnabled = false; + + function setTreeMode() { + appState.setGlobalState("showNavigation", appState.getGlobalState("isTablet") === false); + } + + function enableResize() { + //only enable when the size is correct and it's not already enabled + if (!resizeEnabled && appState.getGlobalState("isTablet") === false) { + element.resizable( + { + containment: $("#mainwrapper"), + autoHide: true, + handles: "e", + alsoResize: ".navigation-inner-container", + resize: function(e, ui) { + var wrapper = $("#mainwrapper"); + var contentPanel = $("#contentwrapper"); + var umbNotification = $("#umb-notifications-wrapper"); + var apps = $("#applications"); + var bottomBar = contentPanel.find(".umb-bottom-bar"); + var navOffeset = $("#navOffset"); + + var leftPanelWidth = ui.element.width() + apps.width(); + + contentPanel.css({ left: leftPanelWidth }); + bottomBar.css({ left: leftPanelWidth }); + umbNotification.css({ left: leftPanelWidth }); + + navOffeset.css({ "margin-left": ui.element.outerWidth() }); + }, + stop: function (e, ui) { + + } + }); + + resizeEnabled = true; + } + } + + function resetResize() { + if (resizeEnabled) { + //kill the resize + element.resizable("destroy"); + element.css("width", ""); + + var navInnerContainer = element.find(".navigation-inner-container"); + + navInnerContainer.css("width", ""); + $("#contentwrapper").css("left", ""); + $("#umb-notifications-wrapper").css("left", ""); + $("#navOffset").css("margin-left", ""); + + resizeEnabled = false; + } + } + + var evts = []; + + //Listen for global state changes + evts.push(eventsService.on("appState.globalState.changed", function (e, args) { + if (args.key === "showNavigation") { + if (args.value === false) { + resetResize(); + } + else { + enableResize(); + } + } + })); + + var resizeCallback = function(size) { + //set the global app state + appState.setGlobalState("isTablet", (size.width <= minScreenSize)); + setTreeMode(); + }; + + windowResizeListener.register(resizeCallback); + + //ensure to unregister from all events and kill jquery plugins + scope.$on('$destroy', function () { + windowResizeListener.unregister(resizeCallback); + for (var e in evts) { + eventsService.unsubscribe(evts[e]); + } + var navInnerContainer = element.find(".navigation-inner-container"); + navInnerContainer.resizable("destroy"); + }); + + //init + //set the global app state + appState.setGlobalState("isTablet", ($(window).width() <= minScreenSize)); + setTreeMode(); + } + }; + }); -angular.module("umbraco.directives.html") - .directive('umbUploadDropzone', function(){ - return { - restrict: 'E', - replace: true, - templateUrl: 'views/directives/_obsolete/umb-upload-dropzone.html' - }; - }); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:navResize -* @restrict A - * - * @description - * Handles how the navigation responds to window resizing and controls how the draggable resize panel works -**/ -angular.module("umbraco.directives") - .directive('navResize', function (appState, eventsService, windowResizeListener) { - return { - restrict: 'A', - link: function (scope, element, attrs, ctrl) { - - var minScreenSize = 1100; - var resizeEnabled = false; - - function setTreeMode() { - appState.setGlobalState("showNavigation", appState.getGlobalState("isTablet") === false); - } - - function enableResize() { - //only enable when the size is correct and it's not already enabled - if (!resizeEnabled && appState.getGlobalState("isTablet") === false) { - element.resizable( - { - containment: $("#mainwrapper"), - autoHide: true, - handles: "e", - alsoResize: ".navigation-inner-container", - resize: function(e, ui) { - var wrapper = $("#mainwrapper"); - var contentPanel = $("#contentwrapper"); - var umbNotification = $("#umb-notifications-wrapper"); - var apps = $("#applications"); - var bottomBar = contentPanel.find(".umb-bottom-bar"); - var navOffeset = $("#navOffset"); - - var leftPanelWidth = ui.element.width() + apps.width(); - - contentPanel.css({ left: leftPanelWidth }); - bottomBar.css({ left: leftPanelWidth }); - umbNotification.css({ left: leftPanelWidth }); - - navOffeset.css({ "margin-left": ui.element.outerWidth() }); - }, - stop: function (e, ui) { - - } - }); - - resizeEnabled = true; - } - } - - function resetResize() { - if (resizeEnabled) { - //kill the resize - element.resizable("destroy"); - element.css("width", ""); - - var navInnerContainer = element.find(".navigation-inner-container"); - - navInnerContainer.css("width", ""); - $("#contentwrapper").css("left", ""); - $("#umb-notifications-wrapper").css("left", ""); - $("#navOffset").css("margin-left", ""); - - resizeEnabled = false; - } - } - - var evts = []; - - //Listen for global state changes - evts.push(eventsService.on("appState.globalState.changed", function (e, args) { - if (args.key === "showNavigation") { - if (args.value === false) { - resetResize(); - } - else { - enableResize(); - } - } - })); - - var resizeCallback = function(size) { - //set the global app state - appState.setGlobalState("isTablet", (size.width <= minScreenSize)); - setTreeMode(); - }; - - windowResizeListener.register(resizeCallback); - - //ensure to unregister from all events and kill jquery plugins - scope.$on('$destroy', function () { - windowResizeListener.unregister(resizeCallback); - for (var e in evts) { - eventsService.unsubscribe(evts[e]); - } - var navInnerContainer = element.find(".navigation-inner-container"); - navInnerContainer.resizable("destroy"); - }); - - //init - //set the global app state - appState.setGlobalState("isTablet", ($(window).width() <= minScreenSize)); - setTreeMode(); - } - }; - }); - -angular.module("umbraco.directives") -.directive('sectionIcon', function ($compile, iconHelper) { - return { - restrict: 'E', - replace: true, - - link: function (scope, element, attrs) { - - var icon = attrs.icon; - - if (iconHelper.isLegacyIcon(icon)) { - //its a known legacy icon, convert to a new one - element.html(""); - } - else if (iconHelper.isFileBasedIcon(icon)) { - var convert = iconHelper.convertFromLegacyImage(icon); - if(convert){ - element.html(""); - }else{ - element.html(""); - } - //it's a file, normally legacy so look in the icon tray images - } - else { - //it's normal - element.html(""); - } - } - }; +angular.module("umbraco.directives") +.directive('sectionIcon', function ($compile, iconHelper) { + return { + restrict: 'E', + replace: true, + + link: function (scope, element, attrs) { + + var icon = attrs.icon; + + if (iconHelper.isLegacyIcon(icon)) { + //its a known legacy icon, convert to a new one + element.html(""); + } + else if (iconHelper.isFileBasedIcon(icon)) { + var convert = iconHelper.convertFromLegacyImage(icon); + if(convert){ + element.html(""); + }else{ + element.html(""); + } + //it's a file, normally legacy so look in the icon tray images + } + else { + //it's normal + element.html(""); + } + } + }; }); angular.module("umbraco.directives") .directive('umbContextMenu', function (navigationService) { @@ -1080,2702 +1080,2702 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se angular.module('umbraco.directives').directive("umbSections", sectionsDirective); -/** -@ngdoc directive -@name umbraco.directives.directive:umbButton -@restrict E -@scope - -@description -Use this directive to render an umbraco button. The directive can be used to generate all types of buttons, set type, style, translation, shortcut and much more. - -

Markup example

-
-    
- - - - -
-
- -

Controller example

-
-    (function () {
-        "use strict";
-
-        function Controller(myService) {
-
-            var vm = this;
-            vm.buttonState = "init";
-
-            vm.clickButton = clickButton;
-
-            function clickButton() {
-
-                vm.buttonState = "busy";
-
-                myService.clickButton().then(function() {
-                    vm.buttonState = "success";
-                }, function() {
-                    vm.buttonState = "error";
-                });
-
-            }
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-
-    })();
-
- -@param {callback} action The button action which should be performed when the button is clicked. -@param {string=} href Url/Path to navigato to. -@param {string=} type Set the button type ("button" or "submit"). -@param {string=} buttonStyle Set the style of the button. The directive uses the default bootstrap styles ("primary", "info", "success", "warning", "danger", "inverse", "link"). -@param {string=} state Set a progress state on the button ("init", "busy", "success", "error"). -@param {string=} shortcut Set a keyboard shortcut for the button ("ctrl+c"). -@param {string=} label Set the button label. -@param {string=} labelKey Set a localization key to make a multi lingual button ("general_buttonText"). -@param {string=} icon Set a button icon. Can only be used when buttonStyle is "link". -@param {boolean=} disabled Set to true to disable the button. -**/ - -(function() { - 'use strict'; - - function ButtonDirective($timeout) { - - function link(scope, el, attr, ctrl) { - - scope.style = null; - - function activate() { - - if (!scope.state) { - scope.state = "init"; - } - - if (scope.buttonStyle) { - scope.style = "btn-" + scope.buttonStyle; - } - - } - - activate(); - - var unbindStateWatcher = scope.$watch('state', function(newValue, oldValue) { - - if (newValue === 'success' || newValue === 'error') { - $timeout(function() { - scope.state = 'init'; - }, 2000); - } - - }); - - scope.$on('$destroy', function() { - unbindStateWatcher(); - }); - - } - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/buttons/umb-button.html', - link: link, - scope: { - action: "&?", - href: "@?", - type: "@", - buttonStyle: "@?", - state: "=?", - shortcut: "@?", - shortcutWhenHidden: "@", - label: "@?", - labelKey: "@?", - icon: "@?", - disabled: "=" - } - }; - - return directive; - - } - - angular.module('umbraco.directives').directive('umbButton', ButtonDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbButtonGroup -@restrict E -@scope - -@description -Use this directive to render a button with a dropdown of alternative actions. - -

Markup example

-
-    
- - - - -
-
- -

Controller example

-
-    (function () {
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-
-            vm.buttonGroup = {
-                defaultButton: {
-                    labelKey: "general_defaultButton",
-                    hotKey: "ctrl+d",
-                    hotKeyWhenHidden: true,
-                    handler: function() {
-                        // do magic here
-                    }
-                },
-                subButtons: [
-                    {
-                        labelKey: "general_subButton",
-                        hotKey: "ctrl+b",
-                        hotKeyWhenHidden: true,
-                        handler: function() {
-                            // do magic here
-                        }
-                    }
-                ]
-            };
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-
-    })();
-
- -

Button model description

-
    -
  • - labekKey - (string) - - Set a localization key to make a multi lingual button ("general_buttonText"). -
  • -
  • - hotKey - (array) - - Set a keyboard shortcut for the button ("ctrl+c"). -
  • -
  • - hotKeyWhenHidden - (boolean) - - As a default the hotkeys only works on elements visible in the UI. Set to true to set a hotkey on the hidden sub buttons. -
  • -
  • - handler - (callback) - - Set a callback to handle button click events. -
  • -
- -@param {object} defaultButton The model of the default button. -@param {array} subButtons Array of sub buttons. -@param {string=} state Set a progress state on the button ("init", "busy", "success", "error"). -@param {string=} direction Set the direction of the dropdown ("up", "down"). -@param {string=} float Set the float of the dropdown. ("left", "right"). -**/ - -(function() { - 'use strict'; - - function ButtonGroupDirective() { - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/buttons/umb-button-group.html', - scope: { - defaultButton: "=", - subButtons: "=", - state: "=?", - direction: "@?", - float: "@?" - } - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbButtonGroup', ButtonGroupDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEditorSubHeader -@restrict E - -@description -Use this directive to construct a sub header in the main editor window. -The sub header is sticky and will follow along down the page when scrolling. - -

Markup example

-
-    
- -
- - - - - - - // sub header content here - - - - - - -
- -
-
- -

Use in combination with

-
    -
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentLeft umbEditorSubHeaderContentLeft}
  • -
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentRight umbEditorSubHeaderContentRight}
  • -
  • {@link umbraco.directives.directive:umbEditorSubHeaderSection umbEditorSubHeaderSection}
  • -
-**/ - -(function() { - 'use strict'; - - function EditorSubHeaderDirective() { - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/subheader/umb-editor-sub-header.html' - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorSubHeader', EditorSubHeaderDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEditorSubHeaderContentLeft -@restrict E - -@description -Use this directive to left align content in a sub header in the main editor window. - -

Markup example

-
-    
- -
- - - - - - - - - // left content here - - - - // right content here - - - - - - - - -
- -
-
- -

Use in combination with

-
    -
  • {@link umbraco.directives.directive:umbEditorSubHeader umbEditorSubHeader}
  • -
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentRight umbEditorSubHeaderContentRight}
  • -
  • {@link umbraco.directives.directive:umbEditorSubHeaderSection umbEditorSubHeaderSection}
  • -
-**/ - -(function() { - 'use strict'; - - function EditorSubHeaderContentLeftDirective() { - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-content-left.html' - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorSubHeaderContentLeft', EditorSubHeaderContentLeftDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEditorSubHeaderContentRight -@restrict E - -@description -Use this directive to rigt align content in a sub header in the main editor window. - -

Markup example

-
-    
- -
- - - - - - - - - // left content here - - - - // right content here - - - - - - - - -
- -
-
- -

Use in combination with

-
    -
  • {@link umbraco.directives.directive:umbEditorSubHeader umbEditorSubHeader}
  • -
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentLeft umbEditorSubHeaderContentLeft}
  • -
  • {@link umbraco.directives.directive:umbEditorSubHeaderSection umbEditorSubHeaderSection}
  • -
-**/ - -(function() { - 'use strict'; - - function EditorSubHeaderContentRightDirective() { - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-content-right.html' - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorSubHeaderContentRight', EditorSubHeaderContentRightDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEditorSubHeaderSection -@restrict E - -@description -Use this directive to create sections, divided by borders, in a sub header in the main editor window. - -

Markup example

-
-    
- -
- - - - - - - - - - - // section content here - - - - // section content here - - - - // section content here - - - - - - - - - - -
- -
-
- -

Use in combination with

-
    -
  • {@link umbraco.directives.directive:umbEditorSubHeader umbEditorSubHeader}
  • -
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentLeft umbEditorSubHeaderContentLeft}
  • -
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentRight umbEditorSubHeaderContentRight}
  • -
-**/ - -(function() { - 'use strict'; - - function EditorSubHeaderSectionDirective() { - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-section.html' - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorSubHeaderSection', EditorSubHeaderSectionDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbBreadcrumbs -@restrict E -@scope - -@description -Use this directive to generate a list of breadcrumbs. - -

Markup example

-
-    
- - -
-
- -

Controller example

-
-    (function () {
-        "use strict";
-
-        function Controller(myService) {
-
-            var vm = this;
-            vm.ancestors = [];
-
-            myService.getAncestors().then(function(ancestors){
-                vm.ancestors = ancestors;
-            });
-
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-    })();
-
- -@param {array} ancestors Array of ancestors -@param {string} entityType The content entity type (member, media, content). -**/ - -(function() { - 'use strict'; - - function BreadcrumbsDirective() { - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-breadcrumbs.html', - scope: { - ancestors: "=", - entityType: "@" - } - }; - - return directive; - - } - - angular.module('umbraco.directives').directive('umbBreadcrumbs', BreadcrumbsDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEditorContainer -@restrict E - -@description -Use this directive to construct a main content area inside the main editor window. - -

Markup example

-
-    
- - - - - - - - // main content here - - - - // footer content here - - - - -
-
- -

Use in combination with

-
    -
  • {@link umbraco.directives.directive:umbEditorView umbEditorView}
  • -
  • {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
  • -
  • {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
  • -
-**/ - -(function() { - 'use strict'; - - function EditorContainerDirective(overlayHelper) { - - function link(scope, el, attr, ctrl) { - - scope.numberOfOverlays = 0; - - scope.$watch(function(){ - return overlayHelper.getNumberOfOverlays(); - }, function (newValue) { - scope.numberOfOverlays = newValue; - }); - - } - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-container.html', - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorContainer', EditorContainerDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEditorFooter -@restrict E - -@description -Use this directive to construct a footer inside the main editor window. - -

Markup example

-
-    
- -
- - - - - - - - // main content here - - - - // footer content here - - - - -
- -
-
- -

Use in combination with

-
    -
  • {@link umbraco.directives.directive:umbEditorView umbEditorView}
  • -
  • {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
  • -
  • {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
  • -
  • {@link umbraco.directives.directive:umbEditorFooterContentLeft umbEditorFooterContentLeft}
  • -
  • {@link umbraco.directives.directive:umbEditorFooterContentRight umbEditorFooterContentRight}
  • -
-**/ - -(function() { - 'use strict'; - - function EditorFooterDirective() { - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-footer.html' - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorFooter', EditorFooterDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEditorFooterContentLeft -@restrict E - -@description -Use this directive to align content left inside the main editor footer. - -

Markup example

-
-    
- -
- - - - - - - // align content left - - - - // align content right - - - - - - -
- -
-
- -

Use in combination with

-
    -
  • {@link umbraco.directives.directive:umbEditorView umbEditorView}
  • -
  • {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
  • -
  • {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
  • -
  • {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
  • -
  • {@link umbraco.directives.directive:umbEditorFooterContentRight umbEditorFooterContentRight}
  • -
-**/ - -(function() { - 'use strict'; - - function EditorFooterContentLeftDirective() { - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-footer-content-left.html' - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorFooterContentLeft', EditorFooterContentLeftDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEditorFooterContentRight -@restrict E - -@description -Use this directive to align content right inside the main editor footer. - -

Markup example

-
-    
- -
- - - - - - - // align content left - - - - // align content right - - - - - - -
- -
-
- -

Use in combination with

-
    -
  • {@link umbraco.directives.directive:umbEditorView umbEditorView}
  • -
  • {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
  • -
  • {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
  • -
  • {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
  • -
  • {@link umbraco.directives.directive:umbEditorFooterContentLeft umbEditorFooterContentLeft}
  • -
-**/ - -(function() { - 'use strict'; - - function EditorFooterContentRightDirective() { - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-footer-content-right.html' - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorFooterContentRight', EditorFooterContentRightDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEditorHeader -@restrict E -@scope - -@description -Use this directive to construct a header inside the main editor window. - -

Markup example

-
-    
- -
- - - - - - - - // main content here - - - - // footer content here - - - - -
- -
-
- -

Markup example - with tabs

-
-    
- -
- - - - - - - - - - -
- // tab 1 content -
- -
- // tab 2 content -
- -
-
-
- - - // footer content here - - -
- -
- -
-
- -

Controller example - with tabs

-
-    (function () {
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-            vm.content = {
-                name: "",
-                tabs: [
-                    {
-                        id: 1,
-                        label: "Tab 1",
-                        alias: "tab1",
-                        active: true
-                    },
-                    {
-                        id: 2,
-                        label: "Tab 2",
-                        alias: "tab2",
-                        active: false
-                    }
-                ]
-            };
-
-        }
-
-        angular.module("umbraco").controller("MySection.Controller", Controller);
-    })();
-
- -

Markup example - with sub views

-
-    
- -
- - - - - - - - - - - - - - - // footer content here - - - - -
- -
-
- -

Controller example - with sub views

-
-    (function () {
-
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-            vm.content = {
-                name: "",
-                navigation: [
-                    {
-                        "name": "Section 1",
-                        "icon": "icon-document-dashed-line",
-                        "view": "/App_Plugins/path/to/html.html",
-                        "active": true
-                    },
-                    {
-                        "name": "Section 2",
-                        "icon": "icon-list",
-                        "view": "/App_Plugins/path/to/html.html",
-                    }
-                ]
-            };
-
-        }
-
-        angular.module("umbraco").controller("MySection.Controller", Controller);
-    })();
-
- -

Use in combination with

-
    -
  • {@link umbraco.directives.directive:umbEditorView umbEditorView}
  • -
  • {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
  • -
  • {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
  • -
- -@param {string} name The content name. -@param {array=} tabs Array of tabs. See example above. -@param {array=} navigation Array of sub views. See example above. -@param {boolean=} nameLocked Set to true to lock the name. -@param {object=} menu Add a context menu to the editor. -@param {string=} icon Show and edit the content icon. Opens an overlay to change the icon. -@param {boolean=} hideIcon Set to true to hide icon. -@param {string=} alias show and edit the content alias. -@param {boolean=} hideAlias Set to true to hide alias. -@param {string=} description Add a description to the content. -@param {boolean=} hideDescription Set to true to hide description. - -**/ - -(function() { - 'use strict'; - - function EditorHeaderDirective(iconHelper) { - - function link(scope, el, attr, ctrl) { - - scope.openIconPicker = function() { - scope.dialogModel = { - view: "iconpicker", - show: true, - submit: function(model) { - if (model.color) { - scope.icon = model.icon + " " + model.color; - } else { - scope.icon = model.icon; - } - - // set form to dirty - ctrl.$setDirty(); - - scope.dialogModel.show = false; - scope.dialogModel = null; - } - }; - }; - } - - var directive = { - require: '^form', - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-header.html', - scope: { - tabs: "=", - actions: "=", - name: "=", - nameLocked: "=", - menu: "=", - icon: "=", - hideIcon: "@", - alias: "=", - hideAlias: "@", - description: "=", - hideDescription: "@", - navigation: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorHeader', EditorHeaderDirective); - -})(); - -(function() { - 'use strict'; - - function EditorMenuDirective($injector, treeService, navigationService, umbModelMapper, appState) { - - function link(scope, el, attr, ctrl) { - - //adds a handler to the context menu item click, we need to handle this differently - //depending on what the menu item is supposed to do. - scope.executeMenuItem = function (action) { - navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection); - }; - - //callback method to go and get the options async - scope.getOptions = function () { - - if (!scope.currentNode) { - return; - } - - //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu) - appState.setMenuState("currentNode", scope.currentNode); - - if (!scope.actions) { - treeService.getMenu({ treeNode: scope.currentNode }) - .then(function (data) { - scope.actions = data.menuItems; - }); - } - }; - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-menu.html', - link: link, - scope: { - currentNode: "=", - currentSection: "@" - } - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorMenu', EditorMenuDirective); - -})(); - -(function() { - 'use strict'; - - function EditorNavigationDirective() { - - function link(scope, el, attr, ctrl) { - - scope.showNavigation = true; - - scope.clickNavigationItem = function(selectedItem) { - setItemToActive(selectedItem); - runItemAction(selectedItem); - }; - - function runItemAction(selectedItem) { - if (selectedItem.action) { - selectedItem.action(selectedItem); - } - } - - function setItemToActive(selectedItem) { - // set all other views to inactive - if (selectedItem.view) { - - for (var index = 0; index < scope.navigation.length; index++) { - var item = scope.navigation[index]; - item.active = false; - } - - // set view to active - selectedItem.active = true; - - } - } - - function activate() { - - // hide navigation if there is only 1 item - if (scope.navigation.length <= 1) { - scope.showNavigation = false; - } - - } - - activate(); - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-navigation.html', - scope: { - navigation: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives.html').directive('umbEditorNavigation', EditorNavigationDirective); - -})(); - -(function() { - 'use strict'; - - function EditorSubViewsDirective() { - - function link(scope, el, attr, ctrl) { - - scope.activeView = {}; - - // set toolbar from selected navigation item - function setActiveView(items) { - - for (var index = 0; index < items.length; index++) { - - var item = items[index]; - - if (item.active && item.view) { - scope.activeView = item; - } - } - } - - // watch for navigation changes - scope.$watch('subViews', function(newValue, oldValue) { - if (newValue) { - setActiveView(newValue); - } - }, true); - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-sub-views.html', - scope: { - subViews: "=", - model: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorSubViews', EditorSubViewsDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEditorView -@restrict E -@scope - -@description -Use this directive to construct the main editor window. - -

Markup example

-
-    
- -
- - - - - - - - // main content here - - - - // footer content here - - - - -
- -
-
-

Controller example

-
-    (function () {
-
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-
-        }
-
-        angular.module("umbraco").controller("MySection.Controller", Controller);
-    })();
-
- - -

Use in combination with

-
    -
  • {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
  • -
  • {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
  • -
  • {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
  • -
-**/ - -(function() { - 'use strict'; - - function EditorViewDirective() { - - function link(scope, el, attr) { - - if(attr.footer) { - scope.footer = attr.footer; - } - - } - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-view.html', - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEditorView', EditorViewDirective); - -})(); - -/** -* @description Utillity directives for key and field events -**/ -angular.module('umbraco.directives') - -.directive('onKeyup', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onKeyup); - }; - elm.on("keyup", f); - scope.$on("$destroy", function(){ elm.off("keyup", f);} ); - } - }; -}) - -.directive('onKeydown', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onKeydown); - }; - elm.on("keydown", f); - scope.$on("$destroy", function(){ elm.off("keydown", f);} ); - } - }; -}) - -.directive('onBlur', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onBlur); - }; - elm.on("blur", f); - scope.$on("$destroy", function(){ elm.off("blur", f);} ); - } - }; -}) - -.directive('onFocus', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onFocus); - }; - elm.on("focus", f); - scope.$on("$destroy", function(){ elm.off("focus", f);} ); - } - }; -}) - -.directive('onDragEnter', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onDragEnter); - }; - elm.on("dragenter", f); - scope.$on("$destroy", function(){ elm.off("dragenter", f);} ); - } - }; -}) - -.directive('onDragLeave', function () { - return function (scope, elm, attrs) { - var f = function (event) { - var rect = this.getBoundingClientRect(); - var getXY = function getCursorPosition(event) { - var x, y; - - if (typeof event.clientX === 'undefined') { - // try touch screen - x = event.pageX + document.documentElement.scrollLeft; - y = event.pageY + document.documentElement.scrollTop; - } else { - x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; - y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; - } - - return { x: x, y : y }; - }; - - var e = getXY(event.originalEvent); - - // Check the mouseEvent coordinates are outside of the rectangle - if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) { - scope.$apply(attrs.onDragLeave); - } - }; - - elm.on("dragleave", f); - scope.$on("$destroy", function(){ elm.off("dragleave", f);} ); - }; -}) - -.directive('onDragOver', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onDragOver); - }; - elm.on("dragover", f); - scope.$on("$destroy", function(){ elm.off("dragover", f);} ); - } - }; -}) - -.directive('onDragStart', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onDragStart); - }; - elm.on("dragstart", f); - scope.$on("$destroy", function(){ elm.off("dragstart", f);} ); - } - }; -}) - -.directive('onDragEnd', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onDragEnd); - }; - elm.on("dragend", f); - scope.$on("$destroy", function(){ elm.off("dragend", f);} ); - } - }; -}) - -.directive('onDrop', function () { - return { - link: function (scope, elm, attrs) { - var f = function () { - scope.$apply(attrs.onDrop); - }; - elm.on("drop", f); - scope.$on("$destroy", function(){ elm.off("drop", f);} ); - } - }; -}) - -.directive('onOutsideClick', function ($timeout) { - return function (scope, element, attrs) { - - var eventBindings = []; - - function oneTimeClick(event) { - var el = event.target.nodeName; - - //ignore link and button clicks - var els = ["INPUT","A","BUTTON"]; - if(els.indexOf(el) >= 0){return;} - - // ignore children of links and buttons - // ignore clicks on new overlay - var parents = $(event.target).parents("a,button,.umb-overlay"); - if(parents.length > 0){ - return; - } - - // ignore clicks on dialog from old dialog service - var oldDialog = $(el).parents("#old-dialog-service"); - if (oldDialog.length === 1) { - return; - } - - // ignore clicks in tinyMCE dropdown(floatpanel) - var floatpanel = $(el).parents(".mce-floatpanel"); - if (floatpanel.length === 1) { - return; - } - - //ignore clicks inside this element - if( $(element).has( $(event.target) ).length > 0 ){ - return; - } - - scope.$apply(attrs.onOutsideClick); - } - - - $timeout(function(){ - - if ("bindClickOn" in attrs) { - - eventBindings.push(scope.$watch(function() { - return attrs.bindClickOn; - }, function(newValue) { - if (newValue === "true") { - $(document).on("click", oneTimeClick); - } else { - $(document).off("click", oneTimeClick); - } - })); - - } else { - $(document).on("click", oneTimeClick); - } - - scope.$on("$destroy", function() { - $(document).off("click", oneTimeClick); - - // unbind watchers - for (var e in eventBindings) { - eventBindings[e](); - } - - }); - }); // Temp removal of 1 sec timeout to prevent bug where overlay does not open. We need to find a better solution. - - }; -}) - -.directive('onRightClick',function(){ - - document.oncontextmenu = function (e) { - if(e.target.hasAttribute('on-right-click')) { - e.preventDefault(); - e.stopPropagation(); - return false; - } - }; - - return function(scope,el,attrs){ - el.on('contextmenu',function(e){ - e.preventDefault(); - e.stopPropagation(); - scope.$apply(attrs.onRightClick); - return false; - }); - }; -}) - -.directive('onDelayedMouseleave', function ($timeout, $parse) { - return { - - restrict: 'A', - - link: function (scope, element, attrs, ctrl) { - var active = false; - var fn = $parse(attrs.onDelayedMouseleave); - - var leave_f = function(event) { - var callback = function() { - fn(scope, {$event:event}); - }; - - active = false; - $timeout(function(){ - if(active === false){ - scope.$apply(callback); - } - }, 650); - }; - - var enter_f = function(event, args){ - active = true; - }; - - - element.on("mouseleave", leave_f); - element.on("mouseenter", enter_f); - - //unsub events - scope.$on("$destroy", function(){ - element.off("mouseleave", leave_f); - element.off("mouseenter", enter_f); - }); - } - }; - }); - -/* - - http://vitalets.github.io/checklist-model/ - -*/ -angular.module('umbraco.directives') -.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) { - // contains - function contains(arr, item) { - if (angular.isArray(arr)) { - for (var i = 0; i < arr.length; i++) { - if (angular.equals(arr[i], item)) { - return true; - } - } - } - return false; - } - - // add - function add(arr, item) { - arr = angular.isArray(arr) ? arr : []; - for (var i = 0; i < arr.length; i++) { - if (angular.equals(arr[i], item)) { - return arr; - } - } - arr.push(item); - return arr; - } - - // remove - function remove(arr, item) { - if (angular.isArray(arr)) { - for (var i = 0; i < arr.length; i++) { - if (angular.equals(arr[i], item)) { - arr.splice(i, 1); - break; - } - } - } - return arr; - } - - // http://stackoverflow.com/a/19228302/1458162 - function postLinkFn(scope, elem, attrs) { - // compile with `ng-model` pointing to `checked` - $compile(elem)(scope); - - // getter / setter for original model - var getter = $parse(attrs.checklistModel); - var setter = getter.assign; - - // value added to list - var value = $parse(attrs.checklistValue)(scope.$parent); - - // watch UI checked change - scope.$watch('checked', function(newValue, oldValue) { - if (newValue === oldValue) { - return; - } - var current = getter(scope.$parent); - if (newValue === true) { - setter(scope.$parent, add(current, value)); - } else { - setter(scope.$parent, remove(current, value)); - } - }); - - // watch original model change - scope.$parent.$watch(attrs.checklistModel, function(newArr, oldArr) { - scope.checked = contains(newArr, value); - }, true); - } - - return { - restrict: 'A', - priority: 1000, - terminal: true, - scope: true, - compile: function(tElement, tAttrs) { - if (tElement[0].tagName !== 'INPUT' || !tElement.attr('type', 'checkbox')) { - throw 'checklist-model should be applied to `input[type="checkbox"]`.'; - } - - if (!tAttrs.checklistValue) { - throw 'You should provide `checklist-value`.'; - } - - // exclude recursion - tElement.removeAttr('checklist-model'); - - // local scope var storing individual checkbox model - tElement.attr('ng-model', 'checked'); - - return postLinkFn; - } - }; +/** +@ngdoc directive +@name umbraco.directives.directive:umbButton +@restrict E +@scope + +@description +Use this directive to render an umbraco button. The directive can be used to generate all types of buttons, set type, style, translation, shortcut and much more. + +

Markup example

+
+    
+ + + + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller(myService) {
+
+            var vm = this;
+            vm.buttonState = "init";
+
+            vm.clickButton = clickButton;
+
+            function clickButton() {
+
+                vm.buttonState = "busy";
+
+                myService.clickButton().then(function() {
+                    vm.buttonState = "success";
+                }, function() {
+                    vm.buttonState = "error";
+                });
+
+            }
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+
+    })();
+
+ +@param {callback} action The button action which should be performed when the button is clicked. +@param {string=} href Url/Path to navigato to. +@param {string=} type Set the button type ("button" or "submit"). +@param {string=} buttonStyle Set the style of the button. The directive uses the default bootstrap styles ("primary", "info", "success", "warning", "danger", "inverse", "link"). +@param {string=} state Set a progress state on the button ("init", "busy", "success", "error"). +@param {string=} shortcut Set a keyboard shortcut for the button ("ctrl+c"). +@param {string=} label Set the button label. +@param {string=} labelKey Set a localization key to make a multi lingual button ("general_buttonText"). +@param {string=} icon Set a button icon. Can only be used when buttonStyle is "link". +@param {boolean=} disabled Set to true to disable the button. +**/ + +(function() { + 'use strict'; + + function ButtonDirective($timeout) { + + function link(scope, el, attr, ctrl) { + + scope.style = null; + + function activate() { + + if (!scope.state) { + scope.state = "init"; + } + + if (scope.buttonStyle) { + scope.style = "btn-" + scope.buttonStyle; + } + + } + + activate(); + + var unbindStateWatcher = scope.$watch('state', function(newValue, oldValue) { + + if (newValue === 'success' || newValue === 'error') { + $timeout(function() { + scope.state = 'init'; + }, 2000); + } + + }); + + scope.$on('$destroy', function() { + unbindStateWatcher(); + }); + + } + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/buttons/umb-button.html', + link: link, + scope: { + action: "&?", + href: "@?", + type: "@", + buttonStyle: "@?", + state: "=?", + shortcut: "@?", + shortcutWhenHidden: "@", + label: "@?", + labelKey: "@?", + icon: "@?", + disabled: "=" + } + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbButton', ButtonDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbButtonGroup +@restrict E +@scope + +@description +Use this directive to render a button with a dropdown of alternative actions. + +

Markup example

+
+    
+ + + + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+
+            vm.buttonGroup = {
+                defaultButton: {
+                    labelKey: "general_defaultButton",
+                    hotKey: "ctrl+d",
+                    hotKeyWhenHidden: true,
+                    handler: function() {
+                        // do magic here
+                    }
+                },
+                subButtons: [
+                    {
+                        labelKey: "general_subButton",
+                        hotKey: "ctrl+b",
+                        hotKeyWhenHidden: true,
+                        handler: function() {
+                            // do magic here
+                        }
+                    }
+                ]
+            };
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+
+    })();
+
+ +

Button model description

+
    +
  • + labekKey + (string) - + Set a localization key to make a multi lingual button ("general_buttonText"). +
  • +
  • + hotKey + (array) - + Set a keyboard shortcut for the button ("ctrl+c"). +
  • +
  • + hotKeyWhenHidden + (boolean) - + As a default the hotkeys only works on elements visible in the UI. Set to true to set a hotkey on the hidden sub buttons. +
  • +
  • + handler + (callback) - + Set a callback to handle button click events. +
  • +
+ +@param {object} defaultButton The model of the default button. +@param {array} subButtons Array of sub buttons. +@param {string=} state Set a progress state on the button ("init", "busy", "success", "error"). +@param {string=} direction Set the direction of the dropdown ("up", "down"). +@param {string=} float Set the float of the dropdown. ("left", "right"). +**/ + +(function() { + 'use strict'; + + function ButtonGroupDirective() { + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/buttons/umb-button-group.html', + scope: { + defaultButton: "=", + subButtons: "=", + state: "=?", + direction: "@?", + float: "@?" + } + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbButtonGroup', ButtonGroupDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEditorSubHeader +@restrict E + +@description +Use this directive to construct a sub header in the main editor window. +The sub header is sticky and will follow along down the page when scrolling. + +

Markup example

+
+    
+ +
+ + + + + + + // sub header content here + + + + + + +
+ +
+
+ +

Use in combination with

+
    +
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentLeft umbEditorSubHeaderContentLeft}
  • +
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentRight umbEditorSubHeaderContentRight}
  • +
  • {@link umbraco.directives.directive:umbEditorSubHeaderSection umbEditorSubHeaderSection}
  • +
+**/ + +(function() { + 'use strict'; + + function EditorSubHeaderDirective() { + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/subheader/umb-editor-sub-header.html' + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorSubHeader', EditorSubHeaderDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEditorSubHeaderContentLeft +@restrict E + +@description +Use this directive to left align content in a sub header in the main editor window. + +

Markup example

+
+    
+ +
+ + + + + + + + + // left content here + + + + // right content here + + + + + + + + +
+ +
+
+ +

Use in combination with

+
    +
  • {@link umbraco.directives.directive:umbEditorSubHeader umbEditorSubHeader}
  • +
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentRight umbEditorSubHeaderContentRight}
  • +
  • {@link umbraco.directives.directive:umbEditorSubHeaderSection umbEditorSubHeaderSection}
  • +
+**/ + +(function() { + 'use strict'; + + function EditorSubHeaderContentLeftDirective() { + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-content-left.html' + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorSubHeaderContentLeft', EditorSubHeaderContentLeftDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEditorSubHeaderContentRight +@restrict E + +@description +Use this directive to rigt align content in a sub header in the main editor window. + +

Markup example

+
+    
+ +
+ + + + + + + + + // left content here + + + + // right content here + + + + + + + + +
+ +
+
+ +

Use in combination with

+
    +
  • {@link umbraco.directives.directive:umbEditorSubHeader umbEditorSubHeader}
  • +
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentLeft umbEditorSubHeaderContentLeft}
  • +
  • {@link umbraco.directives.directive:umbEditorSubHeaderSection umbEditorSubHeaderSection}
  • +
+**/ + +(function() { + 'use strict'; + + function EditorSubHeaderContentRightDirective() { + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-content-right.html' + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorSubHeaderContentRight', EditorSubHeaderContentRightDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEditorSubHeaderSection +@restrict E + +@description +Use this directive to create sections, divided by borders, in a sub header in the main editor window. + +

Markup example

+
+    
+ +
+ + + + + + + + + + + // section content here + + + + // section content here + + + + // section content here + + + + + + + + + + +
+ +
+
+ +

Use in combination with

+
    +
  • {@link umbraco.directives.directive:umbEditorSubHeader umbEditorSubHeader}
  • +
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentLeft umbEditorSubHeaderContentLeft}
  • +
  • {@link umbraco.directives.directive:umbEditorSubHeaderContentRight umbEditorSubHeaderContentRight}
  • +
+**/ + +(function() { + 'use strict'; + + function EditorSubHeaderSectionDirective() { + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-section.html' + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorSubHeaderSection', EditorSubHeaderSectionDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbBreadcrumbs +@restrict E +@scope + +@description +Use this directive to generate a list of breadcrumbs. + +

Markup example

+
+    
+ + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller(myService) {
+
+            var vm = this;
+            vm.ancestors = [];
+
+            myService.getAncestors().then(function(ancestors){
+                vm.ancestors = ancestors;
+            });
+
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+ +@param {array} ancestors Array of ancestors +@param {string} entityType The content entity type (member, media, content). +**/ + +(function() { + 'use strict'; + + function BreadcrumbsDirective() { + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-breadcrumbs.html', + scope: { + ancestors: "=", + entityType: "@" + } + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbBreadcrumbs', BreadcrumbsDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEditorContainer +@restrict E + +@description +Use this directive to construct a main content area inside the main editor window. + +

Markup example

+
+    
+ + + + + + + + // main content here + + + + // footer content here + + + + +
+
+ +

Use in combination with

+
    +
  • {@link umbraco.directives.directive:umbEditorView umbEditorView}
  • +
  • {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
  • +
  • {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
  • +
+**/ + +(function() { + 'use strict'; + + function EditorContainerDirective(overlayHelper) { + + function link(scope, el, attr, ctrl) { + + scope.numberOfOverlays = 0; + + scope.$watch(function(){ + return overlayHelper.getNumberOfOverlays(); + }, function (newValue) { + scope.numberOfOverlays = newValue; + }); + + } + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-container.html', + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorContainer', EditorContainerDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEditorFooter +@restrict E + +@description +Use this directive to construct a footer inside the main editor window. + +

Markup example

+
+    
+ +
+ + + + + + + + // main content here + + + + // footer content here + + + + +
+ +
+
+ +

Use in combination with

+
    +
  • {@link umbraco.directives.directive:umbEditorView umbEditorView}
  • +
  • {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
  • +
  • {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
  • +
  • {@link umbraco.directives.directive:umbEditorFooterContentLeft umbEditorFooterContentLeft}
  • +
  • {@link umbraco.directives.directive:umbEditorFooterContentRight umbEditorFooterContentRight}
  • +
+**/ + +(function() { + 'use strict'; + + function EditorFooterDirective() { + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-footer.html' + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorFooter', EditorFooterDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEditorFooterContentLeft +@restrict E + +@description +Use this directive to align content left inside the main editor footer. + +

Markup example

+
+    
+ +
+ + + + + + + // align content left + + + + // align content right + + + + + + +
+ +
+
+ +

Use in combination with

+
    +
  • {@link umbraco.directives.directive:umbEditorView umbEditorView}
  • +
  • {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
  • +
  • {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
  • +
  • {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
  • +
  • {@link umbraco.directives.directive:umbEditorFooterContentRight umbEditorFooterContentRight}
  • +
+**/ + +(function() { + 'use strict'; + + function EditorFooterContentLeftDirective() { + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-footer-content-left.html' + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorFooterContentLeft', EditorFooterContentLeftDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEditorFooterContentRight +@restrict E + +@description +Use this directive to align content right inside the main editor footer. + +

Markup example

+
+    
+ +
+ + + + + + + // align content left + + + + // align content right + + + + + + +
+ +
+
+ +

Use in combination with

+
    +
  • {@link umbraco.directives.directive:umbEditorView umbEditorView}
  • +
  • {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
  • +
  • {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
  • +
  • {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
  • +
  • {@link umbraco.directives.directive:umbEditorFooterContentLeft umbEditorFooterContentLeft}
  • +
+**/ + +(function() { + 'use strict'; + + function EditorFooterContentRightDirective() { + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-footer-content-right.html' + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorFooterContentRight', EditorFooterContentRightDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEditorHeader +@restrict E +@scope + +@description +Use this directive to construct a header inside the main editor window. + +

Markup example

+
+    
+ +
+ + + + + + + + // main content here + + + + // footer content here + + + + +
+ +
+
+ +

Markup example - with tabs

+
+    
+ +
+ + + + + + + + + + +
+ // tab 1 content +
+ +
+ // tab 2 content +
+ +
+
+
+ + + // footer content here + + +
+ +
+ +
+
+ +

Controller example - with tabs

+
+    (function () {
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+            vm.content = {
+                name: "",
+                tabs: [
+                    {
+                        id: 1,
+                        label: "Tab 1",
+                        alias: "tab1",
+                        active: true
+                    },
+                    {
+                        id: 2,
+                        label: "Tab 2",
+                        alias: "tab2",
+                        active: false
+                    }
+                ]
+            };
+
+        }
+
+        angular.module("umbraco").controller("MySection.Controller", Controller);
+    })();
+
+ +

Markup example - with sub views

+
+    
+ +
+ + + + + + + + + + + + + + + // footer content here + + + + +
+ +
+
+ +

Controller example - with sub views

+
+    (function () {
+
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+            vm.content = {
+                name: "",
+                navigation: [
+                    {
+                        "name": "Section 1",
+                        "icon": "icon-document-dashed-line",
+                        "view": "/App_Plugins/path/to/html.html",
+                        "active": true
+                    },
+                    {
+                        "name": "Section 2",
+                        "icon": "icon-list",
+                        "view": "/App_Plugins/path/to/html.html",
+                    }
+                ]
+            };
+
+        }
+
+        angular.module("umbraco").controller("MySection.Controller", Controller);
+    })();
+
+ +

Use in combination with

+
    +
  • {@link umbraco.directives.directive:umbEditorView umbEditorView}
  • +
  • {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
  • +
  • {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
  • +
+ +@param {string} name The content name. +@param {array=} tabs Array of tabs. See example above. +@param {array=} navigation Array of sub views. See example above. +@param {boolean=} nameLocked Set to true to lock the name. +@param {object=} menu Add a context menu to the editor. +@param {string=} icon Show and edit the content icon. Opens an overlay to change the icon. +@param {boolean=} hideIcon Set to true to hide icon. +@param {string=} alias show and edit the content alias. +@param {boolean=} hideAlias Set to true to hide alias. +@param {string=} description Add a description to the content. +@param {boolean=} hideDescription Set to true to hide description. + +**/ + +(function() { + 'use strict'; + + function EditorHeaderDirective(iconHelper) { + + function link(scope, el, attr, ctrl) { + + scope.openIconPicker = function() { + scope.dialogModel = { + view: "iconpicker", + show: true, + submit: function(model) { + if (model.color) { + scope.icon = model.icon + " " + model.color; + } else { + scope.icon = model.icon; + } + + // set form to dirty + ctrl.$setDirty(); + + scope.dialogModel.show = false; + scope.dialogModel = null; + } + }; + }; + } + + var directive = { + require: '^form', + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-header.html', + scope: { + tabs: "=", + actions: "=", + name: "=", + nameLocked: "=", + menu: "=", + icon: "=", + hideIcon: "@", + alias: "=", + hideAlias: "@", + description: "=", + hideDescription: "@", + navigation: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorHeader', EditorHeaderDirective); + +})(); + +(function() { + 'use strict'; + + function EditorMenuDirective($injector, treeService, navigationService, umbModelMapper, appState) { + + function link(scope, el, attr, ctrl) { + + //adds a handler to the context menu item click, we need to handle this differently + //depending on what the menu item is supposed to do. + scope.executeMenuItem = function (action) { + navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection); + }; + + //callback method to go and get the options async + scope.getOptions = function () { + + if (!scope.currentNode) { + return; + } + + //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu) + appState.setMenuState("currentNode", scope.currentNode); + + if (!scope.actions) { + treeService.getMenu({ treeNode: scope.currentNode }) + .then(function (data) { + scope.actions = data.menuItems; + }); + } + }; + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-menu.html', + link: link, + scope: { + currentNode: "=", + currentSection: "@" + } + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorMenu', EditorMenuDirective); + +})(); + +(function() { + 'use strict'; + + function EditorNavigationDirective() { + + function link(scope, el, attr, ctrl) { + + scope.showNavigation = true; + + scope.clickNavigationItem = function(selectedItem) { + setItemToActive(selectedItem); + runItemAction(selectedItem); + }; + + function runItemAction(selectedItem) { + if (selectedItem.action) { + selectedItem.action(selectedItem); + } + } + + function setItemToActive(selectedItem) { + // set all other views to inactive + if (selectedItem.view) { + + for (var index = 0; index < scope.navigation.length; index++) { + var item = scope.navigation[index]; + item.active = false; + } + + // set view to active + selectedItem.active = true; + + } + } + + function activate() { + + // hide navigation if there is only 1 item + if (scope.navigation.length <= 1) { + scope.showNavigation = false; + } + + } + + activate(); + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-navigation.html', + scope: { + navigation: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives.html').directive('umbEditorNavigation', EditorNavigationDirective); + +})(); + +(function() { + 'use strict'; + + function EditorSubViewsDirective() { + + function link(scope, el, attr, ctrl) { + + scope.activeView = {}; + + // set toolbar from selected navigation item + function setActiveView(items) { + + for (var index = 0; index < items.length; index++) { + + var item = items[index]; + + if (item.active && item.view) { + scope.activeView = item; + } + } + } + + // watch for navigation changes + scope.$watch('subViews', function(newValue, oldValue) { + if (newValue) { + setActiveView(newValue); + } + }, true); + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-sub-views.html', + scope: { + subViews: "=", + model: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorSubViews', EditorSubViewsDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEditorView +@restrict E +@scope + +@description +Use this directive to construct the main editor window. + +

Markup example

+
+    
+ +
+ + + + + + + + // main content here + + + + // footer content here + + + + +
+ +
+
+

Controller example

+
+    (function () {
+
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+
+        }
+
+        angular.module("umbraco").controller("MySection.Controller", Controller);
+    })();
+
+ + +

Use in combination with

+
    +
  • {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
  • +
  • {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
  • +
  • {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
  • +
+**/ + +(function() { + 'use strict'; + + function EditorViewDirective() { + + function link(scope, el, attr) { + + if(attr.footer) { + scope.footer = attr.footer; + } + + } + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-view.html', + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorView', EditorViewDirective); + +})(); + +/** +* @description Utillity directives for key and field events +**/ +angular.module('umbraco.directives') + +.directive('onKeyup', function () { + return { + link: function (scope, elm, attrs) { + var f = function () { + scope.$apply(attrs.onKeyup); + }; + elm.on("keyup", f); + scope.$on("$destroy", function(){ elm.off("keyup", f);} ); + } + }; +}) + +.directive('onKeydown', function () { + return { + link: function (scope, elm, attrs) { + var f = function () { + scope.$apply(attrs.onKeydown); + }; + elm.on("keydown", f); + scope.$on("$destroy", function(){ elm.off("keydown", f);} ); + } + }; +}) + +.directive('onBlur', function () { + return { + link: function (scope, elm, attrs) { + var f = function () { + scope.$apply(attrs.onBlur); + }; + elm.on("blur", f); + scope.$on("$destroy", function(){ elm.off("blur", f);} ); + } + }; +}) + +.directive('onFocus', function () { + return { + link: function (scope, elm, attrs) { + var f = function () { + scope.$apply(attrs.onFocus); + }; + elm.on("focus", f); + scope.$on("$destroy", function(){ elm.off("focus", f);} ); + } + }; +}) + +.directive('onDragEnter', function () { + return { + link: function (scope, elm, attrs) { + var f = function () { + scope.$apply(attrs.onDragEnter); + }; + elm.on("dragenter", f); + scope.$on("$destroy", function(){ elm.off("dragenter", f);} ); + } + }; +}) + +.directive('onDragLeave', function () { + return function (scope, elm, attrs) { + var f = function (event) { + var rect = this.getBoundingClientRect(); + var getXY = function getCursorPosition(event) { + var x, y; + + if (typeof event.clientX === 'undefined') { + // try touch screen + x = event.pageX + document.documentElement.scrollLeft; + y = event.pageY + document.documentElement.scrollTop; + } else { + x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; + y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; + } + + return { x: x, y : y }; + }; + + var e = getXY(event.originalEvent); + + // Check the mouseEvent coordinates are outside of the rectangle + if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) { + scope.$apply(attrs.onDragLeave); + } + }; + + elm.on("dragleave", f); + scope.$on("$destroy", function(){ elm.off("dragleave", f);} ); + }; +}) + +.directive('onDragOver', function () { + return { + link: function (scope, elm, attrs) { + var f = function () { + scope.$apply(attrs.onDragOver); + }; + elm.on("dragover", f); + scope.$on("$destroy", function(){ elm.off("dragover", f);} ); + } + }; +}) + +.directive('onDragStart', function () { + return { + link: function (scope, elm, attrs) { + var f = function () { + scope.$apply(attrs.onDragStart); + }; + elm.on("dragstart", f); + scope.$on("$destroy", function(){ elm.off("dragstart", f);} ); + } + }; +}) + +.directive('onDragEnd', function () { + return { + link: function (scope, elm, attrs) { + var f = function () { + scope.$apply(attrs.onDragEnd); + }; + elm.on("dragend", f); + scope.$on("$destroy", function(){ elm.off("dragend", f);} ); + } + }; +}) + +.directive('onDrop', function () { + return { + link: function (scope, elm, attrs) { + var f = function () { + scope.$apply(attrs.onDrop); + }; + elm.on("drop", f); + scope.$on("$destroy", function(){ elm.off("drop", f);} ); + } + }; +}) + +.directive('onOutsideClick', function ($timeout) { + return function (scope, element, attrs) { + + var eventBindings = []; + + function oneTimeClick(event) { + var el = event.target.nodeName; + + //ignore link and button clicks + var els = ["INPUT","A","BUTTON"]; + if(els.indexOf(el) >= 0){return;} + + // ignore children of links and buttons + // ignore clicks on new overlay + var parents = $(event.target).parents("a,button,.umb-overlay"); + if(parents.length > 0){ + return; + } + + // ignore clicks on dialog from old dialog service + var oldDialog = $(el).parents("#old-dialog-service"); + if (oldDialog.length === 1) { + return; + } + + // ignore clicks in tinyMCE dropdown(floatpanel) + var floatpanel = $(el).parents(".mce-floatpanel"); + if (floatpanel.length === 1) { + return; + } + + //ignore clicks inside this element + if( $(element).has( $(event.target) ).length > 0 ){ + return; + } + + scope.$apply(attrs.onOutsideClick); + } + + + $timeout(function(){ + + if ("bindClickOn" in attrs) { + + eventBindings.push(scope.$watch(function() { + return attrs.bindClickOn; + }, function(newValue) { + if (newValue === "true") { + $(document).on("click", oneTimeClick); + } else { + $(document).off("click", oneTimeClick); + } + })); + + } else { + $(document).on("click", oneTimeClick); + } + + scope.$on("$destroy", function() { + $(document).off("click", oneTimeClick); + + // unbind watchers + for (var e in eventBindings) { + eventBindings[e](); + } + + }); + }); // Temp removal of 1 sec timeout to prevent bug where overlay does not open. We need to find a better solution. + + }; +}) + +.directive('onRightClick',function(){ + + document.oncontextmenu = function (e) { + if(e.target.hasAttribute('on-right-click')) { + e.preventDefault(); + e.stopPropagation(); + return false; + } + }; + + return function(scope,el,attrs){ + el.on('contextmenu',function(e){ + e.preventDefault(); + e.stopPropagation(); + scope.$apply(attrs.onRightClick); + return false; + }); + }; +}) + +.directive('onDelayedMouseleave', function ($timeout, $parse) { + return { + + restrict: 'A', + + link: function (scope, element, attrs, ctrl) { + var active = false; + var fn = $parse(attrs.onDelayedMouseleave); + + var leave_f = function(event) { + var callback = function() { + fn(scope, {$event:event}); + }; + + active = false; + $timeout(function(){ + if(active === false){ + scope.$apply(callback); + } + }, 650); + }; + + var enter_f = function(event, args){ + active = true; + }; + + + element.on("mouseleave", leave_f); + element.on("mouseenter", enter_f); + + //unsub events + scope.$on("$destroy", function(){ + element.off("mouseleave", leave_f); + element.off("mouseenter", enter_f); + }); + } + }; + }); + +/* + + http://vitalets.github.io/checklist-model/ + +*/ +angular.module('umbraco.directives') +.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) { + // contains + function contains(arr, item) { + if (angular.isArray(arr)) { + for (var i = 0; i < arr.length; i++) { + if (angular.equals(arr[i], item)) { + return true; + } + } + } + return false; + } + + // add + function add(arr, item) { + arr = angular.isArray(arr) ? arr : []; + for (var i = 0; i < arr.length; i++) { + if (angular.equals(arr[i], item)) { + return arr; + } + } + arr.push(item); + return arr; + } + + // remove + function remove(arr, item) { + if (angular.isArray(arr)) { + for (var i = 0; i < arr.length; i++) { + if (angular.equals(arr[i], item)) { + arr.splice(i, 1); + break; + } + } + } + return arr; + } + + // http://stackoverflow.com/a/19228302/1458162 + function postLinkFn(scope, elem, attrs) { + // compile with `ng-model` pointing to `checked` + $compile(elem)(scope); + + // getter / setter for original model + var getter = $parse(attrs.checklistModel); + var setter = getter.assign; + + // value added to list + var value = $parse(attrs.checklistValue)(scope.$parent); + + // watch UI checked change + scope.$watch('checked', function(newValue, oldValue) { + if (newValue === oldValue) { + return; + } + var current = getter(scope.$parent); + if (newValue === true) { + setter(scope.$parent, add(current, value)); + } else { + setter(scope.$parent, remove(current, value)); + } + }); + + // watch original model change + scope.$parent.$watch(attrs.checklistModel, function(newArr, oldArr) { + scope.checked = contains(newArr, value); + }, true); + } + + return { + restrict: 'A', + priority: 1000, + terminal: true, + scope: true, + compile: function(tElement, tAttrs) { + if (tElement[0].tagName !== 'INPUT' || !tElement.attr('type', 'checkbox')) { + throw 'checklist-model should be applied to `input[type="checkbox"]`.'; + } + + if (!tAttrs.checklistValue) { + throw 'You should provide `checklist-value`.'; + } + + // exclude recursion + tElement.removeAttr('checklist-model'); + + // local scope var storing individual checkbox model + tElement.attr('ng-model', 'checked'); + + return postLinkFn; + } + }; }]); -angular.module("umbraco.directives") -.directive("contenteditable", function() { - - return { - require: "ngModel", - link: function(scope, element, attrs, ngModel) { - - function read() { - ngModel.$setViewValue(element.html()); - } - - ngModel.$render = function() { - element.html(ngModel.$viewValue || ""); - }; - - - element.bind("focus", function(){ - - var range = document.createRange(); - range.selectNodeContents(element[0]); - - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - - }); - - element.bind("blur keyup change", function() { - scope.$apply(read); - }); - } - - }; - +angular.module("umbraco.directives") +.directive("contenteditable", function() { + + return { + require: "ngModel", + link: function(scope, element, attrs, ngModel) { + + function read() { + ngModel.$setViewValue(element.html()); + } + + ngModel.$render = function() { + element.html(ngModel.$viewValue || ""); + }; + + + element.bind("focus", function(){ + + var range = document.createRange(); + range.selectNodeContents(element[0]); + + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + + }); + + element.bind("blur keyup change", function() { + scope.$apply(read); + }); + } + + }; + }); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:fixNumber -* @restrict A -* @description Used in conjunction with type='number' input fields to ensure that the bound value is converted to a number when using ng-model -* because normally it thinks it's a string and also validation doesn't work correctly due to an angular bug. -**/ -function fixNumber($parse) { - return { - restrict: "A", - require: "ngModel", - - link: function (scope, elem, attrs, ctrl) { - - //parse ngModel onload - var modelVal = scope.$eval(attrs.ngModel); - if (modelVal) { - var asNum = parseFloat(modelVal, 10); - if (!isNaN(asNum)) { - $parse(attrs.ngModel).assign(scope, asNum); - } - } - - //always return an int to the model - ctrl.$parsers.push(function (value) { - if (value === 0) { - return 0; - } - return parseFloat(value || '', 10); - }); - - //always try to format the model value as an int - ctrl.$formatters.push(function (value) { - if (angular.isString(value)) { - return parseFloat(value, 10); - } - return value; - }); - - //This fixes this angular issue: - //https://github.com/angular/angular.js/issues/2144 - // which doesn't actually validate the number input properly since the model only changes when a real number is entered - // but the input box still allows non-numbers to be entered which do not validate (only via html5) - if (typeof elem.prop('validity') === 'undefined') { - return; - } - - elem.bind('input', function (e) { - var validity = elem.prop('validity'); - scope.$apply(function () { - ctrl.$setValidity('number', !validity.badInput); - }); - }); - } - }; -} + +/** +* @ngdoc directive +* @name umbraco.directives.directive:fixNumber +* @restrict A +* @description Used in conjunction with type='number' input fields to ensure that the bound value is converted to a number when using ng-model +* because normally it thinks it's a string and also validation doesn't work correctly due to an angular bug. +**/ +function fixNumber($parse) { + return { + restrict: "A", + require: "ngModel", + + link: function (scope, elem, attrs, ctrl) { + + //parse ngModel onload + var modelVal = scope.$eval(attrs.ngModel); + if (modelVal) { + var asNum = parseFloat(modelVal, 10); + if (!isNaN(asNum)) { + $parse(attrs.ngModel).assign(scope, asNum); + } + } + + //always return an int to the model + ctrl.$parsers.push(function (value) { + if (value === 0) { + return 0; + } + return parseFloat(value || '', 10); + }); + + //always try to format the model value as an int + ctrl.$formatters.push(function (value) { + if (angular.isString(value)) { + return parseFloat(value, 10); + } + return value; + }); + + //This fixes this angular issue: + //https://github.com/angular/angular.js/issues/2144 + // which doesn't actually validate the number input properly since the model only changes when a real number is entered + // but the input box still allows non-numbers to be entered which do not validate (only via html5) + if (typeof elem.prop('validity') === 'undefined') { + return; + } + + elem.bind('input', function (e) { + var validity = elem.prop('validity'); + scope.$apply(function () { + ctrl.$setValidity('number', !validity.badInput); + }); + }); + } + }; +} angular.module('umbraco.directives').directive("fixNumber", fixNumber); -angular.module("umbraco.directives").directive('focusWhen', function ($timeout) { - return { - restrict: 'A', - link: function (scope, elm, attrs, ctrl) { - attrs.$observe("focusWhen", function (newValue) { - if (newValue === "true") { - $timeout(function () { - elm.focus(); - }); - } - }); - } - }; -}); +angular.module("umbraco.directives").directive('focusWhen', function ($timeout) { + return { + restrict: 'A', + link: function (scope, elm, attrs, ctrl) { + attrs.$observe("focusWhen", function (newValue) { + if (newValue === "true") { + $timeout(function () { + elm.focus(); + }); + } + }); + } + }; +}); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:hexBgColor -* @restrict A -* @description Used to set a hex background color on an element, this will detect valid hex and when it is valid it will set the color, otherwise -* a color will not be set. -**/ -function hexBgColor() { - return { - restrict: "A", - link: function (scope, element, attr, formCtrl) { - - var origColor = null; - if (attr.hexBgOrig) { - //set the orig based on the attribute if there is one - origColor = attr.hexBgOrig; - } - - attr.$observe("hexBgColor", function (newVal) { - if (newVal) { - if (!origColor) { - //get the orig color before changing it - origColor = element.css("border-color"); - } - //validate it - test with and without the leading hash. - if (/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) { - element.css("background-color", "#" + newVal); - return; - } - if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) { - element.css("background-color", newVal); - return; - } - } - element.css("background-color", origColor); - }); - - } - }; -} + +/** +* @ngdoc directive +* @name umbraco.directives.directive:hexBgColor +* @restrict A +* @description Used to set a hex background color on an element, this will detect valid hex and when it is valid it will set the color, otherwise +* a color will not be set. +**/ +function hexBgColor() { + return { + restrict: "A", + link: function (scope, element, attr, formCtrl) { + + var origColor = null; + if (attr.hexBgOrig) { + //set the orig based on the attribute if there is one + origColor = attr.hexBgOrig; + } + + attr.$observe("hexBgColor", function (newVal) { + if (newVal) { + if (!origColor) { + //get the orig color before changing it + origColor = element.css("border-color"); + } + //validate it - test with and without the leading hash. + if (/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) { + element.css("background-color", "#" + newVal); + return; + } + if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) { + element.css("background-color", newVal); + return; + } + } + element.css("background-color", origColor); + }); + + } + }; +} angular.module('umbraco.directives').directive("hexBgColor", hexBgColor); -/** -* @ngdoc directive -* @name umbraco.directives.directive:hotkey -**/ +/** +* @ngdoc directive +* @name umbraco.directives.directive:hotkey +**/ + +angular.module("umbraco.directives") + .directive('hotkey', function($window, keyboardService, $log) { + + return function(scope, el, attrs) { + + var options = {}; + var keyCombo = attrs.hotkey; + + if (!keyCombo) { + //support data binding + keyCombo = scope.$eval(attrs["hotkey"]); + } + + function activate() { + + if (keyCombo) { + + // disable shortcuts in input fields if keycombo is 1 character + if (keyCombo.length === 1) { + options = { + inputDisabled: true + }; + } + + keyboardService.bind(keyCombo, function() { + + var element = $(el); + var activeElementType = document.activeElement.tagName; + var clickableElements = ["A", "BUTTON"]; + + if (element.is("a,div,button,input[type='button'],input[type='submit'],input[type='checkbox']") && !element.is(':disabled')) { + + if (element.is(':visible') || attrs.hotkeyWhenHidden) { + + if (attrs.hotkeyWhen && attrs.hotkeyWhen === "false") { + return; + } + + // when keycombo is enter and a link or button has focus - click the link or button instead of using the hotkey + if (keyCombo === "enter" && clickableElements.indexOf(activeElementType) === 0) { + document.activeElement.click(); + } else { + element.click(); + } + + } + + } else { + element.focus(); + } + + }, options); + + el.on('$destroy', function() { + keyboardService.unbind(keyCombo); + }); + + } + + } + + activate(); + + }; + }); -angular.module("umbraco.directives") - .directive('hotkey', function($window, keyboardService, $log) { +/** +@ngdoc directive +@name umbraco.directives.directive:preventDefault + +@description +Use this directive to prevent default action of an element. Effectively implementing jQuery's preventdefault + +

Markup example

+ +
+    Don't go to Umbraco.com
+
+ +**/ +angular.module("umbraco.directives") + .directive('preventDefault', function() { + return function(scope, element, attrs) { + + var enabled = true; + //check if there's a value for the attribute, if there is and it's false then we conditionally don't + //prevent default. + if (attrs.preventDefault) { + attrs.$observe("preventDefault", function (newVal) { + enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true; + }); + } + + $(element).click(function (event) { + if (event.metaKey || event.ctrlKey) { + return; + } + else { + if (enabled === true) { + event.preventDefault(); + } + } + }); + }; + }); - return function(scope, el, attrs) { - - var options = {}; - var keyCombo = attrs.hotkey; - - if (!keyCombo) { - //support data binding - keyCombo = scope.$eval(attrs["hotkey"]); - } - - function activate() { - - if (keyCombo) { - - // disable shortcuts in input fields if keycombo is 1 character - if (keyCombo.length === 1) { - options = { - inputDisabled: true - }; - } - - keyboardService.bind(keyCombo, function() { - - var element = $(el); - var activeElementType = document.activeElement.tagName; - var clickableElements = ["A", "BUTTON"]; - - if (element.is("a,div,button,input[type='button'],input[type='submit'],input[type='checkbox']") && !element.is(':disabled')) { - - if (element.is(':visible') || attrs.hotkeyWhenHidden) { - - if (attrs.hotkeyWhen && attrs.hotkeyWhen === "false") { - return; - } - - // when keycombo is enter and a link or button has focus - click the link or button instead of using the hotkey - if (keyCombo === "enter" && clickableElements.indexOf(activeElementType) === 0) { - document.activeElement.click(); - } else { - element.click(); - } - - } - - } else { - element.focus(); - } - - }, options); - - el.on('$destroy', function() { - keyboardService.unbind(keyCombo); - }); - - } - - } - - activate(); - - }; - }); - -/** -@ngdoc directive -@name umbraco.directives.directive:preventDefault - -@description -Use this directive to prevent default action of an element. Effectively implementing jQuery's preventdefault - -

Markup example

- -
-    Don't go to Umbraco.com
-
- -**/ -angular.module("umbraco.directives") - .directive('preventDefault', function() { - return function(scope, element, attrs) { - - var enabled = true; - //check if there's a value for the attribute, if there is and it's false then we conditionally don't - //prevent default. - if (attrs.preventDefault) { - attrs.$observe("preventDefault", function (newVal) { - enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true; - }); - } - - $(element).click(function (event) { - if (event.metaKey || event.ctrlKey) { - return; - } - else { - if (enabled === true) { - event.preventDefault(); - } - } - }); - }; - }); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:preventEnterSubmit -* @description prevents a form from submitting when the enter key is pressed on an input field -**/ -angular.module("umbraco.directives") - .directive('preventEnterSubmit', function() { - return function(scope, element, attrs) { - - var enabled = true; - //check if there's a value for the attribute, if there is and it's false then we conditionally don't - //prevent default. - if (attrs.preventEnterSubmit) { - attrs.$observe("preventEnterSubmit", function (newVal) { - enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true; - }); - } - - $(element).keypress(function (event) { - if (event.which === 13) { - event.preventDefault(); - } - }); - }; - }); -/** - * @ngdoc directive - * @name umbraco.directives.directive:resizeToContent - * @element div - * @function - * - * @description - * Resize iframe's automatically to fit to the content they contain - * - * @example - - - - - - */ -angular.module("umbraco.directives") - .directive('resizeToContent', function ($window, $timeout) { - return function (scope, el, attrs) { - var iframe = el[0]; - var iframeWin = iframe.contentWindow || iframe.contentDocument.parentWindow; - if (iframeWin.document.body) { - - $timeout(function(){ - var height = iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight; - el.height(height); - }, 3000); - } - }; - }); - -angular.module("umbraco.directives") - .directive('selectOnFocus', function () { - return function (scope, el, attrs) { - $(el).bind("click", function () { - var editmode = $(el).data("editmode"); - //If editmode is true a click is handled like a normal click - if (!editmode) { - //Initial click, select entire text - this.select(); - //Set the edit mode so subsequent clicks work normally - $(el).data("editmode", true); - } - }). - bind("blur", function () { - //Reset on focus lost - $(el).data("editmode", false); - }); - }; - }); - -angular.module("umbraco.directives") - .directive('umbAutoFocus', function($timeout) { - - return function(scope, element, attr){ - var update = function() { - //if it uses its default naming - if(element.val() === "" || attr.focusOnFilled){ - element.focus(); - } - }; - - $timeout(function() { - update(); - }); - }; -}); - -angular.module("umbraco.directives") - .directive('umbAutoResize', function($timeout) { - return { - require: ["^?umbTabs", "ngModel"], - link: function(scope, element, attr, controllersArr) { - - var domEl = element[0]; - var domElType = domEl.type; - var umbTabsController = controllersArr[0]; - var ngModelController = controllersArr[1]; - - // IE elements - var isIEFlag = false; - var wrapper = angular.element('#umb-ie-resize-input-wrapper'); - var mirror = angular.element(''); - - function isIE() { - - var ua = window.navigator.userAgent; - var msie = ua.indexOf("MSIE "); - - if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./) || navigator.userAgent.match(/Edge\/\d+/)) { - return true; - } else { - return false; - } - - } - - function activate() { - - // check if browser is Internet Explorere - isIEFlag = isIE(); - - // scrollWidth on element does not work in IE on inputs - // we have to do some dirty dom element copying. - if (isIEFlag === true && domElType === "text") { - setupInternetExplorerElements(); - } - - } - - function setupInternetExplorerElements() { - - if (!wrapper.length) { - wrapper = angular.element('
'); - angular.element('body').append(wrapper); - } - - angular.forEach(['fontFamily', 'fontSize', 'fontWeight', 'fontStyle', - 'letterSpacing', 'textTransform', 'wordSpacing', 'textIndent', - 'boxSizing', 'borderRightWidth', 'borderLeftWidth', 'borderLeftStyle', 'borderRightStyle', - 'paddingLeft', 'paddingRight', 'marginLeft', 'marginRight' - ], function(value) { - mirror.css(value, element.css(value)); - }); - - wrapper.append(mirror); - - } - - function resizeInternetExplorerInput() { - - mirror.text(element.val() || attr.placeholder); - element.css('width', mirror.outerWidth() + 1); - - } - - function resizeInput() { - - if (domEl.scrollWidth !== domEl.clientWidth) { - if (ngModelController.$modelValue) { - element.width(domEl.scrollWidth); - } - } - - if(!ngModelController.$modelValue && attr.placeholder) { - attr.$set('size', attr.placeholder.length); - element.width('auto'); - } - - } - - function resizeTextarea() { - - if(domEl.scrollHeight !== domEl.clientHeight) { - - element.height(domEl.scrollHeight); - - } - - } - - var update = function(force) { - - - if (force === true) { - - if (domElType === "textarea") { - element.height(0); - } else if (domElType === "text") { - element.width(0); - } - - } - - - if (isIEFlag === true && domElType === "text") { - - resizeInternetExplorerInput(); - - } else { - - if (domElType === "textarea") { - - resizeTextarea(); - - } else if (domElType === "text") { - - resizeInput(); - - } - - } - - }; - - activate(); - - //listen for tab changes - if (umbTabsController != null) { - umbTabsController.onTabShown(function(args) { - update(); - }); - } - - // listen for ng-model changes - var unbindModelWatcher = scope.$watch(function() { - return ngModelController.$modelValue; - }, function(newValue) { - update(true); - }); - - scope.$on('$destroy', function() { - element.unbind('keyup keydown keypress change', update); - element.unbind('blur', update(true)); - unbindModelWatcher(); - - // clean up IE dom element - if (isIEFlag === true && domElType === "text") { - mirror.remove(); - } - - }); - } - }; - }); - -/* -example usage: - -jsonEditing is a string which we edit in a textarea. we try parsing to JSON with each change. when it is valid, propagate model changes via ngModelCtrl - -use isolate scope to prevent model propagation when invalid - will update manually. cannot replace with template, or will override ngModelCtrl, and not hide behind facade - -will override element type to textarea and add own attribute ngModel tied to jsonEditing - */ - -angular.module("umbraco.directives") - .directive('umbRawModel', function () { - return { - restrict: 'A', - require: 'ngModel', - template: '', - replace : true, - scope: { - model: '=umbRawModel', - validateOn:'=' - }, - link: function (scope, element, attrs, ngModelCtrl) { - - function setEditing (value) { - scope.jsonEditing = angular.copy( jsonToString(value)); - } - - function updateModel (value) { - scope.model = stringToJson(value); - } - - function setValid() { - ngModelCtrl.$setValidity('json', true); - } - - function setInvalid () { - ngModelCtrl.$setValidity('json', false); - } - - function stringToJson(text) { - try { - return angular.fromJson(text); - } catch (err) { - setInvalid(); - return text; - } - } - - function jsonToString(object) { - // better than JSON.stringify(), because it formats + filters $$hashKey etc. - // NOTE that this will remove all $-prefixed values - return angular.toJson(object, true); - } - - function isValidJson(model) { - var flag = true; - try { - angular.fromJson(model); - } catch (err) { - flag = false; - } - return flag; - } - - //init - setEditing(scope.model); - - var onInputChange = function(newval,oldval){ - if (newval !== oldval) { - if (isValidJson(newval)) { - setValid(); - updateModel(newval); - } else { - setInvalid(); - } - } - }; - - if(scope.validateOn){ - element.on(scope.validateOn, function(){ - scope.$apply(function(){ - onInputChange(scope.jsonEditing); - }); - }); - }else{ - //check for changes going out - scope.$watch('jsonEditing', onInputChange, true); - } - - //check for changes coming in - scope.$watch('model', function (newval, oldval) { - if (newval !== oldval) { - setEditing(newval); - } - }, true); - - } - }; - }); - -(function() { - 'use strict'; - - function SelectWhen($timeout) { - - function link(scope, el, attr, ctrl) { - - attr.$observe("umbSelectWhen", function(newValue) { - if (newValue === "true") { - $timeout(function() { - el.select(); - }); - } - }); - - } - - var directive = { - restrict: 'A', - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbSelectWhen', SelectWhen); - -})(); - -angular.module("umbraco.directives") - .directive('gridRte', function (tinyMceService, stylesheetResource, angularHelper, assetsService, $q, $timeout) { - return { - scope: { - uniqueId: '=', - value: '=', - onClick: '&', - onFocus: '&', - onBlur: '&', - configuration:"=", - onMediaPickerClick: "=", - onEmbedClick: "=", - onMacroPickerClick: "=", - onLinkPickerClick: "=" - }, - template: "", - replace: true, - link: function (scope, element, attrs) { - - var initTiny = function () { - - //we always fetch the default one, and then override parts with our own - tinyMceService.configuration().then(function (tinyMceConfig) { - - - - //config value from general tinymce.config file - var validElements = tinyMceConfig.validElements; - var fallbackStyles = [{title: "Page header", block: "h2"}, {title: "Section header", block: "h3"}, {title: "Paragraph header", block: "h4"}, {title: "Normal", block: "p"}, {title: "Quote", block: "blockquote"}, {title: "Code", block: "code"}]; - - //These are absolutely required in order for the macros to render inline - //we put these as extended elements because they get merged on top of the normal allowed elements by tiny mce - var extendedValidElements = "@[id|class|style],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style]"; - - var invalidElements = tinyMceConfig.inValidElements; - var plugins = _.map(tinyMceConfig.plugins, function (plugin) { - if (plugin.useOnFrontend) { - return plugin.name; - } - }).join(" ") + " autoresize"; - - //config value on the data type - var toolbar = ["code", "styleselect", "bold", "italic", "alignleft", "aligncenter", "alignright", "bullist", "numlist", "link", "umbmediapicker", "umbembeddialog"].join(" | "); - var stylesheets = []; - - var styleFormats = []; - var await = []; - - //queue file loading - if (typeof (tinymce) === "undefined") { - await.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope)); - } - - - if(scope.configuration && scope.configuration.toolbar){ - toolbar = scope.configuration.toolbar.join(' | '); - } - - - if(scope.configuration && scope.configuration.stylesheets){ - angular.forEach(scope.configuration.stylesheets, function(stylesheet, key){ - - stylesheets.push(Umbraco.Sys.ServerVariables.umbracoSettings.cssPath + "/" + stylesheet + ".css"); - await.push(stylesheetResource.getRulesByName(stylesheet).then(function (rules) { - angular.forEach(rules, function (rule) { - var r = {}; - r.title = rule.name; - if (rule.selector[0] === ".") { - r.inline = "span"; - r.classes = rule.selector.substring(1); - }else if (rule.selector[0] === "#") { - //Even though this will render in the style drop down, it will not actually be applied - // to the elements, don't think TinyMCE even supports this and it doesn't really make much sense - // since only one element can have one id. - r.inline = "span"; - r.attributes = { id: rule.selector.substring(1) }; - }else { - r.block = rule.selector; - } - styleFormats.push(r); - }); - })); - }); - }else{ - stylesheets.push("views/propertyeditors/grid/config/grid.default.rtestyles.css"); - styleFormats = fallbackStyles; - } - - //stores a reference to the editor - var tinyMceEditor = null; - $q.all(await).then(function () { - - var uniqueId = scope.uniqueId; - - //create a baseline Config to exten upon - var baseLineConfigObj = { - mode: "exact", - skin: "umbraco", - plugins: plugins, - valid_elements: validElements, - invalid_elements: invalidElements, - extended_valid_elements: extendedValidElements, - menubar: false, - statusbar: false, - relative_urls: false, - toolbar: toolbar, - content_css: stylesheets, - style_formats: styleFormats, - autoresize_bottom_margin: 0 - }; - - - if (tinyMceConfig.customConfig) { - - //if there is some custom config, we need to see if the string value of each item might actually be json and if so, we need to - // convert it to json instead of having it as a string since this is what tinymce requires - for (var i in tinyMceConfig.customConfig) { - var val = tinyMceConfig.customConfig[i]; - if (val) { - val = val.toString().trim(); - if (val.detectIsJson()) { - try { - tinyMceConfig.customConfig[i] = JSON.parse(val); - //now we need to check if this custom config key is defined in our baseline, if it is we don't want to - //overwrite the baseline config item if it is an array, we want to concat the items in the array, otherwise - //if it's an object it will overwrite the baseline - if (angular.isArray(baseLineConfigObj[i]) && angular.isArray(tinyMceConfig.customConfig[i])) { - //concat it and below this concat'd array will overwrite the baseline in angular.extend - tinyMceConfig.customConfig[i] = baseLineConfigObj[i].concat(tinyMceConfig.customConfig[i]); - } - } - catch (e) { - //cannot parse, we'll just leave it - } - } - } - } - - angular.extend(baseLineConfigObj, tinyMceConfig.customConfig); - } - - //set all the things that user configs should not be able to override - baseLineConfigObj.elements = uniqueId; - baseLineConfigObj.setup = function (editor) { - - //set the reference - tinyMceEditor = editor; - - - //enable browser based spell checking - editor.on('init', function (e) { - - editor.getBody().setAttribute('spellcheck', true); - - //force overflow to hidden to prevent no needed scroll - editor.getBody().style.overflow = "hidden"; - - $timeout(function(){ - if(scope.value === null){ - editor.focus(); - } - }, 400); - - }); - - //when we leave the editor (maybe) - editor.on('blur', function (e) { - editor.save(); - angularHelper.safeApply(scope, function () { - scope.value = editor.getContent(); - - var _toolbar = $(editor.editorContainer) - .find(".mce-toolbar"); - - if(scope.onBlur){ - scope.onBlur(); - } - - }); - }); - - // Focus on editor - editor.on('focus', function (e) { - angularHelper.safeApply(scope, function () { - - if(scope.onFocus){ - scope.onFocus(); - } - - }); - }); - - // Click on editor - editor.on('click', function (e) { - angularHelper.safeApply(scope, function () { - - if(scope.onClick){ - scope.onClick(); - } - - }); - }); - - //when buttons modify content - editor.on('ExecCommand', function (e) { - editor.save(); - angularHelper.safeApply(scope, function () { - scope.value = editor.getContent(); - }); - }); - - // Update model on keypress - editor.on('KeyUp', function (e) { - editor.save(); - angularHelper.safeApply(scope, function () { - scope.value = editor.getContent(); - }); - }); - - // Update model on change, i.e. copy/pasted text, plugins altering content - editor.on('SetContent', function (e) { - if (!e.initial) { - editor.save(); - angularHelper.safeApply(scope, function () { - scope.value = editor.getContent(); - }); - } - }); - - editor.on('ObjectResized', function (e) { - var qs = "?width=" + e.width + "&height=" + e.height; - var srcAttr = $(e.target).attr("src"); - var path = srcAttr.split("?")[0]; - $(e.target).attr("data-mce-src", path + qs); - }); - - //Create the insert link plugin - tinyMceService.createLinkPicker(editor, scope, function(currentTarget, anchorElement){ - if(scope.onLinkPickerClick) { - scope.onLinkPickerClick(editor, currentTarget, anchorElement); - } - }); - - //Create the insert media plugin - tinyMceService.createMediaPicker(editor, scope, function(currentTarget, userData){ - if(scope.onMediaPickerClick) { - scope.onMediaPickerClick(editor, currentTarget, userData); - } - }); - - //Create the embedded plugin - tinyMceService.createInsertEmbeddedMedia(editor, scope, function(){ - if(scope.onEmbedClick) { - scope.onEmbedClick(editor); - } - }); - - //Create the insert macro plugin - tinyMceService.createInsertMacro(editor, scope, function(dialogData){ - if(scope.onMacroPickerClick) { - scope.onMacroPickerClick(editor, dialogData); - } - }); - - }; - - /** Loads in the editor */ - function loadTinyMce() { - - //we need to add a timeout here, to force a redraw so TinyMCE can find - //the elements needed - $timeout(function () { - tinymce.DOM.events.domLoaded = true; - tinymce.init(baseLineConfigObj); - }, 150, false); - } - - loadTinyMce(); - - //here we declare a special method which will be called whenever the value has changed from the server - //this is instead of doing a watch on the model.value = faster - //scope.model.onValueChanged = function (newVal, oldVal) { - // //update the display val again if it has changed from the server; - // tinyMceEditor.setContent(newVal, { format: 'raw' }); - // //we need to manually fire this event since it is only ever fired based on loading from the DOM, this - // // is required for our plugins listening to this event to execute - // tinyMceEditor.fire('LoadContent', null); - //}; - - //listen for formSubmitting event (the result is callback used to remove the event subscription) - var unsubscribe = scope.$on("formSubmitting", function () { - //TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer - // we do parse it out on the server side but would be nice to do that on the client side before as well. - scope.value = tinyMceEditor.getContent(); - }); - - //when the element is disposed we need to unsubscribe! - // NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom - // element might still be there even after the modal has been hidden. - scope.$on('$destroy', function () { - unsubscribe(); - }); - - }); - - }); - - }; - - initTiny(); - - } - }; +/** +* @ngdoc directive +* @name umbraco.directives.directive:preventEnterSubmit +* @description prevents a form from submitting when the enter key is pressed on an input field +**/ +angular.module("umbraco.directives") + .directive('preventEnterSubmit', function() { + return function(scope, element, attrs) { + + var enabled = true; + //check if there's a value for the attribute, if there is and it's false then we conditionally don't + //prevent default. + if (attrs.preventEnterSubmit) { + attrs.$observe("preventEnterSubmit", function (newVal) { + enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true; + }); + } + + $(element).keypress(function (event) { + if (event.which === 13) { + event.preventDefault(); + } + }); + }; }); +/** + * @ngdoc directive + * @name umbraco.directives.directive:resizeToContent + * @element div + * @function + * + * @description + * Resize iframe's automatically to fit to the content they contain + * + * @example + + + + + + */ +angular.module("umbraco.directives") + .directive('resizeToContent', function ($window, $timeout) { + return function (scope, el, attrs) { + var iframe = el[0]; + var iframeWin = iframe.contentWindow || iframe.contentDocument.parentWindow; + if (iframeWin.document.body) { + + $timeout(function(){ + var height = iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight; + el.height(height); + }, 3000); + } + }; + }); + +angular.module("umbraco.directives") + .directive('selectOnFocus', function () { + return function (scope, el, attrs) { + $(el).bind("click", function () { + var editmode = $(el).data("editmode"); + //If editmode is true a click is handled like a normal click + if (!editmode) { + //Initial click, select entire text + this.select(); + //Set the edit mode so subsequent clicks work normally + $(el).data("editmode", true); + } + }). + bind("blur", function () { + //Reset on focus lost + $(el).data("editmode", false); + }); + }; + }); + +angular.module("umbraco.directives") + .directive('umbAutoFocus', function($timeout) { + + return function(scope, element, attr){ + var update = function() { + //if it uses its default naming + if(element.val() === "" || attr.focusOnFilled){ + element.focus(); + } + }; + + $timeout(function() { + update(); + }); + }; +}); + +angular.module("umbraco.directives") + .directive('umbAutoResize', function($timeout) { + return { + require: ["^?umbTabs", "ngModel"], + link: function(scope, element, attr, controllersArr) { + + var domEl = element[0]; + var domElType = domEl.type; + var umbTabsController = controllersArr[0]; + var ngModelController = controllersArr[1]; + + // IE elements + var isIEFlag = false; + var wrapper = angular.element('#umb-ie-resize-input-wrapper'); + var mirror = angular.element(''); + + function isIE() { + + var ua = window.navigator.userAgent; + var msie = ua.indexOf("MSIE "); + + if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./) || navigator.userAgent.match(/Edge\/\d+/)) { + return true; + } else { + return false; + } + + } + + function activate() { + + // check if browser is Internet Explorere + isIEFlag = isIE(); + + // scrollWidth on element does not work in IE on inputs + // we have to do some dirty dom element copying. + if (isIEFlag === true && domElType === "text") { + setupInternetExplorerElements(); + } + + } + + function setupInternetExplorerElements() { + + if (!wrapper.length) { + wrapper = angular.element('
'); + angular.element('body').append(wrapper); + } + + angular.forEach(['fontFamily', 'fontSize', 'fontWeight', 'fontStyle', + 'letterSpacing', 'textTransform', 'wordSpacing', 'textIndent', + 'boxSizing', 'borderRightWidth', 'borderLeftWidth', 'borderLeftStyle', 'borderRightStyle', + 'paddingLeft', 'paddingRight', 'marginLeft', 'marginRight' + ], function(value) { + mirror.css(value, element.css(value)); + }); + + wrapper.append(mirror); + + } + + function resizeInternetExplorerInput() { + + mirror.text(element.val() || attr.placeholder); + element.css('width', mirror.outerWidth() + 1); + + } + + function resizeInput() { + + if (domEl.scrollWidth !== domEl.clientWidth) { + if (ngModelController.$modelValue) { + element.width(domEl.scrollWidth); + } + } + + if(!ngModelController.$modelValue && attr.placeholder) { + attr.$set('size', attr.placeholder.length); + element.width('auto'); + } + + } + + function resizeTextarea() { + + if(domEl.scrollHeight !== domEl.clientHeight) { + + element.height(domEl.scrollHeight); + + } + + } + + var update = function(force) { + + + if (force === true) { + + if (domElType === "textarea") { + element.height(0); + } else if (domElType === "text") { + element.width(0); + } + + } + + + if (isIEFlag === true && domElType === "text") { + + resizeInternetExplorerInput(); + + } else { + + if (domElType === "textarea") { + + resizeTextarea(); + + } else if (domElType === "text") { + + resizeInput(); + + } + + } + + }; + + activate(); + + //listen for tab changes + if (umbTabsController != null) { + umbTabsController.onTabShown(function(args) { + update(); + }); + } + + // listen for ng-model changes + var unbindModelWatcher = scope.$watch(function() { + return ngModelController.$modelValue; + }, function(newValue) { + update(true); + }); + + scope.$on('$destroy', function() { + element.unbind('keyup keydown keypress change', update); + element.unbind('blur', update(true)); + unbindModelWatcher(); + + // clean up IE dom element + if (isIEFlag === true && domElType === "text") { + mirror.remove(); + } + + }); + } + }; + }); + +/* +example usage: + +jsonEditing is a string which we edit in a textarea. we try parsing to JSON with each change. when it is valid, propagate model changes via ngModelCtrl + +use isolate scope to prevent model propagation when invalid - will update manually. cannot replace with template, or will override ngModelCtrl, and not hide behind facade + +will override element type to textarea and add own attribute ngModel tied to jsonEditing + */ + +angular.module("umbraco.directives") + .directive('umbRawModel', function () { + return { + restrict: 'A', + require: 'ngModel', + template: '', + replace : true, + scope: { + model: '=umbRawModel', + validateOn:'=' + }, + link: function (scope, element, attrs, ngModelCtrl) { + + function setEditing (value) { + scope.jsonEditing = angular.copy( jsonToString(value)); + } + + function updateModel (value) { + scope.model = stringToJson(value); + } + + function setValid() { + ngModelCtrl.$setValidity('json', true); + } + + function setInvalid () { + ngModelCtrl.$setValidity('json', false); + } + + function stringToJson(text) { + try { + return angular.fromJson(text); + } catch (err) { + setInvalid(); + return text; + } + } + + function jsonToString(object) { + // better than JSON.stringify(), because it formats + filters $$hashKey etc. + // NOTE that this will remove all $-prefixed values + return angular.toJson(object, true); + } + + function isValidJson(model) { + var flag = true; + try { + angular.fromJson(model); + } catch (err) { + flag = false; + } + return flag; + } + + //init + setEditing(scope.model); + + var onInputChange = function(newval,oldval){ + if (newval !== oldval) { + if (isValidJson(newval)) { + setValid(); + updateModel(newval); + } else { + setInvalid(); + } + } + }; + + if(scope.validateOn){ + element.on(scope.validateOn, function(){ + scope.$apply(function(){ + onInputChange(scope.jsonEditing); + }); + }); + }else{ + //check for changes going out + scope.$watch('jsonEditing', onInputChange, true); + } + + //check for changes coming in + scope.$watch('model', function (newval, oldval) { + if (newval !== oldval) { + setEditing(newval); + } + }, true); + + } + }; + }); + +(function() { + 'use strict'; + + function SelectWhen($timeout) { + + function link(scope, el, attr, ctrl) { + + attr.$observe("umbSelectWhen", function(newValue) { + if (newValue === "true") { + $timeout(function() { + el.select(); + }); + } + }); + + } + + var directive = { + restrict: 'A', + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbSelectWhen', SelectWhen); + +})(); + +angular.module("umbraco.directives") + .directive('gridRte', function (tinyMceService, stylesheetResource, angularHelper, assetsService, $q, $timeout) { + return { + scope: { + uniqueId: '=', + value: '=', + onClick: '&', + onFocus: '&', + onBlur: '&', + configuration:"=", + onMediaPickerClick: "=", + onEmbedClick: "=", + onMacroPickerClick: "=", + onLinkPickerClick: "=" + }, + template: "", + replace: true, + link: function (scope, element, attrs) { + + var initTiny = function () { + + //we always fetch the default one, and then override parts with our own + tinyMceService.configuration().then(function (tinyMceConfig) { + + + + //config value from general tinymce.config file + var validElements = tinyMceConfig.validElements; + var fallbackStyles = [{title: "Page header", block: "h2"}, {title: "Section header", block: "h3"}, {title: "Paragraph header", block: "h4"}, {title: "Normal", block: "p"}, {title: "Quote", block: "blockquote"}, {title: "Code", block: "code"}]; + + //These are absolutely required in order for the macros to render inline + //we put these as extended elements because they get merged on top of the normal allowed elements by tiny mce + var extendedValidElements = "@[id|class|style],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style]"; + + var invalidElements = tinyMceConfig.inValidElements; + var plugins = _.map(tinyMceConfig.plugins, function (plugin) { + if (plugin.useOnFrontend) { + return plugin.name; + } + }).join(" ") + " autoresize"; + + //config value on the data type + var toolbar = ["code", "styleselect", "bold", "italic", "alignleft", "aligncenter", "alignright", "bullist", "numlist", "link", "umbmediapicker", "umbembeddialog"].join(" | "); + var stylesheets = []; + + var styleFormats = []; + var await = []; + + //queue file loading + if (typeof (tinymce) === "undefined") { + await.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope)); + } + + + if(scope.configuration && scope.configuration.toolbar){ + toolbar = scope.configuration.toolbar.join(' | '); + } + + + if(scope.configuration && scope.configuration.stylesheets){ + angular.forEach(scope.configuration.stylesheets, function(stylesheet, key){ + + stylesheets.push(Umbraco.Sys.ServerVariables.umbracoSettings.cssPath + "/" + stylesheet + ".css"); + await.push(stylesheetResource.getRulesByName(stylesheet).then(function (rules) { + angular.forEach(rules, function (rule) { + var r = {}; + r.title = rule.name; + if (rule.selector[0] === ".") { + r.inline = "span"; + r.classes = rule.selector.substring(1); + }else if (rule.selector[0] === "#") { + //Even though this will render in the style drop down, it will not actually be applied + // to the elements, don't think TinyMCE even supports this and it doesn't really make much sense + // since only one element can have one id. + r.inline = "span"; + r.attributes = { id: rule.selector.substring(1) }; + }else { + r.block = rule.selector; + } + styleFormats.push(r); + }); + })); + }); + }else{ + stylesheets.push("views/propertyeditors/grid/config/grid.default.rtestyles.css"); + styleFormats = fallbackStyles; + } + + //stores a reference to the editor + var tinyMceEditor = null; + $q.all(await).then(function () { + + var uniqueId = scope.uniqueId; + + //create a baseline Config to exten upon + var baseLineConfigObj = { + mode: "exact", + skin: "umbraco", + plugins: plugins, + valid_elements: validElements, + invalid_elements: invalidElements, + extended_valid_elements: extendedValidElements, + menubar: false, + statusbar: false, + relative_urls: false, + toolbar: toolbar, + content_css: stylesheets, + style_formats: styleFormats, + autoresize_bottom_margin: 0 + }; + + + if (tinyMceConfig.customConfig) { + + //if there is some custom config, we need to see if the string value of each item might actually be json and if so, we need to + // convert it to json instead of having it as a string since this is what tinymce requires + for (var i in tinyMceConfig.customConfig) { + var val = tinyMceConfig.customConfig[i]; + if (val) { + val = val.toString().trim(); + if (val.detectIsJson()) { + try { + tinyMceConfig.customConfig[i] = JSON.parse(val); + //now we need to check if this custom config key is defined in our baseline, if it is we don't want to + //overwrite the baseline config item if it is an array, we want to concat the items in the array, otherwise + //if it's an object it will overwrite the baseline + if (angular.isArray(baseLineConfigObj[i]) && angular.isArray(tinyMceConfig.customConfig[i])) { + //concat it and below this concat'd array will overwrite the baseline in angular.extend + tinyMceConfig.customConfig[i] = baseLineConfigObj[i].concat(tinyMceConfig.customConfig[i]); + } + } + catch (e) { + //cannot parse, we'll just leave it + } + } + } + } + + angular.extend(baseLineConfigObj, tinyMceConfig.customConfig); + } + + //set all the things that user configs should not be able to override + baseLineConfigObj.elements = uniqueId; + baseLineConfigObj.setup = function (editor) { + + //set the reference + tinyMceEditor = editor; + + + //enable browser based spell checking + editor.on('init', function (e) { + + editor.getBody().setAttribute('spellcheck', true); + + //force overflow to hidden to prevent no needed scroll + editor.getBody().style.overflow = "hidden"; + + $timeout(function(){ + if(scope.value === null){ + editor.focus(); + } + }, 400); + + }); + + //when we leave the editor (maybe) + editor.on('blur', function (e) { + editor.save(); + angularHelper.safeApply(scope, function () { + scope.value = editor.getContent(); + + var _toolbar = $(editor.editorContainer) + .find(".mce-toolbar"); + + if(scope.onBlur){ + scope.onBlur(); + } + + }); + }); + + // Focus on editor + editor.on('focus', function (e) { + angularHelper.safeApply(scope, function () { + + if(scope.onFocus){ + scope.onFocus(); + } + + }); + }); + + // Click on editor + editor.on('click', function (e) { + angularHelper.safeApply(scope, function () { + + if(scope.onClick){ + scope.onClick(); + } + + }); + }); + + //when buttons modify content + editor.on('ExecCommand', function (e) { + editor.save(); + angularHelper.safeApply(scope, function () { + scope.value = editor.getContent(); + }); + }); + + // Update model on keypress + editor.on('KeyUp', function (e) { + editor.save(); + angularHelper.safeApply(scope, function () { + scope.value = editor.getContent(); + }); + }); + + // Update model on change, i.e. copy/pasted text, plugins altering content + editor.on('SetContent', function (e) { + if (!e.initial) { + editor.save(); + angularHelper.safeApply(scope, function () { + scope.value = editor.getContent(); + }); + } + }); + + editor.on('ObjectResized', function (e) { + var qs = "?width=" + e.width + "&height=" + e.height; + var srcAttr = $(e.target).attr("src"); + var path = srcAttr.split("?")[0]; + $(e.target).attr("data-mce-src", path + qs); + }); + + //Create the insert link plugin + tinyMceService.createLinkPicker(editor, scope, function(currentTarget, anchorElement){ + if(scope.onLinkPickerClick) { + scope.onLinkPickerClick(editor, currentTarget, anchorElement); + } + }); + + //Create the insert media plugin + tinyMceService.createMediaPicker(editor, scope, function(currentTarget, userData){ + if(scope.onMediaPickerClick) { + scope.onMediaPickerClick(editor, currentTarget, userData); + } + }); + + //Create the embedded plugin + tinyMceService.createInsertEmbeddedMedia(editor, scope, function(){ + if(scope.onEmbedClick) { + scope.onEmbedClick(editor); + } + }); + + //Create the insert macro plugin + tinyMceService.createInsertMacro(editor, scope, function(dialogData){ + if(scope.onMacroPickerClick) { + scope.onMacroPickerClick(editor, dialogData); + } + }); + + }; + + /** Loads in the editor */ + function loadTinyMce() { + + //we need to add a timeout here, to force a redraw so TinyMCE can find + //the elements needed + $timeout(function () { + tinymce.DOM.events.domLoaded = true; + tinymce.init(baseLineConfigObj); + }, 150, false); + } + + loadTinyMce(); + + //here we declare a special method which will be called whenever the value has changed from the server + //this is instead of doing a watch on the model.value = faster + //scope.model.onValueChanged = function (newVal, oldVal) { + // //update the display val again if it has changed from the server; + // tinyMceEditor.setContent(newVal, { format: 'raw' }); + // //we need to manually fire this event since it is only ever fired based on loading from the DOM, this + // // is required for our plugins listening to this event to execute + // tinyMceEditor.fire('LoadContent', null); + //}; + + //listen for formSubmitting event (the result is callback used to remove the event subscription) + var unsubscribe = scope.$on("formSubmitting", function () { + //TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer + // we do parse it out on the server side but would be nice to do that on the client side before as well. + scope.value = tinyMceEditor.getContent(); + }); + + //when the element is disposed we need to unsubscribe! + // NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom + // element might still be there even after the modal has been hidden. + scope.$on('$destroy', function () { + unsubscribe(); + }); + + }); + + }); + + }; + + initTiny(); + + } + }; + }); /** * @ngdoc directive @@ -3824,20 +3824,20 @@ angular.module("umbraco.directives.html") }; }); -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbPane -* @restrict E -**/ -angular.module("umbraco.directives.html") - .directive('umbPane', function () { - return { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/html/umb-pane.html' - }; - }); +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbPane +* @restrict E +**/ +angular.module("umbraco.directives.html") + .directive('umbPane', function () { + return { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/html/umb-pane.html' + }; + }); /** * @ngdoc directive @@ -3854,570 +3854,570 @@ angular.module("umbraco.directives.html") }; }); -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbImageCrop -* @restrict E -* @function -**/ -angular.module("umbraco.directives") - .directive('umbImageCrop', - function ($timeout, localizationService, cropperHelper, $log) { - return { - restrict: 'E', - replace: true, - templateUrl: 'views/components/imaging/umb-image-crop.html', - scope: { - src: '=', - width: '@', - height: '@', - crop: "=", - center: "=", - maxSize: '@' - }, - - link: function(scope, element, attrs) { - scope.width = 400; - scope.height = 320; - - scope.dimensions = { - image: {}, - cropper:{}, - viewport:{}, - margin: 20, - scale: { - min: 0.3, - max: 3, - current: 1 - } - }; - - - //live rendering of viewport and image styles - scope.style = function () { - return { - 'height': (parseInt(scope.dimensions.viewport.height, 10)) + 'px', - 'width': (parseInt(scope.dimensions.viewport.width, 10)) + 'px' - }; - }; - - - //elements - var $viewport = element.find(".viewport"); - var $image = element.find("img"); - var $overlay = element.find(".overlay"); - var $container = element.find(".crop-container"); - - //default constraints for drag n drop - var constraints = {left: {max: scope.dimensions.margin, min: scope.dimensions.margin}, top: {max: scope.dimensions.margin, min: scope.dimensions.margin}, }; - scope.constraints = constraints; - - - //set constaints for cropping drag and drop - var setConstraints = function(){ - constraints.left.min = scope.dimensions.margin + scope.dimensions.cropper.width - scope.dimensions.image.width; - constraints.top.min = scope.dimensions.margin + scope.dimensions.cropper.height - scope.dimensions.image.height; - }; - - - var setDimensions = function(originalImage){ - originalImage.width("auto"); - originalImage.height("auto"); - - var image = {}; - image.originalWidth = originalImage.width(); - image.originalHeight = originalImage.height(); - - image.width = image.originalWidth; - image.height = image.originalHeight; - image.left = originalImage[0].offsetLeft; - image.top = originalImage[0].offsetTop; - - scope.dimensions.image = image; - - //unscaled editor size - //var viewPortW = $viewport.width(); - //var viewPortH = $viewport.height(); - var _viewPortW = parseInt(scope.width, 10); - var _viewPortH = parseInt(scope.height, 10); - - //if we set a constraint we will scale it down if needed - if(scope.maxSize){ - var ratioCalculation = cropperHelper.scaleToMaxSize( - _viewPortW, - _viewPortH, - scope.maxSize); - - //so if we have a max size, override the thumb sizes - _viewPortW = ratioCalculation.width; - _viewPortH = ratioCalculation.height; - } - - scope.dimensions.viewport.width = _viewPortW + 2 * scope.dimensions.margin; - scope.dimensions.viewport.height = _viewPortH + 2 * scope.dimensions.margin; - scope.dimensions.cropper.width = _viewPortW; // scope.dimensions.viewport.width - 2 * scope.dimensions.margin; - scope.dimensions.cropper.height = _viewPortH; // scope.dimensions.viewport.height - 2 * scope.dimensions.margin; - }; - - - //when loading an image without any crop info, we center and fit it - var resizeImageToEditor = function(){ - //returns size fitting the cropper - var size = cropperHelper.calculateAspectRatioFit( - scope.dimensions.image.width, - scope.dimensions.image.height, - scope.dimensions.cropper.width, - scope.dimensions.cropper.height, - true); - - //sets the image size and updates the scope - scope.dimensions.image.width = size.width; - scope.dimensions.image.height = size.height; - - //calculate the best suited ratios - scope.dimensions.scale.min = size.ratio; - scope.dimensions.scale.max = 2; - scope.dimensions.scale.current = size.ratio; - - //center the image - var position = cropperHelper.centerInsideViewPort(scope.dimensions.image, scope.dimensions.cropper); - scope.dimensions.top = position.top; - scope.dimensions.left = position.left; - - setConstraints(); - }; - - //resize to a given ratio - var resizeImageToScale = function(ratio){ - //do stuff - var size = cropperHelper.calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio); - scope.dimensions.image.width = size.width; - scope.dimensions.image.height = size.height; - - setConstraints(); - validatePosition(scope.dimensions.image.left, scope.dimensions.image.top); - }; - - //resize the image to a predefined crop coordinate - var resizeImageToCrop = function(){ - scope.dimensions.image = cropperHelper.convertToStyle( - scope.crop, - {width: scope.dimensions.image.originalWidth, height: scope.dimensions.image.originalHeight}, - scope.dimensions.cropper, - scope.dimensions.margin); - - var ratioCalculation = cropperHelper.calculateAspectRatioFit( - scope.dimensions.image.originalWidth, - scope.dimensions.image.originalHeight, - scope.dimensions.cropper.width, - scope.dimensions.cropper.height, - true); - - scope.dimensions.scale.current = scope.dimensions.image.ratio; - - //min max based on original width/height - scope.dimensions.scale.min = ratioCalculation.ratio; - scope.dimensions.scale.max = 2; - }; - - - - var validatePosition = function(left, top){ - if(left > constraints.left.max) - { - left = constraints.left.max; - } - - if(left <= constraints.left.min){ - left = constraints.left.min; - } - - if(top > constraints.top.max) - { - top = constraints.top.max; - } - if(top <= constraints.top.min){ - top = constraints.top.min; - } - - if(scope.dimensions.image.left !== left){ - scope.dimensions.image.left = left; - } - - if(scope.dimensions.image.top !== top){ - scope.dimensions.image.top = top; - } - }; - - - //sets scope.crop to the recalculated % based crop - var calculateCropBox = function(){ - scope.crop = cropperHelper.pixelsToCoordinates(scope.dimensions.image, scope.dimensions.cropper.width, scope.dimensions.cropper.height, scope.dimensions.margin); - }; - - - //Drag and drop positioning, using jquery ui draggable - var onStartDragPosition, top, left; - $overlay.draggable({ - drag: function(event, ui) { - scope.$apply(function(){ - validatePosition(ui.position.left, ui.position.top); - }); - }, - stop: function(event, ui){ - scope.$apply(function(){ - //make sure that every validates one more time... - validatePosition(ui.position.left, ui.position.top); - - calculateCropBox(); - scope.dimensions.image.rnd = Math.random(); - }); - } - }); - - - - var init = function(image){ - scope.loaded = false; - - //set dimensions on image, viewport, cropper etc - setDimensions(image); - - //if we have a crop already position the image - if(scope.crop){ - resizeImageToCrop(); - }else{ - resizeImageToEditor(); - } - - //sets constaints for the cropper - setConstraints(); - scope.loaded = true; - }; - - - /// WATCHERS //// - scope.$watchCollection('[width, height]', function(newValues, oldValues){ - //we have to reinit the whole thing if - //one of the external params changes - if(newValues !== oldValues){ - setDimensions($image); - setConstraints(); - } - }); - - var throttledResizing = _.throttle(function(){ - resizeImageToScale(scope.dimensions.scale.current); - calculateCropBox(); - }, 100); - - - //happens when we change the scale - scope.$watch("dimensions.scale.current", function(){ - if(scope.loaded){ - throttledResizing(); - } - }); - - //ie hack - if(window.navigator.userAgent.indexOf("MSIE ")){ - var ranger = element.find("input"); - ranger.bind("change",function(){ - scope.$apply(function(){ - scope.dimensions.scale.current = ranger.val(); - }); - }); - } - - //// INIT ///// - $image.load(function(){ - $timeout(function(){ - init($image); - }); - }); - } - }; - }); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbImageGravity -* @restrict E -* @function -* @description -**/ -angular.module("umbraco.directives") - .directive('umbImageGravity', function ($timeout, localizationService, $log) { - return { - restrict: 'E', - replace: true, - templateUrl: 'views/components/imaging/umb-image-gravity.html', - scope: { - src: '=', - center: "=", - onImageLoaded: "=" - }, - link: function(scope, element, attrs) { - - //Internal values for keeping track of the dot and the size of the editor - scope.dimensions = { - width: 0, - height: 0, - left: 0, - top: 0 - }; - - scope.loaded = false; - - //elements - var $viewport = element.find(".viewport"); - var $image = element.find("img"); - var $overlay = element.find(".overlay"); - - scope.style = function () { - if(scope.dimensions.width <= 0){ - setDimensions(); - } - - return { - 'top': scope.dimensions.top + 'px', - 'left': scope.dimensions.left + 'px' - }; - }; - - scope.setFocalPoint = function(event) { - - scope.$emit("imageFocalPointStart"); - - var offsetX = event.offsetX - 10; - var offsetY = event.offsetY - 10; - - calculateGravity(offsetX, offsetY); - - lazyEndEvent(); - - }; - - var setDimensions = function(){ - scope.dimensions.width = $image.width(); - scope.dimensions.height = $image.height(); - - if(scope.center){ - scope.dimensions.left = scope.center.left * scope.dimensions.width -10; - scope.dimensions.top = scope.center.top * scope.dimensions.height -10; - }else{ - scope.center = { left: 0.5, top: 0.5 }; - } - }; - - var calculateGravity = function(offsetX, offsetY){ - scope.dimensions.left = offsetX; - scope.dimensions.top = offsetY; - - scope.center.left = (scope.dimensions.left+10) / scope.dimensions.width; - scope.center.top = (scope.dimensions.top+10) / scope.dimensions.height; - }; - - var lazyEndEvent = _.debounce(function(){ - scope.$apply(function(){ - scope.$emit("imageFocalPointStop"); - }); - }, 2000); - - - //Drag and drop positioning, using jquery ui draggable - //TODO ensure that the point doesnt go outside the box - $overlay.draggable({ - containment: "parent", - start: function(){ - scope.$apply(function(){ - scope.$emit("imageFocalPointStart"); - }); - }, - stop: function() { - scope.$apply(function(){ - var offsetX = $overlay[0].offsetLeft; - var offsetY = $overlay[0].offsetTop; - calculateGravity(offsetX, offsetY); - }); - - lazyEndEvent(); - } - }); - - //// INIT ///// - $image.load(function(){ - $timeout(function(){ - setDimensions(); - scope.loaded = true; - scope.onImageLoaded(); - }); - }); - - $(window).on('resize.umbImageGravity', function(){ - scope.$apply(function(){ - $timeout(function(){ - setDimensions(); - }); - var offsetX = $overlay[0].offsetLeft; - var offsetY = $overlay[0].offsetTop; - calculateGravity(offsetX, offsetY); - }); - }); - - scope.$on('$destroy', function() { - $(window).off('.umbImageGravity'); - }); - - } - }; - }); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbImageThumbnail -* @restrict E -* @function -* @description -**/ -angular.module("umbraco.directives") - .directive('umbImageThumbnail', - function ($timeout, localizationService, cropperHelper, $log) { - return { - restrict: 'E', - replace: true, - templateUrl: 'views/components/imaging/umb-image-thumbnail.html', - - scope: { - src: '=', - width: '@', - height: '@', - center: "=", - crop: "=", - maxSize: '@' - }, - - link: function(scope, element, attrs) { - //// INIT ///// - var $image = element.find("img"); - scope.loaded = false; - - $image.load(function(){ - $timeout(function(){ - $image.width("auto"); - $image.height("auto"); - - scope.image = {}; - scope.image.width = $image[0].width; - scope.image.height = $image[0].height; - - //we force a lower thumbnail size to fit the max size - //we do not compare to the image dimensions, but the thumbs - if(scope.maxSize){ - var ratioCalculation = cropperHelper.calculateAspectRatioFit( - scope.width, - scope.height, - scope.maxSize, - scope.maxSize, - false); - - //so if we have a max size, override the thumb sizes - scope.width = ratioCalculation.width; - scope.height = ratioCalculation.height; - } - - setPreviewStyle(); - scope.loaded = true; - }); - }); - - /// WATCHERS //// - scope.$watchCollection('[crop, center]', function(newValues, oldValues){ - //we have to reinit the whole thing if - //one of the external params changes - setPreviewStyle(); - }); - - scope.$watch("center", function(){ - setPreviewStyle(); - }, true); - - function setPreviewStyle(){ - if(scope.crop && scope.image){ - scope.preview = cropperHelper.convertToStyle( - scope.crop, - scope.image, - {width: scope.width, height: scope.height}, - 0); - }else if(scope.image){ - - //returns size fitting the cropper - var p = cropperHelper.calculateAspectRatioFit( - scope.image.width, - scope.image.height, - scope.width, - scope.height, - true); - - - if(scope.center){ - var xy = cropperHelper.alignToCoordinates(p, scope.center, {width: scope.width, height: scope.height}); - p.top = xy.top; - p.left = xy.left; - }else{ - - } - - p.position = "absolute"; - scope.preview = p; - } - } - } - }; - }); - -angular.module("umbraco.directives") - - .directive('localize', function ($log, localizationService) { - return { - restrict: 'E', - scope:{ - key: '@' - }, - replace: true, - - link: function (scope, element, attrs) { - var key = scope.key; - localizationService.localize(key).then(function(value){ - element.html(value); - }); - } - }; - }) - - .directive('localize', function ($log, localizationService) { - return { - restrict: 'A', - link: function (scope, element, attrs) { - var keys = attrs.localize.split(','); - - angular.forEach(keys, function(value, key){ - var attr = element.attr(value); - - if(attr){ - if(attr[0] === '@'){ - - var t = localizationService.tokenize(attr.substring(1), scope); - localizationService.localize(t.key, t.tokens).then(function(val){ - element.attr(value, val); - }); - - } - } - }); - } - }; - +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbImageCrop +* @restrict E +* @function +**/ +angular.module("umbraco.directives") + .directive('umbImageCrop', + function ($timeout, localizationService, cropperHelper, $log) { + return { + restrict: 'E', + replace: true, + templateUrl: 'views/components/imaging/umb-image-crop.html', + scope: { + src: '=', + width: '@', + height: '@', + crop: "=", + center: "=", + maxSize: '@' + }, + + link: function(scope, element, attrs) { + scope.width = 400; + scope.height = 320; + + scope.dimensions = { + image: {}, + cropper:{}, + viewport:{}, + margin: 20, + scale: { + min: 0.3, + max: 3, + current: 1 + } + }; + + + //live rendering of viewport and image styles + scope.style = function () { + return { + 'height': (parseInt(scope.dimensions.viewport.height, 10)) + 'px', + 'width': (parseInt(scope.dimensions.viewport.width, 10)) + 'px' + }; + }; + + + //elements + var $viewport = element.find(".viewport"); + var $image = element.find("img"); + var $overlay = element.find(".overlay"); + var $container = element.find(".crop-container"); + + //default constraints for drag n drop + var constraints = {left: {max: scope.dimensions.margin, min: scope.dimensions.margin}, top: {max: scope.dimensions.margin, min: scope.dimensions.margin}, }; + scope.constraints = constraints; + + + //set constaints for cropping drag and drop + var setConstraints = function(){ + constraints.left.min = scope.dimensions.margin + scope.dimensions.cropper.width - scope.dimensions.image.width; + constraints.top.min = scope.dimensions.margin + scope.dimensions.cropper.height - scope.dimensions.image.height; + }; + + + var setDimensions = function(originalImage){ + originalImage.width("auto"); + originalImage.height("auto"); + + var image = {}; + image.originalWidth = originalImage.width(); + image.originalHeight = originalImage.height(); + + image.width = image.originalWidth; + image.height = image.originalHeight; + image.left = originalImage[0].offsetLeft; + image.top = originalImage[0].offsetTop; + + scope.dimensions.image = image; + + //unscaled editor size + //var viewPortW = $viewport.width(); + //var viewPortH = $viewport.height(); + var _viewPortW = parseInt(scope.width, 10); + var _viewPortH = parseInt(scope.height, 10); + + //if we set a constraint we will scale it down if needed + if(scope.maxSize){ + var ratioCalculation = cropperHelper.scaleToMaxSize( + _viewPortW, + _viewPortH, + scope.maxSize); + + //so if we have a max size, override the thumb sizes + _viewPortW = ratioCalculation.width; + _viewPortH = ratioCalculation.height; + } + + scope.dimensions.viewport.width = _viewPortW + 2 * scope.dimensions.margin; + scope.dimensions.viewport.height = _viewPortH + 2 * scope.dimensions.margin; + scope.dimensions.cropper.width = _viewPortW; // scope.dimensions.viewport.width - 2 * scope.dimensions.margin; + scope.dimensions.cropper.height = _viewPortH; // scope.dimensions.viewport.height - 2 * scope.dimensions.margin; + }; + + + //when loading an image without any crop info, we center and fit it + var resizeImageToEditor = function(){ + //returns size fitting the cropper + var size = cropperHelper.calculateAspectRatioFit( + scope.dimensions.image.width, + scope.dimensions.image.height, + scope.dimensions.cropper.width, + scope.dimensions.cropper.height, + true); + + //sets the image size and updates the scope + scope.dimensions.image.width = size.width; + scope.dimensions.image.height = size.height; + + //calculate the best suited ratios + scope.dimensions.scale.min = size.ratio; + scope.dimensions.scale.max = 2; + scope.dimensions.scale.current = size.ratio; + + //center the image + var position = cropperHelper.centerInsideViewPort(scope.dimensions.image, scope.dimensions.cropper); + scope.dimensions.top = position.top; + scope.dimensions.left = position.left; + + setConstraints(); + }; + + //resize to a given ratio + var resizeImageToScale = function(ratio){ + //do stuff + var size = cropperHelper.calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio); + scope.dimensions.image.width = size.width; + scope.dimensions.image.height = size.height; + + setConstraints(); + validatePosition(scope.dimensions.image.left, scope.dimensions.image.top); + }; + + //resize the image to a predefined crop coordinate + var resizeImageToCrop = function(){ + scope.dimensions.image = cropperHelper.convertToStyle( + scope.crop, + {width: scope.dimensions.image.originalWidth, height: scope.dimensions.image.originalHeight}, + scope.dimensions.cropper, + scope.dimensions.margin); + + var ratioCalculation = cropperHelper.calculateAspectRatioFit( + scope.dimensions.image.originalWidth, + scope.dimensions.image.originalHeight, + scope.dimensions.cropper.width, + scope.dimensions.cropper.height, + true); + + scope.dimensions.scale.current = scope.dimensions.image.ratio; + + //min max based on original width/height + scope.dimensions.scale.min = ratioCalculation.ratio; + scope.dimensions.scale.max = 2; + }; + + + + var validatePosition = function(left, top){ + if(left > constraints.left.max) + { + left = constraints.left.max; + } + + if(left <= constraints.left.min){ + left = constraints.left.min; + } + + if(top > constraints.top.max) + { + top = constraints.top.max; + } + if(top <= constraints.top.min){ + top = constraints.top.min; + } + + if(scope.dimensions.image.left !== left){ + scope.dimensions.image.left = left; + } + + if(scope.dimensions.image.top !== top){ + scope.dimensions.image.top = top; + } + }; + + + //sets scope.crop to the recalculated % based crop + var calculateCropBox = function(){ + scope.crop = cropperHelper.pixelsToCoordinates(scope.dimensions.image, scope.dimensions.cropper.width, scope.dimensions.cropper.height, scope.dimensions.margin); + }; + + + //Drag and drop positioning, using jquery ui draggable + var onStartDragPosition, top, left; + $overlay.draggable({ + drag: function(event, ui) { + scope.$apply(function(){ + validatePosition(ui.position.left, ui.position.top); + }); + }, + stop: function(event, ui){ + scope.$apply(function(){ + //make sure that every validates one more time... + validatePosition(ui.position.left, ui.position.top); + + calculateCropBox(); + scope.dimensions.image.rnd = Math.random(); + }); + } + }); + + + + var init = function(image){ + scope.loaded = false; + + //set dimensions on image, viewport, cropper etc + setDimensions(image); + + //if we have a crop already position the image + if(scope.crop){ + resizeImageToCrop(); + }else{ + resizeImageToEditor(); + } + + //sets constaints for the cropper + setConstraints(); + scope.loaded = true; + }; + + + /// WATCHERS //// + scope.$watchCollection('[width, height]', function(newValues, oldValues){ + //we have to reinit the whole thing if + //one of the external params changes + if(newValues !== oldValues){ + setDimensions($image); + setConstraints(); + } + }); + + var throttledResizing = _.throttle(function(){ + resizeImageToScale(scope.dimensions.scale.current); + calculateCropBox(); + }, 100); + + + //happens when we change the scale + scope.$watch("dimensions.scale.current", function(){ + if(scope.loaded){ + throttledResizing(); + } + }); + + //ie hack + if(window.navigator.userAgent.indexOf("MSIE ")){ + var ranger = element.find("input"); + ranger.bind("change",function(){ + scope.$apply(function(){ + scope.dimensions.scale.current = ranger.val(); + }); + }); + } + + //// INIT ///// + $image.load(function(){ + $timeout(function(){ + init($image); + }); + }); + } + }; + }); + +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbImageGravity +* @restrict E +* @function +* @description +**/ +angular.module("umbraco.directives") + .directive('umbImageGravity', function ($timeout, localizationService, $log) { + return { + restrict: 'E', + replace: true, + templateUrl: 'views/components/imaging/umb-image-gravity.html', + scope: { + src: '=', + center: "=", + onImageLoaded: "=" + }, + link: function(scope, element, attrs) { + + //Internal values for keeping track of the dot and the size of the editor + scope.dimensions = { + width: 0, + height: 0, + left: 0, + top: 0 + }; + + scope.loaded = false; + + //elements + var $viewport = element.find(".viewport"); + var $image = element.find("img"); + var $overlay = element.find(".overlay"); + + scope.style = function () { + if(scope.dimensions.width <= 0){ + setDimensions(); + } + + return { + 'top': scope.dimensions.top + 'px', + 'left': scope.dimensions.left + 'px' + }; + }; + + scope.setFocalPoint = function(event) { + + scope.$emit("imageFocalPointStart"); + + var offsetX = event.offsetX - 10; + var offsetY = event.offsetY - 10; + + calculateGravity(offsetX, offsetY); + + lazyEndEvent(); + + }; + + var setDimensions = function(){ + scope.dimensions.width = $image.width(); + scope.dimensions.height = $image.height(); + + if(scope.center){ + scope.dimensions.left = scope.center.left * scope.dimensions.width -10; + scope.dimensions.top = scope.center.top * scope.dimensions.height -10; + }else{ + scope.center = { left: 0.5, top: 0.5 }; + } + }; + + var calculateGravity = function(offsetX, offsetY){ + scope.dimensions.left = offsetX; + scope.dimensions.top = offsetY; + + scope.center.left = (scope.dimensions.left+10) / scope.dimensions.width; + scope.center.top = (scope.dimensions.top+10) / scope.dimensions.height; + }; + + var lazyEndEvent = _.debounce(function(){ + scope.$apply(function(){ + scope.$emit("imageFocalPointStop"); + }); + }, 2000); + + + //Drag and drop positioning, using jquery ui draggable + //TODO ensure that the point doesnt go outside the box + $overlay.draggable({ + containment: "parent", + start: function(){ + scope.$apply(function(){ + scope.$emit("imageFocalPointStart"); + }); + }, + stop: function() { + scope.$apply(function(){ + var offsetX = $overlay[0].offsetLeft; + var offsetY = $overlay[0].offsetTop; + calculateGravity(offsetX, offsetY); + }); + + lazyEndEvent(); + } + }); + + //// INIT ///// + $image.load(function(){ + $timeout(function(){ + setDimensions(); + scope.loaded = true; + scope.onImageLoaded(); + }); + }); + + $(window).on('resize.umbImageGravity', function(){ + scope.$apply(function(){ + $timeout(function(){ + setDimensions(); + }); + var offsetX = $overlay[0].offsetLeft; + var offsetY = $overlay[0].offsetTop; + calculateGravity(offsetX, offsetY); + }); + }); + + scope.$on('$destroy', function() { + $(window).off('.umbImageGravity'); + }); + + } + }; + }); + +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbImageThumbnail +* @restrict E +* @function +* @description +**/ +angular.module("umbraco.directives") + .directive('umbImageThumbnail', + function ($timeout, localizationService, cropperHelper, $log) { + return { + restrict: 'E', + replace: true, + templateUrl: 'views/components/imaging/umb-image-thumbnail.html', + + scope: { + src: '=', + width: '@', + height: '@', + center: "=", + crop: "=", + maxSize: '@' + }, + + link: function(scope, element, attrs) { + //// INIT ///// + var $image = element.find("img"); + scope.loaded = false; + + $image.load(function(){ + $timeout(function(){ + $image.width("auto"); + $image.height("auto"); + + scope.image = {}; + scope.image.width = $image[0].width; + scope.image.height = $image[0].height; + + //we force a lower thumbnail size to fit the max size + //we do not compare to the image dimensions, but the thumbs + if(scope.maxSize){ + var ratioCalculation = cropperHelper.calculateAspectRatioFit( + scope.width, + scope.height, + scope.maxSize, + scope.maxSize, + false); + + //so if we have a max size, override the thumb sizes + scope.width = ratioCalculation.width; + scope.height = ratioCalculation.height; + } + + setPreviewStyle(); + scope.loaded = true; + }); + }); + + /// WATCHERS //// + scope.$watchCollection('[crop, center]', function(newValues, oldValues){ + //we have to reinit the whole thing if + //one of the external params changes + setPreviewStyle(); + }); + + scope.$watch("center", function(){ + setPreviewStyle(); + }, true); + + function setPreviewStyle(){ + if(scope.crop && scope.image){ + scope.preview = cropperHelper.convertToStyle( + scope.crop, + scope.image, + {width: scope.width, height: scope.height}, + 0); + }else if(scope.image){ + + //returns size fitting the cropper + var p = cropperHelper.calculateAspectRatioFit( + scope.image.width, + scope.image.height, + scope.width, + scope.height, + true); + + + if(scope.center){ + var xy = cropperHelper.alignToCoordinates(p, scope.center, {width: scope.width, height: scope.height}); + p.top = xy.top; + p.left = xy.left; + }else{ + + } + + p.position = "absolute"; + scope.preview = p; + } + } + } + }; + }); + +angular.module("umbraco.directives") + + .directive('localize', function ($log, localizationService) { + return { + restrict: 'E', + scope:{ + key: '@' + }, + replace: true, + + link: function (scope, element, attrs) { + var key = scope.key; + localizationService.localize(key).then(function(value){ + element.html(value); + }); + } + }; + }) + + .directive('localize', function ($log, localizationService) { + return { + restrict: 'A', + link: function (scope, element, attrs) { + var keys = attrs.localize.split(','); + + angular.forEach(keys, function(value, key){ + var attr = element.attr(value); + + if(attr){ + if(attr[0] === '@'){ + + var t = localizationService.tokenize(attr.substring(1), scope); + localizationService.localize(t.key, t.tokens).then(function(val){ + element.attr(value, val); + }); + + } + } + }); + } + }; + }); /** * @ngdoc directive @@ -4456,969 +4456,969 @@ angular.module("umbraco.directives") })(); -/** -@ngdoc directive -@name umbraco.directives.directive:umbOverlay -@restrict E -@scope - -@description - -

Markup example

-
-    
- - - - - - -
-
- -

Controller example

-
-    (function () {
-
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-
-            vm.openOverlay = openOverlay;
-
-            function openOverlay() {
-
-                vm.overlay = {
-                    view: "mediapicker",
-                    show: true,
-                    submit: function(model) {
-
-                        vm.overlay.show = false;
-                        vm.overlay = null;
-                    },
-                    close: function(oldModel) {
-                        vm.overlay.show = false;
-                        vm.overlay = null;
-                    }
-                }
-
-            };
-
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-    })();
-
- -

General Options

-Lorem ipsum dolor sit amet.. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParamTypeDetails
model.titleStringSet the title of the overlay.
model.subTitleStringSet the subtitle of the overlay.
model.submitButtonLabelStringSet an alternate submit button text
model.submitButtonLabelKeyStringSet an alternate submit button label key for localized texts
model.hideSubmitButtonBooleanHides the submit button
model.closeButtonLabelStringSet an alternate close button text
model.closeButtonLabelKeyStringSet an alternate close button label key for localized texts
model.showBooleanShow/hide the overlay
model.submitFunctionCallback function when the overlay submits. Returns the overlay model object
model.closeFunctionCallback function when the overlay closes. Returns a copy of the overlay model object before being modified
- - -

Content Picker

-Opens a content picker.
-view: contentpicker - - - - - - - - - - - - - -
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
- - - - - - - - - - - - - -
ReturnsTypeDetails
model.selectionArrayArray of content objects
- - -

Icon Picker

-Opens an icon picker.
-view: iconpicker - - - - - - - - - - - - - -
ReturnsTypeDetails
model.iconStringThe icon class
- -

Item Picker

-Opens an item picker.
-view: itempicker - - - - - - - - - - - - - - - - - - - - - - - - - -
ParamTypeDetails
model.availableItemsArrayArray of available items
model.selectedItemsArrayArray of selected items. When passed in the selected items will be filtered from the available items.
model.filterBooleanSet to false to hide the filter
- - - - - - - - - - - - - -
ReturnsTypeDetails
model.selectedItemObjectThe selected item
- -

Macro Picker

-Opens a media picker.
-view: macropicker - - - - - - - - - - - - - - - -
ParamTypeDetails
model.dialogDataObjectObject which contains array of allowedMacros. Set to null to allow all.
- - - - - - - - - - - - - - - - - - - - -
ReturnsTypeDetails
model.macroParamsArrayArray of macro params
model.selectedMacroObjectThe selected macro
- -

Media Picker

-Opens a media picker.
-view: mediapicker - - - - - - - - - - - - - - - - - - - - - - - - - -
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
model.onlyImagesBooleanOnly display files that have an image file-extension
model.disableFolderSelectBooleanDisable folder selection
- - - - - - - - - - - - - - - -
ReturnsTypeDetails
model.selectedImagesArrayArray of selected images
- -

Member Group Picker

-Opens a member group picker.
-view: membergrouppicker - - - - - - - - - - - - - - - -
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
- - - - - - - - - - - - - - - - - - - - -
ReturnsTypeDetails
model.selectedMemberGroupStringThe selected member group
model.selectedMemberGroups (multiPicker)ArrayThe selected member groups
- -

Member Picker

-Opens a member picker.
-view: memberpicker - - - - - - - - - - - - - - - -
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
- - - - - - - - - - - - - - -
ReturnsTypeDetails
model.selectionArrayArray of selected members/td> -
- -

YSOD

-Opens an overlay to show a custom YSOD.
-view: ysod - - - - - - - - - - - - - - - -
ParamTypeDetails
model.errorObjectError object
- -@param {object} model Overlay options. -@param {string} view Path to view or one of the default view names. -@param {string} position The overlay position ("left", "right", "center": "target"). -**/ - -(function() { - 'use strict'; - - function OverlayDirective($timeout, formHelper, overlayHelper, localizationService) { - - function link(scope, el, attr, ctrl) { - - scope.directive = { - enableConfirmButton: false - }; - - var overlayNumber = 0; - var numberOfOverlays = 0; - var isRegistered = false; - - var modelCopy = {}; - - function activate() { - - setView(); - - setButtonText(); - - modelCopy = makeModelCopy(scope.model); - - $timeout(function() { - - if (scope.position === "target") { - setTargetPosition(); - } - - // this has to be done inside a timeout to ensure the destroy - // event on other overlays is run before registering a new one - registerOverlay(); - - setOverlayIndent(); - - }); - - } - - function setView() { - - if (scope.view) { - - if (scope.view.indexOf(".html") === -1) { - var viewAlias = scope.view.toLowerCase(); - scope.view = "views/common/overlays/" + viewAlias + "/" + viewAlias + ".html"; - } - - } - - } - - function setButtonText() { - if (!scope.model.closeButtonLabelKey && !scope.model.closeButtonLabel) { - scope.model.closeButtonLabel = localizationService.localize("general_close"); - } - if (!scope.model.submitButtonLabelKey && !scope.model.submitButtonLabel) { - scope.model.submitButtonLabel = localizationService.localize("general_submit"); - } - } - - function registerOverlay() { - - overlayNumber = overlayHelper.registerOverlay(); - - $(document).bind("keydown.overlay-" + overlayNumber, function(event) { - - if (event.which === 27) { - - numberOfOverlays = overlayHelper.getNumberOfOverlays(); - - if(numberOfOverlays === overlayNumber) { - scope.closeOverLay(); - } - - event.preventDefault(); - } - - if (event.which === 13) { - - numberOfOverlays = overlayHelper.getNumberOfOverlays(); - - if(numberOfOverlays === overlayNumber) { - - var activeElementType = document.activeElement.tagName; - var clickableElements = ["A", "BUTTON"]; - var submitOnEnter = document.activeElement.hasAttribute("overlay-submit-on-enter"); - - if(clickableElements.indexOf(activeElementType) === 0) { - document.activeElement.click(); - event.preventDefault(); - } else if(activeElementType === "TEXTAREA" && !submitOnEnter) { - - - } else { - scope.$apply(function () { - scope.submitForm(scope.model); - }); - event.preventDefault(); - } - - } - - } - - }); - - isRegistered = true; - - } - - function unregisterOverlay() { - - if(isRegistered) { - - overlayHelper.unregisterOverlay(); - - $(document).unbind("keydown.overlay-" + overlayNumber); - - isRegistered = false; - } - - } - - function makeModelCopy(object) { - - var newObject = {}; - - for (var key in object) { - if (key !== "event") { - newObject[key] = angular.copy(object[key]); - } - } - - return newObject; - - } - - function setOverlayIndent() { - - var overlayIndex = overlayNumber - 1; - var indentSize = overlayIndex * 20; - var overlayWidth = el.context.clientWidth; - - el.css('width', overlayWidth - indentSize); - - if(scope.position === "center" || scope.position === "target") { - var overlayTopPosition = el.context.offsetTop; - el.css('top', overlayTopPosition + indentSize); - } - - } - - function setTargetPosition() { - - var container = $("#contentwrapper"); - var containerLeft = container[0].offsetLeft; - var containerRight = containerLeft + container[0].offsetWidth; - var containerTop = container[0].offsetTop; - var containerBottom = containerTop + container[0].offsetHeight; - - var mousePositionClickX = null; - var mousePositionClickY = null; - var elementHeight = null; - var elementWidth = null; - - var position = { - right: "inherit", - left: "inherit", - top: "inherit", - bottom: "inherit" - }; - - // if mouse click position is know place element with mouse in center - if (scope.model.event && scope.model.event) { - - // click position - mousePositionClickX = scope.model.event.pageX; - mousePositionClickY = scope.model.event.pageY; - - // element size - elementHeight = el.context.clientHeight; - elementWidth = el.context.clientWidth; - - // move element to this position - position.left = mousePositionClickX - (elementWidth / 2); - position.top = mousePositionClickY - (elementHeight / 2); - - // check to see if element is outside screen - // outside right - if (position.left + elementWidth > containerRight) { - position.right = 10; - position.left = "inherit"; - } - - // outside bottom - if (position.top + elementHeight > containerBottom) { - position.bottom = 10; - position.top = "inherit"; - } - - // outside left - if (position.left < containerLeft) { - position.left = containerLeft + 10; - position.right = "inherit"; - } - - // outside top - if (position.top < containerTop) { - position.top = 10; - position.bottom = "inherit"; - } - - el.css(position); - - } - - } - - scope.submitForm = function(model) { - if(scope.model.submit) { - if (formHelper.submitForm({scope: scope})) { - formHelper.resetForm({ scope: scope }); - - if(scope.model.confirmSubmit && scope.model.confirmSubmit.enable && !scope.directive.enableConfirmButton) { - scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton); - } else { - unregisterOverlay(); - scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton); - } - - } - } - }; - - scope.cancelConfirmSubmit = function() { - scope.model.confirmSubmit.show = false; - }; - - scope.closeOverLay = function() { - - unregisterOverlay(); - - if (scope.model.close) { - scope.model = modelCopy; - scope.model.close(scope.model); - } else { - scope.model.show = false; - scope.model = null; - } - - }; - - // angular does not support ng-show on custom directives - // width isolated scopes. So we have to make our own. - if (attr.hasOwnProperty("ngShow")) { - scope.$watch("ngShow", function(value) { - if (value) { - el.show(); - activate(); - } else { - unregisterOverlay(); - el.hide(); - } - }); - } else { - activate(); - } - - scope.$on('$destroy', function(){ - unregisterOverlay(); - }); - - } - - var directive = { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/overlays/umb-overlay.html', - scope: { - ngShow: "=", - model: "=", - view: "=", - position: "@" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbOverlay', OverlayDirective); - -})(); - -(function() { - 'use strict'; - - function OverlayBackdropDirective(overlayHelper) { - - function link(scope, el, attr, ctrl) { - - scope.numberOfOverlays = 0; - - scope.$watch(function(){ - return overlayHelper.getNumberOfOverlays(); - }, function (newValue) { - scope.numberOfOverlays = newValue; - }); - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/overlays/umb-overlay-backdrop.html', - link: link - }; - - return directive; - - } - - angular.module('umbraco.directives').directive('umbOverlayBackdrop', OverlayBackdropDirective); - -})(); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbProperty -* @restrict E -**/ -angular.module("umbraco.directives") - .directive('umbProperty', function (umbPropEditorHelper) { - return { - scope: { - property: "=" - }, - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/property/umb-property.html', - link: function(scope) { - scope.propertyAlias = Umbraco.Sys.ServerVariables.isDebuggingEnabled === true ? scope.property.alias : null; - }, - //Define a controller for this directive to expose APIs to other directives - controller: function ($scope, $timeout) { - - var self = this; - - //set the API properties/methods - - self.property = $scope.property; - self.setPropertyError = function(errorMsg) { - $scope.property.propertyErrorMessage = errorMsg; - }; - } - }; +/** +@ngdoc directive +@name umbraco.directives.directive:umbOverlay +@restrict E +@scope + +@description + +

Markup example

+
+    
+ + + + + + +
+
+ +

Controller example

+
+    (function () {
+
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+
+            vm.openOverlay = openOverlay;
+
+            function openOverlay() {
+
+                vm.overlay = {
+                    view: "mediapicker",
+                    show: true,
+                    submit: function(model) {
+
+                        vm.overlay.show = false;
+                        vm.overlay = null;
+                    },
+                    close: function(oldModel) {
+                        vm.overlay.show = false;
+                        vm.overlay = null;
+                    }
+                }
+
+            };
+
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+ +

General Options

+Lorem ipsum dolor sit amet.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParamTypeDetails
model.titleStringSet the title of the overlay.
model.subTitleStringSet the subtitle of the overlay.
model.submitButtonLabelStringSet an alternate submit button text
model.submitButtonLabelKeyStringSet an alternate submit button label key for localized texts
model.hideSubmitButtonBooleanHides the submit button
model.closeButtonLabelStringSet an alternate close button text
model.closeButtonLabelKeyStringSet an alternate close button label key for localized texts
model.showBooleanShow/hide the overlay
model.submitFunctionCallback function when the overlay submits. Returns the overlay model object
model.closeFunctionCallback function when the overlay closes. Returns a copy of the overlay model object before being modified
+ + +

Content Picker

+Opens a content picker.
+view: contentpicker + + + + + + + + + + + + + +
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
+ + + + + + + + + + + + + +
ReturnsTypeDetails
model.selectionArrayArray of content objects
+ + +

Icon Picker

+Opens an icon picker.
+view: iconpicker + + + + + + + + + + + + + +
ReturnsTypeDetails
model.iconStringThe icon class
+ +

Item Picker

+Opens an item picker.
+view: itempicker + + + + + + + + + + + + + + + + + + + + + + + + + +
ParamTypeDetails
model.availableItemsArrayArray of available items
model.selectedItemsArrayArray of selected items. When passed in the selected items will be filtered from the available items.
model.filterBooleanSet to false to hide the filter
+ + + + + + + + + + + + + +
ReturnsTypeDetails
model.selectedItemObjectThe selected item
+ +

Macro Picker

+Opens a media picker.
+view: macropicker + + + + + + + + + + + + + + + +
ParamTypeDetails
model.dialogDataObjectObject which contains array of allowedMacros. Set to null to allow all.
+ + + + + + + + + + + + + + + + + + + + +
ReturnsTypeDetails
model.macroParamsArrayArray of macro params
model.selectedMacroObjectThe selected macro
+ +

Media Picker

+Opens a media picker.
+view: mediapicker + + + + + + + + + + + + + + + + + + + + + + + + + +
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
model.onlyImagesBooleanOnly display files that have an image file-extension
model.disableFolderSelectBooleanDisable folder selection
+ + + + + + + + + + + + + + + +
ReturnsTypeDetails
model.selectedImagesArrayArray of selected images
+ +

Member Group Picker

+Opens a member group picker.
+view: membergrouppicker + + + + + + + + + + + + + + + +
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
+ + + + + + + + + + + + + + + + + + + + +
ReturnsTypeDetails
model.selectedMemberGroupStringThe selected member group
model.selectedMemberGroups (multiPicker)ArrayThe selected member groups
+ +

Member Picker

+Opens a member picker.
+view: memberpicker + + + + + + + + + + + + + + + +
ParamTypeDetails
model.multiPickerBooleanPick one or multiple items
+ + + + + + + + + + + + + + +
ReturnsTypeDetails
model.selectionArrayArray of selected members/td> +
+ +

YSOD

+Opens an overlay to show a custom YSOD.
+view: ysod + + + + + + + + + + + + + + + +
ParamTypeDetails
model.errorObjectError object
+ +@param {object} model Overlay options. +@param {string} view Path to view or one of the default view names. +@param {string} position The overlay position ("left", "right", "center": "target"). +**/ + +(function() { + 'use strict'; + + function OverlayDirective($timeout, formHelper, overlayHelper, localizationService) { + + function link(scope, el, attr, ctrl) { + + scope.directive = { + enableConfirmButton: false + }; + + var overlayNumber = 0; + var numberOfOverlays = 0; + var isRegistered = false; + + var modelCopy = {}; + + function activate() { + + setView(); + + setButtonText(); + + modelCopy = makeModelCopy(scope.model); + + $timeout(function() { + + if (scope.position === "target") { + setTargetPosition(); + } + + // this has to be done inside a timeout to ensure the destroy + // event on other overlays is run before registering a new one + registerOverlay(); + + setOverlayIndent(); + + }); + + } + + function setView() { + + if (scope.view) { + + if (scope.view.indexOf(".html") === -1) { + var viewAlias = scope.view.toLowerCase(); + scope.view = "views/common/overlays/" + viewAlias + "/" + viewAlias + ".html"; + } + + } + + } + + function setButtonText() { + if (!scope.model.closeButtonLabelKey && !scope.model.closeButtonLabel) { + scope.model.closeButtonLabel = localizationService.localize("general_close"); + } + if (!scope.model.submitButtonLabelKey && !scope.model.submitButtonLabel) { + scope.model.submitButtonLabel = localizationService.localize("general_submit"); + } + } + + function registerOverlay() { + + overlayNumber = overlayHelper.registerOverlay(); + + $(document).bind("keydown.overlay-" + overlayNumber, function(event) { + + if (event.which === 27) { + + numberOfOverlays = overlayHelper.getNumberOfOverlays(); + + if(numberOfOverlays === overlayNumber) { + scope.closeOverLay(); + } + + event.preventDefault(); + } + + if (event.which === 13) { + + numberOfOverlays = overlayHelper.getNumberOfOverlays(); + + if(numberOfOverlays === overlayNumber) { + + var activeElementType = document.activeElement.tagName; + var clickableElements = ["A", "BUTTON"]; + var submitOnEnter = document.activeElement.hasAttribute("overlay-submit-on-enter"); + + if(clickableElements.indexOf(activeElementType) === 0) { + document.activeElement.click(); + event.preventDefault(); + } else if(activeElementType === "TEXTAREA" && !submitOnEnter) { + + + } else { + scope.$apply(function () { + scope.submitForm(scope.model); + }); + event.preventDefault(); + } + + } + + } + + }); + + isRegistered = true; + + } + + function unregisterOverlay() { + + if(isRegistered) { + + overlayHelper.unregisterOverlay(); + + $(document).unbind("keydown.overlay-" + overlayNumber); + + isRegistered = false; + } + + } + + function makeModelCopy(object) { + + var newObject = {}; + + for (var key in object) { + if (key !== "event") { + newObject[key] = angular.copy(object[key]); + } + } + + return newObject; + + } + + function setOverlayIndent() { + + var overlayIndex = overlayNumber - 1; + var indentSize = overlayIndex * 20; + var overlayWidth = el.context.clientWidth; + + el.css('width', overlayWidth - indentSize); + + if(scope.position === "center" || scope.position === "target") { + var overlayTopPosition = el.context.offsetTop; + el.css('top', overlayTopPosition + indentSize); + } + + } + + function setTargetPosition() { + + var container = $("#contentwrapper"); + var containerLeft = container[0].offsetLeft; + var containerRight = containerLeft + container[0].offsetWidth; + var containerTop = container[0].offsetTop; + var containerBottom = containerTop + container[0].offsetHeight; + + var mousePositionClickX = null; + var mousePositionClickY = null; + var elementHeight = null; + var elementWidth = null; + + var position = { + right: "inherit", + left: "inherit", + top: "inherit", + bottom: "inherit" + }; + + // if mouse click position is know place element with mouse in center + if (scope.model.event && scope.model.event) { + + // click position + mousePositionClickX = scope.model.event.pageX; + mousePositionClickY = scope.model.event.pageY; + + // element size + elementHeight = el.context.clientHeight; + elementWidth = el.context.clientWidth; + + // move element to this position + position.left = mousePositionClickX - (elementWidth / 2); + position.top = mousePositionClickY - (elementHeight / 2); + + // check to see if element is outside screen + // outside right + if (position.left + elementWidth > containerRight) { + position.right = 10; + position.left = "inherit"; + } + + // outside bottom + if (position.top + elementHeight > containerBottom) { + position.bottom = 10; + position.top = "inherit"; + } + + // outside left + if (position.left < containerLeft) { + position.left = containerLeft + 10; + position.right = "inherit"; + } + + // outside top + if (position.top < containerTop) { + position.top = 10; + position.bottom = "inherit"; + } + + el.css(position); + + } + + } + + scope.submitForm = function(model) { + if(scope.model.submit) { + if (formHelper.submitForm({scope: scope})) { + formHelper.resetForm({ scope: scope }); + + if(scope.model.confirmSubmit && scope.model.confirmSubmit.enable && !scope.directive.enableConfirmButton) { + scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton); + } else { + unregisterOverlay(); + scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton); + } + + } + } + }; + + scope.cancelConfirmSubmit = function() { + scope.model.confirmSubmit.show = false; + }; + + scope.closeOverLay = function() { + + unregisterOverlay(); + + if (scope.model.close) { + scope.model = modelCopy; + scope.model.close(scope.model); + } else { + scope.model.show = false; + scope.model = null; + } + + }; + + // angular does not support ng-show on custom directives + // width isolated scopes. So we have to make our own. + if (attr.hasOwnProperty("ngShow")) { + scope.$watch("ngShow", function(value) { + if (value) { + el.show(); + activate(); + } else { + unregisterOverlay(); + el.hide(); + } + }); + } else { + activate(); + } + + scope.$on('$destroy', function(){ + unregisterOverlay(); + }); + + } + + var directive = { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/overlays/umb-overlay.html', + scope: { + ngShow: "=", + model: "=", + view: "=", + position: "@" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbOverlay', OverlayDirective); + +})(); + +(function() { + 'use strict'; + + function OverlayBackdropDirective(overlayHelper) { + + function link(scope, el, attr, ctrl) { + + scope.numberOfOverlays = 0; + + scope.$watch(function(){ + return overlayHelper.getNumberOfOverlays(); + }, function (newValue) { + scope.numberOfOverlays = newValue; + }); + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/overlays/umb-overlay-backdrop.html', + link: link + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbOverlayBackdrop', OverlayBackdropDirective); + +})(); + +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbProperty +* @restrict E +**/ +angular.module("umbraco.directives") + .directive('umbProperty', function (umbPropEditorHelper) { + return { + scope: { + property: "=" + }, + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/property/umb-property.html', + link: function(scope) { + scope.propertyAlias = Umbraco.Sys.ServerVariables.isDebuggingEnabled === true ? scope.property.alias : null; + }, + //Define a controller for this directive to expose APIs to other directives + controller: function ($scope, $timeout) { + + var self = this; + + //set the API properties/methods + + self.property = $scope.property; + self.setPropertyError = function(errorMsg) { + $scope.property.propertyErrorMessage = errorMsg; + }; + } + }; }); -/** -* @ngdoc directive -* @function -* @name umbraco.directives.directive:umbPropertyEditor -* @requires formController -* @restrict E -**/ +/** +* @ngdoc directive +* @function +* @name umbraco.directives.directive:umbPropertyEditor +* @requires formController +* @restrict E +**/ + +//share property editor directive function +var _umbPropertyEditor = function (umbPropEditorHelper) { + return { + scope: { + model: "=", + isPreValue: "@", + preview: "@" + }, + + require: "^form", + restrict: 'E', + replace: true, + templateUrl: 'views/components/property/umb-property-editor.html', + link: function (scope, element, attrs, ctrl) { + + //we need to copy the form controller val to our isolated scope so that + //it get's carried down to the child scopes of this! + //we'll also maintain the current form name. + scope[ctrl.$name] = ctrl; + + if(!scope.model.alias){ + scope.model.alias = Math.random().toString(36).slice(2); + } + + scope.$watch("model.view", function(val){ + scope.propertyEditorView = umbPropEditorHelper.getViewPath(scope.model.view, scope.isPreValue); + }); + } + }; + }; + +//Preffered is the umb-property-editor as its more explicit - but we keep umb-editor for backwards compat +angular.module("umbraco.directives").directive('umbPropertyEditor', _umbPropertyEditor); +angular.module("umbraco.directives").directive('umbEditor', _umbPropertyEditor); -//share property editor directive function -var _umbPropertyEditor = function (umbPropEditorHelper) { - return { - scope: { - model: "=", - isPreValue: "@", - preview: "@" - }, - - require: "^form", - restrict: 'E', - replace: true, - templateUrl: 'views/components/property/umb-property-editor.html', - link: function (scope, element, attrs, ctrl) { - - //we need to copy the form controller val to our isolated scope so that - //it get's carried down to the child scopes of this! - //we'll also maintain the current form name. - scope[ctrl.$name] = ctrl; - - if(!scope.model.alias){ - scope.model.alias = Math.random().toString(36).slice(2); - } - - scope.$watch("model.view", function(val){ - scope.propertyEditorView = umbPropEditorHelper.getViewPath(scope.model.view, scope.isPreValue); - }); - } - }; - }; - -//Preffered is the umb-property-editor as its more explicit - but we keep umb-editor for backwards compat -angular.module("umbraco.directives").directive('umbPropertyEditor', _umbPropertyEditor); -angular.module("umbraco.directives").directive('umbEditor', _umbPropertyEditor); - -angular.module("umbraco.directives.html") - .directive('umbPropertyGroup', function () { - return { - transclude: true, - restrict: 'E', - replace: true, - templateUrl: 'views/components/property/umb-property-group.html' - }; +angular.module("umbraco.directives.html") + .directive('umbPropertyGroup', function () { + return { + transclude: true, + restrict: 'E', + replace: true, + templateUrl: 'views/components/property/umb-property-group.html' + }; }); -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbTab -* @restrict E -**/ -angular.module("umbraco.directives") -.directive('umbTab', function ($parse, $timeout) { - return { - restrict: 'E', - replace: true, - transclude: 'true', - templateUrl: 'views/components/tabs/umb-tab.html' - }; +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbTab +* @restrict E +**/ +angular.module("umbraco.directives") +.directive('umbTab', function ($parse, $timeout) { + return { + restrict: 'E', + replace: true, + transclude: 'true', + templateUrl: 'views/components/tabs/umb-tab.html' + }; +}); + +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbTabs +* @restrict A +* @description Used to bind to bootstrap tab events so that sub directives can use this API to listen to tab changes +**/ +angular.module("umbraco.directives") +.directive('umbTabs', function () { + return { + restrict: 'A', + controller: function ($scope, $element, $attrs) { + + var callbacks = []; + this.onTabShown = function(cb) { + callbacks.push(cb); + }; + + function tabShown(event) { + + var curr = $(event.target); // active tab + var prev = $(event.relatedTarget); // previous tab + + for (var c in callbacks) { + callbacks[c].apply(this, [{current: curr, previous: prev}]); + } + } + + //NOTE: it MUST be done this way - binding to an ancestor element that exists + // in the DOM to bind to the dynamic elements that will be created. + // It would be nicer to create this event handler as a directive for which child + // directives can attach to. + $element.on('shown', '.nav-tabs a', tabShown); + + //ensure to unregister + $scope.$on('$destroy', function () { + $element.off('shown', '.nav-tabs a', tabShown); + + for (var c in callbacks) { + delete callbacks[c]; + } + callbacks = null; + }); + } + }; }); +(function() { + 'use strict'; + + function UmbTabsContentDirective() { + + function link(scope, el, attr, ctrl) { + + scope.view = attr.view; + + } + + var directive = { + restrict: "E", + replace: true, + transclude: 'true', + templateUrl: "views/components/tabs/umb-tabs-content.html", + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbTabsContent', UmbTabsContentDirective); + +})(); -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbTabs -* @restrict A -* @description Used to bind to bootstrap tab events so that sub directives can use this API to listen to tab changes -**/ -angular.module("umbraco.directives") -.directive('umbTabs', function () { - return { - restrict: 'A', - controller: function ($scope, $element, $attrs) { - - var callbacks = []; - this.onTabShown = function(cb) { - callbacks.push(cb); - }; - - function tabShown(event) { - - var curr = $(event.target); // active tab - var prev = $(event.relatedTarget); // previous tab - - for (var c in callbacks) { - callbacks[c].apply(this, [{current: curr, previous: prev}]); - } - } - - //NOTE: it MUST be done this way - binding to an ancestor element that exists - // in the DOM to bind to the dynamic elements that will be created. - // It would be nicer to create this event handler as a directive for which child - // directives can attach to. - $element.on('shown', '.nav-tabs a', tabShown); - - //ensure to unregister - $scope.$on('$destroy', function () { - $element.off('shown', '.nav-tabs a', tabShown); - - for (var c in callbacks) { - delete callbacks[c]; - } - callbacks = null; - }); - } - }; -}); -(function() { - 'use strict'; - - function UmbTabsContentDirective() { - - function link(scope, el, attr, ctrl) { - - scope.view = attr.view; - - } - - var directive = { - restrict: "E", - replace: true, - transclude: 'true', - templateUrl: "views/components/tabs/umb-tabs-content.html", - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbTabsContent', UmbTabsContentDirective); - -})(); - -(function() { - 'use strict'; - - function UmbTabsNavDirective($timeout) { - - function link(scope, el, attr) { - - function activate() { - - $timeout(function () { - - //use bootstrap tabs API to show the first one - el.find("a:first").tab('show'); - - //enable the tab drop - el.tabdrop(); - - }); - - } - - var unbindModelWatch = scope.$watch('model', function(newValue, oldValue){ - - activate(); - - }); - - - scope.$on('$destroy', function () { - - //ensure to destroy tabdrop (unbinds window resize listeners) - el.tabdrop("destroy"); - - unbindModelWatch(); - - }); - - } - - var directive = { - restrict: "E", - replace: true, - templateUrl: "views/components/tabs/umb-tabs-nav.html", - scope: { - model: "=", - tabdrop: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbTabsNav', UmbTabsNavDirective); - -})(); +(function() { + 'use strict'; + + function UmbTabsNavDirective($timeout) { + + function link(scope, el, attr) { + + function activate() { + + $timeout(function () { + + //use bootstrap tabs API to show the first one + el.find("a:first").tab('show'); + + //enable the tab drop + el.tabdrop(); + + }); + + } + + var unbindModelWatch = scope.$watch('model', function(newValue, oldValue){ + + activate(); + + }); + + + scope.$on('$destroy', function () { + + //ensure to destroy tabdrop (unbinds window resize listeners) + el.tabdrop("destroy"); + + unbindModelWatch(); + + }); + + } + + var directive = { + restrict: "E", + replace: true, + templateUrl: "views/components/tabs/umb-tabs-nav.html", + scope: { + model: "=", + tabdrop: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbTabsNav', UmbTabsNavDirective); + +})(); /** * @ngdoc directive @@ -6078,475 +6078,475 @@ angular.module("umbraco.directives") }; }); -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbTreeSearchBox -* @function -* @element ANY -* @restrict E -**/ -function treeSearchBox(localizationService, searchService, $q) { - return { - scope: { - searchFromId: "@", - searchFromName: "@", - showSearch: "@", - section: "@", - hideSearchCallback: "=", - searchCallback: "=" - }, - restrict: "E", // restrict to an element - replace: true, // replace the html element with the template - templateUrl: 'views/components/tree/umb-tree-search-box.html', - link: function (scope, element, attrs, ctrl) { - - scope.term = ""; - scope.hideSearch = function() { - scope.term = ""; - scope.hideSearchCallback(); - }; - - localizationService.localize("general_typeToSearch").then(function (value) { - scope.searchPlaceholderText = value; - }); - - if (!scope.showSearch) { - scope.showSearch = "false"; - } - - //used to cancel any request in progress if another one needs to take it's place - var canceler = null; - - function performSearch() { - if (scope.term) { - scope.results = []; - - //a canceler exists, so perform the cancelation operation and reset - if (canceler) { - console.log("CANCELED!"); - canceler.resolve(); - canceler = $q.defer(); - } - else { - canceler = $q.defer(); - } - - var searchArgs = { - term: scope.term, - canceler: canceler - }; - - //append a start node context if there is one - if (scope.searchFromId) { - searchArgs["searchFrom"] = scope.searchFromId; - } - - searcher(searchArgs).then(function (data) { - scope.searchCallback(data); - //set back to null so it can be re-created - canceler = null; - }); - } - } - - scope.$watch("term", _.debounce(function(newVal, oldVal) { - scope.$apply(function() { - if (newVal !== null && newVal !== undefined && newVal !== oldVal) { - performSearch(); - } - }); - }, 200)); - - var searcher = searchService.searchContent; - //search - if (scope.section === "member") { - searcher = searchService.searchMembers; - } - else if (scope.section === "media") { - searcher = searchService.searchMedia; - } - } - }; -} -angular.module('umbraco.directives').directive("umbTreeSearchBox", treeSearchBox); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbTreeSearchResults -* @function -* @element ANY -* @restrict E -**/ -function treeSearchResults() { - return { - scope: { - results: "=", - selectResultCallback: "=" - }, - restrict: "E", // restrict to an element - replace: true, // replace the html element with the template - templateUrl: 'views/components/tree/umb-tree-search-results.html', - link: function (scope, element, attrs, ctrl) { - - } - }; -} -angular.module('umbraco.directives').directive("umbTreeSearchResults", treeSearchResults); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbGenerateAlias -@restrict E -@scope - -@description -Use this directive to generate a camelCased umbraco alias. -When the aliasFrom value is changed the directive will get a formatted alias from the server and update the alias model. If "enableLock" is set to true -the directive will use {@link umbraco.directives.directive:umbLockedField umbLockedField} to lock and unlock the alias. - -

Markup example

-
-    
- - - - - - -
-
- -

Controller example

-
-    (function () {
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-
-            vm.name = "";
-            vm.alias = "";
-
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-    })();
-
- -@param {string} alias (binding): The model where the alias is bound. -@param {string} aliasFrom (binding): The model to generate the alias from. -@param {boolean=} enableLock (binding): Set to true to add a lock next to the alias from where it can be unlocked and changed. -**/ - -angular.module("umbraco.directives") - .directive('umbGenerateAlias', function ($timeout, entityResource) { - return { - restrict: 'E', - templateUrl: 'views/components/umb-generate-alias.html', - replace: true, - scope: { - alias: '=', - aliasFrom: '=', - enableLock: '=?', - serverValidationField: '@' - }, - link: function (scope, element, attrs, ctrl) { - - var eventBindings = []; - var bindWatcher = true; - var generateAliasTimeout = ""; - var updateAlias = false; - - scope.locked = true; - scope.placeholderText = "Enter alias..."; - - function generateAlias(value) { - - if (generateAliasTimeout) { - $timeout.cancel(generateAliasTimeout); - } - - if( value !== undefined && value !== "" && value !== null) { - - scope.alias = ""; - scope.placeholderText = "Generating Alias..."; - - generateAliasTimeout = $timeout(function () { - updateAlias = true; - entityResource.getSafeAlias(value, true).then(function (safeAlias) { - if (updateAlias) { - scope.alias = safeAlias.alias; - } - }); - }, 500); - - } else { - updateAlias = true; - scope.alias = ""; - scope.placeholderText = "Enter alias..."; - } - - } - - // if alias gets unlocked - stop watching alias - eventBindings.push(scope.$watch('locked', function(newValue, oldValue){ - if(newValue === false) { - bindWatcher = false; - } - })); - - // validate custom entered alias - eventBindings.push(scope.$watch('alias', function(newValue, oldValue){ - - if(scope.alias === "" && bindWatcher === true || scope.alias === null && bindWatcher === true) { - // add watcher - eventBindings.push(scope.$watch('aliasFrom', function(newValue, oldValue) { - if(bindWatcher) { - generateAlias(newValue); - } - })); - } - - })); - - // clean up - scope.$on('$destroy', function(){ - // unbind watchers - for(var e in eventBindings) { - eventBindings[e](); - } - }); - - } - }; - }); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbChildSelector -@restrict E -@scope - -@description -Use this directive to render a ui component for selecting child items to a parent node. - -

Markup example

-
-	
- - - - - - - - -
-
- -

Controller example

-
-	(function () {
-		"use strict";
-
-		function Controller() {
-
-            var vm = this;
-
-            vm.id = 1;
-            vm.name = "My Parent element";
-            vm.icon = "icon-document";
-            vm.selectedChildren = [];
-            vm.availableChildren = [
-                {
-                    id: 1,
-                    alias: "item1",
-                    name: "Item 1",
-                    icon: "icon-document"
-                },
-                {
-                    id: 2,
-                    alias: "item2",
-                    name: "Item 2",
-                    icon: "icon-document"
-                }
-            ];
-
-            vm.addChild = addChild;
-            vm.removeChild = removeChild;
-
-            function addChild($event) {
-                vm.overlay = {
-                    view: "itempicker",
-                    title: "Choose child",
-                    availableItems: vm.availableChildren,
-                    selectedItems: vm.selectedChildren,
-                    event: $event,
-                    show: true,
-                    submit: function(model) {
-
-                        // add selected child
-                        vm.selectedChildren.push(model.selectedItem);
-
-                        // close overlay
-                        vm.overlay.show = false;
-                        vm.overlay = null;
-                    }
-                };
-            }
-
-            function removeChild($index) {
-                vm.selectedChildren.splice($index, 1);
-            }
-
-        }
-
-		angular.module("umbraco").controller("My.Controller", Controller);
-
-	})();
-
- -@param {array} selectedChildren (binding): Array of selected children. -@param {array} availableChildren (binding: Array of items available for selection. -@param {string} parentName (binding): The parent name. -@param {string} parentIcon (binding): The parent icon. -@param {number} parentId (binding): The parent id. -@param {callback} onRemove (binding): Callback when the remove button is clicked on an item. -

The callback returns:

-
    -
  • child: The selected item.
  • -
  • $index: The selected item index.
  • -
-@param {callback} onAdd (binding): Callback when the add button is clicked. -

The callback returns:

-
    -
  • $event: The select event.
  • -
-**/ - -(function() { - 'use strict'; - - function ChildSelectorDirective() { - - function link(scope, el, attr, ctrl) { - - var eventBindings = []; - scope.dialogModel = {}; - scope.showDialog = false; - - scope.removeChild = function(selectedChild, $index) { - if(scope.onRemove) { - scope.onRemove(selectedChild, $index); - } - }; - - scope.addChild = function($event) { - if(scope.onAdd) { - scope.onAdd($event); - } - }; - - function syncParentName() { - - // update name on available item - angular.forEach(scope.availableChildren, function(availableChild){ - if(availableChild.id === scope.parentId) { - availableChild.name = scope.parentName; - } - }); - - // update name on selected child - angular.forEach(scope.selectedChildren, function(selectedChild){ - if(selectedChild.id === scope.parentId) { - selectedChild.name = scope.parentName; - } - }); - - } - - function syncParentIcon() { - - // update icon on available item - angular.forEach(scope.availableChildren, function(availableChild){ - if(availableChild.id === scope.parentId) { - availableChild.icon = scope.parentIcon; - } - }); - - // update icon on selected child - angular.forEach(scope.selectedChildren, function(selectedChild){ - if(selectedChild.id === scope.parentId) { - selectedChild.icon = scope.parentIcon; - } - }); - - } - - eventBindings.push(scope.$watch('parentName', function(newValue, oldValue){ - - if (newValue === oldValue) { return; } - if ( oldValue === undefined || newValue === undefined) { return; } - - syncParentName(); - - })); - - eventBindings.push(scope.$watch('parentIcon', function(newValue, oldValue){ - - if (newValue === oldValue) { return; } - if ( oldValue === undefined || newValue === undefined) { return; } - - syncParentIcon(); - })); - - // clean up - scope.$on('$destroy', function(){ - // unbind watchers - for(var e in eventBindings) { - eventBindings[e](); - } - }); - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-child-selector.html', - scope: { - selectedChildren: '=', - availableChildren: "=", - parentName: "=", - parentIcon: "=", - parentId: "=", - onRemove: "=", - onAdd: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbChildSelector', ChildSelectorDirective); - -})(); +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbTreeSearchBox +* @function +* @element ANY +* @restrict E +**/ +function treeSearchBox(localizationService, searchService, $q) { + return { + scope: { + searchFromId: "@", + searchFromName: "@", + showSearch: "@", + section: "@", + hideSearchCallback: "=", + searchCallback: "=" + }, + restrict: "E", // restrict to an element + replace: true, // replace the html element with the template + templateUrl: 'views/components/tree/umb-tree-search-box.html', + link: function (scope, element, attrs, ctrl) { + + scope.term = ""; + scope.hideSearch = function() { + scope.term = ""; + scope.hideSearchCallback(); + }; + + localizationService.localize("general_typeToSearch").then(function (value) { + scope.searchPlaceholderText = value; + }); + + if (!scope.showSearch) { + scope.showSearch = "false"; + } + + //used to cancel any request in progress if another one needs to take it's place + var canceler = null; + + function performSearch() { + if (scope.term) { + scope.results = []; + + //a canceler exists, so perform the cancelation operation and reset + if (canceler) { + console.log("CANCELED!"); + canceler.resolve(); + canceler = $q.defer(); + } + else { + canceler = $q.defer(); + } + + var searchArgs = { + term: scope.term, + canceler: canceler + }; + + //append a start node context if there is one + if (scope.searchFromId) { + searchArgs["searchFrom"] = scope.searchFromId; + } + + searcher(searchArgs).then(function (data) { + scope.searchCallback(data); + //set back to null so it can be re-created + canceler = null; + }); + } + } + + scope.$watch("term", _.debounce(function(newVal, oldVal) { + scope.$apply(function() { + if (newVal !== null && newVal !== undefined && newVal !== oldVal) { + performSearch(); + } + }); + }, 200)); + + var searcher = searchService.searchContent; + //search + if (scope.section === "member") { + searcher = searchService.searchMembers; + } + else if (scope.section === "media") { + searcher = searchService.searchMedia; + } + } + }; +} +angular.module('umbraco.directives').directive("umbTreeSearchBox", treeSearchBox); + +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbTreeSearchResults +* @function +* @element ANY +* @restrict E +**/ +function treeSearchResults() { + return { + scope: { + results: "=", + selectResultCallback: "=" + }, + restrict: "E", // restrict to an element + replace: true, // replace the html element with the template + templateUrl: 'views/components/tree/umb-tree-search-results.html', + link: function (scope, element, attrs, ctrl) { + + } + }; +} +angular.module('umbraco.directives').directive("umbTreeSearchResults", treeSearchResults); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbGenerateAlias +@restrict E +@scope + +@description +Use this directive to generate a camelCased umbraco alias. +When the aliasFrom value is changed the directive will get a formatted alias from the server and update the alias model. If "enableLock" is set to true +the directive will use {@link umbraco.directives.directive:umbLockedField umbLockedField} to lock and unlock the alias. + +

Markup example

+
+    
+ + + + + + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+
+            vm.name = "";
+            vm.alias = "";
+
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+ +@param {string} alias (binding): The model where the alias is bound. +@param {string} aliasFrom (binding): The model to generate the alias from. +@param {boolean=} enableLock (binding): Set to true to add a lock next to the alias from where it can be unlocked and changed. +**/ + +angular.module("umbraco.directives") + .directive('umbGenerateAlias', function ($timeout, entityResource) { + return { + restrict: 'E', + templateUrl: 'views/components/umb-generate-alias.html', + replace: true, + scope: { + alias: '=', + aliasFrom: '=', + enableLock: '=?', + serverValidationField: '@' + }, + link: function (scope, element, attrs, ctrl) { + + var eventBindings = []; + var bindWatcher = true; + var generateAliasTimeout = ""; + var updateAlias = false; + + scope.locked = true; + scope.placeholderText = "Enter alias..."; + + function generateAlias(value) { + + if (generateAliasTimeout) { + $timeout.cancel(generateAliasTimeout); + } + + if( value !== undefined && value !== "" && value !== null) { + + scope.alias = ""; + scope.placeholderText = "Generating Alias..."; + + generateAliasTimeout = $timeout(function () { + updateAlias = true; + entityResource.getSafeAlias(value, true).then(function (safeAlias) { + if (updateAlias) { + scope.alias = safeAlias.alias; + } + }); + }, 500); + + } else { + updateAlias = true; + scope.alias = ""; + scope.placeholderText = "Enter alias..."; + } + + } + + // if alias gets unlocked - stop watching alias + eventBindings.push(scope.$watch('locked', function(newValue, oldValue){ + if(newValue === false) { + bindWatcher = false; + } + })); + + // validate custom entered alias + eventBindings.push(scope.$watch('alias', function(newValue, oldValue){ + + if(scope.alias === "" && bindWatcher === true || scope.alias === null && bindWatcher === true) { + // add watcher + eventBindings.push(scope.$watch('aliasFrom', function(newValue, oldValue) { + if(bindWatcher) { + generateAlias(newValue); + } + })); + } + + })); + + // clean up + scope.$on('$destroy', function(){ + // unbind watchers + for(var e in eventBindings) { + eventBindings[e](); + } + }); + + } + }; + }); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbChildSelector +@restrict E +@scope + +@description +Use this directive to render a ui component for selecting child items to a parent node. + +

Markup example

+
+	
+ + + + + + + + +
+
+ +

Controller example

+
+	(function () {
+		"use strict";
+
+		function Controller() {
+
+            var vm = this;
+
+            vm.id = 1;
+            vm.name = "My Parent element";
+            vm.icon = "icon-document";
+            vm.selectedChildren = [];
+            vm.availableChildren = [
+                {
+                    id: 1,
+                    alias: "item1",
+                    name: "Item 1",
+                    icon: "icon-document"
+                },
+                {
+                    id: 2,
+                    alias: "item2",
+                    name: "Item 2",
+                    icon: "icon-document"
+                }
+            ];
+
+            vm.addChild = addChild;
+            vm.removeChild = removeChild;
+
+            function addChild($event) {
+                vm.overlay = {
+                    view: "itempicker",
+                    title: "Choose child",
+                    availableItems: vm.availableChildren,
+                    selectedItems: vm.selectedChildren,
+                    event: $event,
+                    show: true,
+                    submit: function(model) {
+
+                        // add selected child
+                        vm.selectedChildren.push(model.selectedItem);
+
+                        // close overlay
+                        vm.overlay.show = false;
+                        vm.overlay = null;
+                    }
+                };
+            }
+
+            function removeChild($index) {
+                vm.selectedChildren.splice($index, 1);
+            }
+
+        }
+
+		angular.module("umbraco").controller("My.Controller", Controller);
+
+	})();
+
+ +@param {array} selectedChildren (binding): Array of selected children. +@param {array} availableChildren (binding: Array of items available for selection. +@param {string} parentName (binding): The parent name. +@param {string} parentIcon (binding): The parent icon. +@param {number} parentId (binding): The parent id. +@param {callback} onRemove (binding): Callback when the remove button is clicked on an item. +

The callback returns:

+
    +
  • child: The selected item.
  • +
  • $index: The selected item index.
  • +
+@param {callback} onAdd (binding): Callback when the add button is clicked. +

The callback returns:

+
    +
  • $event: The select event.
  • +
+**/ + +(function() { + 'use strict'; + + function ChildSelectorDirective() { + + function link(scope, el, attr, ctrl) { + + var eventBindings = []; + scope.dialogModel = {}; + scope.showDialog = false; + + scope.removeChild = function(selectedChild, $index) { + if(scope.onRemove) { + scope.onRemove(selectedChild, $index); + } + }; + + scope.addChild = function($event) { + if(scope.onAdd) { + scope.onAdd($event); + } + }; + + function syncParentName() { + + // update name on available item + angular.forEach(scope.availableChildren, function(availableChild){ + if(availableChild.id === scope.parentId) { + availableChild.name = scope.parentName; + } + }); + + // update name on selected child + angular.forEach(scope.selectedChildren, function(selectedChild){ + if(selectedChild.id === scope.parentId) { + selectedChild.name = scope.parentName; + } + }); + + } + + function syncParentIcon() { + + // update icon on available item + angular.forEach(scope.availableChildren, function(availableChild){ + if(availableChild.id === scope.parentId) { + availableChild.icon = scope.parentIcon; + } + }); + + // update icon on selected child + angular.forEach(scope.selectedChildren, function(selectedChild){ + if(selectedChild.id === scope.parentId) { + selectedChild.icon = scope.parentIcon; + } + }); + + } + + eventBindings.push(scope.$watch('parentName', function(newValue, oldValue){ + + if (newValue === oldValue) { return; } + if ( oldValue === undefined || newValue === undefined) { return; } + + syncParentName(); + + })); + + eventBindings.push(scope.$watch('parentIcon', function(newValue, oldValue){ + + if (newValue === oldValue) { return; } + if ( oldValue === undefined || newValue === undefined) { return; } + + syncParentIcon(); + })); + + // clean up + scope.$on('$destroy', function(){ + // unbind watchers + for(var e in eventBindings) { + eventBindings[e](); + } + }); + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-child-selector.html', + scope: { + selectedChildren: '=', + availableChildren: "=", + parentName: "=", + parentIcon: "=", + parentId: "=", + onRemove: "=", + onAdd: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbChildSelector', ChildSelectorDirective); + +})(); /** * @ngdoc directive @@ -6574,3047 +6574,3047 @@ function confirmDirective() { } angular.module('umbraco.directives').directive("umbConfirm", confirmDirective); -/** -@ngdoc directive -@name umbraco.directives.directive:umbConfirmAction -@restrict E -@scope - -@description -

Use this directive to toggle a confirmation prompt for an action. -The prompt consists of a checkmark and a cross to confirm or cancel the action. -The prompt can be opened in four direction up, down, left or right.

- -

Markup example

-
-    
- -
- - - -
- -
-
- -

Controller example

-
-    (function () {
-
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-            vm.promptIsVisible = false;
-
-            vm.confirmAction = confirmAction;
-            vm.showPrompt = showPrompt;
-            vm.hidePrompt = hidePrompt;
-
-            function confirmAction() {
-                // confirm logic here
-            }
-
-            function showPrompt() {
-                vm.promptIsVisible = true;
-            }
-
-            function hidePrompt() {
-                vm.promptIsVisible = false;
-            }
-
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-    })();
-
- -@param {string} direction The direction the prompt opens ("up", "down", "left", "right"). -@param {callback} onConfirm Callback when the checkmark is clicked. -@param {callback} onCancel Callback when the cross is clicked. -**/ - -(function() { - 'use strict'; - - function ConfirmAction() { - - function link(scope, el, attr, ctrl) { - - scope.clickConfirm = function() { - if(scope.onConfirm) { - scope.onConfirm(); - } - }; - - scope.clickCancel = function() { - if(scope.onCancel) { - scope.onCancel(); - } - }; - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-confirm-action.html', - scope: { - direction: "@", - onConfirm: "&", - onCancel: "&" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbConfirmAction', ConfirmAction); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbContentGrid -@restrict E -@scope - -@description -Use this directive to generate a list of content items presented as a flexbox grid. - -

Markup example

-
-    
- - - - -
-
- -

Controller example

-
-    (function () {
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-            vm.contentItems = [
-                {
-                    "name": "Cape",
-                    "published": true,
-                    "icon": "icon-document",
-                    "updateDate": "15-02-2016",
-                    "owner": "Mr. Batman",
-                    "selected": false
-                },
-                {
-                    "name": "Utility Belt",
-                    "published": true,
-                    "icon": "icon-document",
-                    "updateDate": "15-02-2016",
-                    "owner": "Mr. Batman",
-                    "selected": false
-                },
-                {
-                    "name": "Cave",
-                    "published": true,
-                    "icon": "icon-document",
-                    "updateDate": "15-02-2016",
-                    "owner": "Mr. Batman",
-                    "selected": false
-                }
-            ];
-            vm.includeProperties = [
-                {
-                  "alias": "updateDate",
-                  "header": "Last edited"
-                },
-                {
-                  "alias": "owner",
-                  "header": "Created by"
-                }
-            ];
-
-            vm.clickItem = clickItem;
-            vm.selectItem = selectItem;
-
-
-            function clickItem(item, $event, $index){
-                // do magic here
-            }
-
-            function selectItem(item, $event, $index) {
-                // set item.selected = true; to select the item
-                // do magic here
-            }
-
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-    })();
-
- -@param {array} content (binding): Array of content items. -@param {array=} contentProperties (binding): Array of content item properties to include in the item. If left empty the item will only show the item icon and name. -@param {callback=} onClick (binding): Callback method to handle click events on the content item. -

The callback returns:

-
    -
  • item: The clicked item
  • -
  • $event: The select event
  • -
  • $index: The item index
  • -
-@param {callback=} onClickName (binding): Callback method to handle click events on the checkmark icon. -

The callback returns:

-
    -
  • item: The selected item
  • -
  • $event: The select event
  • -
  • $index: The item index
  • -
-**/ - -(function() { - 'use strict'; - - function ContentGridDirective() { - - function link(scope, el, attr, ctrl) { - - scope.clickItem = function(item, $event, $index) { - if(scope.onClick) { - scope.onClick(item, $event, $index); - } - }; - - scope.clickItemName = function(item, $event, $index) { - if(scope.onClickName) { - scope.onClickName(item, $event, $index); - } - }; - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-content-grid.html', - scope: { - content: '=', - contentProperties: "=", - onClick: "=", - onClickName: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbContentGrid', ContentGridDirective); - -})(); - -(function() { - 'use strict'; - - function UmbDisableFormValidation() { - - var directive = { - restrict: 'A', - require: '?form', - link: function (scope, elm, attrs, ctrl) { - //override the $setValidity function of the form to disable validation - ctrl.$setValidity = function () { }; - } - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbDisableFormValidation', UmbDisableFormValidation); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbEmptyState -@restrict E -@scope - -@description -Use this directive to show an empty state message. - -

Markup example

-
-    
- - - // Empty state content - - -
-
- -@param {string=} size Set the size of the text ("small", "large"). -@param {string=} position Set the position of the text ("center"). -**/ - -(function() { - 'use strict'; - - function EmptyStateDirective() { - - var directive = { - restrict: 'E', - replace: true, - transclude: true, - templateUrl: 'views/components/umb-empty-state.html', - scope: { - size: '@', - position: '@' - } - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbEmptyState', EmptyStateDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbFolderGrid -@restrict E -@scope - -@description -Use this directive to generate a list of folders presented as a flexbox grid. - -

Markup example

-
-    
- - -
-
- -

Controller example

-
-    (function () {
-        "use strict";
-
-        function Controller(myService) {
-
-            var vm = this;
-            vm.folders = [
-                {
-                    "name": "Folder 1",
-                    "icon": "icon-folder",
-                    "selected": false
-                },
-                {
-                    "name": "Folder 2",
-                    "icon": "icon-folder",
-                    "selected": false
-                }
-
-            ];
-
-            vm.clickFolder = clickFolder;
-            vm.selectFolder = selectFolder;
-
-            myService.getFolders().then(function(folders){
-                vm.folders = folders;
-            });
-
-            function clickFolder(folder){
-                // Execute when clicking folder name/link
-            }
-
-            function selectFolder(folder, event, index) {
-                // Execute when clicking folder
-                // set folder.selected = true; to show checkmark icon
-            }
-
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-    })();
-
- -@param {array} folders (binding): Array of folders -@param {callback=} onClick (binding): Callback method to handle click events on the folder. -

The callback returns:

-
    -
  • folder: The selected folder
  • -
-@param {callback=} onSelect (binding): Callback method to handle click events on the checkmark icon. -

The callback returns:

-
    -
  • folder: The selected folder
  • -
  • $event: The select event
  • -
  • $index: The folder index
  • -
-**/ - -(function() { - 'use strict'; - - function FolderGridDirective() { - - function link(scope, el, attr, ctrl) { - - scope.clickFolder = function(folder, $event, $index) { - if(scope.onClick) { - scope.onClick(folder, $event, $index); - } - }; - - scope.clickFolderName = function(folder, $event, $index) { - if(scope.onClickName) { - scope.onClickName(folder, $event, $index); - $event.stopPropagation(); - } - }; - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-folder-grid.html', - scope: { - folders: '=', - onClick: "=", - onClickName: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbFolderGrid', FolderGridDirective); - -})(); - -(function() { - 'use strict'; - - function GridSelector() { - - function link(scope, el, attr, ctrl) { - - var eventBindings = []; - scope.dialogModel = {}; - scope.showDialog = false; - scope.itemLabel = ""; - - // set default item name - if(!scope.itemName){ - scope.itemLabel = "item"; - } else { - scope.itemLabel = scope.itemName; - } - - scope.removeItem = function(selectedItem) { - var selectedItemIndex = scope.selectedItems.indexOf(selectedItem); - scope.selectedItems.splice(selectedItemIndex, 1); - }; - - scope.removeDefaultItem = function() { - - // it will be the last item so we can clear the array - scope.selectedItems = []; - - // remove as default item - scope.defaultItem = null; - - }; - - scope.openItemPicker = function($event){ - scope.dialogModel = { - view: "itempicker", - title: "Choose " + scope.itemLabel, - availableItems: scope.availableItems, - selectedItems: scope.selectedItems, - event: $event, - show: true, - submit: function(model) { - scope.selectedItems.push(model.selectedItem); - - // if no default item - set item as default - if(scope.defaultItem === null) { - scope.setAsDefaultItem(model.selectedItem); - } - - scope.dialogModel.show = false; - scope.dialogModel = null; - } - }; - }; - - scope.setAsDefaultItem = function(selectedItem) { - - // clear default item - scope.defaultItem = {}; - - // set as default item - scope.defaultItem = selectedItem; - }; - - function updatePlaceholders() { - - // update default item - if(scope.defaultItem !== null && scope.defaultItem.placeholder) { - - scope.defaultItem.name = scope.name; - - if(scope.alias !== null && scope.alias !== undefined) { - scope.defaultItem.alias = scope.alias; - } - - } - - // update selected items - angular.forEach(scope.selectedItems, function(selectedItem) { - if(selectedItem.placeholder) { - - selectedItem.name = scope.name; - - if(scope.alias !== null && scope.alias !== undefined) { - selectedItem.alias = scope.alias; - } - - } - }); - - // update availableItems - angular.forEach(scope.availableItems, function(availableItem) { - if(availableItem.placeholder) { - - availableItem.name = scope.name; - - if(scope.alias !== null && scope.alias !== undefined) { - availableItem.alias = scope.alias; - } - - } - }); - - } - - function activate() { - - // add watchers for updating placeholde name and alias - if(scope.updatePlaceholder) { - eventBindings.push(scope.$watch('name', function(newValue, oldValue){ - updatePlaceholders(); - })); - - eventBindings.push(scope.$watch('alias', function(newValue, oldValue){ - updatePlaceholders(); - })); - } - - } - - activate(); - - // clean up - scope.$on('$destroy', function(){ - - // clear watchers - for(var e in eventBindings) { - eventBindings[e](); - } - - }); - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-grid-selector.html', - scope: { - name: "=", - alias: "=", - selectedItems: '=', - availableItems: "=", - defaultItem: "=", - itemName: "@", - updatePlaceholder: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbGridSelector', GridSelector); - -})(); - -(function() { - 'use strict'; - - function GroupsBuilderDirective(contentTypeHelper, contentTypeResource, mediaTypeResource, dataTypeHelper, dataTypeResource, $filter, iconHelper, $q, $timeout, notificationsService, localizationService) { - - function link(scope, el, attr, ctrl) { - - var validationTranslated = ""; - var tabNoSortOrderTranslated = ""; - - scope.sortingMode = false; - scope.toolbar = []; - scope.sortableOptionsGroup = {}; - scope.sortableOptionsProperty = {}; - scope.sortingButtonKey = "general_reorder"; - - function activate() { - - setSortingOptions(); - - // set placeholder property on each group - if (scope.model.groups.length !== 0) { - angular.forEach(scope.model.groups, function(group) { - addInitProperty(group); - }); - } - - // add init tab - addInitGroup(scope.model.groups); - - activateFirstGroup(scope.model.groups); - - // localize texts - localizationService.localize("validation_validation").then(function(value) { - validationTranslated = value; - }); - - localizationService.localize("contentTypeEditor_tabHasNoSortOrder").then(function(value) { - tabNoSortOrderTranslated = value; - }); - } - - function setSortingOptions() { - - scope.sortableOptionsGroup = { - distance: 10, - tolerance: "pointer", - opacity: 0.7, - scroll: true, - cursor: "move", - placeholder: "umb-group-builder__group-sortable-placeholder", - zIndex: 6000, - handle: ".umb-group-builder__group-handle", - items: ".umb-group-builder__group-sortable", - start: function(e, ui) { - ui.placeholder.height(ui.item.height()); - }, - stop: function(e, ui) { - updateTabsSortOrder(); - }, - }; - - scope.sortableOptionsProperty = { - distance: 10, - tolerance: "pointer", - connectWith: ".umb-group-builder__properties", - opacity: 0.7, - scroll: true, - cursor: "move", - placeholder: "umb-group-builder__property_sortable-placeholder", - zIndex: 6000, - handle: ".umb-group-builder__property-handle", - items: ".umb-group-builder__property-sortable", - start: function(e, ui) { - ui.placeholder.height(ui.item.height()); - }, - stop: function(e, ui) { - updatePropertiesSortOrder(); - } - }; - - } - - function updateTabsSortOrder() { - - var first = true; - var prevSortOrder = 0; - - scope.model.groups.map(function(group){ - - var index = scope.model.groups.indexOf(group); - - if(group.tabState !== "init") { - - // set the first not inherited tab to sort order 0 - if(!group.inherited && first) { - - // set the first tab sort order to 0 if prev is 0 - if( prevSortOrder === 0 ) { - group.sortOrder = 0; - // when the first tab is inherited and sort order is not 0 - } else { - group.sortOrder = prevSortOrder + 1; - } - - first = false; - - } else if(!group.inherited && !first) { - - // find next group - var nextGroup = scope.model.groups[index + 1]; - - // if a groups is dropped in the middle of to groups with - // same sort order. Give it the dropped group same sort order - if( prevSortOrder === nextGroup.sortOrder ) { - group.sortOrder = prevSortOrder; - } else { - group.sortOrder = prevSortOrder + 1; - } - - } - - // store this tabs sort order as reference for the next - prevSortOrder = group.sortOrder; - - } - - }); - - } - - function filterAvailableCompositions(selectedContentType, selecting) { - - //selecting = true if the user has check the item, false if the user has unchecked the item - - var selectedContentTypeAliases = selecting ? - //the user has selected the item so add to the current list - _.union(scope.compositionsDialogModel.compositeContentTypes, [selectedContentType.alias]) : - //the user has unselected the item so remove from the current list - _.reject(scope.compositionsDialogModel.compositeContentTypes, function(i) { - return i === selectedContentType.alias; - }); - - //get the currently assigned property type aliases - ensure we pass these to the server side filer - var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) { - return _.map(g.properties, function(p) { - return p.alias; - }); - })), function (f) { - return f !== null && f !== undefined; - }); - - //use a different resource lookup depending on the content type type - var resourceLookup = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes; - - return resourceLookup(scope.model.id, selectedContentTypeAliases, propAliasesExisting).then(function (filteredAvailableCompositeTypes) { - _.each(scope.compositionsDialogModel.availableCompositeContentTypes, function (current) { - //reset first - current.allowed = true; - //see if this list item is found in the response (allowed) list - var found = _.find(filteredAvailableCompositeTypes, function (f) { - return current.contentType.alias === f.contentType.alias; - }); - - //allow if the item was found in the response (allowed) list - - // and ensure its set to allowed if it is currently checked, - // DO not allow if it's a locked content type. - current.allowed = scope.model.lockedCompositeContentTypes.indexOf(current.contentType.alias) === -1 && - (selectedContentTypeAliases.indexOf(current.contentType.alias) !== -1) || ((found !== null && found !== undefined) ? found.allowed : false); - - }); - }); - } - - function updatePropertiesSortOrder() { - - angular.forEach(scope.model.groups, function(group){ - if( group.tabState !== "init" ) { - group.properties = contentTypeHelper.updatePropertiesSortOrder(group.properties); - } - }); - - } - - function setupAvailableContentTypesModel(result) { - scope.compositionsDialogModel.availableCompositeContentTypes = result; - //iterate each one and set it up - _.each(scope.compositionsDialogModel.availableCompositeContentTypes, function (c) { - //enable it if it's part of the selected model - if (scope.compositionsDialogModel.compositeContentTypes.indexOf(c.contentType.alias) !== -1) { - c.allowed = true; - } - - //set the inherited flags - c.inherited = false; - if (scope.model.lockedCompositeContentTypes.indexOf(c.contentType.alias) > -1) { - c.inherited = true; - } - // convert icons for composite content types - iconHelper.formatContentTypeIcons([c.contentType]); - }); - } - - /* ---------- DELETE PROMT ---------- */ - - scope.togglePrompt = function (object) { - object.deletePrompt = !object.deletePrompt; - }; - - scope.hidePrompt = function (object) { - object.deletePrompt = false; - }; - - /* ---------- TOOLBAR ---------- */ - - scope.toggleSortingMode = function(tool) { - - if (scope.sortingMode === true) { - - var sortOrderMissing = false; - - for (var i = 0; i < scope.model.groups.length; i++) { - var group = scope.model.groups[i]; - if (group.tabState !== "init" && group.sortOrder === undefined) { - sortOrderMissing = true; - group.showSortOrderMissing = true; - notificationsService.error(validationTranslated + ": " + group.name + " " + tabNoSortOrderTranslated); - } - } - - if (!sortOrderMissing) { - scope.sortingMode = false; - scope.sortingButtonKey = "general_reorder"; - } - - } else { - - scope.sortingMode = true; - scope.sortingButtonKey = "general_reorderDone"; - - } - - }; - - scope.openCompositionsDialog = function() { - - scope.compositionsDialogModel = { - title: "Compositions", - contentType: scope.model, - compositeContentTypes: scope.model.compositeContentTypes, - view: "views/common/overlays/contenttypeeditor/compositions/compositions.html", - confirmSubmit: { - title: "Warning", - description: "Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?", - checkboxLabel: "I know what I'm doing", - enable: true - }, - submit: function(model, oldModel, confirmed) { - - var compositionRemoved = false; - - // check if any compositions has been removed - for(var i = 0; oldModel.compositeContentTypes.length > i; i++) { - - var oldComposition = oldModel.compositeContentTypes[i]; - - if(_.contains(model.compositeContentTypes, oldComposition) === false) { - compositionRemoved = true; - } - - } - - // show overlay confirm box if compositions has been removed. - if(compositionRemoved && confirmed === false) { - - scope.compositionsDialogModel.confirmSubmit.show = true; - - // submit overlay if no compositions has been removed - // or the action has been confirmed - } else { - - // make sure that all tabs has an init property - if (scope.model.groups.length !== 0) { - angular.forEach(scope.model.groups, function(group) { - addInitProperty(group); - }); - } - - // remove overlay - scope.compositionsDialogModel.show = false; - scope.compositionsDialogModel = null; - } - - }, - close: function(oldModel) { - - // reset composition changes - scope.model.groups = oldModel.contentType.groups; - scope.model.compositeContentTypes = oldModel.contentType.compositeContentTypes; - - // remove overlay - scope.compositionsDialogModel.show = false; - scope.compositionsDialogModel = null; - - }, - selectCompositeContentType: function (selectedContentType) { - - //first check if this is a new selection - we need to store this value here before any further digests/async - // because after that the scope.model.compositeContentTypes will be populated with the selected value. - var newSelection = scope.model.compositeContentTypes.indexOf(selectedContentType.alias) === -1; - - if (newSelection) { - //merge composition with content type - - //use a different resource lookup depending on the content type type - var resourceLookup = scope.contentType === "documentType" ? contentTypeResource.getById : mediaTypeResource.getById; - - resourceLookup(selectedContentType.id).then(function (composition) { - //based on the above filtering we shouldn't be able to select an invalid one, but let's be safe and - // double check here. - var overlappingAliases = contentTypeHelper.validateAddingComposition(scope.model, composition); - if (overlappingAliases.length > 0) { - //this will create an invalid composition, need to uncheck it - scope.compositionsDialogModel.compositeContentTypes.splice( - scope.compositionsDialogModel.compositeContentTypes.indexOf(composition.alias), 1); - //dissallow this until something else is unchecked - selectedContentType.allowed = false; - } - else { - contentTypeHelper.mergeCompositeContentType(scope.model, composition); - } - - //based on the selection, we need to filter the available composite types list - filterAvailableCompositions(selectedContentType, newSelection).then(function () { - //TODO: Here we could probably re-enable selection if we previously showed a throbber or something - }); - }); - } - else { - // split composition from content type - contentTypeHelper.splitCompositeContentType(scope.model, selectedContentType); - - //based on the selection, we need to filter the available composite types list - filterAvailableCompositions(selectedContentType, newSelection).then(function () { - //TODO: Here we could probably re-enable selection if we previously showed a throbber or something - }); - } - - } - }; - - var availableContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes; - var countContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getCount : mediaTypeResource.getCount; - - //get the currently assigned property type aliases - ensure we pass these to the server side filer - var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) { - return _.map(g.properties, function(p) { - return p.alias; - }); - })), function(f) { - return f !== null && f !== undefined; - }); - $q.all([ - //get available composite types - availableContentTypeResource(scope.model.id, [], propAliasesExisting).then(function (result) { - setupAvailableContentTypesModel(result); - }), - //get content type count - countContentTypeResource().then(function(result) { - scope.compositionsDialogModel.totalContentTypes = parseInt(result, 10); - }) - ]).then(function() { - //resolves when both other promises are done, now show it - scope.compositionsDialogModel.show = true; - }); - - }; - - - /* ---------- GROUPS ---------- */ - - scope.addGroup = function(group) { - - // set group sort order - var index = scope.model.groups.indexOf(group); - var prevGroup = scope.model.groups[index - 1]; - - if( index > 0) { - // set index to 1 higher than the previous groups sort order - group.sortOrder = prevGroup.sortOrder + 1; - - } else { - // first group - sort order will be 0 - group.sortOrder = 0; - } - - // activate group - scope.activateGroup(group); - - }; - - scope.activateGroup = function(selectedGroup) { - - // set all other groups that are inactive to active - angular.forEach(scope.model.groups, function(group) { - // skip init tab - if (group.tabState !== "init") { - group.tabState = "inActive"; - } - }); - - selectedGroup.tabState = "active"; - - }; - - scope.removeGroup = function(groupIndex) { - scope.model.groups.splice(groupIndex, 1); - addInitGroup(scope.model.groups); - }; - - scope.updateGroupTitle = function(group) { - if (group.properties.length === 0) { - addInitProperty(group); - } - }; - - scope.changeSortOrderValue = function(group) { - - if (group.sortOrder !== undefined) { - group.showSortOrderMissing = false; - } - scope.model.groups = $filter('orderBy')(scope.model.groups, 'sortOrder'); - }; - - function addInitGroup(groups) { - - // check i init tab already exists - var addGroup = true; - - angular.forEach(groups, function(group) { - if (group.tabState === "init") { - addGroup = false; - } - }); - - if (addGroup) { - groups.push({ - properties: [], - parentTabContentTypes: [], - parentTabContentTypeNames: [], - name: "", - tabState: "init" - }); - } - - return groups; - } - - function activateFirstGroup(groups) { - if (groups && groups.length > 0) { - var firstGroup = groups[0]; - if(!firstGroup.tabState || firstGroup.tabState === "inActive") { - firstGroup.tabState = "active"; - } - } - } - - /* ---------- PROPERTIES ---------- */ - - scope.addProperty = function(property, group) { - - // set property sort order - var index = group.properties.indexOf(property); - var prevProperty = group.properties[index - 1]; - - if( index > 0) { - // set index to 1 higher than the previous property sort order - property.sortOrder = prevProperty.sortOrder + 1; - - } else { - // first property - sort order will be 0 - property.sortOrder = 0; - } - - // open property settings dialog - scope.editPropertyTypeSettings(property, group); - - }; - - scope.editPropertyTypeSettings = function(property, group) { - - if (!property.inherited && !property.locked) { - - scope.propertySettingsDialogModel = {}; - scope.propertySettingsDialogModel.title = "Property settings"; - scope.propertySettingsDialogModel.property = property; - scope.propertySettingsDialogModel.contentType = scope.contentType; - scope.propertySettingsDialogModel.contentTypeName = scope.model.name; - scope.propertySettingsDialogModel.view = "views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html"; - scope.propertySettingsDialogModel.show = true; - - // set state to active to access the preview - property.propertyState = "active"; - - // set property states - property.dialogIsOpen = true; - - scope.propertySettingsDialogModel.submit = function(model) { - - property.inherited = false; - property.dialogIsOpen = false; - - // update existing data types - if(model.updateSameDataTypes) { - updateSameDataTypes(property); - } - - // remove dialog - scope.propertySettingsDialogModel.show = false; - scope.propertySettingsDialogModel = null; - - // push new init property to group - addInitProperty(group); - - // set focus on init property - var numberOfProperties = group.properties.length; - group.properties[numberOfProperties - 1].focus = true; - - // push new init tab to the scope - addInitGroup(scope.model.groups); - - }; - - scope.propertySettingsDialogModel.close = function(oldModel) { - - // reset all property changes - property.label = oldModel.property.label; - property.alias = oldModel.property.alias; - property.description = oldModel.property.description; - property.config = oldModel.property.config; - property.editor = oldModel.property.editor; - property.view = oldModel.property.view; - property.dataTypeId = oldModel.property.dataTypeId; - property.dataTypeIcon = oldModel.property.dataTypeIcon; - property.dataTypeName = oldModel.property.dataTypeName; - property.validation.mandatory = oldModel.property.validation.mandatory; - property.validation.pattern = oldModel.property.validation.pattern; - property.showOnMemberProfile = oldModel.property.showOnMemberProfile; - property.memberCanEdit = oldModel.property.memberCanEdit; - - // because we set state to active, to show a preview, we have to check if has been filled out - // label is required so if it is not filled we know it is a placeholder - if(oldModel.property.editor === undefined || oldModel.property.editor === null || oldModel.property.editor === "") { - property.propertyState = "init"; - } else { - property.propertyState = oldModel.property.propertyState; - } - - // remove dialog - scope.propertySettingsDialogModel.show = false; - scope.propertySettingsDialogModel = null; - - }; - - } - }; - - scope.deleteProperty = function(tab, propertyIndex) { - - // remove property - tab.properties.splice(propertyIndex, 1); - - // if the last property in group is an placeholder - remove add new tab placeholder - if(tab.properties.length === 1 && tab.properties[0].propertyState === "init") { - - angular.forEach(scope.model.groups, function(group, index, groups){ - if(group.tabState === 'init') { - groups.splice(index, 1); - } - }); - - } - - }; - - function addInitProperty(group) { - - var addInitPropertyBool = true; - var initProperty = { - label: null, - alias: null, - propertyState: "init", - validation: { - mandatory: false, - pattern: null - } - }; - - // check if there already is an init property - angular.forEach(group.properties, function(property) { - if (property.propertyState === "init") { - addInitPropertyBool = false; - } - }); - - if (addInitPropertyBool) { - group.properties.push(initProperty); - } - - return group; - } - - function updateSameDataTypes(newProperty) { - - // find each property - angular.forEach(scope.model.groups, function(group){ - angular.forEach(group.properties, function(property){ - - if(property.dataTypeId === newProperty.dataTypeId) { - - // update property data - property.config = newProperty.config; - property.editor = newProperty.editor; - property.view = newProperty.view; - property.dataTypeId = newProperty.dataTypeId; - property.dataTypeIcon = newProperty.dataTypeIcon; - property.dataTypeName = newProperty.dataTypeName; - - } - - }); - }); - } - - - var unbindModelWatcher = scope.$watch('model', function(newValue, oldValue) { - if (newValue !== undefined && newValue.groups !== undefined) { - activate(); - } - }); - - // clean up - scope.$on('$destroy', function(){ - unbindModelWatcher(); - }); - - } - - var directive = { - restrict: "E", - replace: true, - templateUrl: "views/components/umb-groups-builder.html", - scope: { - model: "=", - compositions: "=", - sorting: "=", - contentType: "@" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbGroupsBuilder', GroupsBuilderDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbkeyboardShortcutsOverview -@restrict E -@scope - -@description - -

Use this directive to show an overview of keyboard shortcuts in an editor. -The directive will render an overview trigger wich shows how the overview is opened. -When this combination is hit an overview is opened with shortcuts based on the model sent to the directive.

- -

Markup example

-
-    
- - - - -
-
- -

Controller example

-
-    (function () {
-
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-
-            vm.keyboardShortcutsOverview = [
-                {
-                    "name": "Sections",
-                    "shortcuts": [
-                        {
-                            "description": "Navigate sections",
-                            "keys": [
-                                {"key": "1"},
-                                {"key": "4"}
-                            ],
-                            "keyRange": true
-                        }
-                    ]
-                },
-                {
-                    "name": "Design",
-                    "shortcuts": [
-                        {
-                            "description": "Add tab",
-                            "keys": [
-                                {"key": "alt"},
-                                {"key": "shift"},
-                                {"key": "t"}
-                            ]
-                        }
-                    ]
-                }
-            ];
-
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-    })();
-
- -

Model description

-
    -
  • - name - (string) - - Sets the shortcut section name. -
  • -
  • - shortcuts - (array) - - Array of available shortcuts in the section. -
  • -
      -
    • - description - (string) - - Short description of the shortcut. -
    • -
    • - keys - (array) - - Array of keys in the shortcut. -
    • -
        -
      • - key - (string) - - The invidual key in the shortcut. -
      • -
      -
    • - keyRange - (boolean) - - Set to true to show a key range. It combines the shortcut keys with "-" instead of "+". -
    • -
    -
- -@param {object} model keyboard shortcut model. See description and example above. -**/ - -(function() { - 'use strict'; - - function KeyboardShortcutsOverviewDirective() { - - function link(scope, el, attr, ctrl) { - - scope.shortcutOverlay = false; - - scope.toggleShortcutsOverlay = function() { - scope.shortcutOverlay = !scope.shortcutOverlay; - }; - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-keyboard-shortcuts-overview.html', - link: link, - scope: { - model: "=" - } - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbKeyboardShortcutsOverview', KeyboardShortcutsOverviewDirective); - -})(); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbLaunchMiniEditor -* @restrict E -* @function -* @description -* Used on a button to launch a mini content editor editor dialog -**/ -angular.module("umbraco.directives") - .directive('umbLaunchMiniEditor', function (dialogService, editorState, fileManager, contentEditingHelper) { - return { - restrict: 'A', - replace: false, - scope: { - node: '=umbLaunchMiniEditor', - }, - link: function(scope, element, attrs) { - - var launched = false; - - element.click(function() { - - if (launched === true) { - return; - } - - launched = true; - - //We need to store the current files selected in the file manager locally because the fileManager - // is a singleton and is shared globally. The mini dialog will also be referencing the fileManager - // and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here, - // clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state. - var currFiles = _.groupBy(fileManager.getFiles(), "alias"); - fileManager.clearFiles(); - - //We need to store the original editorState entity because it will need to change when the mini editor is loaded so that - // any property editors that are working with editorState get given the correct entity, otherwise strange things will - // start happening. - var currEditorState = editorState.getCurrent(); - - dialogService.open({ - template: "views/common/dialogs/content/edit.html", - id: scope.node.id, - closeOnSave: true, - tabFilter: ["Generic properties"], - callback: function (data) { - - //set the node name back - scope.node.name = data.name; - - //reset the fileManager to what it was - fileManager.clearFiles(); - _.each(currFiles, function (val, key) { - fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); - }); - - //reset the editor state - editorState.set(currEditorState); - - //Now we need to check if the content item that was edited was actually the same content item - // as the main content editor and if so, update all property data - if (data.id === currEditorState.id) { - var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data); - } - - launched = false; - }, - closeCallback: function () { - //reset the fileManager to what it was - fileManager.clearFiles(); - _.each(currFiles, function (val, key) { - fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); - }); - - //reset the editor state - editorState.set(currEditorState); - - launched = false; - } - }); - - }); - - } - }; +/** +@ngdoc directive +@name umbraco.directives.directive:umbConfirmAction +@restrict E +@scope + +@description +

Use this directive to toggle a confirmation prompt for an action. +The prompt consists of a checkmark and a cross to confirm or cancel the action. +The prompt can be opened in four direction up, down, left or right.

+ +

Markup example

+
+    
+ +
+ + + +
+ +
+
+ +

Controller example

+
+    (function () {
+
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+            vm.promptIsVisible = false;
+
+            vm.confirmAction = confirmAction;
+            vm.showPrompt = showPrompt;
+            vm.hidePrompt = hidePrompt;
+
+            function confirmAction() {
+                // confirm logic here
+            }
+
+            function showPrompt() {
+                vm.promptIsVisible = true;
+            }
+
+            function hidePrompt() {
+                vm.promptIsVisible = false;
+            }
+
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+ +@param {string} direction The direction the prompt opens ("up", "down", "left", "right"). +@param {callback} onConfirm Callback when the checkmark is clicked. +@param {callback} onCancel Callback when the cross is clicked. +**/ + +(function() { + 'use strict'; + + function ConfirmAction() { + + function link(scope, el, attr, ctrl) { + + scope.clickConfirm = function() { + if(scope.onConfirm) { + scope.onConfirm(); + } + }; + + scope.clickCancel = function() { + if(scope.onCancel) { + scope.onCancel(); + } + }; + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-confirm-action.html', + scope: { + direction: "@", + onConfirm: "&", + onCancel: "&" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbConfirmAction', ConfirmAction); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbContentGrid +@restrict E +@scope + +@description +Use this directive to generate a list of content items presented as a flexbox grid. + +

Markup example

+
+    
+ + + + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+            vm.contentItems = [
+                {
+                    "name": "Cape",
+                    "published": true,
+                    "icon": "icon-document",
+                    "updateDate": "15-02-2016",
+                    "owner": "Mr. Batman",
+                    "selected": false
+                },
+                {
+                    "name": "Utility Belt",
+                    "published": true,
+                    "icon": "icon-document",
+                    "updateDate": "15-02-2016",
+                    "owner": "Mr. Batman",
+                    "selected": false
+                },
+                {
+                    "name": "Cave",
+                    "published": true,
+                    "icon": "icon-document",
+                    "updateDate": "15-02-2016",
+                    "owner": "Mr. Batman",
+                    "selected": false
+                }
+            ];
+            vm.includeProperties = [
+                {
+                  "alias": "updateDate",
+                  "header": "Last edited"
+                },
+                {
+                  "alias": "owner",
+                  "header": "Created by"
+                }
+            ];
+
+            vm.clickItem = clickItem;
+            vm.selectItem = selectItem;
+
+
+            function clickItem(item, $event, $index){
+                // do magic here
+            }
+
+            function selectItem(item, $event, $index) {
+                // set item.selected = true; to select the item
+                // do magic here
+            }
+
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+ +@param {array} content (binding): Array of content items. +@param {array=} contentProperties (binding): Array of content item properties to include in the item. If left empty the item will only show the item icon and name. +@param {callback=} onClick (binding): Callback method to handle click events on the content item. +

The callback returns:

+
    +
  • item: The clicked item
  • +
  • $event: The select event
  • +
  • $index: The item index
  • +
+@param {callback=} onClickName (binding): Callback method to handle click events on the checkmark icon. +

The callback returns:

+
    +
  • item: The selected item
  • +
  • $event: The select event
  • +
  • $index: The item index
  • +
+**/ + +(function() { + 'use strict'; + + function ContentGridDirective() { + + function link(scope, el, attr, ctrl) { + + scope.clickItem = function(item, $event, $index) { + if(scope.onClick) { + scope.onClick(item, $event, $index); + } + }; + + scope.clickItemName = function(item, $event, $index) { + if(scope.onClickName) { + scope.onClickName(item, $event, $index); + } + }; + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-content-grid.html', + scope: { + content: '=', + contentProperties: "=", + onClick: "=", + onClickName: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbContentGrid', ContentGridDirective); + +})(); + +(function() { + 'use strict'; + + function UmbDisableFormValidation() { + + var directive = { + restrict: 'A', + require: '?form', + link: function (scope, elm, attrs, ctrl) { + //override the $setValidity function of the form to disable validation + ctrl.$setValidity = function () { }; + } + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbDisableFormValidation', UmbDisableFormValidation); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbEmptyState +@restrict E +@scope + +@description +Use this directive to show an empty state message. + +

Markup example

+
+    
+ + + // Empty state content + + +
+
+ +@param {string=} size Set the size of the text ("small", "large"). +@param {string=} position Set the position of the text ("center"). +**/ + +(function() { + 'use strict'; + + function EmptyStateDirective() { + + var directive = { + restrict: 'E', + replace: true, + transclude: true, + templateUrl: 'views/components/umb-empty-state.html', + scope: { + size: '@', + position: '@' + } + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEmptyState', EmptyStateDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbFolderGrid +@restrict E +@scope + +@description +Use this directive to generate a list of folders presented as a flexbox grid. + +

Markup example

+
+    
+ + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller(myService) {
+
+            var vm = this;
+            vm.folders = [
+                {
+                    "name": "Folder 1",
+                    "icon": "icon-folder",
+                    "selected": false
+                },
+                {
+                    "name": "Folder 2",
+                    "icon": "icon-folder",
+                    "selected": false
+                }
+
+            ];
+
+            vm.clickFolder = clickFolder;
+            vm.selectFolder = selectFolder;
+
+            myService.getFolders().then(function(folders){
+                vm.folders = folders;
+            });
+
+            function clickFolder(folder){
+                // Execute when clicking folder name/link
+            }
+
+            function selectFolder(folder, event, index) {
+                // Execute when clicking folder
+                // set folder.selected = true; to show checkmark icon
+            }
+
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+ +@param {array} folders (binding): Array of folders +@param {callback=} onClick (binding): Callback method to handle click events on the folder. +

The callback returns:

+
    +
  • folder: The selected folder
  • +
+@param {callback=} onSelect (binding): Callback method to handle click events on the checkmark icon. +

The callback returns:

+
    +
  • folder: The selected folder
  • +
  • $event: The select event
  • +
  • $index: The folder index
  • +
+**/ + +(function() { + 'use strict'; + + function FolderGridDirective() { + + function link(scope, el, attr, ctrl) { + + scope.clickFolder = function(folder, $event, $index) { + if(scope.onClick) { + scope.onClick(folder, $event, $index); + } + }; + + scope.clickFolderName = function(folder, $event, $index) { + if(scope.onClickName) { + scope.onClickName(folder, $event, $index); + $event.stopPropagation(); + } + }; + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-folder-grid.html', + scope: { + folders: '=', + onClick: "=", + onClickName: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbFolderGrid', FolderGridDirective); + +})(); + +(function() { + 'use strict'; + + function GridSelector() { + + function link(scope, el, attr, ctrl) { + + var eventBindings = []; + scope.dialogModel = {}; + scope.showDialog = false; + scope.itemLabel = ""; + + // set default item name + if(!scope.itemName){ + scope.itemLabel = "item"; + } else { + scope.itemLabel = scope.itemName; + } + + scope.removeItem = function(selectedItem) { + var selectedItemIndex = scope.selectedItems.indexOf(selectedItem); + scope.selectedItems.splice(selectedItemIndex, 1); + }; + + scope.removeDefaultItem = function() { + + // it will be the last item so we can clear the array + scope.selectedItems = []; + + // remove as default item + scope.defaultItem = null; + + }; + + scope.openItemPicker = function($event){ + scope.dialogModel = { + view: "itempicker", + title: "Choose " + scope.itemLabel, + availableItems: scope.availableItems, + selectedItems: scope.selectedItems, + event: $event, + show: true, + submit: function(model) { + scope.selectedItems.push(model.selectedItem); + + // if no default item - set item as default + if(scope.defaultItem === null) { + scope.setAsDefaultItem(model.selectedItem); + } + + scope.dialogModel.show = false; + scope.dialogModel = null; + } + }; + }; + + scope.setAsDefaultItem = function(selectedItem) { + + // clear default item + scope.defaultItem = {}; + + // set as default item + scope.defaultItem = selectedItem; + }; + + function updatePlaceholders() { + + // update default item + if(scope.defaultItem !== null && scope.defaultItem.placeholder) { + + scope.defaultItem.name = scope.name; + + if(scope.alias !== null && scope.alias !== undefined) { + scope.defaultItem.alias = scope.alias; + } + + } + + // update selected items + angular.forEach(scope.selectedItems, function(selectedItem) { + if(selectedItem.placeholder) { + + selectedItem.name = scope.name; + + if(scope.alias !== null && scope.alias !== undefined) { + selectedItem.alias = scope.alias; + } + + } + }); + + // update availableItems + angular.forEach(scope.availableItems, function(availableItem) { + if(availableItem.placeholder) { + + availableItem.name = scope.name; + + if(scope.alias !== null && scope.alias !== undefined) { + availableItem.alias = scope.alias; + } + + } + }); + + } + + function activate() { + + // add watchers for updating placeholde name and alias + if(scope.updatePlaceholder) { + eventBindings.push(scope.$watch('name', function(newValue, oldValue){ + updatePlaceholders(); + })); + + eventBindings.push(scope.$watch('alias', function(newValue, oldValue){ + updatePlaceholders(); + })); + } + + } + + activate(); + + // clean up + scope.$on('$destroy', function(){ + + // clear watchers + for(var e in eventBindings) { + eventBindings[e](); + } + + }); + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-grid-selector.html', + scope: { + name: "=", + alias: "=", + selectedItems: '=', + availableItems: "=", + defaultItem: "=", + itemName: "@", + updatePlaceholder: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbGridSelector', GridSelector); + +})(); + +(function() { + 'use strict'; + + function GroupsBuilderDirective(contentTypeHelper, contentTypeResource, mediaTypeResource, dataTypeHelper, dataTypeResource, $filter, iconHelper, $q, $timeout, notificationsService, localizationService) { + + function link(scope, el, attr, ctrl) { + + var validationTranslated = ""; + var tabNoSortOrderTranslated = ""; + + scope.sortingMode = false; + scope.toolbar = []; + scope.sortableOptionsGroup = {}; + scope.sortableOptionsProperty = {}; + scope.sortingButtonKey = "general_reorder"; + + function activate() { + + setSortingOptions(); + + // set placeholder property on each group + if (scope.model.groups.length !== 0) { + angular.forEach(scope.model.groups, function(group) { + addInitProperty(group); + }); + } + + // add init tab + addInitGroup(scope.model.groups); + + activateFirstGroup(scope.model.groups); + + // localize texts + localizationService.localize("validation_validation").then(function(value) { + validationTranslated = value; + }); + + localizationService.localize("contentTypeEditor_tabHasNoSortOrder").then(function(value) { + tabNoSortOrderTranslated = value; + }); + } + + function setSortingOptions() { + + scope.sortableOptionsGroup = { + distance: 10, + tolerance: "pointer", + opacity: 0.7, + scroll: true, + cursor: "move", + placeholder: "umb-group-builder__group-sortable-placeholder", + zIndex: 6000, + handle: ".umb-group-builder__group-handle", + items: ".umb-group-builder__group-sortable", + start: function(e, ui) { + ui.placeholder.height(ui.item.height()); + }, + stop: function(e, ui) { + updateTabsSortOrder(); + }, + }; + + scope.sortableOptionsProperty = { + distance: 10, + tolerance: "pointer", + connectWith: ".umb-group-builder__properties", + opacity: 0.7, + scroll: true, + cursor: "move", + placeholder: "umb-group-builder__property_sortable-placeholder", + zIndex: 6000, + handle: ".umb-group-builder__property-handle", + items: ".umb-group-builder__property-sortable", + start: function(e, ui) { + ui.placeholder.height(ui.item.height()); + }, + stop: function(e, ui) { + updatePropertiesSortOrder(); + } + }; + + } + + function updateTabsSortOrder() { + + var first = true; + var prevSortOrder = 0; + + scope.model.groups.map(function(group){ + + var index = scope.model.groups.indexOf(group); + + if(group.tabState !== "init") { + + // set the first not inherited tab to sort order 0 + if(!group.inherited && first) { + + // set the first tab sort order to 0 if prev is 0 + if( prevSortOrder === 0 ) { + group.sortOrder = 0; + // when the first tab is inherited and sort order is not 0 + } else { + group.sortOrder = prevSortOrder + 1; + } + + first = false; + + } else if(!group.inherited && !first) { + + // find next group + var nextGroup = scope.model.groups[index + 1]; + + // if a groups is dropped in the middle of to groups with + // same sort order. Give it the dropped group same sort order + if( prevSortOrder === nextGroup.sortOrder ) { + group.sortOrder = prevSortOrder; + } else { + group.sortOrder = prevSortOrder + 1; + } + + } + + // store this tabs sort order as reference for the next + prevSortOrder = group.sortOrder; + + } + + }); + + } + + function filterAvailableCompositions(selectedContentType, selecting) { + + //selecting = true if the user has check the item, false if the user has unchecked the item + + var selectedContentTypeAliases = selecting ? + //the user has selected the item so add to the current list + _.union(scope.compositionsDialogModel.compositeContentTypes, [selectedContentType.alias]) : + //the user has unselected the item so remove from the current list + _.reject(scope.compositionsDialogModel.compositeContentTypes, function(i) { + return i === selectedContentType.alias; + }); + + //get the currently assigned property type aliases - ensure we pass these to the server side filer + var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) { + return _.map(g.properties, function(p) { + return p.alias; + }); + })), function (f) { + return f !== null && f !== undefined; + }); + + //use a different resource lookup depending on the content type type + var resourceLookup = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes; + + return resourceLookup(scope.model.id, selectedContentTypeAliases, propAliasesExisting).then(function (filteredAvailableCompositeTypes) { + _.each(scope.compositionsDialogModel.availableCompositeContentTypes, function (current) { + //reset first + current.allowed = true; + //see if this list item is found in the response (allowed) list + var found = _.find(filteredAvailableCompositeTypes, function (f) { + return current.contentType.alias === f.contentType.alias; + }); + + //allow if the item was found in the response (allowed) list - + // and ensure its set to allowed if it is currently checked, + // DO not allow if it's a locked content type. + current.allowed = scope.model.lockedCompositeContentTypes.indexOf(current.contentType.alias) === -1 && + (selectedContentTypeAliases.indexOf(current.contentType.alias) !== -1) || ((found !== null && found !== undefined) ? found.allowed : false); + + }); + }); + } + + function updatePropertiesSortOrder() { + + angular.forEach(scope.model.groups, function(group){ + if( group.tabState !== "init" ) { + group.properties = contentTypeHelper.updatePropertiesSortOrder(group.properties); + } + }); + + } + + function setupAvailableContentTypesModel(result) { + scope.compositionsDialogModel.availableCompositeContentTypes = result; + //iterate each one and set it up + _.each(scope.compositionsDialogModel.availableCompositeContentTypes, function (c) { + //enable it if it's part of the selected model + if (scope.compositionsDialogModel.compositeContentTypes.indexOf(c.contentType.alias) !== -1) { + c.allowed = true; + } + + //set the inherited flags + c.inherited = false; + if (scope.model.lockedCompositeContentTypes.indexOf(c.contentType.alias) > -1) { + c.inherited = true; + } + // convert icons for composite content types + iconHelper.formatContentTypeIcons([c.contentType]); + }); + } + + /* ---------- DELETE PROMT ---------- */ + + scope.togglePrompt = function (object) { + object.deletePrompt = !object.deletePrompt; + }; + + scope.hidePrompt = function (object) { + object.deletePrompt = false; + }; + + /* ---------- TOOLBAR ---------- */ + + scope.toggleSortingMode = function(tool) { + + if (scope.sortingMode === true) { + + var sortOrderMissing = false; + + for (var i = 0; i < scope.model.groups.length; i++) { + var group = scope.model.groups[i]; + if (group.tabState !== "init" && group.sortOrder === undefined) { + sortOrderMissing = true; + group.showSortOrderMissing = true; + notificationsService.error(validationTranslated + ": " + group.name + " " + tabNoSortOrderTranslated); + } + } + + if (!sortOrderMissing) { + scope.sortingMode = false; + scope.sortingButtonKey = "general_reorder"; + } + + } else { + + scope.sortingMode = true; + scope.sortingButtonKey = "general_reorderDone"; + + } + + }; + + scope.openCompositionsDialog = function() { + + scope.compositionsDialogModel = { + title: "Compositions", + contentType: scope.model, + compositeContentTypes: scope.model.compositeContentTypes, + view: "views/common/overlays/contenttypeeditor/compositions/compositions.html", + confirmSubmit: { + title: "Warning", + description: "Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?", + checkboxLabel: "I know what I'm doing", + enable: true + }, + submit: function(model, oldModel, confirmed) { + + var compositionRemoved = false; + + // check if any compositions has been removed + for(var i = 0; oldModel.compositeContentTypes.length > i; i++) { + + var oldComposition = oldModel.compositeContentTypes[i]; + + if(_.contains(model.compositeContentTypes, oldComposition) === false) { + compositionRemoved = true; + } + + } + + // show overlay confirm box if compositions has been removed. + if(compositionRemoved && confirmed === false) { + + scope.compositionsDialogModel.confirmSubmit.show = true; + + // submit overlay if no compositions has been removed + // or the action has been confirmed + } else { + + // make sure that all tabs has an init property + if (scope.model.groups.length !== 0) { + angular.forEach(scope.model.groups, function(group) { + addInitProperty(group); + }); + } + + // remove overlay + scope.compositionsDialogModel.show = false; + scope.compositionsDialogModel = null; + } + + }, + close: function(oldModel) { + + // reset composition changes + scope.model.groups = oldModel.contentType.groups; + scope.model.compositeContentTypes = oldModel.contentType.compositeContentTypes; + + // remove overlay + scope.compositionsDialogModel.show = false; + scope.compositionsDialogModel = null; + + }, + selectCompositeContentType: function (selectedContentType) { + + //first check if this is a new selection - we need to store this value here before any further digests/async + // because after that the scope.model.compositeContentTypes will be populated with the selected value. + var newSelection = scope.model.compositeContentTypes.indexOf(selectedContentType.alias) === -1; + + if (newSelection) { + //merge composition with content type + + //use a different resource lookup depending on the content type type + var resourceLookup = scope.contentType === "documentType" ? contentTypeResource.getById : mediaTypeResource.getById; + + resourceLookup(selectedContentType.id).then(function (composition) { + //based on the above filtering we shouldn't be able to select an invalid one, but let's be safe and + // double check here. + var overlappingAliases = contentTypeHelper.validateAddingComposition(scope.model, composition); + if (overlappingAliases.length > 0) { + //this will create an invalid composition, need to uncheck it + scope.compositionsDialogModel.compositeContentTypes.splice( + scope.compositionsDialogModel.compositeContentTypes.indexOf(composition.alias), 1); + //dissallow this until something else is unchecked + selectedContentType.allowed = false; + } + else { + contentTypeHelper.mergeCompositeContentType(scope.model, composition); + } + + //based on the selection, we need to filter the available composite types list + filterAvailableCompositions(selectedContentType, newSelection).then(function () { + //TODO: Here we could probably re-enable selection if we previously showed a throbber or something + }); + }); + } + else { + // split composition from content type + contentTypeHelper.splitCompositeContentType(scope.model, selectedContentType); + + //based on the selection, we need to filter the available composite types list + filterAvailableCompositions(selectedContentType, newSelection).then(function () { + //TODO: Here we could probably re-enable selection if we previously showed a throbber or something + }); + } + + } + }; + + var availableContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes; + var countContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getCount : mediaTypeResource.getCount; + + //get the currently assigned property type aliases - ensure we pass these to the server side filer + var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) { + return _.map(g.properties, function(p) { + return p.alias; + }); + })), function(f) { + return f !== null && f !== undefined; + }); + $q.all([ + //get available composite types + availableContentTypeResource(scope.model.id, [], propAliasesExisting).then(function (result) { + setupAvailableContentTypesModel(result); + }), + //get content type count + countContentTypeResource().then(function(result) { + scope.compositionsDialogModel.totalContentTypes = parseInt(result, 10); + }) + ]).then(function() { + //resolves when both other promises are done, now show it + scope.compositionsDialogModel.show = true; + }); + + }; + + + /* ---------- GROUPS ---------- */ + + scope.addGroup = function(group) { + + // set group sort order + var index = scope.model.groups.indexOf(group); + var prevGroup = scope.model.groups[index - 1]; + + if( index > 0) { + // set index to 1 higher than the previous groups sort order + group.sortOrder = prevGroup.sortOrder + 1; + + } else { + // first group - sort order will be 0 + group.sortOrder = 0; + } + + // activate group + scope.activateGroup(group); + + }; + + scope.activateGroup = function(selectedGroup) { + + // set all other groups that are inactive to active + angular.forEach(scope.model.groups, function(group) { + // skip init tab + if (group.tabState !== "init") { + group.tabState = "inActive"; + } + }); + + selectedGroup.tabState = "active"; + + }; + + scope.removeGroup = function(groupIndex) { + scope.model.groups.splice(groupIndex, 1); + addInitGroup(scope.model.groups); + }; + + scope.updateGroupTitle = function(group) { + if (group.properties.length === 0) { + addInitProperty(group); + } + }; + + scope.changeSortOrderValue = function(group) { + + if (group.sortOrder !== undefined) { + group.showSortOrderMissing = false; + } + scope.model.groups = $filter('orderBy')(scope.model.groups, 'sortOrder'); + }; + + function addInitGroup(groups) { + + // check i init tab already exists + var addGroup = true; + + angular.forEach(groups, function(group) { + if (group.tabState === "init") { + addGroup = false; + } + }); + + if (addGroup) { + groups.push({ + properties: [], + parentTabContentTypes: [], + parentTabContentTypeNames: [], + name: "", + tabState: "init" + }); + } + + return groups; + } + + function activateFirstGroup(groups) { + if (groups && groups.length > 0) { + var firstGroup = groups[0]; + if(!firstGroup.tabState || firstGroup.tabState === "inActive") { + firstGroup.tabState = "active"; + } + } + } + + /* ---------- PROPERTIES ---------- */ + + scope.addProperty = function(property, group) { + + // set property sort order + var index = group.properties.indexOf(property); + var prevProperty = group.properties[index - 1]; + + if( index > 0) { + // set index to 1 higher than the previous property sort order + property.sortOrder = prevProperty.sortOrder + 1; + + } else { + // first property - sort order will be 0 + property.sortOrder = 0; + } + + // open property settings dialog + scope.editPropertyTypeSettings(property, group); + + }; + + scope.editPropertyTypeSettings = function(property, group) { + + if (!property.inherited && !property.locked) { + + scope.propertySettingsDialogModel = {}; + scope.propertySettingsDialogModel.title = "Property settings"; + scope.propertySettingsDialogModel.property = property; + scope.propertySettingsDialogModel.contentType = scope.contentType; + scope.propertySettingsDialogModel.contentTypeName = scope.model.name; + scope.propertySettingsDialogModel.view = "views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html"; + scope.propertySettingsDialogModel.show = true; + + // set state to active to access the preview + property.propertyState = "active"; + + // set property states + property.dialogIsOpen = true; + + scope.propertySettingsDialogModel.submit = function(model) { + + property.inherited = false; + property.dialogIsOpen = false; + + // update existing data types + if(model.updateSameDataTypes) { + updateSameDataTypes(property); + } + + // remove dialog + scope.propertySettingsDialogModel.show = false; + scope.propertySettingsDialogModel = null; + + // push new init property to group + addInitProperty(group); + + // set focus on init property + var numberOfProperties = group.properties.length; + group.properties[numberOfProperties - 1].focus = true; + + // push new init tab to the scope + addInitGroup(scope.model.groups); + + }; + + scope.propertySettingsDialogModel.close = function(oldModel) { + + // reset all property changes + property.label = oldModel.property.label; + property.alias = oldModel.property.alias; + property.description = oldModel.property.description; + property.config = oldModel.property.config; + property.editor = oldModel.property.editor; + property.view = oldModel.property.view; + property.dataTypeId = oldModel.property.dataTypeId; + property.dataTypeIcon = oldModel.property.dataTypeIcon; + property.dataTypeName = oldModel.property.dataTypeName; + property.validation.mandatory = oldModel.property.validation.mandatory; + property.validation.pattern = oldModel.property.validation.pattern; + property.showOnMemberProfile = oldModel.property.showOnMemberProfile; + property.memberCanEdit = oldModel.property.memberCanEdit; + + // because we set state to active, to show a preview, we have to check if has been filled out + // label is required so if it is not filled we know it is a placeholder + if(oldModel.property.editor === undefined || oldModel.property.editor === null || oldModel.property.editor === "") { + property.propertyState = "init"; + } else { + property.propertyState = oldModel.property.propertyState; + } + + // remove dialog + scope.propertySettingsDialogModel.show = false; + scope.propertySettingsDialogModel = null; + + }; + + } + }; + + scope.deleteProperty = function(tab, propertyIndex) { + + // remove property + tab.properties.splice(propertyIndex, 1); + + // if the last property in group is an placeholder - remove add new tab placeholder + if(tab.properties.length === 1 && tab.properties[0].propertyState === "init") { + + angular.forEach(scope.model.groups, function(group, index, groups){ + if(group.tabState === 'init') { + groups.splice(index, 1); + } + }); + + } + + }; + + function addInitProperty(group) { + + var addInitPropertyBool = true; + var initProperty = { + label: null, + alias: null, + propertyState: "init", + validation: { + mandatory: false, + pattern: null + } + }; + + // check if there already is an init property + angular.forEach(group.properties, function(property) { + if (property.propertyState === "init") { + addInitPropertyBool = false; + } + }); + + if (addInitPropertyBool) { + group.properties.push(initProperty); + } + + return group; + } + + function updateSameDataTypes(newProperty) { + + // find each property + angular.forEach(scope.model.groups, function(group){ + angular.forEach(group.properties, function(property){ + + if(property.dataTypeId === newProperty.dataTypeId) { + + // update property data + property.config = newProperty.config; + property.editor = newProperty.editor; + property.view = newProperty.view; + property.dataTypeId = newProperty.dataTypeId; + property.dataTypeIcon = newProperty.dataTypeIcon; + property.dataTypeName = newProperty.dataTypeName; + + } + + }); + }); + } + + + var unbindModelWatcher = scope.$watch('model', function(newValue, oldValue) { + if (newValue !== undefined && newValue.groups !== undefined) { + activate(); + } + }); + + // clean up + scope.$on('$destroy', function(){ + unbindModelWatcher(); + }); + + } + + var directive = { + restrict: "E", + replace: true, + templateUrl: "views/components/umb-groups-builder.html", + scope: { + model: "=", + compositions: "=", + sorting: "=", + contentType: "@" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbGroupsBuilder', GroupsBuilderDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbkeyboardShortcutsOverview +@restrict E +@scope + +@description + +

Use this directive to show an overview of keyboard shortcuts in an editor. +The directive will render an overview trigger wich shows how the overview is opened. +When this combination is hit an overview is opened with shortcuts based on the model sent to the directive.

+ +

Markup example

+
+    
+ + + + +
+
+ +

Controller example

+
+    (function () {
+
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+
+            vm.keyboardShortcutsOverview = [
+                {
+                    "name": "Sections",
+                    "shortcuts": [
+                        {
+                            "description": "Navigate sections",
+                            "keys": [
+                                {"key": "1"},
+                                {"key": "4"}
+                            ],
+                            "keyRange": true
+                        }
+                    ]
+                },
+                {
+                    "name": "Design",
+                    "shortcuts": [
+                        {
+                            "description": "Add tab",
+                            "keys": [
+                                {"key": "alt"},
+                                {"key": "shift"},
+                                {"key": "t"}
+                            ]
+                        }
+                    ]
+                }
+            ];
+
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+ +

Model description

+
    +
  • + name + (string) - + Sets the shortcut section name. +
  • +
  • + shortcuts + (array) - + Array of available shortcuts in the section. +
  • +
      +
    • + description + (string) - + Short description of the shortcut. +
    • +
    • + keys + (array) - + Array of keys in the shortcut. +
    • +
        +
      • + key + (string) - + The invidual key in the shortcut. +
      • +
      +
    • + keyRange + (boolean) - + Set to true to show a key range. It combines the shortcut keys with "-" instead of "+". +
    • +
    +
+ +@param {object} model keyboard shortcut model. See description and example above. +**/ + +(function() { + 'use strict'; + + function KeyboardShortcutsOverviewDirective() { + + function link(scope, el, attr, ctrl) { + + scope.shortcutOverlay = false; + + scope.toggleShortcutsOverlay = function() { + scope.shortcutOverlay = !scope.shortcutOverlay; + }; + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-keyboard-shortcuts-overview.html', + link: link, + scope: { + model: "=" + } + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbKeyboardShortcutsOverview', KeyboardShortcutsOverviewDirective); + +})(); + +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbLaunchMiniEditor +* @restrict E +* @function +* @description +* Used on a button to launch a mini content editor editor dialog +**/ +angular.module("umbraco.directives") + .directive('umbLaunchMiniEditor', function (dialogService, editorState, fileManager, contentEditingHelper) { + return { + restrict: 'A', + replace: false, + scope: { + node: '=umbLaunchMiniEditor', + }, + link: function(scope, element, attrs) { + + var launched = false; + + element.click(function() { + + if (launched === true) { + return; + } + + launched = true; + + //We need to store the current files selected in the file manager locally because the fileManager + // is a singleton and is shared globally. The mini dialog will also be referencing the fileManager + // and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here, + // clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state. + var currFiles = _.groupBy(fileManager.getFiles(), "alias"); + fileManager.clearFiles(); + + //We need to store the original editorState entity because it will need to change when the mini editor is loaded so that + // any property editors that are working with editorState get given the correct entity, otherwise strange things will + // start happening. + var currEditorState = editorState.getCurrent(); + + dialogService.open({ + template: "views/common/dialogs/content/edit.html", + id: scope.node.id, + closeOnSave: true, + tabFilter: ["Generic properties"], + callback: function (data) { + + //set the node name back + scope.node.name = data.name; + + //reset the fileManager to what it was + fileManager.clearFiles(); + _.each(currFiles, function (val, key) { + fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); + }); + + //reset the editor state + editorState.set(currEditorState); + + //Now we need to check if the content item that was edited was actually the same content item + // as the main content editor and if so, update all property data + if (data.id === currEditorState.id) { + var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data); + } + + launched = false; + }, + closeCallback: function () { + //reset the fileManager to what it was + fileManager.clearFiles(); + _.each(currFiles, function (val, key) { + fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; })); + }); + + //reset the editor state + editorState.set(currEditorState); + + launched = false; + } + }); + + }); + + } + }; }); -(function() { - 'use strict'; - - function LayoutSelectorDirective() { - - function link(scope, el, attr, ctrl) { - - scope.layoutDropDownIsOpen = false; - scope.showLayoutSelector = true; - - function activate() { - - setVisibility(); - - setActiveLayout(scope.layouts); - - } - - function setVisibility() { - - var numberOfAllowedLayouts = getNumberOfAllowedLayouts(scope.layouts); - - if(numberOfAllowedLayouts === 1) { - scope.showLayoutSelector = false; - } - - } - - function getNumberOfAllowedLayouts(layouts) { - - var allowedLayouts = 0; - - for (var i = 0; layouts.length > i; i++) { - - var layout = layouts[i]; - - if(layout.selected === true) { - allowedLayouts++; - } - - } - - return allowedLayouts; - } - - function setActiveLayout(layouts) { - - for (var i = 0; layouts.length > i; i++) { - var layout = layouts[i]; - if(layout.path === scope.activeLayout.path) { - layout.active = true; - } - } - - } - - scope.pickLayout = function(selectedLayout) { - if(scope.onLayoutSelect) { - scope.onLayoutSelect(selectedLayout); - scope.layoutDropDownIsOpen = false; - } - }; - - scope.toggleLayoutDropdown = function() { - scope.layoutDropDownIsOpen = !scope.layoutDropDownIsOpen; - }; - - scope.closeLayoutDropdown = function() { - scope.layoutDropDownIsOpen = false; - }; - - activate(); - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-layout-selector.html', - scope: { - layouts: '=', - activeLayout: '=', - onLayoutSelect: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbLayoutSelector', LayoutSelectorDirective); - -})(); - -(function() { - 'use strict'; - - function ListViewLayoutDirective() { - - function link(scope, el, attr, ctrl) { - - scope.getContent = function(contentId) { - if(scope.onGetContent) { - scope.onGetContent(contentId); - } - }; - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-list-view-layout.html', - scope: { - contentId: '=', - folders: '=', - items: '=', - selection: '=', - options: '=', - entityType: '@', - onGetContent: '=' - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbListViewLayout', ListViewLayoutDirective); - -})(); - -(function() { - 'use strict'; - - function ListViewSettingsDirective(contentTypeResource, dataTypeResource, dataTypeHelper) { - - function link(scope, el, attr, ctrl) { - - scope.dataType = {}; - scope.editDataTypeSettings = false; - scope.customListViewCreated = false; - - /* ---------- INIT ---------- */ - - function activate() { - - if(scope.enableListView) { - - dataTypeResource.getByName(scope.listViewName) - .then(function(dataType) { - - scope.dataType = dataType; - - scope.customListViewCreated = checkForCustomListView(); - - }); - - } else { - - scope.dataType = {}; - - } - - } - - /* ----------- LIST VIEW SETTINGS --------- */ - - scope.toggleEditListViewDataTypeSettings = function() { - scope.editDataTypeSettings = !scope.editDataTypeSettings; - }; - - scope.saveListViewDataType = function() { - - var preValues = dataTypeHelper.createPreValueProps(scope.dataType.preValues); - - dataTypeResource.save(scope.dataType, preValues, false).then(function(dataType) { - - // store data type - scope.dataType = dataType; - - // hide settings panel - scope.editDataTypeSettings = false; - - }); - - }; - - - /* ---------- CUSTOM LIST VIEW ---------- */ - - scope.createCustomListViewDataType = function() { - - dataTypeResource.createCustomListView(scope.modelAlias).then(function(dataType) { - - // store data type - scope.dataType = dataType; - - // set list view name on scope - scope.listViewName = dataType.name; - - // change state to custom list view - scope.customListViewCreated = true; - - // show settings panel - scope.editDataTypeSettings = true; - - }); - - }; - - scope.removeCustomListDataType = function() { - - scope.editDataTypeSettings = false; - - // delete custom list view data type - dataTypeResource.deleteById(scope.dataType.id).then(function(dataType) { - - // set list view name on scope - if(scope.contentType === "documentType") { - - scope.listViewName = "List View - Content"; - - } else if(scope.contentType === "mediaType") { - - scope.listViewName = "List View - Media"; - - } - - // get default data type - dataTypeResource.getByName(scope.listViewName) - .then(function(dataType) { - - // store data type - scope.dataType = dataType; - - // change state to default list view - scope.customListViewCreated = false; - - }); - }); - - }; - - /* ----------- SCOPE WATCHERS ----------- */ - var unbindEnableListViewWatcher = scope.$watch('enableListView', function(newValue, oldValue){ - - if(newValue !== undefined) { - activate(); - } - - }); - - // clean up - scope.$on('$destroy', function(){ - unbindEnableListViewWatcher(); - }); - - /* ----------- METHODS ---------- */ - - function checkForCustomListView() { - return scope.dataType.name === "List View - " + scope.modelAlias; - } - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-list-view-settings.html', - scope: { - enableListView: "=", - listViewName: "=", - modelAlias: "=", - contentType: "@" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbListViewSettings', ListViewSettingsDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbLoadIndicator -@restrict E - -@description -Use this directive to generate a loading indicator. - -

Markup example

-
-    
- - - - -
-

{{content}}

-
- -
-
- -

Controller example

-
-    (function () {
-        "use strict";
-
-        function Controller(myService) {
-
-            var vm = this;
-
-            vm.content = "";
-            vm.loading = true;
-
-            myService.getContent().then(function(content){
-                vm.content = content;
-                vm.loading = false;
-            });
-
-        }
-½
-        angular.module("umbraco").controller("My.Controller", Controller);
-    })();
-
-**/ - -(function() { - 'use strict'; - - function UmbLoadIndicatorDirective() { - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-load-indicator.html' - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbLoadIndicator', UmbLoadIndicatorDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbLockedField -@restrict E -@scope - -@description -Use this directive to render a value with a lock next to it. When the lock is clicked the value gets unlocked and can be edited. - -

Markup example

-
-	
- - - - -
-
- -

Controller example

-
-	(function () {
-		"use strict";
-
-		function Controller() {
-
-			var vm = this;
-			vm.value = "My locked text";
-
-        }
-
-		angular.module("umbraco").controller("My.Controller", Controller);
-
-	})();
-
- -@param {string} ngModel (binding): The locked text. -@param {boolean=} locked (binding): true by default. Set to false to unlock the text. -@param {string=} placeholderText (binding): If ngModel is empty this text will be shown. -@param {string=} regexValidation (binding): Set a regex expression for validation of the field. -@param {string=} serverValidationField (attribute): Set a server validation field. -**/ - -(function() { - 'use strict'; - - function LockedFieldDirective($timeout, localizationService) { - - function link(scope, el, attr, ngModelCtrl) { - - function activate() { - - // if locked state is not defined as an attr set default state - if (scope.locked === undefined || scope.locked === null) { - scope.locked = true; - } - - // if regex validation is not defined as an attr set default state - // if this is set to an empty string then regex validation can be ignored. - if (scope.regexValidation === undefined || scope.regexValidation === null) { - scope.regexValidation = "^[a-zA-Z]\\w.*$"; - } - - if (scope.serverValidationField === undefined || scope.serverValidationField === null) { - scope.serverValidationField = ""; - } - - // if locked state is not defined as an attr set default state - if (scope.placeholderText === undefined || scope.placeholderText === null) { - scope.placeholderText = "Enter value..."; - } - - } - - scope.lock = function() { - scope.locked = true; - }; - - scope.unlock = function() { - scope.locked = false; - }; - - activate(); - - } - - var directive = { - require: "ngModel", - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-locked-field.html', - scope: { - ngModel: "=", - locked: "=?", - placeholderText: "=?", - regexValidation: "=?", - serverValidationField: "@" - }, - link: link - }; - - return directive; - - } - - angular.module('umbraco.directives').directive('umbLockedField', LockedFieldDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbMediaGrid -@restrict E -@scope - -@description -Use this directive to generate a thumbnail grid of media items. - -

Markup example

-
-    
- - - - -
-
- -

Controller example

-
-    (function () {
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-            vm.mediaItems = [];
-
-            vm.clickItem = clickItem;
-            vm.clickItemName = clickItemName;
-
-            myService.getMediaItems().then(function (mediaItems) {
-                vm.mediaItems = mediaItems;
-            });
-
-            function clickItem(item, $event, $index){
-                // do magic here
-            }
-
-            function clickItemName(item, $event, $index) {
-                // set item.selected = true; to select the item
-                // do magic here
-            }
-
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-    })();
-
- -@param {array} items (binding): Array of media items. -@param {callback=} onDetailsHover (binding): Callback method when the details icon is hovered. -

The callback returns:

-
    -
  • item: The hovered item
  • -
  • $event: The hover event
  • -
  • hover: Boolean to tell if the item is hovered or not
  • -
-@param {callback=} onClick (binding): Callback method to handle click events on the media item. -

The callback returns:

-
    -
  • item: The clicked item
  • -
  • $event: The click event
  • -
  • $index: The item index
  • -
-@param {callback=} onClickName (binding): Callback method to handle click events on the media item name. -

The callback returns:

-
    -
  • item: The clicked item
  • -
  • $event: The click event
  • -
  • $index: The item index
  • -
-@param {string=} filterBy (binding): String to filter media items by -@param {string=} itemMaxWidth (attribute): Sets a max width on the media item thumbnails. -@param {string=} itemMaxHeight (attribute): Sets a max height on the media item thumbnails. -@param {string=} itemMinWidth (attribute): Sets a min width on the media item thumbnails. -@param {string=} itemMinHeight (attribute): Sets a min height on the media item thumbnails. - -**/ - -(function() { - 'use strict'; - - function MediaGridDirective($filter, mediaHelper) { - - function link(scope, el, attr, ctrl) { - - var itemDefaultHeight = 200; - var itemDefaultWidth = 200; - var itemMaxWidth = 200; - var itemMaxHeight = 200; - var itemMinWidth = 125; - var itemMinHeight = 125; - - function activate() { - - if (scope.itemMaxWidth) { - itemMaxWidth = scope.itemMaxWidth; - } - - if (scope.itemMaxHeight) { - itemMaxHeight = scope.itemMaxHeight; - } - - if (scope.itemMinWidth) { - itemMinWidth = scope.itemMinWidth; - } - - if (scope.itemMinWidth) { - itemMinHeight = scope.itemMinHeight; - } - - for (var i = 0; scope.items.length > i; i++) { - var item = scope.items[i]; - setItemData(item); - setOriginalSize(item, itemMaxHeight); - } - - if (scope.items.length > 0) { - setFlexValues(scope.items); - } - - } - - function setItemData(item) { - item.isFolder = !mediaHelper.hasFilePropertyType(item); - if (!item.isFolder) { - item.thumbnail = mediaHelper.resolveFile(item, true); - item.image = mediaHelper.resolveFile(item, false); - } - } - - function setOriginalSize(item, maxHeight) { - - //set to a square by default - item.width = itemDefaultWidth; - item.height = itemDefaultHeight; - item.aspectRatio = 1; - - var widthProp = _.find(item.properties, function(v) { - return (v.alias === "umbracoWidth"); - }); - - if (widthProp && widthProp.value) { - item.width = parseInt(widthProp.value, 10); - if (isNaN(item.width)) { - item.width = itemDefaultWidth; - } - } - - var heightProp = _.find(item.properties, function(v) { - return (v.alias === "umbracoHeight"); - }); - - if (heightProp && heightProp.value) { - item.height = parseInt(heightProp.value, 10); - if (isNaN(item.height)) { - item.height = itemDefaultWidth; - } - } - - item.aspectRatio = item.width / item.height; - - // set max width and height - // landscape - if (item.aspectRatio >= 1) { - if (item.width > itemMaxWidth) { - item.width = itemMaxWidth; - item.height = itemMaxWidth / item.aspectRatio; - } - // portrait - } else { - if (item.height > itemMaxHeight) { - item.height = itemMaxHeight; - item.width = itemMaxHeight * item.aspectRatio; - } - } - - } - - function setFlexValues(mediaItems) { - - var flexSortArray = mediaItems; - var smallestImageWidth = null; - var widestImageAspectRatio = null; - - // sort array after image width with the widest image first - flexSortArray = $filter('orderBy')(flexSortArray, 'width', true); - - // find widest image aspect ratio - widestImageAspectRatio = flexSortArray[0].aspectRatio; - - // find smallest image width - smallestImageWidth = flexSortArray[flexSortArray.length - 1].width; - - for (var i = 0; flexSortArray.length > i; i++) { - - var mediaItem = flexSortArray[i]; - var flex = 1 / (widestImageAspectRatio / mediaItem.aspectRatio); - - if (flex === 0) { - flex = 1; - } - - var imageMinFlexWidth = smallestImageWidth * flex; - - var flexStyle = { - "flex": flex + " 1 " + imageMinFlexWidth + "px", - "max-width": mediaItem.width + "px", - "min-width": itemMinWidth + "px", - "min-height": itemMinHeight + "px" - }; - - mediaItem.flexStyle = flexStyle; - - } - - } - - scope.clickItem = function(item, $event, $index) { - if (scope.onClick) { - scope.onClick(item, $event, $index); - } - }; - - scope.clickItemName = function(item, $event, $index) { - if (scope.onClickName) { - scope.onClickName(item, $event, $index); - $event.stopPropagation(); - } - }; - - scope.hoverItemDetails = function(item, $event, hover) { - if (scope.onDetailsHover) { - scope.onDetailsHover(item, $event, hover); - } - }; - - var unbindItemsWatcher = scope.$watch('items', function(newValue, oldValue) { - if (angular.isArray(newValue)) { - activate(); - } - }); - - scope.$on('$destroy', function() { - unbindItemsWatcher(); - }); - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-media-grid.html', - scope: { - items: '=', - onDetailsHover: "=", - onClick: '=', - onClickName: "=", - filterBy: "=", - itemMaxWidth: "@", - itemMaxHeight: "@", - itemMinWidth: "@", - itemMinHeight: "@" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbMediaGrid', MediaGridDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbPagination -@restrict E -@scope - -@description -Use this directive to generate a pagination. - -

Markup example

-
-    
- - - - -
-
- -

Controller example

-
-    (function () {
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-
-            vm.pagination = {
-                pageNumber: 1,
-                totalPages: 10
-            }
-
-            vm.nextPage = nextPage;
-            vm.prevPage = prevPage;
-            vm.goToPage = goToPage;
-
-            function nextPage(pageNumber) {
-                // do magic here
-                console.log(pageNumber);
-                alert("nextpage");
-            }
-
-            function prevPage(pageNumber) {
-                // do magic here
-                console.log(pageNumber);
-                alert("prevpage");
-            }
-
-            function goToPage(pageNumber) {
-                // do magic here
-                console.log(pageNumber);
-                alert("go to");
-            }
-
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-    })();
-
- -@param {number} pageNumber (binding): Current page number. -@param {number} totalPages (binding): The total number of pages. -@param {callback} onNext (binding): Callback method to go to the next page. -

The callback returns:

-
    -
  • pageNumber: The page number
  • -
-@param {callback=} onPrev (binding): Callback method to go to the previous page. -

The callback returns:

-
    -
  • pageNumber: The page number
  • -
-@param {callback=} onGoToPage (binding): Callback method to go to a specific page. -

The callback returns:

-
    -
  • pageNumber: The page number
  • -
-**/ - -(function() { - 'use strict'; - - function PaginationDirective() { - - function link(scope, el, attr, ctrl) { - - function activate() { - - scope.pagination = []; - - var i = 0; - - if (scope.totalPages <= 10) { - for (i = 0; i < scope.totalPages; i++) { - scope.pagination.push({ - val: (i + 1), - isActive: scope.pageNumber === (i + 1) - }); - } - } - else { - //if there is more than 10 pages, we need to do some fancy bits - - //get the max index to start - var maxIndex = scope.totalPages - 10; - //set the start, but it can't be below zero - var start = Math.max(scope.pageNumber - 5, 0); - //ensure that it's not too far either - start = Math.min(maxIndex, start); - - for (i = start; i < (10 + start) ; i++) { - scope.pagination.push({ - val: (i + 1), - isActive: scope.pageNumber === (i + 1) - }); - } - - //now, if the start is greater than 0 then '1' will not be displayed, so do the elipses thing - if (start > 0) { - scope.pagination.unshift({ name: "First", val: 1, isActive: false }, {val: "...",isActive: false}); - } - - //same for the end - if (start < maxIndex) { - scope.pagination.push({ val: "...", isActive: false }, { name: "Last", val: scope.totalPages, isActive: false }); - } - } - - } - - scope.next = function() { - if (scope.onNext && scope.pageNumber < scope.totalPages) { - scope.pageNumber++; - scope.onNext(scope.pageNumber); - } - }; - - scope.prev = function(pageNumber) { - if (scope.onPrev && scope.pageNumber > 1) { - scope.pageNumber--; - scope.onPrev(scope.pageNumber); - } - }; - - scope.goToPage = function(pageNumber) { - if(scope.onGoToPage) { - scope.pageNumber = pageNumber + 1; - scope.onGoToPage(scope.pageNumber); - } - }; - - var unbindPageNumberWatcher = scope.$watch('pageNumber', function(newValue, oldValue){ - activate(); - }); - - scope.$on('$destroy', function(){ - unbindPageNumberWatcher(); - }); - - activate(); - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-pagination.html', - scope: { - pageNumber: "=", - totalPages: "=", - onNext: "=", - onPrev: "=", - onGoToPage: "=" - }, - link: link - }; - - return directive; - - } - - angular.module('umbraco.directives').directive('umbPagination', PaginationDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbStickyBar -@restrict A - -@description -Use this directive make an element sticky and follow the page when scrolling. - -

Markup example

-
-    
- -
-
- -
-
- -

CSS example

-
-    .my-sticky-bar {
-        padding: 15px 0;
-        background: #000000;
-        position: relative;
-        top: 0;
-    }
-
-    .my-sticky-bar.-umb-sticky-bar {
-        top: 100px;
-    }
-
- -@param {string} scrollableContainer Set the class (".element") or the id ("#element") of the scrollable container element. -**/ - -(function() { - 'use strict'; - - function StickyBarDirective($rootScope) { - - function link(scope, el, attr, ctrl) { - - var bar = $(el); - var scrollableContainer = null; - var clonedBar = null; - var cloneIsMade = false; - var barTop = bar.context.offsetTop; - - function activate() { - - if (attr.scrollableContainer) { - scrollableContainer = $(attr.scrollableContainer); - } else { - scrollableContainer = $(window); - } - - scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll"); - $(window).on('resize.umbStickyBar', determineVisibility); - - scope.$on('$destroy', function() { - scrollableContainer.off('.umbStickyBar'); - $(window).off('.umbStickyBar'); - }); - - } - - function determineVisibility() { - - var scrollTop = scrollableContainer.scrollTop(); - - if (scrollTop > barTop) { - - if (!cloneIsMade) { - - createClone(); - - clonedBar.css({ - 'visibility': 'visible' - }); - - } else { - - calculateSize(); - - } - - } else { - - if (cloneIsMade) { - - //remove cloned element (switched places with original on creation) - bar.remove(); - bar = clonedBar; - clonedBar = null; - - bar.removeClass('-umb-sticky-bar'); - bar.css({ - position: 'relative', - 'width': 'auto', - 'height': 'auto', - 'z-index': 'auto', - 'visibility': 'visible' - }); - - cloneIsMade = false; - - } - - } - - } - - function calculateSize() { - clonedBar.css({ - width: bar.outerWidth(), - height: bar.height() - }); - } - - function createClone() { - //switch place with cloned element, to keep binding intact - clonedBar = bar; - bar = clonedBar.clone(); - clonedBar.after(bar); - clonedBar.addClass('-umb-sticky-bar'); - clonedBar.css({ - 'position': 'fixed', - 'z-index': 500, - 'visibility': 'hidden' - }); - - cloneIsMade = true; - calculateSize(); - - } - - activate(); - - } - - var directive = { - restrict: 'A', - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbStickyBar', StickyBarDirective); - -})(); - -(function() { - 'use strict'; - - function TableDirective() { - - function link(scope, el, attr, ctrl) { - - scope.clickItem = function(item, $event) { - if(scope.onClick) { - scope.onClick(item); - $event.stopPropagation(); - } - }; - - scope.selectItem = function(item, $index, $event) { - if(scope.onSelect) { - scope.onSelect(item, $index, $event); - $event.stopPropagation(); - } - }; - - scope.selectAll = function($event) { - if(scope.onSelectAll) { - scope.onSelectAll($event); - } - }; - - scope.isSelectedAll = function() { - if(scope.onSelectedAll && scope.items && scope.items.length > 0) { - return scope.onSelectedAll(); - } - }; - - scope.isSortDirection = function (col, direction) { - if (scope.onSortingDirection) { - return scope.onSortingDirection(col, direction); - } - }; - - scope.sort = function(field, allow) { - if(scope.onSort) { - scope.onSort(field, allow); - } - }; - - } - - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/umb-table.html', - scope: { - items: '=', - itemProperties: '=', - allowSelectAll: '=', - onSelect: '=', - onClick: '=', - onSelectAll: '=', - onSelectedAll: '=', - onSortingDirection: '=', - onSort: '=' - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbTable', TableDirective); - -})(); - -/** -@ngdoc directive -@name umbraco.directives.directive:umbTooltip -@restrict E -@scope - -@description -Use this directive to render a tooltip. - -

Markup example

-
-    
- -
- Hover me -
- - - // tooltip content here - - -
-
- -

Controller example

-
-    (function () {
-        "use strict";
-
-        function Controller() {
-
-            var vm = this;
-            vm.tooltip = {
-                show: false,
-                event: null
-            };
-
-            vm.mouseOver = mouseOver;
-            vm.mouseLeave = mouseLeave;
-
-            function mouseOver($event) {
-                vm.tooltip = {
-                    show: true,
-                    event: $event
-                };
-            }
-
-            function mouseLeave() {
-                vm.tooltip = {
-                    show: false,
-                    event: null
-                };
-            }
-
-        }
-
-        angular.module("umbraco").controller("My.Controller", Controller);
-
-    })();
-
- -@param {string} event Set the $event from the target element to position the tooltip relative to the mouse cursor. -**/ - -(function() { - 'use strict'; - - function TooltipDirective($timeout) { - - function link(scope, el, attr, ctrl) { - - scope.tooltipStyles = {}; - scope.tooltipStyles.left = 0; - scope.tooltipStyles.top = 0; - - function activate() { - - $timeout(function() { - setTooltipPosition(scope.event); - }); - - } - - function setTooltipPosition(event) { - - var container = $("#contentwrapper"); - var containerLeft = container[0].offsetLeft; - var containerRight = containerLeft + container[0].offsetWidth; - var containerTop = container[0].offsetTop; - var containerBottom = containerTop + container[0].offsetHeight; - - var elementHeight = null; - var elementWidth = null; - - var position = { - right: "inherit", - left: "inherit", - top: "inherit", - bottom: "inherit" - }; - - // element size - elementHeight = el.context.clientHeight; - elementWidth = el.context.clientWidth; - - position.left = event.pageX - (elementWidth / 2); - position.top = event.pageY; - - // check to see if element is outside screen - // outside right - if (position.left + elementWidth > containerRight) { - position.right = 10; - position.left = "inherit"; - } - - // outside bottom - if (position.top + elementHeight > containerBottom) { - position.bottom = 10; - position.top = "inherit"; - } - - // outside left - if (position.left < containerLeft) { - position.left = containerLeft + 10; - position.right = "inherit"; - } - - // outside top - if (position.top < containerTop) { - position.top = 10; - position.bottom = "inherit"; - } - - scope.tooltipStyles = position; - - el.css(position); - - } - - activate(); - - } - - var directive = { - restrict: 'E', - transclude: true, - replace: true, - templateUrl: 'views/components/umb-tooltip.html', - scope: { - event: "=" - }, - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbTooltip', TooltipDirective); - -})(); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbFileDropzone -* @restrict E -* @function -* @description -* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form. -**/ - -/* -TODO -.directive("umbFileDrop", function ($timeout, $upload, localizationService, umbRequestHelper){ - - return{ - restrict: "A", - link: function(scope, element, attrs){ - - //load in the options model - - - } - } -}) -*/ - -angular.module("umbraco.directives") - -.directive('umbFileDropzone', function ($timeout, Upload, localizationService, umbRequestHelper) { - return { - - restrict: 'E', - replace: true, - - templateUrl: 'views/components/upload/umb-file-dropzone.html', - - scope: { - parentId: '@', - contentTypeAlias: '@', - propertyAlias: '@', - accept: '@', - maxFileSize: '@', - - compact: '@', - hideDropzone: '@', - - filesQueued: '=', - handleFile: '=', - filesUploaded: '=' - }, - - link: function(scope, element, attrs) { - - scope.queue = []; - scope.done = []; - scope.rejected = []; - scope.currentFile = undefined; - - function _filterFile(file) { - - var ignoreFileNames = ['Thumbs.db']; - var ignoreFileTypes = ['directory']; - - // ignore files with names from the list - // ignore files with types from the list - // ignore files which starts with "." - if(ignoreFileNames.indexOf(file.name) === -1 && - ignoreFileTypes.indexOf(file.type) === -1 && - file.name.indexOf(".") !== 0) { - return true; - } else { - return false; - } - - } - - function _filesQueued(files, event){ - - //Push into the queue - angular.forEach(files, function(file){ - - if(_filterFile(file) === true) { - - if(file.$error) { - scope.rejected.push(file); - } else { - scope.queue.push(file); - } - - } - - }); - - //when queue is done, kick the uploader - if(!scope.working){ - _processQueueItem(); - } - } - - - function _processQueueItem(){ - - if(scope.queue.length > 0){ - scope.currentFile = scope.queue.shift(); - _upload(scope.currentFile); - }else if(scope.done.length > 0){ - - if(scope.filesUploaded){ - //queue is empty, trigger the done action - scope.filesUploaded(scope.done); - } - - //auto-clear the done queue after 3 secs - var currentLength = scope.done.length; - $timeout(function(){ - scope.done.splice(0, currentLength); - }, 3000); - } - } - - function _upload(file) { - - scope.propertyAlias = scope.propertyAlias ? scope.propertyAlias : "umbracoFile"; - scope.contentTypeAlias = scope.contentTypeAlias ? scope.contentTypeAlias : "Image"; - - Upload.upload({ - url: umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostAddFile"), - fields: { - 'currentFolder': scope.parentId, - 'contentTypeAlias': scope.contentTypeAlias, - 'propertyAlias': scope.propertyAlias, - 'path': file.path - }, - file: file - }).progress(function (evt) { - - // calculate progress in percentage - var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10); - - // set percentage property on file - file.uploadProgress = progressPercentage; - - // set uploading status on file - file.uploadStatus = "uploading"; - - }).success(function (data, status, headers, config) { - - if(data.notifications && data.notifications.length > 0) { - - // set error status on file - file.uploadStatus = "error"; - - // Throw message back to user with the cause of the error - file.serverErrorMessage = data.notifications[0].message; - - // Put the file in the rejected pool - scope.rejected.push(file); - - } else { - - // set done status on file - file.uploadStatus = "done"; - - // set date/time for when done - used for sorting - file.doneDate = new Date(); - - // Put the file in the done pool - scope.done.push(file); - - } - - scope.currentFile = undefined; - - //after processing, test if everthing is done - _processQueueItem(); - - }).error( function (evt, status, headers, config) { - - // set status done - file.uploadStatus = "error"; - - //if the service returns a detailed error - if (evt.InnerException) { - file.serverErrorMessage = evt.InnerException.ExceptionMessage; - - //Check if its the common "too large file" exception - if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) { - file.serverErrorMessage = "File too large to upload"; - } - - } else if (evt.Message) { - file.serverErrorMessage = evt.Message; - } - - // If file not found, server will return a 404 and display this message - if(status === 404 ) { - file.serverErrorMessage = "File not found"; - } - - //after processing, test if everthing is done - scope.rejected.push(file); - scope.currentFile = undefined; - - _processQueueItem(); - }); - } - - - scope.handleFiles = function(files, event){ - if(scope.filesQueued){ - scope.filesQueued(files, event); - } - - _filesQueued(files, event); - - }; - - } - - - }; - }); +(function() { + 'use strict'; + + function LayoutSelectorDirective() { + + function link(scope, el, attr, ctrl) { + + scope.layoutDropDownIsOpen = false; + scope.showLayoutSelector = true; + + function activate() { + + setVisibility(); + + setActiveLayout(scope.layouts); + + } + + function setVisibility() { + + var numberOfAllowedLayouts = getNumberOfAllowedLayouts(scope.layouts); + + if(numberOfAllowedLayouts === 1) { + scope.showLayoutSelector = false; + } + + } + + function getNumberOfAllowedLayouts(layouts) { + + var allowedLayouts = 0; + + for (var i = 0; layouts.length > i; i++) { + + var layout = layouts[i]; + + if(layout.selected === true) { + allowedLayouts++; + } + + } + + return allowedLayouts; + } + + function setActiveLayout(layouts) { + + for (var i = 0; layouts.length > i; i++) { + var layout = layouts[i]; + if(layout.path === scope.activeLayout.path) { + layout.active = true; + } + } + + } + + scope.pickLayout = function(selectedLayout) { + if(scope.onLayoutSelect) { + scope.onLayoutSelect(selectedLayout); + scope.layoutDropDownIsOpen = false; + } + }; + + scope.toggleLayoutDropdown = function() { + scope.layoutDropDownIsOpen = !scope.layoutDropDownIsOpen; + }; + + scope.closeLayoutDropdown = function() { + scope.layoutDropDownIsOpen = false; + }; + + activate(); + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-layout-selector.html', + scope: { + layouts: '=', + activeLayout: '=', + onLayoutSelect: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbLayoutSelector', LayoutSelectorDirective); + +})(); + +(function() { + 'use strict'; + + function ListViewLayoutDirective() { + + function link(scope, el, attr, ctrl) { + + scope.getContent = function(contentId) { + if(scope.onGetContent) { + scope.onGetContent(contentId); + } + }; + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-list-view-layout.html', + scope: { + contentId: '=', + folders: '=', + items: '=', + selection: '=', + options: '=', + entityType: '@', + onGetContent: '=' + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbListViewLayout', ListViewLayoutDirective); + +})(); + +(function() { + 'use strict'; + + function ListViewSettingsDirective(contentTypeResource, dataTypeResource, dataTypeHelper) { + + function link(scope, el, attr, ctrl) { + + scope.dataType = {}; + scope.editDataTypeSettings = false; + scope.customListViewCreated = false; + + /* ---------- INIT ---------- */ + + function activate() { + + if(scope.enableListView) { + + dataTypeResource.getByName(scope.listViewName) + .then(function(dataType) { + + scope.dataType = dataType; + + scope.customListViewCreated = checkForCustomListView(); + + }); + + } else { + + scope.dataType = {}; + + } + + } + + /* ----------- LIST VIEW SETTINGS --------- */ + + scope.toggleEditListViewDataTypeSettings = function() { + scope.editDataTypeSettings = !scope.editDataTypeSettings; + }; + + scope.saveListViewDataType = function() { + + var preValues = dataTypeHelper.createPreValueProps(scope.dataType.preValues); + + dataTypeResource.save(scope.dataType, preValues, false).then(function(dataType) { + + // store data type + scope.dataType = dataType; + + // hide settings panel + scope.editDataTypeSettings = false; + + }); + + }; + + + /* ---------- CUSTOM LIST VIEW ---------- */ + + scope.createCustomListViewDataType = function() { + + dataTypeResource.createCustomListView(scope.modelAlias).then(function(dataType) { + + // store data type + scope.dataType = dataType; + + // set list view name on scope + scope.listViewName = dataType.name; + + // change state to custom list view + scope.customListViewCreated = true; + + // show settings panel + scope.editDataTypeSettings = true; + + }); + + }; + + scope.removeCustomListDataType = function() { + + scope.editDataTypeSettings = false; + + // delete custom list view data type + dataTypeResource.deleteById(scope.dataType.id).then(function(dataType) { + + // set list view name on scope + if(scope.contentType === "documentType") { + + scope.listViewName = "List View - Content"; + + } else if(scope.contentType === "mediaType") { + + scope.listViewName = "List View - Media"; + + } + + // get default data type + dataTypeResource.getByName(scope.listViewName) + .then(function(dataType) { + + // store data type + scope.dataType = dataType; + + // change state to default list view + scope.customListViewCreated = false; + + }); + }); + + }; + + /* ----------- SCOPE WATCHERS ----------- */ + var unbindEnableListViewWatcher = scope.$watch('enableListView', function(newValue, oldValue){ + + if(newValue !== undefined) { + activate(); + } + + }); + + // clean up + scope.$on('$destroy', function(){ + unbindEnableListViewWatcher(); + }); + + /* ----------- METHODS ---------- */ + + function checkForCustomListView() { + return scope.dataType.name === "List View - " + scope.modelAlias; + } + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-list-view-settings.html', + scope: { + enableListView: "=", + listViewName: "=", + modelAlias: "=", + contentType: "@" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbListViewSettings', ListViewSettingsDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbLoadIndicator +@restrict E + +@description +Use this directive to generate a loading indicator. + +

Markup example

+
+    
+ + + + +
+

{{content}}

+
+ +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller(myService) {
+
+            var vm = this;
+
+            vm.content = "";
+            vm.loading = true;
+
+            myService.getContent().then(function(content){
+                vm.content = content;
+                vm.loading = false;
+            });
+
+        }
+½
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+**/ + +(function() { + 'use strict'; + + function UmbLoadIndicatorDirective() { + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-load-indicator.html' + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbLoadIndicator', UmbLoadIndicatorDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbLockedField +@restrict E +@scope + +@description +Use this directive to render a value with a lock next to it. When the lock is clicked the value gets unlocked and can be edited. + +

Markup example

+
+	
+ + + + +
+
+ +

Controller example

+
+	(function () {
+		"use strict";
+
+		function Controller() {
+
+			var vm = this;
+			vm.value = "My locked text";
+
+        }
+
+		angular.module("umbraco").controller("My.Controller", Controller);
+
+	})();
+
+ +@param {string} ngModel (binding): The locked text. +@param {boolean=} locked (binding): true by default. Set to false to unlock the text. +@param {string=} placeholderText (binding): If ngModel is empty this text will be shown. +@param {string=} regexValidation (binding): Set a regex expression for validation of the field. +@param {string=} serverValidationField (attribute): Set a server validation field. +**/ + +(function() { + 'use strict'; + + function LockedFieldDirective($timeout, localizationService) { + + function link(scope, el, attr, ngModelCtrl) { + + function activate() { + + // if locked state is not defined as an attr set default state + if (scope.locked === undefined || scope.locked === null) { + scope.locked = true; + } + + // if regex validation is not defined as an attr set default state + // if this is set to an empty string then regex validation can be ignored. + if (scope.regexValidation === undefined || scope.regexValidation === null) { + scope.regexValidation = "^[a-zA-Z]\\w.*$"; + } + + if (scope.serverValidationField === undefined || scope.serverValidationField === null) { + scope.serverValidationField = ""; + } + + // if locked state is not defined as an attr set default state + if (scope.placeholderText === undefined || scope.placeholderText === null) { + scope.placeholderText = "Enter value..."; + } + + } + + scope.lock = function() { + scope.locked = true; + }; + + scope.unlock = function() { + scope.locked = false; + }; + + activate(); + + } + + var directive = { + require: "ngModel", + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-locked-field.html', + scope: { + ngModel: "=", + locked: "=?", + placeholderText: "=?", + regexValidation: "=?", + serverValidationField: "@" + }, + link: link + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbLockedField', LockedFieldDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbMediaGrid +@restrict E +@scope + +@description +Use this directive to generate a thumbnail grid of media items. + +

Markup example

+
+    
+ + + + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+            vm.mediaItems = [];
+
+            vm.clickItem = clickItem;
+            vm.clickItemName = clickItemName;
+
+            myService.getMediaItems().then(function (mediaItems) {
+                vm.mediaItems = mediaItems;
+            });
+
+            function clickItem(item, $event, $index){
+                // do magic here
+            }
+
+            function clickItemName(item, $event, $index) {
+                // set item.selected = true; to select the item
+                // do magic here
+            }
+
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+ +@param {array} items (binding): Array of media items. +@param {callback=} onDetailsHover (binding): Callback method when the details icon is hovered. +

The callback returns:

+
    +
  • item: The hovered item
  • +
  • $event: The hover event
  • +
  • hover: Boolean to tell if the item is hovered or not
  • +
+@param {callback=} onClick (binding): Callback method to handle click events on the media item. +

The callback returns:

+
    +
  • item: The clicked item
  • +
  • $event: The click event
  • +
  • $index: The item index
  • +
+@param {callback=} onClickName (binding): Callback method to handle click events on the media item name. +

The callback returns:

+
    +
  • item: The clicked item
  • +
  • $event: The click event
  • +
  • $index: The item index
  • +
+@param {string=} filterBy (binding): String to filter media items by +@param {string=} itemMaxWidth (attribute): Sets a max width on the media item thumbnails. +@param {string=} itemMaxHeight (attribute): Sets a max height on the media item thumbnails. +@param {string=} itemMinWidth (attribute): Sets a min width on the media item thumbnails. +@param {string=} itemMinHeight (attribute): Sets a min height on the media item thumbnails. + +**/ + +(function() { + 'use strict'; + + function MediaGridDirective($filter, mediaHelper) { + + function link(scope, el, attr, ctrl) { + + var itemDefaultHeight = 200; + var itemDefaultWidth = 200; + var itemMaxWidth = 200; + var itemMaxHeight = 200; + var itemMinWidth = 125; + var itemMinHeight = 125; + + function activate() { + + if (scope.itemMaxWidth) { + itemMaxWidth = scope.itemMaxWidth; + } + + if (scope.itemMaxHeight) { + itemMaxHeight = scope.itemMaxHeight; + } + + if (scope.itemMinWidth) { + itemMinWidth = scope.itemMinWidth; + } + + if (scope.itemMinWidth) { + itemMinHeight = scope.itemMinHeight; + } + + for (var i = 0; scope.items.length > i; i++) { + var item = scope.items[i]; + setItemData(item); + setOriginalSize(item, itemMaxHeight); + } + + if (scope.items.length > 0) { + setFlexValues(scope.items); + } + + } + + function setItemData(item) { + item.isFolder = !mediaHelper.hasFilePropertyType(item); + if (!item.isFolder) { + item.thumbnail = mediaHelper.resolveFile(item, true); + item.image = mediaHelper.resolveFile(item, false); + } + } + + function setOriginalSize(item, maxHeight) { + + //set to a square by default + item.width = itemDefaultWidth; + item.height = itemDefaultHeight; + item.aspectRatio = 1; + + var widthProp = _.find(item.properties, function(v) { + return (v.alias === "umbracoWidth"); + }); + + if (widthProp && widthProp.value) { + item.width = parseInt(widthProp.value, 10); + if (isNaN(item.width)) { + item.width = itemDefaultWidth; + } + } + + var heightProp = _.find(item.properties, function(v) { + return (v.alias === "umbracoHeight"); + }); + + if (heightProp && heightProp.value) { + item.height = parseInt(heightProp.value, 10); + if (isNaN(item.height)) { + item.height = itemDefaultWidth; + } + } + + item.aspectRatio = item.width / item.height; + + // set max width and height + // landscape + if (item.aspectRatio >= 1) { + if (item.width > itemMaxWidth) { + item.width = itemMaxWidth; + item.height = itemMaxWidth / item.aspectRatio; + } + // portrait + } else { + if (item.height > itemMaxHeight) { + item.height = itemMaxHeight; + item.width = itemMaxHeight * item.aspectRatio; + } + } + + } + + function setFlexValues(mediaItems) { + + var flexSortArray = mediaItems; + var smallestImageWidth = null; + var widestImageAspectRatio = null; + + // sort array after image width with the widest image first + flexSortArray = $filter('orderBy')(flexSortArray, 'width', true); + + // find widest image aspect ratio + widestImageAspectRatio = flexSortArray[0].aspectRatio; + + // find smallest image width + smallestImageWidth = flexSortArray[flexSortArray.length - 1].width; + + for (var i = 0; flexSortArray.length > i; i++) { + + var mediaItem = flexSortArray[i]; + var flex = 1 / (widestImageAspectRatio / mediaItem.aspectRatio); + + if (flex === 0) { + flex = 1; + } + + var imageMinFlexWidth = smallestImageWidth * flex; + + var flexStyle = { + "flex": flex + " 1 " + imageMinFlexWidth + "px", + "max-width": mediaItem.width + "px", + "min-width": itemMinWidth + "px", + "min-height": itemMinHeight + "px" + }; + + mediaItem.flexStyle = flexStyle; + + } + + } + + scope.clickItem = function(item, $event, $index) { + if (scope.onClick) { + scope.onClick(item, $event, $index); + } + }; + + scope.clickItemName = function(item, $event, $index) { + if (scope.onClickName) { + scope.onClickName(item, $event, $index); + $event.stopPropagation(); + } + }; + + scope.hoverItemDetails = function(item, $event, hover) { + if (scope.onDetailsHover) { + scope.onDetailsHover(item, $event, hover); + } + }; + + var unbindItemsWatcher = scope.$watch('items', function(newValue, oldValue) { + if (angular.isArray(newValue)) { + activate(); + } + }); + + scope.$on('$destroy', function() { + unbindItemsWatcher(); + }); + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-media-grid.html', + scope: { + items: '=', + onDetailsHover: "=", + onClick: '=', + onClickName: "=", + filterBy: "=", + itemMaxWidth: "@", + itemMaxHeight: "@", + itemMinWidth: "@", + itemMinHeight: "@" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbMediaGrid', MediaGridDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbPagination +@restrict E +@scope + +@description +Use this directive to generate a pagination. + +

Markup example

+
+    
+ + + + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+
+            vm.pagination = {
+                pageNumber: 1,
+                totalPages: 10
+            }
+
+            vm.nextPage = nextPage;
+            vm.prevPage = prevPage;
+            vm.goToPage = goToPage;
+
+            function nextPage(pageNumber) {
+                // do magic here
+                console.log(pageNumber);
+                alert("nextpage");
+            }
+
+            function prevPage(pageNumber) {
+                // do magic here
+                console.log(pageNumber);
+                alert("prevpage");
+            }
+
+            function goToPage(pageNumber) {
+                // do magic here
+                console.log(pageNumber);
+                alert("go to");
+            }
+
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+    })();
+
+ +@param {number} pageNumber (binding): Current page number. +@param {number} totalPages (binding): The total number of pages. +@param {callback} onNext (binding): Callback method to go to the next page. +

The callback returns:

+
    +
  • pageNumber: The page number
  • +
+@param {callback=} onPrev (binding): Callback method to go to the previous page. +

The callback returns:

+
    +
  • pageNumber: The page number
  • +
+@param {callback=} onGoToPage (binding): Callback method to go to a specific page. +

The callback returns:

+
    +
  • pageNumber: The page number
  • +
+**/ + +(function() { + 'use strict'; + + function PaginationDirective() { + + function link(scope, el, attr, ctrl) { + + function activate() { + + scope.pagination = []; + + var i = 0; + + if (scope.totalPages <= 10) { + for (i = 0; i < scope.totalPages; i++) { + scope.pagination.push({ + val: (i + 1), + isActive: scope.pageNumber === (i + 1) + }); + } + } + else { + //if there is more than 10 pages, we need to do some fancy bits + + //get the max index to start + var maxIndex = scope.totalPages - 10; + //set the start, but it can't be below zero + var start = Math.max(scope.pageNumber - 5, 0); + //ensure that it's not too far either + start = Math.min(maxIndex, start); + + for (i = start; i < (10 + start) ; i++) { + scope.pagination.push({ + val: (i + 1), + isActive: scope.pageNumber === (i + 1) + }); + } + + //now, if the start is greater than 0 then '1' will not be displayed, so do the elipses thing + if (start > 0) { + scope.pagination.unshift({ name: "First", val: 1, isActive: false }, {val: "...",isActive: false}); + } + + //same for the end + if (start < maxIndex) { + scope.pagination.push({ val: "...", isActive: false }, { name: "Last", val: scope.totalPages, isActive: false }); + } + } + + } + + scope.next = function() { + if (scope.onNext && scope.pageNumber < scope.totalPages) { + scope.pageNumber++; + scope.onNext(scope.pageNumber); + } + }; + + scope.prev = function(pageNumber) { + if (scope.onPrev && scope.pageNumber > 1) { + scope.pageNumber--; + scope.onPrev(scope.pageNumber); + } + }; + + scope.goToPage = function(pageNumber) { + if(scope.onGoToPage) { + scope.pageNumber = pageNumber + 1; + scope.onGoToPage(scope.pageNumber); + } + }; + + var unbindPageNumberWatcher = scope.$watch('pageNumber', function(newValue, oldValue){ + activate(); + }); + + scope.$on('$destroy', function(){ + unbindPageNumberWatcher(); + }); + + activate(); + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-pagination.html', + scope: { + pageNumber: "=", + totalPages: "=", + onNext: "=", + onPrev: "=", + onGoToPage: "=" + }, + link: link + }; + + return directive; + + } + + angular.module('umbraco.directives').directive('umbPagination', PaginationDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbStickyBar +@restrict A + +@description +Use this directive make an element sticky and follow the page when scrolling. + +

Markup example

+
+    
+ +
+
+ +
+
+ +

CSS example

+
+    .my-sticky-bar {
+        padding: 15px 0;
+        background: #000000;
+        position: relative;
+        top: 0;
+    }
+
+    .my-sticky-bar.-umb-sticky-bar {
+        top: 100px;
+    }
+
+ +@param {string} scrollableContainer Set the class (".element") or the id ("#element") of the scrollable container element. +**/ + +(function() { + 'use strict'; + + function StickyBarDirective($rootScope) { + + function link(scope, el, attr, ctrl) { + + var bar = $(el); + var scrollableContainer = null; + var clonedBar = null; + var cloneIsMade = false; + var barTop = bar.context.offsetTop; + + function activate() { + + if (attr.scrollableContainer) { + scrollableContainer = $(attr.scrollableContainer); + } else { + scrollableContainer = $(window); + } + + scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll"); + $(window).on('resize.umbStickyBar', determineVisibility); + + scope.$on('$destroy', function() { + scrollableContainer.off('.umbStickyBar'); + $(window).off('.umbStickyBar'); + }); + + } + + function determineVisibility() { + + var scrollTop = scrollableContainer.scrollTop(); + + if (scrollTop > barTop) { + + if (!cloneIsMade) { + + createClone(); + + clonedBar.css({ + 'visibility': 'visible' + }); + + } else { + + calculateSize(); + + } + + } else { + + if (cloneIsMade) { + + //remove cloned element (switched places with original on creation) + bar.remove(); + bar = clonedBar; + clonedBar = null; + + bar.removeClass('-umb-sticky-bar'); + bar.css({ + position: 'relative', + 'width': 'auto', + 'height': 'auto', + 'z-index': 'auto', + 'visibility': 'visible' + }); + + cloneIsMade = false; + + } + + } + + } + + function calculateSize() { + clonedBar.css({ + width: bar.outerWidth(), + height: bar.height() + }); + } + + function createClone() { + //switch place with cloned element, to keep binding intact + clonedBar = bar; + bar = clonedBar.clone(); + clonedBar.after(bar); + clonedBar.addClass('-umb-sticky-bar'); + clonedBar.css({ + 'position': 'fixed', + 'z-index': 500, + 'visibility': 'hidden' + }); + + cloneIsMade = true; + calculateSize(); + + } + + activate(); + + } + + var directive = { + restrict: 'A', + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbStickyBar', StickyBarDirective); + +})(); + +(function() { + 'use strict'; + + function TableDirective() { + + function link(scope, el, attr, ctrl) { + + scope.clickItem = function(item, $event) { + if(scope.onClick) { + scope.onClick(item); + $event.stopPropagation(); + } + }; + + scope.selectItem = function(item, $index, $event) { + if(scope.onSelect) { + scope.onSelect(item, $index, $event); + $event.stopPropagation(); + } + }; + + scope.selectAll = function($event) { + if(scope.onSelectAll) { + scope.onSelectAll($event); + } + }; + + scope.isSelectedAll = function() { + if(scope.onSelectedAll && scope.items && scope.items.length > 0) { + return scope.onSelectedAll(); + } + }; + + scope.isSortDirection = function (col, direction) { + if (scope.onSortingDirection) { + return scope.onSortingDirection(col, direction); + } + }; + + scope.sort = function(field, allow) { + if(scope.onSort) { + scope.onSort(field, allow); + } + }; + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/umb-table.html', + scope: { + items: '=', + itemProperties: '=', + allowSelectAll: '=', + onSelect: '=', + onClick: '=', + onSelectAll: '=', + onSelectedAll: '=', + onSortingDirection: '=', + onSort: '=' + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbTable', TableDirective); + +})(); + +/** +@ngdoc directive +@name umbraco.directives.directive:umbTooltip +@restrict E +@scope + +@description +Use this directive to render a tooltip. + +

Markup example

+
+    
+ +
+ Hover me +
+ + + // tooltip content here + + +
+
+ +

Controller example

+
+    (function () {
+        "use strict";
+
+        function Controller() {
+
+            var vm = this;
+            vm.tooltip = {
+                show: false,
+                event: null
+            };
+
+            vm.mouseOver = mouseOver;
+            vm.mouseLeave = mouseLeave;
+
+            function mouseOver($event) {
+                vm.tooltip = {
+                    show: true,
+                    event: $event
+                };
+            }
+
+            function mouseLeave() {
+                vm.tooltip = {
+                    show: false,
+                    event: null
+                };
+            }
+
+        }
+
+        angular.module("umbraco").controller("My.Controller", Controller);
+
+    })();
+
+ +@param {string} event Set the $event from the target element to position the tooltip relative to the mouse cursor. +**/ + +(function() { + 'use strict'; + + function TooltipDirective($timeout) { + + function link(scope, el, attr, ctrl) { + + scope.tooltipStyles = {}; + scope.tooltipStyles.left = 0; + scope.tooltipStyles.top = 0; + + function activate() { + + $timeout(function() { + setTooltipPosition(scope.event); + }); + + } + + function setTooltipPosition(event) { + + var container = $("#contentwrapper"); + var containerLeft = container[0].offsetLeft; + var containerRight = containerLeft + container[0].offsetWidth; + var containerTop = container[0].offsetTop; + var containerBottom = containerTop + container[0].offsetHeight; + + var elementHeight = null; + var elementWidth = null; + + var position = { + right: "inherit", + left: "inherit", + top: "inherit", + bottom: "inherit" + }; + + // element size + elementHeight = el.context.clientHeight; + elementWidth = el.context.clientWidth; + + position.left = event.pageX - (elementWidth / 2); + position.top = event.pageY; + + // check to see if element is outside screen + // outside right + if (position.left + elementWidth > containerRight) { + position.right = 10; + position.left = "inherit"; + } + + // outside bottom + if (position.top + elementHeight > containerBottom) { + position.bottom = 10; + position.top = "inherit"; + } + + // outside left + if (position.left < containerLeft) { + position.left = containerLeft + 10; + position.right = "inherit"; + } + + // outside top + if (position.top < containerTop) { + position.top = 10; + position.bottom = "inherit"; + } + + scope.tooltipStyles = position; + + el.css(position); + + } + + activate(); + + } + + var directive = { + restrict: 'E', + transclude: true, + replace: true, + templateUrl: 'views/components/umb-tooltip.html', + scope: { + event: "=" + }, + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbTooltip', TooltipDirective); + +})(); + +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbFileDropzone +* @restrict E +* @function +* @description +* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form. +**/ + +/* +TODO +.directive("umbFileDrop", function ($timeout, $upload, localizationService, umbRequestHelper){ + + return{ + restrict: "A", + link: function(scope, element, attrs){ + + //load in the options model + + + } + } +}) +*/ + +angular.module("umbraco.directives") + +.directive('umbFileDropzone', function ($timeout, Upload, localizationService, umbRequestHelper) { + return { + + restrict: 'E', + replace: true, + + templateUrl: 'views/components/upload/umb-file-dropzone.html', + + scope: { + parentId: '@', + contentTypeAlias: '@', + propertyAlias: '@', + accept: '@', + maxFileSize: '@', + + compact: '@', + hideDropzone: '@', + + filesQueued: '=', + handleFile: '=', + filesUploaded: '=' + }, + + link: function(scope, element, attrs) { + + scope.queue = []; + scope.done = []; + scope.rejected = []; + scope.currentFile = undefined; + + function _filterFile(file) { + + var ignoreFileNames = ['Thumbs.db']; + var ignoreFileTypes = ['directory']; + + // ignore files with names from the list + // ignore files with types from the list + // ignore files which starts with "." + if(ignoreFileNames.indexOf(file.name) === -1 && + ignoreFileTypes.indexOf(file.type) === -1 && + file.name.indexOf(".") !== 0) { + return true; + } else { + return false; + } + + } + + function _filesQueued(files, event){ + + //Push into the queue + angular.forEach(files, function(file){ + + if(_filterFile(file) === true) { + + if(file.$error) { + scope.rejected.push(file); + } else { + scope.queue.push(file); + } + + } + + }); + + //when queue is done, kick the uploader + if(!scope.working){ + _processQueueItem(); + } + } + + + function _processQueueItem(){ + + if(scope.queue.length > 0){ + scope.currentFile = scope.queue.shift(); + _upload(scope.currentFile); + }else if(scope.done.length > 0){ + + if(scope.filesUploaded){ + //queue is empty, trigger the done action + scope.filesUploaded(scope.done); + } + + //auto-clear the done queue after 3 secs + var currentLength = scope.done.length; + $timeout(function(){ + scope.done.splice(0, currentLength); + }, 3000); + } + } + + function _upload(file) { + + scope.propertyAlias = scope.propertyAlias ? scope.propertyAlias : "umbracoFile"; + scope.contentTypeAlias = scope.contentTypeAlias ? scope.contentTypeAlias : "Image"; + + Upload.upload({ + url: umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostAddFile"), + fields: { + 'currentFolder': scope.parentId, + 'contentTypeAlias': scope.contentTypeAlias, + 'propertyAlias': scope.propertyAlias, + 'path': file.path + }, + file: file + }).progress(function (evt) { + + // calculate progress in percentage + var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10); + + // set percentage property on file + file.uploadProgress = progressPercentage; + + // set uploading status on file + file.uploadStatus = "uploading"; + + }).success(function (data, status, headers, config) { + + if(data.notifications && data.notifications.length > 0) { + + // set error status on file + file.uploadStatus = "error"; + + // Throw message back to user with the cause of the error + file.serverErrorMessage = data.notifications[0].message; + + // Put the file in the rejected pool + scope.rejected.push(file); + + } else { + + // set done status on file + file.uploadStatus = "done"; + + // set date/time for when done - used for sorting + file.doneDate = new Date(); + + // Put the file in the done pool + scope.done.push(file); + + } + + scope.currentFile = undefined; + + //after processing, test if everthing is done + _processQueueItem(); + + }).error( function (evt, status, headers, config) { + + // set status done + file.uploadStatus = "error"; + + //if the service returns a detailed error + if (evt.InnerException) { + file.serverErrorMessage = evt.InnerException.ExceptionMessage; + + //Check if its the common "too large file" exception + if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) { + file.serverErrorMessage = "File too large to upload"; + } + + } else if (evt.Message) { + file.serverErrorMessage = evt.Message; + } + + // If file not found, server will return a 404 and display this message + if(status === 404 ) { + file.serverErrorMessage = "File not found"; + } + + //after processing, test if everthing is done + scope.rejected.push(file); + scope.currentFile = undefined; + + _processQueueItem(); + }); + } + + + scope.handleFiles = function(files, event){ + if(scope.filesQueued){ + scope.filesQueued(files, event); + } + + _filesQueued(files, event); + + }; + + } + + + }; + }); /** * @ngdoc directive @@ -9640,209 +9640,209 @@ function umbFileUpload() { } angular.module('umbraco.directives').directive("umbFileUpload", umbFileUpload); -/** -* @ngdoc directive -* @name umbraco.directives.directive:umbSingleFileUpload -* @function -* @restrict A -* @scope -* @description -* A single file upload field that will reset itself based on the object passed in for the rebuild parameter. This -* is required because the only way to reset an upload control is to replace it's html. -**/ -function umbSingleFileUpload($compile) { - return { - restrict: "E", - scope: { - rebuild: "=" - }, - replace: true, - template: "
", - link: function (scope, el, attrs) { - - scope.$watch("rebuild", function (newVal, oldVal) { - if (newVal && newVal !== oldVal) { - //recompile it! - el.html(""); - $compile(el.contents())(scope); - } - }); - - } - }; -} - +/** +* @ngdoc directive +* @name umbraco.directives.directive:umbSingleFileUpload +* @function +* @restrict A +* @scope +* @description +* A single file upload field that will reset itself based on the object passed in for the rebuild parameter. This +* is required because the only way to reset an upload control is to replace it's html. +**/ +function umbSingleFileUpload($compile) { + return { + restrict: "E", + scope: { + rebuild: "=" + }, + replace: true, + template: "
", + link: function (scope, el, attrs) { + + scope.$watch("rebuild", function (newVal, oldVal) { + if (newVal && newVal !== oldVal) { + //recompile it! + el.html(""); + $compile(el.contents())(scope); + } + }); + + } + }; +} + angular.module('umbraco.directives').directive("umbSingleFileUpload", umbSingleFileUpload); -/** -* @ngdoc directive -* @name umbraco.directives.directive:noDirtyCheck -* @restrict A -* @description Can be attached to form inputs to prevent them from setting the form as dirty (http://stackoverflow.com/questions/17089090/prevent-input-from-setting-form-dirty-angularjs) -**/ -function noDirtyCheck() { - return { - restrict: 'A', - require: 'ngModel', - link: function (scope, elm, attrs, ctrl) { - elm.focus(function () { - ctrl.$pristine = false; - }); - } - }; -} +/** +* @ngdoc directive +* @name umbraco.directives.directive:noDirtyCheck +* @restrict A +* @description Can be attached to form inputs to prevent them from setting the form as dirty (http://stackoverflow.com/questions/17089090/prevent-input-from-setting-form-dirty-angularjs) +**/ +function noDirtyCheck() { + return { + restrict: 'A', + require: 'ngModel', + link: function (scope, elm, attrs, ctrl) { + elm.focus(function () { + ctrl.$pristine = false; + }); + } + }; +} angular.module('umbraco.directives.validation').directive("noDirtyCheck", noDirtyCheck); -(function() { - 'use strict'; +(function() { + 'use strict'; + + function SetDirtyOnChange() { + + function link(scope, el, attr, ctrl) { + + var initValue = attr.umbSetDirtyOnChange; + + attr.$observe("umbSetDirtyOnChange", function (newValue) { + if(newValue !== initValue) { + ctrl.$setDirty(); + } + }); + + } + + var directive = { + require: "^form", + restrict: 'A', + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbSetDirtyOnChange', SetDirtyOnChange); + +})(); - function SetDirtyOnChange() { - - function link(scope, el, attr, ctrl) { - - var initValue = attr.umbSetDirtyOnChange; - - attr.$observe("umbSetDirtyOnChange", function (newValue) { - if(newValue !== initValue) { - ctrl.$setDirty(); - } - }); - - } - - var directive = { - require: "^form", - restrict: 'A', - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('umbSetDirtyOnChange', SetDirtyOnChange); - -})(); - -/** - * General-purpose validator for ngModel. - * angular.js comes with several built-in validation mechanism for input fields (ngRequired, ngPattern etc.) but using - * an arbitrary validation function requires creation of a custom formatters and / or parsers. - * The ui-validate directive makes it easy to use any function(s) defined in scope as a validator function(s). - * A validator function will trigger validation on both model and input changes. - * - * @example - * @example - * @example - * @example - * - * @param val-custom {string|object literal} If strings is passed it should be a scope's function to be used as a validator. - * If an object literal is passed a key denotes a validation error key while a value should be a validator function. - * In both cases validator function should take a value to validate as its argument and should return true/false indicating a validation result. - */ - - /* - This code comes from the angular UI project, we had to change the directive name and module - but other then that its unmodified - */ -angular.module('umbraco.directives.validation') -.directive('valCustom', function () { - - return { - restrict: 'A', - require: 'ngModel', - link: function (scope, elm, attrs, ctrl) { - var validateFn, watch, validators = {}, - validateExpr = scope.$eval(attrs.valCustom); - - if (!validateExpr){ return;} - - if (angular.isString(validateExpr)) { - validateExpr = { validator: validateExpr }; - } - - angular.forEach(validateExpr, function (exprssn, key) { - validateFn = function (valueToValidate) { - var expression = scope.$eval(exprssn, { '$value' : valueToValidate }); - if (angular.isObject(expression) && angular.isFunction(expression.then)) { - // expression is a promise - expression.then(function(){ - ctrl.$setValidity(key, true); - }, function(){ - ctrl.$setValidity(key, false); - }); - return valueToValidate; - } else if (expression) { - // expression is true - ctrl.$setValidity(key, true); - return valueToValidate; - } else { - // expression is false - ctrl.$setValidity(key, false); - return undefined; - } - }; - validators[key] = validateFn; - - ctrl.$parsers.push(validateFn); - }); - - function apply_watch(watch) - { - //string - update all validators on expression change - if (angular.isString(watch)) - { - scope.$watch(watch, function(){ - angular.forEach(validators, function(validatorFn){ - validatorFn(ctrl.$modelValue); - }); - }); - return; - } - - //array - update all validators on change of any expression - if (angular.isArray(watch)) - { - angular.forEach(watch, function(expression){ - scope.$watch(expression, function() - { - angular.forEach(validators, function(validatorFn){ - validatorFn(ctrl.$modelValue); - }); - }); - }); - return; - } - - //object - update appropriate validator - if (angular.isObject(watch)) - { - angular.forEach(watch, function(expression, validatorKey) - { - //value is string - look after one expression - if (angular.isString(expression)) - { - scope.$watch(expression, function(){ - validators[validatorKey](ctrl.$modelValue); - }); - } - - //value is array - look after all expressions in array - if (angular.isArray(expression)) - { - angular.forEach(expression, function(intExpression) - { - scope.$watch(intExpression, function(){ - validators[validatorKey](ctrl.$modelValue); - }); - }); - } - }); - } - } - // Support for val-custom-watch - if (attrs.valCustomWatch){ - apply_watch( scope.$eval(attrs.valCustomWatch) ); - } - } - }; +/** + * General-purpose validator for ngModel. + * angular.js comes with several built-in validation mechanism for input fields (ngRequired, ngPattern etc.) but using + * an arbitrary validation function requires creation of a custom formatters and / or parsers. + * The ui-validate directive makes it easy to use any function(s) defined in scope as a validator function(s). + * A validator function will trigger validation on both model and input changes. + * + * @example + * @example + * @example + * @example + * + * @param val-custom {string|object literal} If strings is passed it should be a scope's function to be used as a validator. + * If an object literal is passed a key denotes a validation error key while a value should be a validator function. + * In both cases validator function should take a value to validate as its argument and should return true/false indicating a validation result. + */ + + /* + This code comes from the angular UI project, we had to change the directive name and module + but other then that its unmodified + */ +angular.module('umbraco.directives.validation') +.directive('valCustom', function () { + + return { + restrict: 'A', + require: 'ngModel', + link: function (scope, elm, attrs, ctrl) { + var validateFn, watch, validators = {}, + validateExpr = scope.$eval(attrs.valCustom); + + if (!validateExpr){ return;} + + if (angular.isString(validateExpr)) { + validateExpr = { validator: validateExpr }; + } + + angular.forEach(validateExpr, function (exprssn, key) { + validateFn = function (valueToValidate) { + var expression = scope.$eval(exprssn, { '$value' : valueToValidate }); + if (angular.isObject(expression) && angular.isFunction(expression.then)) { + // expression is a promise + expression.then(function(){ + ctrl.$setValidity(key, true); + }, function(){ + ctrl.$setValidity(key, false); + }); + return valueToValidate; + } else if (expression) { + // expression is true + ctrl.$setValidity(key, true); + return valueToValidate; + } else { + // expression is false + ctrl.$setValidity(key, false); + return undefined; + } + }; + validators[key] = validateFn; + + ctrl.$parsers.push(validateFn); + }); + + function apply_watch(watch) + { + //string - update all validators on expression change + if (angular.isString(watch)) + { + scope.$watch(watch, function(){ + angular.forEach(validators, function(validatorFn){ + validatorFn(ctrl.$modelValue); + }); + }); + return; + } + + //array - update all validators on change of any expression + if (angular.isArray(watch)) + { + angular.forEach(watch, function(expression){ + scope.$watch(expression, function() + { + angular.forEach(validators, function(validatorFn){ + validatorFn(ctrl.$modelValue); + }); + }); + }); + return; + } + + //object - update appropriate validator + if (angular.isObject(watch)) + { + angular.forEach(watch, function(expression, validatorKey) + { + //value is string - look after one expression + if (angular.isString(expression)) + { + scope.$watch(expression, function(){ + validators[validatorKey](ctrl.$modelValue); + }); + } + + //value is array - look after all expressions in array + if (angular.isArray(expression)) + { + angular.forEach(expression, function(intExpression) + { + scope.$watch(intExpression, function(){ + validators[validatorKey](ctrl.$modelValue); + }); + }); + } + }); + } + } + // Support for val-custom-watch + if (attrs.valCustomWatch){ + apply_watch( scope.$eval(attrs.valCustomWatch) ); + } + } + }; }); /** * @ngdoc directive @@ -9897,48 +9897,48 @@ angular.module('umbraco.directives.validation') } }; }); -/** - * @ngdoc directive - * @name umbraco.directives.directive:valEmail - * @restrict A - * @description A custom directive to validate an email address string, this is required because angular's default validator is incorrect. - **/ -function valEmail(valEmailExpression) { - - return { - require: 'ngModel', - restrict: "A", - link: function (scope, elm, attrs, ctrl) { - - var patternValidator = function (viewValue) { - //NOTE: we don't validate on empty values, use required validator for that - if (!viewValue || valEmailExpression.EMAIL_REGEXP.test(viewValue)) { - // it is valid - ctrl.$setValidity('valEmail', true); - //assign a message to the validator - ctrl.errorMsg = ""; - return viewValue; - } - else { - // it is invalid, return undefined (no model update) - ctrl.$setValidity('valEmail', false); - //assign a message to the validator - ctrl.errorMsg = "Invalid email"; - return undefined; - } - }; - - ctrl.$parsers.push(patternValidator); - } - }; -} - -angular.module('umbraco.directives.validation') - .directive("valEmail", valEmail) - .factory('valEmailExpression', function() { - return { - EMAIL_REGEXP: /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i - }; +/** + * @ngdoc directive + * @name umbraco.directives.directive:valEmail + * @restrict A + * @description A custom directive to validate an email address string, this is required because angular's default validator is incorrect. + **/ +function valEmail(valEmailExpression) { + + return { + require: 'ngModel', + restrict: "A", + link: function (scope, elm, attrs, ctrl) { + + var patternValidator = function (viewValue) { + //NOTE: we don't validate on empty values, use required validator for that + if (!viewValue || valEmailExpression.EMAIL_REGEXP.test(viewValue)) { + // it is valid + ctrl.$setValidity('valEmail', true); + //assign a message to the validator + ctrl.errorMsg = ""; + return viewValue; + } + else { + // it is invalid, return undefined (no model update) + ctrl.$setValidity('valEmail', false); + //assign a message to the validator + ctrl.errorMsg = "Invalid email"; + return undefined; + } + }; + + ctrl.$parsers.push(patternValidator); + } + }; +} + +angular.module('umbraco.directives.validation') + .directive("valEmail", valEmail) + .factory('valEmailExpression', function() { + return { + EMAIL_REGEXP: /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i + }; }); /** * @ngdoc directive @@ -10269,74 +10269,74 @@ function valPropertyMsg(serverValidationManager) { }; } angular.module('umbraco.directives.validation').directive("valPropertyMsg", valPropertyMsg); -/** -* @ngdoc directive -* @name umbraco.directives.directive:valPropertyValidator -* @restrict A -* @description Performs any custom property value validation checks on the client side. This allows property editors to be highly flexible when it comes to validation - on the client side. Typically if a property editor stores a primitive value (i.e. string) then the client side validation can easily be taken care of - with standard angular directives such as ng-required. However since some property editors store complex data such as JSON, a given property editor - might require custom validation. This directive can be used to validate an Umbraco property in any way that a developer would like by specifying a - callback method to perform the validation. The result of this method must return an object in the format of - {isValid: true, errorKey: 'required', errorMsg: 'Something went wrong' } - The error message returned will also be displayed for the property level validation message. - This directive should only be used when dealing with complex models, if custom validation needs to be performed with primitive values, use the simpler - angular validation directives instead since this will watch the entire model. -**/ - -function valPropertyValidator(serverValidationManager) { - return { - scope: { - valPropertyValidator: "=" - }, - - // The element must have ng-model attribute and be inside an umbProperty directive - require: ['ngModel', '?^umbProperty'], - - restrict: "A", - - link: function (scope, element, attrs, ctrls) { - - var modelCtrl = ctrls[0]; - var propCtrl = ctrls.length > 1 ? ctrls[1] : null; - - // Check whether the scope has a valPropertyValidator method - if (!scope.valPropertyValidator || !angular.isFunction(scope.valPropertyValidator)) { - throw new Error('val-property-validator directive must specify a function to call'); - } - - var initResult = scope.valPropertyValidator(); - - // Validation method - var validate = function (viewValue) { - // Calls the validition method - var result = scope.valPropertyValidator(); - if (!result.errorKey || result.isValid === undefined || !result.errorMsg) { - throw "The result object from valPropertyValidator does not contain required properties: isValid, errorKey, errorMsg"; - } - if (result.isValid === true) { - // Tell the controller that the value is valid - modelCtrl.$setValidity(result.errorKey, true); - if (propCtrl) { - propCtrl.setPropertyError(null); - } - } - else { - // Tell the controller that the value is invalid - modelCtrl.$setValidity(result.errorKey, false); - if (propCtrl) { - propCtrl.setPropertyError(result.errorMsg); - } - } - }; - - // Parsers are called as soon as the value in the form input is modified - modelCtrl.$parsers.push(validate); - - } - }; -} -angular.module('umbraco.directives.validation').directive("valPropertyValidator", valPropertyValidator); +/** +* @ngdoc directive +* @name umbraco.directives.directive:valPropertyValidator +* @restrict A +* @description Performs any custom property value validation checks on the client side. This allows property editors to be highly flexible when it comes to validation + on the client side. Typically if a property editor stores a primitive value (i.e. string) then the client side validation can easily be taken care of + with standard angular directives such as ng-required. However since some property editors store complex data such as JSON, a given property editor + might require custom validation. This directive can be used to validate an Umbraco property in any way that a developer would like by specifying a + callback method to perform the validation. The result of this method must return an object in the format of + {isValid: true, errorKey: 'required', errorMsg: 'Something went wrong' } + The error message returned will also be displayed for the property level validation message. + This directive should only be used when dealing with complex models, if custom validation needs to be performed with primitive values, use the simpler + angular validation directives instead since this will watch the entire model. +**/ + +function valPropertyValidator(serverValidationManager) { + return { + scope: { + valPropertyValidator: "=" + }, + + // The element must have ng-model attribute and be inside an umbProperty directive + require: ['ngModel', '?^umbProperty'], + + restrict: "A", + + link: function (scope, element, attrs, ctrls) { + + var modelCtrl = ctrls[0]; + var propCtrl = ctrls.length > 1 ? ctrls[1] : null; + + // Check whether the scope has a valPropertyValidator method + if (!scope.valPropertyValidator || !angular.isFunction(scope.valPropertyValidator)) { + throw new Error('val-property-validator directive must specify a function to call'); + } + + var initResult = scope.valPropertyValidator(); + + // Validation method + var validate = function (viewValue) { + // Calls the validition method + var result = scope.valPropertyValidator(); + if (!result.errorKey || result.isValid === undefined || !result.errorMsg) { + throw "The result object from valPropertyValidator does not contain required properties: isValid, errorKey, errorMsg"; + } + if (result.isValid === true) { + // Tell the controller that the value is valid + modelCtrl.$setValidity(result.errorKey, true); + if (propCtrl) { + propCtrl.setPropertyError(null); + } + } + else { + // Tell the controller that the value is invalid + modelCtrl.$setValidity(result.errorKey, false); + if (propCtrl) { + propCtrl.setPropertyError(result.errorMsg); + } + } + }; + + // Parsers are called as soon as the value in the form input is modified + modelCtrl.$parsers.push(validate); + + } + }; +} +angular.module('umbraco.directives.validation').directive("valPropertyValidator", valPropertyValidator); /** * @ngdoc directive @@ -10705,167 +10705,167 @@ function valToggleMsg(serverValidationManager) { * @description This directive will show/hide an error based on: is the value + the given validator invalid? AND, has the form been submitted ? **/ angular.module('umbraco.directives.validation').directive("valToggleMsg", valToggleMsg); -angular.module('umbraco.directives.validation') -.directive('valTriggerChange', function($sniffer) { - return { - link : function(scope, elem, attrs) { - elem.bind('click', function(){ - $(attrs.valTriggerChange).trigger($sniffer.hasEvent('input') ? 'input' : 'change'); - }); - }, - priority : 1 - }; +angular.module('umbraco.directives.validation') +.directive('valTriggerChange', function($sniffer) { + return { + link : function(scope, elem, attrs) { + elem.bind('click', function(){ + $(attrs.valTriggerChange).trigger($sniffer.hasEvent('input') ? 'input' : 'change'); + }); + }, + priority : 1 + }; }); -/** - * Konami Code directive for AngularJS - * @version v0.0.1 - * @license MIT License, http://www.opensource.org/licenses/MIT - */ - -angular.module('umbraco.directives') - .directive('konamiCode', ['$document', function ($document) { - var konamiKeysDefault = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]; - - return { - restrict: 'A', - link: function (scope, element, attr) { - - if (!attr.konamiCode) { - throw ('Konami directive must receive an expression as value.'); - } - - // Let user define a custom code. - var konamiKeys = attr.konamiKeys || konamiKeysDefault; - var keyIndex = 0; - - /** - * Fired when konami code is type. - */ - function activated() { - if ('konamiOnce' in attr) { - stopListening(); - } - // Execute expression. - scope.$eval(attr.konamiCode); - } - - /** - * Handle keydown events. - */ - function keydown(e) { - if (e.keyCode === konamiKeys[keyIndex++]) { - if (keyIndex === konamiKeys.length) { - keyIndex = 0; - activated(); - } - } else { - keyIndex = 0; - } - } - - /** - * Stop to listen typing. - */ - function stopListening() { - $document.off('keydown', keydown); - } - - // Start listening to key typing. - $document.on('keydown', keydown); - - // Stop listening when scope is destroyed. - scope.$on('$destroy', stopListening); - } - }; +/** + * Konami Code directive for AngularJS + * @version v0.0.1 + * @license MIT License, http://www.opensource.org/licenses/MIT + */ + +angular.module('umbraco.directives') + .directive('konamiCode', ['$document', function ($document) { + var konamiKeysDefault = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]; + + return { + restrict: 'A', + link: function (scope, element, attr) { + + if (!attr.konamiCode) { + throw ('Konami directive must receive an expression as value.'); + } + + // Let user define a custom code. + var konamiKeys = attr.konamiKeys || konamiKeysDefault; + var keyIndex = 0; + + /** + * Fired when konami code is type. + */ + function activated() { + if ('konamiOnce' in attr) { + stopListening(); + } + // Execute expression. + scope.$eval(attr.konamiCode); + } + + /** + * Handle keydown events. + */ + function keydown(e) { + if (e.keyCode === konamiKeys[keyIndex++]) { + if (keyIndex === konamiKeys.length) { + keyIndex = 0; + activated(); + } + } else { + keyIndex = 0; + } + } + + /** + * Stop to listen typing. + */ + function stopListening() { + $document.off('keydown', keydown); + } + + // Start listening to key typing. + $document.on('keydown', keydown); + + // Stop listening when scope is destroyed. + scope.$on('$destroy', stopListening); + } + }; }]); -(function() { - 'use strict'; +(function() { + 'use strict'; + + function ValRequireComponentDirective() { + + function link(scope, el, attr, ngModel) { + + var unbindModelWatcher = scope.$watch(function () { + return ngModel.$modelValue; + }, function(newValue) { + + if(newValue === undefined || newValue === null || newValue === "") { + ngModel.$setValidity("valRequiredComponent", false); + } else { + ngModel.$setValidity("valRequiredComponent", true); + } + + }); + + // clean up + scope.$on('$destroy', function(){ + unbindModelWatcher(); + }); + + } + + var directive = { + require: 'ngModel', + restrict: "A", + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('valRequireComponent', ValRequireComponentDirective); + +})(); - function ValRequireComponentDirective() { - - function link(scope, el, attr, ngModel) { - - var unbindModelWatcher = scope.$watch(function () { - return ngModel.$modelValue; - }, function(newValue) { - - if(newValue === undefined || newValue === null || newValue === "") { - ngModel.$setValidity("valRequiredComponent", false); - } else { - ngModel.$setValidity("valRequiredComponent", true); - } - - }); - - // clean up - scope.$on('$destroy', function(){ - unbindModelWatcher(); - }); - - } - - var directive = { - require: 'ngModel', - restrict: "A", - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('valRequireComponent', ValRequireComponentDirective); - -})(); - -/** -* @ngdoc directive -* @name umbraco.directives.directive:valSubView -* @restrict A -* @description Used to show validation warnings for a editor sub view to indicate that the section content has validation errors in its data. -* In order for this directive to work, the valFormManager directive must be placed on the containing form. -**/ -(function() { - 'use strict'; - - function valSubViewDirective() { - - function link(scope, el, attr, ctrl) { - - var valFormManager = ctrl[1]; - scope.subView.hasError = false; - - //listen for form validation changes - valFormManager.onValidationStatusChanged(function (evt, args) { - if (!args.form.$valid) { - - var subViewContent = el.find(".ng-invalid"); - - if (subViewContent.length > 0) { - scope.subView.hasError = true; - } else { - scope.subView.hasError = false; - } - - } - else { - scope.subView.hasError = false; - } - }); - - } - - var directive = { - require: ['^form', '^valFormManager'], - restrict: "A", - link: link - }; - - return directive; - } - - angular.module('umbraco.directives').directive('valSubView', valSubViewDirective); - -})(); +/** +* @ngdoc directive +* @name umbraco.directives.directive:valSubView +* @restrict A +* @description Used to show validation warnings for a editor sub view to indicate that the section content has validation errors in its data. +* In order for this directive to work, the valFormManager directive must be placed on the containing form. +**/ +(function() { + 'use strict'; + + function valSubViewDirective() { + + function link(scope, el, attr, ctrl) { + + var valFormManager = ctrl[1]; + scope.subView.hasError = false; + + //listen for form validation changes + valFormManager.onValidationStatusChanged(function (evt, args) { + if (!args.form.$valid) { + + var subViewContent = el.find(".ng-invalid"); + + if (subViewContent.length > 0) { + scope.subView.hasError = true; + } else { + scope.subView.hasError = false; + } + + } + else { + scope.subView.hasError = false; + } + }); + + } + + var directive = { + require: ['^form', '^valFormManager'], + restrict: "A", + link: link + }; + + return directive; + } + + angular.module('umbraco.directives').directive('valSubView', valSubViewDirective); + +})(); })(); \ No newline at end of file diff --git a/OurUmbraco.Site/umbraco/js/umbraco.services.js b/OurUmbraco.Site/umbraco/js/umbraco.services.js index f75b1a11..91857444 100644 --- a/OurUmbraco.Site/umbraco/js/umbraco.services.js +++ b/OurUmbraco.Site/umbraco/js/umbraco.services.js @@ -164,303 +164,303 @@ function angularHelper($log, $q) { }; } angular.module('umbraco.services').factory('angularHelper', angularHelper); -/** - * @ngdoc service - * @name umbraco.services.appState - * @function - * - * @description - * Tracks the various application state variables when working in the back office, raises events when state changes. - * - * ##Samples - * - * ####Subscribe to global state changes: - * - *
-  *    scope.showTree = appState.getGlobalState("showNavigation");
-  *
-  *    eventsService.on("appState.globalState.changed", function (e, args) {
-  *               if (args.key === "showNavigation") {
-  *                   scope.showTree = args.value;
-  *               }
-  *           });  
-  * 
- * - * ####Subscribe to section-state changes - * - *
- *    scope.currentSection = appState.getSectionState("currentSection");
- *
- *    eventsService.on("appState.sectionState.changed", function (e, args) {
- *               if (args.key === "currentSection") {
- *                   scope.currentSection = args.value;
- *               }
- *           });  
- * 
- */ -function appState(eventsService) { - - //Define all variables here - we are never returning this objects so they cannot be publicly mutable - // changed, we only expose methods to interact with the values. - - var globalState = { - showNavigation: null, - touchDevice: null, - showTray: null, - stickyNavigation: null, - navMode: null, - isReady: null, - isTablet: null - }; - - var sectionState = { - //The currently active section - currentSection: null, - showSearchResults: null - }; - - var treeState = { - //The currently selected node - selectedNode: null, - //The currently loaded root node reference - depending on the section loaded this could be a section root or a normal root. - //We keep this reference so we can lookup nodes to interact with in the UI via the tree service - currentRootNode: null - }; - - var menuState = { - //this list of menu items to display - menuActions: null, - //the title to display in the context menu dialog - dialogTitle: null, - //The tree node that the ctx menu is launched for - currentNode: null, - //Whether the menu's dialog is being shown or not - showMenuDialog: null, - //Whether the context menu is being shown or not - showMenu: null - }; - - /** function to validate and set the state on a state object */ - function setState(stateObj, key, value, stateObjName) { - if (!_.has(stateObj, key)) { - throw "The variable " + key + " does not exist in " + stateObjName; - } - var changed = stateObj[key] !== value; - stateObj[key] = value; - if (changed) { - eventsService.emit("appState." + stateObjName + ".changed", { key: key, value: value }); - } - } - - /** function to validate and set the state on a state object */ - function getState(stateObj, key, stateObjName) { - if (!_.has(stateObj, key)) { - throw "The variable " + key + " does not exist in " + stateObjName; - } - return stateObj[key]; - } - - return { - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#getGlobalState - * @methodOf umbraco.services.appState - * @function - * - * @description - * Returns the current global state value by key - we do not return an object reference here - we do NOT want this - * to be publicly mutable and allow setting arbitrary values - * - */ - getGlobalState: function (key) { - return getState(globalState, key, "globalState"); - }, - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#setGlobalState - * @methodOf umbraco.services.appState - * @function - * - * @description - * Sets a global state value by key - * - */ - setGlobalState: function (key, value) { - setState(globalState, key, value, "globalState"); - }, - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#getSectionState - * @methodOf umbraco.services.appState - * @function - * - * @description - * Returns the current section state value by key - we do not return an object here - we do NOT want this - * to be publicly mutable and allow setting arbitrary values - * - */ - getSectionState: function (key) { - return getState(sectionState, key, "sectionState"); - }, - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#setSectionState - * @methodOf umbraco.services.appState - * @function - * - * @description - * Sets a section state value by key - * - */ - setSectionState: function(key, value) { - setState(sectionState, key, value, "sectionState"); - }, - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#getTreeState - * @methodOf umbraco.services.appState - * @function - * - * @description - * Returns the current tree state value by key - we do not return an object here - we do NOT want this - * to be publicly mutable and allow setting arbitrary values - * - */ - getTreeState: function (key) { - return getState(treeState, key, "treeState"); - }, - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#setTreeState - * @methodOf umbraco.services.appState - * @function - * - * @description - * Sets a section state value by key - * - */ - setTreeState: function (key, value) { - setState(treeState, key, value, "treeState"); - }, - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#getMenuState - * @methodOf umbraco.services.appState - * @function - * - * @description - * Returns the current menu state value by key - we do not return an object here - we do NOT want this - * to be publicly mutable and allow setting arbitrary values - * - */ - getMenuState: function (key) { - return getState(menuState, key, "menuState"); - }, - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#setMenuState - * @methodOf umbraco.services.appState - * @function - * - * @description - * Sets a section state value by key - * - */ - setMenuState: function (key, value) { - setState(menuState, key, value, "menuState"); - }, - - }; -} -angular.module('umbraco.services').factory('appState', appState); - -/** - * @ngdoc service - * @name umbraco.services.editorState - * @function - * - * @description - * Tracks the parent object for complex editors by exposing it as - * an object reference via editorState.current.entity - * - * it is possible to modify this object, so should be used with care - */ -angular.module('umbraco.services').factory("editorState", function() { - - var current = null; - var state = { - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#set - * @methodOf umbraco.services.editorState - * @function - * - * @description - * Sets the current entity object for the currently active editor - * This is only used when implementing an editor with a complex model - * like the content editor, where the model is modified by several - * child controllers. - */ - set: function (entity) { - current = entity; - }, - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#reset - * @methodOf umbraco.services.editorState - * @function - * - * @description - * Since the editorstate entity is read-only, you cannot set it to null - * only through the reset() method - */ - reset: function() { - current = null; - }, - - /** - * @ngdoc function - * @name umbraco.services.angularHelper#getCurrent - * @methodOf umbraco.services.editorState - * @function - * - * @description - * Returns an object reference to the current editor entity. - * the entity is the root object of the editor. - * EditorState is used by property/parameter editors that need - * access to the entire entity being edited, not just the property/parameter - * - * editorState.current can not be overwritten, you should only read values from it - * since modifying individual properties should be handled by the property editors - */ - getCurrent: function() { - return current; - } - }; - - //TODO: This shouldn't be removed! use getCurrent() method instead of a hacked readonly property which is confusing. - - //create a get/set property but don't allow setting - Object.defineProperty(state, "current", { - get: function () { - return current; - }, - set: function (value) { - throw "Use editorState.set to set the value of the current entity"; - }, - }); - - return state; +/** + * @ngdoc service + * @name umbraco.services.appState + * @function + * + * @description + * Tracks the various application state variables when working in the back office, raises events when state changes. + * + * ##Samples + * + * ####Subscribe to global state changes: + * + *
+  *    scope.showTree = appState.getGlobalState("showNavigation");
+  *
+  *    eventsService.on("appState.globalState.changed", function (e, args) {
+  *               if (args.key === "showNavigation") {
+  *                   scope.showTree = args.value;
+  *               }
+  *           });  
+  * 
+ * + * ####Subscribe to section-state changes + * + *
+ *    scope.currentSection = appState.getSectionState("currentSection");
+ *
+ *    eventsService.on("appState.sectionState.changed", function (e, args) {
+ *               if (args.key === "currentSection") {
+ *                   scope.currentSection = args.value;
+ *               }
+ *           });  
+ * 
+ */ +function appState(eventsService) { + + //Define all variables here - we are never returning this objects so they cannot be publicly mutable + // changed, we only expose methods to interact with the values. + + var globalState = { + showNavigation: null, + touchDevice: null, + showTray: null, + stickyNavigation: null, + navMode: null, + isReady: null, + isTablet: null + }; + + var sectionState = { + //The currently active section + currentSection: null, + showSearchResults: null + }; + + var treeState = { + //The currently selected node + selectedNode: null, + //The currently loaded root node reference - depending on the section loaded this could be a section root or a normal root. + //We keep this reference so we can lookup nodes to interact with in the UI via the tree service + currentRootNode: null + }; + + var menuState = { + //this list of menu items to display + menuActions: null, + //the title to display in the context menu dialog + dialogTitle: null, + //The tree node that the ctx menu is launched for + currentNode: null, + //Whether the menu's dialog is being shown or not + showMenuDialog: null, + //Whether the context menu is being shown or not + showMenu: null + }; + + /** function to validate and set the state on a state object */ + function setState(stateObj, key, value, stateObjName) { + if (!_.has(stateObj, key)) { + throw "The variable " + key + " does not exist in " + stateObjName; + } + var changed = stateObj[key] !== value; + stateObj[key] = value; + if (changed) { + eventsService.emit("appState." + stateObjName + ".changed", { key: key, value: value }); + } + } + + /** function to validate and set the state on a state object */ + function getState(stateObj, key, stateObjName) { + if (!_.has(stateObj, key)) { + throw "The variable " + key + " does not exist in " + stateObjName; + } + return stateObj[key]; + } + + return { + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#getGlobalState + * @methodOf umbraco.services.appState + * @function + * + * @description + * Returns the current global state value by key - we do not return an object reference here - we do NOT want this + * to be publicly mutable and allow setting arbitrary values + * + */ + getGlobalState: function (key) { + return getState(globalState, key, "globalState"); + }, + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#setGlobalState + * @methodOf umbraco.services.appState + * @function + * + * @description + * Sets a global state value by key + * + */ + setGlobalState: function (key, value) { + setState(globalState, key, value, "globalState"); + }, + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#getSectionState + * @methodOf umbraco.services.appState + * @function + * + * @description + * Returns the current section state value by key - we do not return an object here - we do NOT want this + * to be publicly mutable and allow setting arbitrary values + * + */ + getSectionState: function (key) { + return getState(sectionState, key, "sectionState"); + }, + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#setSectionState + * @methodOf umbraco.services.appState + * @function + * + * @description + * Sets a section state value by key + * + */ + setSectionState: function(key, value) { + setState(sectionState, key, value, "sectionState"); + }, + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#getTreeState + * @methodOf umbraco.services.appState + * @function + * + * @description + * Returns the current tree state value by key - we do not return an object here - we do NOT want this + * to be publicly mutable and allow setting arbitrary values + * + */ + getTreeState: function (key) { + return getState(treeState, key, "treeState"); + }, + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#setTreeState + * @methodOf umbraco.services.appState + * @function + * + * @description + * Sets a section state value by key + * + */ + setTreeState: function (key, value) { + setState(treeState, key, value, "treeState"); + }, + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#getMenuState + * @methodOf umbraco.services.appState + * @function + * + * @description + * Returns the current menu state value by key - we do not return an object here - we do NOT want this + * to be publicly mutable and allow setting arbitrary values + * + */ + getMenuState: function (key) { + return getState(menuState, key, "menuState"); + }, + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#setMenuState + * @methodOf umbraco.services.appState + * @function + * + * @description + * Sets a section state value by key + * + */ + setMenuState: function (key, value) { + setState(menuState, key, value, "menuState"); + }, + + }; +} +angular.module('umbraco.services').factory('appState', appState); + +/** + * @ngdoc service + * @name umbraco.services.editorState + * @function + * + * @description + * Tracks the parent object for complex editors by exposing it as + * an object reference via editorState.current.entity + * + * it is possible to modify this object, so should be used with care + */ +angular.module('umbraco.services').factory("editorState", function() { + + var current = null; + var state = { + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#set + * @methodOf umbraco.services.editorState + * @function + * + * @description + * Sets the current entity object for the currently active editor + * This is only used when implementing an editor with a complex model + * like the content editor, where the model is modified by several + * child controllers. + */ + set: function (entity) { + current = entity; + }, + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#reset + * @methodOf umbraco.services.editorState + * @function + * + * @description + * Since the editorstate entity is read-only, you cannot set it to null + * only through the reset() method + */ + reset: function() { + current = null; + }, + + /** + * @ngdoc function + * @name umbraco.services.angularHelper#getCurrent + * @methodOf umbraco.services.editorState + * @function + * + * @description + * Returns an object reference to the current editor entity. + * the entity is the root object of the editor. + * EditorState is used by property/parameter editors that need + * access to the entire entity being edited, not just the property/parameter + * + * editorState.current can not be overwritten, you should only read values from it + * since modifying individual properties should be handled by the property editors + */ + getCurrent: function() { + return current; + } + }; + + //TODO: This shouldn't be removed! use getCurrent() method instead of a hacked readonly property which is confusing. + + //create a get/set property but don't allow setting + Object.defineProperty(state, "current", { + get: function () { + return current; + }, + set: function (value) { + throw "Use editorState.set to set the value of the current entity"; + }, + }); + + return state; }); /** * @ngdoc service @@ -1261,583 +1261,583 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica } angular.module('umbraco.services').factory('contentEditingHelper', contentEditingHelper); -/** - * @ngdoc service - * @name umbraco.services.contentTypeHelper - * @description A helper service for the content type editor - **/ -function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $injector, $q) { - - var contentTypeHelperService = { - - createIdArray: function(array) { - - var newArray = []; - - angular.forEach(array, function(arrayItem){ - - if(angular.isObject(arrayItem)) { - newArray.push(arrayItem.id); - } else { - newArray.push(arrayItem); - } - - }); - - return newArray; - - }, - - generateModels: function () { - var deferred = $q.defer(); - var modelsResource = $injector.has("modelsBuilderResource") ? $injector.get("modelsBuilderResource") : null; - var modelsBuilderEnabled = Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder.enabled; - if (modelsBuilderEnabled && modelsResource) { - modelsResource.buildModels().then(function(result) { - deferred.resolve(result); - - //just calling this to get the servar back to life - modelsResource.getModelsOutOfDateStatus(); - - }, function(e) { - deferred.reject(e); - }); - } - else { - deferred.resolve(false); - } - return deferred.promise; - }, - - checkModelsBuilderStatus: function () { - var deferred = $q.defer(); - var modelsResource = $injector.has("modelsBuilderResource") ? $injector.get("modelsBuilderResource") : null; - var modelsBuilderEnabled = (Umbraco && Umbraco.Sys && Umbraco.Sys.ServerVariables && Umbraco.Sys.ServerVariables.umbracoPlugins && Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder && Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder.enabled === true); - - if (modelsBuilderEnabled && modelsResource) { - modelsResource.getModelsOutOfDateStatus().then(function(result) { - //Generate models buttons should be enabled if it is 0 - deferred.resolve(result.status === 0); - }); - } - else { - deferred.resolve(false); - } - return deferred.promise; - }, - - makeObjectArrayFromId: function (idArray, objectArray) { - var newArray = []; - - for (var idIndex = 0; idArray.length > idIndex; idIndex++) { - var id = idArray[idIndex]; - - for (var objectIndex = 0; objectArray.length > objectIndex; objectIndex++) { - var object = objectArray[objectIndex]; - if (id === object.id) { - newArray.push(object); - } - } - - } - - return newArray; - }, - - validateAddingComposition: function(contentType, compositeContentType) { - - //Validate that by adding this group that we are not adding duplicate property type aliases - - var propertiesAdding = _.flatten(_.map(compositeContentType.groups, function(g) { - return _.map(g.properties, function(p) { - return p.alias; - }); - })); - var propAliasesExisting = _.filter(_.flatten(_.map(contentType.groups, function(g) { - return _.map(g.properties, function(p) { - return p.alias; - }); - })), function(f) { - return f !== null && f !== undefined; - }); - - var intersec = _.intersection(propertiesAdding, propAliasesExisting); - if (intersec.length > 0) { - //return the overlapping property aliases - return intersec; - } - - //no overlapping property aliases - return []; - }, - - mergeCompositeContentType: function(contentType, compositeContentType) { - - //Validate that there are no overlapping aliases - var overlappingAliases = this.validateAddingComposition(contentType, compositeContentType); - if (overlappingAliases.length > 0) { - throw new Error("Cannot add this composition, these properties already exist on the content type: " + overlappingAliases.join()); - } - - angular.forEach(compositeContentType.groups, function(compositionGroup) { - - // order composition groups based on sort order - compositionGroup.properties = $filter('orderBy')(compositionGroup.properties, 'sortOrder'); - - // get data type details - angular.forEach(compositionGroup.properties, function(property) { - dataTypeResource.getById(property.dataTypeId) - .then(function(dataType) { - property.dataTypeIcon = dataType.icon; - property.dataTypeName = dataType.name; - }); - }); - - // set inherited state on tab - compositionGroup.inherited = true; - - // set inherited state on properties - angular.forEach(compositionGroup.properties, function(compositionProperty) { - compositionProperty.inherited = true; - }); - - // set tab state - compositionGroup.tabState = "inActive"; - - // if groups are named the same - merge the groups - angular.forEach(contentType.groups, function(contentTypeGroup) { - - if (contentTypeGroup.name === compositionGroup.name) { - - // set flag to show if properties has been merged into a tab - compositionGroup.groupIsMerged = true; - - // make group inherited - contentTypeGroup.inherited = true; - - // add properties to the top of the array - contentTypeGroup.properties = compositionGroup.properties.concat(contentTypeGroup.properties); - - // update sort order on all properties in merged group - contentTypeGroup.properties = contentTypeHelperService.updatePropertiesSortOrder(contentTypeGroup.properties); - - // make parentTabContentTypeNames to an array so we can push values - if (contentTypeGroup.parentTabContentTypeNames === null || contentTypeGroup.parentTabContentTypeNames === undefined) { - contentTypeGroup.parentTabContentTypeNames = []; - } - - // push name to array of merged composite content types - contentTypeGroup.parentTabContentTypeNames.push(compositeContentType.name); - - // make parentTabContentTypes to an array so we can push values - if (contentTypeGroup.parentTabContentTypes === null || contentTypeGroup.parentTabContentTypes === undefined) { - contentTypeGroup.parentTabContentTypes = []; - } - - // push id to array of merged composite content types - contentTypeGroup.parentTabContentTypes.push(compositeContentType.id); - - // get sort order from composition - contentTypeGroup.sortOrder = compositionGroup.sortOrder; - - // splice group to the top of the array - var contentTypeGroupCopy = angular.copy(contentTypeGroup); - var index = contentType.groups.indexOf(contentTypeGroup); - contentType.groups.splice(index, 1); - contentType.groups.unshift(contentTypeGroupCopy); - - } - - }); - - // if group is not merged - push it to the end of the array - before init tab - if (compositionGroup.groupIsMerged === false || compositionGroup.groupIsMerged === undefined) { - - // make parentTabContentTypeNames to an array so we can push values - if (compositionGroup.parentTabContentTypeNames === null || compositionGroup.parentTabContentTypeNames === undefined) { - compositionGroup.parentTabContentTypeNames = []; - } - - // push name to array of merged composite content types - compositionGroup.parentTabContentTypeNames.push(compositeContentType.name); - - // make parentTabContentTypes to an array so we can push values - if (compositionGroup.parentTabContentTypes === null || compositionGroup.parentTabContentTypes === undefined) { - compositionGroup.parentTabContentTypes = []; - } - - // push id to array of merged composite content types - compositionGroup.parentTabContentTypes.push(compositeContentType.id); - - // push group before placeholder tab - contentType.groups.unshift(compositionGroup); - - } - - }); - - // sort all groups by sortOrder property - contentType.groups = $filter('orderBy')(contentType.groups, 'sortOrder'); - - return contentType; - - }, - - splitCompositeContentType: function (contentType, compositeContentType) { - - var groups = []; - - angular.forEach(contentType.groups, function(contentTypeGroup){ - - if( contentTypeGroup.tabState !== "init" ) { - - var idIndex = contentTypeGroup.parentTabContentTypes.indexOf(compositeContentType.id); - var nameIndex = contentTypeGroup.parentTabContentTypeNames.indexOf(compositeContentType.name); - var groupIndex = contentType.groups.indexOf(contentTypeGroup); - - - if( idIndex !== -1 ) { - - var properties = []; - - // remove all properties from composite content type - angular.forEach(contentTypeGroup.properties, function(property){ - if(property.contentTypeId !== compositeContentType.id) { - properties.push(property); - } - }); - - // set new properties array to properties - contentTypeGroup.properties = properties; - - // remove composite content type name and id from inherited arrays - contentTypeGroup.parentTabContentTypes.splice(idIndex, 1); - contentTypeGroup.parentTabContentTypeNames.splice(nameIndex, 1); - - // remove inherited state if there are no inherited properties - if(contentTypeGroup.parentTabContentTypes.length === 0) { - contentTypeGroup.inherited = false; - } - - // remove group if there are no properties left - if(contentTypeGroup.properties.length > 1) { - //contentType.groups.splice(groupIndex, 1); - groups.push(contentTypeGroup); - } - - } else { - groups.push(contentTypeGroup); - } - - } else { - groups.push(contentTypeGroup); - } - - // update sort order on properties - contentTypeGroup.properties = contentTypeHelperService.updatePropertiesSortOrder(contentTypeGroup.properties); - - }); - - contentType.groups = groups; - - }, - - updatePropertiesSortOrder: function (properties) { - - var sortOrder = 0; - - angular.forEach(properties, function(property) { - if( !property.inherited && property.propertyState !== "init") { - property.sortOrder = sortOrder; - } - sortOrder++; - }); - - return properties; - - }, - - getTemplatePlaceholder: function() { - - var templatePlaceholder = { - "name": "", - "icon": "icon-layout", - "alias": "templatePlaceholder", - "placeholder": true - }; - - return templatePlaceholder; - - }, - - insertDefaultTemplatePlaceholder: function(defaultTemplate) { - - // get template placeholder - var templatePlaceholder = contentTypeHelperService.getTemplatePlaceholder(); - - // add as default template - defaultTemplate = templatePlaceholder; - - return defaultTemplate; - - }, - - insertTemplatePlaceholder: function(array) { - - // get template placeholder - var templatePlaceholder = contentTypeHelperService.getTemplatePlaceholder(); - - // add as selected item - array.push(templatePlaceholder); - - return array; - - }, - - insertChildNodePlaceholder: function (array, name, icon, id) { - - var placeholder = { - "name": name, - "icon": icon, - "id": id - }; - - array.push(placeholder); - - } - - }; - - return contentTypeHelperService; -} -angular.module('umbraco.services').factory('contentTypeHelper', contentTypeHelper); - -/** -* @ngdoc service -* @name umbraco.services.cropperHelper -* @description A helper object used for dealing with image cropper data -**/ -function cropperHelper(umbRequestHelper, $http) { - var service = { - - /** - * @ngdoc method - * @name umbraco.services.cropperHelper#configuration - * @methodOf umbraco.services.cropperHelper - * - * @description - * Returns a collection of plugins available to the tinyMCE editor - * - */ - configuration: function (mediaTypeAlias) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "imageCropperApiBaseUrl", - "GetConfiguration", - [{ mediaTypeAlias: mediaTypeAlias}])), - 'Failed to retrieve tinymce configuration'); - }, - - - //utill for getting either min/max aspect ratio to scale image after - calculateAspectRatioFit : function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) { - var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ]; - - if(maximize){ - ratio = Math.max(ratio[0], ratio[1]); - }else{ - ratio = Math.min(ratio[0], ratio[1]); - } - - return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; - }, - - //utill for scaling width / height given a ratio - calculateSizeToRatio : function(srcWidth, srcHeight, ratio) { - return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; - }, - - scaleToMaxSize : function(srcWidth, srcHeight, maxSize) { - - var retVal = {height: srcHeight, width: srcWidth}; - - if(srcWidth > maxSize ||srcHeight > maxSize){ - var ratio = [maxSize / srcWidth, maxSize / srcHeight ]; - ratio = Math.min(ratio[0], ratio[1]); - - retVal.height = srcHeight * ratio; - retVal.width = srcWidth * ratio; - } - - return retVal; - }, - - //returns a ng-style object with top,left,width,height pixel measurements - //expects {left,right,top,bottom} - {width,height}, {width,height}, int - //offset is just to push the image position a number of pixels from top,left - convertToStyle : function(coordinates, originalSize, viewPort, offset){ - - var coordinates_px = service.coordinatesToPixels(coordinates, originalSize, offset); - var _offset = offset || 0; - - var x = 1 - (coordinates.x1 + Math.abs(coordinates.x2)); - var left_of_x = originalSize.width * x; - var ratio = viewPort.width / left_of_x; - - var style = { - position: "absolute", - top: -(coordinates_px.y1*ratio)+ _offset, - left: -(coordinates_px.x1* ratio)+ _offset, - width: Math.floor(originalSize.width * ratio), - height: Math.floor(originalSize.height * ratio), - originalWidth: originalSize.width, - originalHeight: originalSize.height, - ratio: ratio - }; - - return style; - }, - - - coordinatesToPixels : function(coordinates, originalSize, offset){ - - var coordinates_px = { - x1: Math.floor(coordinates.x1 * originalSize.width), - y1: Math.floor(coordinates.y1 * originalSize.height), - x2: Math.floor(coordinates.x2 * originalSize.width), - y2: Math.floor(coordinates.y2 * originalSize.height) - }; - - return coordinates_px; - }, - - pixelsToCoordinates : function(image, width, height, offset){ - - var x1_px = Math.abs(image.left-offset); - var y1_px = Math.abs(image.top-offset); - - var x2_px = image.width - (x1_px + width); - var y2_px = image.height - (y1_px + height); - - - //crop coordinates in % - var crop = {}; - crop.x1 = x1_px / image.width; - crop.y1 = y1_px / image.height; - crop.x2 = x2_px / image.width; - crop.y2 = y2_px / image.height; - - for(var coord in crop){ - if(crop[coord] < 0){ - crop[coord] = 0; - } - } - - return crop; - }, - - centerInsideViewPort : function(img, viewport){ - var left = viewport.width/ 2 - img.width / 2, - top = viewport.height / 2 - img.height / 2; - - return {left: left, top: top}; - }, - - alignToCoordinates : function(image, center, viewport){ - - var min_left = (image.width) - (viewport.width); - var min_top = (image.height) - (viewport.height); - - var c_top = -(center.top * image.height) + (viewport.height / 2); - var c_left = -(center.left * image.width) + (viewport.width / 2); - - if(c_top < -min_top){ - c_top = -min_top; - } - if(c_top > 0){ - c_top = 0; - } - if(c_left < -min_left){ - c_left = -min_left; - } - if(c_left > 0){ - c_left = 0; - } - return {left: c_left, top: c_top}; - }, - - - syncElements : function(source, target){ - target.height(source.height()); - target.width(source.width()); - - target.css({ - "top": source[0].offsetTop, - "left": source[0].offsetLeft - }); - } - }; - - return service; -} - -angular.module('umbraco.services').factory('cropperHelper', cropperHelper); - -/** - * @ngdoc service - * @name umbraco.services.dataTypeHelper - * @description A helper service for data types - **/ -function dataTypeHelper() { - - var dataTypeHelperService = { - - createPreValueProps: function(preVals) { - - var preValues = []; - - for (var i = 0; i < preVals.length; i++) { - preValues.push({ - hideLabel: preVals[i].hideLabel, - alias: preVals[i].key, - description: preVals[i].description, - label: preVals[i].label, - view: preVals[i].view, - value: preVals[i].value - }); - } - - return preValues; - - }, - - rebindChangedProperties: function (origContent, savedContent) { - - //a method to ignore built-in prop changes - var shouldIgnore = function (propName) { - return _.some(["notifications", "ModelState"], function (i) { - return i === propName; - }); - }; - //check for changed built-in properties of the content - for (var o in origContent) { - - //ignore the ones listed in the array - if (shouldIgnore(o)) { - continue; - } - - if (!_.isEqual(origContent[o], savedContent[o])) { - origContent[o] = savedContent[o]; - } - } - } - - }; - - return dataTypeHelperService; -} +/** + * @ngdoc service + * @name umbraco.services.contentTypeHelper + * @description A helper service for the content type editor + **/ +function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $injector, $q) { + + var contentTypeHelperService = { + + createIdArray: function(array) { + + var newArray = []; + + angular.forEach(array, function(arrayItem){ + + if(angular.isObject(arrayItem)) { + newArray.push(arrayItem.id); + } else { + newArray.push(arrayItem); + } + + }); + + return newArray; + + }, + + generateModels: function () { + var deferred = $q.defer(); + var modelsResource = $injector.has("modelsBuilderResource") ? $injector.get("modelsBuilderResource") : null; + var modelsBuilderEnabled = Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder.enabled; + if (modelsBuilderEnabled && modelsResource) { + modelsResource.buildModels().then(function(result) { + deferred.resolve(result); + + //just calling this to get the servar back to life + modelsResource.getModelsOutOfDateStatus(); + + }, function(e) { + deferred.reject(e); + }); + } + else { + deferred.resolve(false); + } + return deferred.promise; + }, + + checkModelsBuilderStatus: function () { + var deferred = $q.defer(); + var modelsResource = $injector.has("modelsBuilderResource") ? $injector.get("modelsBuilderResource") : null; + var modelsBuilderEnabled = (Umbraco && Umbraco.Sys && Umbraco.Sys.ServerVariables && Umbraco.Sys.ServerVariables.umbracoPlugins && Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder && Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder.enabled === true); + + if (modelsBuilderEnabled && modelsResource) { + modelsResource.getModelsOutOfDateStatus().then(function(result) { + //Generate models buttons should be enabled if it is 0 + deferred.resolve(result.status === 0); + }); + } + else { + deferred.resolve(false); + } + return deferred.promise; + }, + + makeObjectArrayFromId: function (idArray, objectArray) { + var newArray = []; + + for (var idIndex = 0; idArray.length > idIndex; idIndex++) { + var id = idArray[idIndex]; + + for (var objectIndex = 0; objectArray.length > objectIndex; objectIndex++) { + var object = objectArray[objectIndex]; + if (id === object.id) { + newArray.push(object); + } + } + + } + + return newArray; + }, + + validateAddingComposition: function(contentType, compositeContentType) { + + //Validate that by adding this group that we are not adding duplicate property type aliases + + var propertiesAdding = _.flatten(_.map(compositeContentType.groups, function(g) { + return _.map(g.properties, function(p) { + return p.alias; + }); + })); + var propAliasesExisting = _.filter(_.flatten(_.map(contentType.groups, function(g) { + return _.map(g.properties, function(p) { + return p.alias; + }); + })), function(f) { + return f !== null && f !== undefined; + }); + + var intersec = _.intersection(propertiesAdding, propAliasesExisting); + if (intersec.length > 0) { + //return the overlapping property aliases + return intersec; + } + + //no overlapping property aliases + return []; + }, + + mergeCompositeContentType: function(contentType, compositeContentType) { + + //Validate that there are no overlapping aliases + var overlappingAliases = this.validateAddingComposition(contentType, compositeContentType); + if (overlappingAliases.length > 0) { + throw new Error("Cannot add this composition, these properties already exist on the content type: " + overlappingAliases.join()); + } + + angular.forEach(compositeContentType.groups, function(compositionGroup) { + + // order composition groups based on sort order + compositionGroup.properties = $filter('orderBy')(compositionGroup.properties, 'sortOrder'); + + // get data type details + angular.forEach(compositionGroup.properties, function(property) { + dataTypeResource.getById(property.dataTypeId) + .then(function(dataType) { + property.dataTypeIcon = dataType.icon; + property.dataTypeName = dataType.name; + }); + }); + + // set inherited state on tab + compositionGroup.inherited = true; + + // set inherited state on properties + angular.forEach(compositionGroup.properties, function(compositionProperty) { + compositionProperty.inherited = true; + }); + + // set tab state + compositionGroup.tabState = "inActive"; + + // if groups are named the same - merge the groups + angular.forEach(contentType.groups, function(contentTypeGroup) { + + if (contentTypeGroup.name === compositionGroup.name) { + + // set flag to show if properties has been merged into a tab + compositionGroup.groupIsMerged = true; + + // make group inherited + contentTypeGroup.inherited = true; + + // add properties to the top of the array + contentTypeGroup.properties = compositionGroup.properties.concat(contentTypeGroup.properties); + + // update sort order on all properties in merged group + contentTypeGroup.properties = contentTypeHelperService.updatePropertiesSortOrder(contentTypeGroup.properties); + + // make parentTabContentTypeNames to an array so we can push values + if (contentTypeGroup.parentTabContentTypeNames === null || contentTypeGroup.parentTabContentTypeNames === undefined) { + contentTypeGroup.parentTabContentTypeNames = []; + } + + // push name to array of merged composite content types + contentTypeGroup.parentTabContentTypeNames.push(compositeContentType.name); + + // make parentTabContentTypes to an array so we can push values + if (contentTypeGroup.parentTabContentTypes === null || contentTypeGroup.parentTabContentTypes === undefined) { + contentTypeGroup.parentTabContentTypes = []; + } + + // push id to array of merged composite content types + contentTypeGroup.parentTabContentTypes.push(compositeContentType.id); + + // get sort order from composition + contentTypeGroup.sortOrder = compositionGroup.sortOrder; + + // splice group to the top of the array + var contentTypeGroupCopy = angular.copy(contentTypeGroup); + var index = contentType.groups.indexOf(contentTypeGroup); + contentType.groups.splice(index, 1); + contentType.groups.unshift(contentTypeGroupCopy); + + } + + }); + + // if group is not merged - push it to the end of the array - before init tab + if (compositionGroup.groupIsMerged === false || compositionGroup.groupIsMerged === undefined) { + + // make parentTabContentTypeNames to an array so we can push values + if (compositionGroup.parentTabContentTypeNames === null || compositionGroup.parentTabContentTypeNames === undefined) { + compositionGroup.parentTabContentTypeNames = []; + } + + // push name to array of merged composite content types + compositionGroup.parentTabContentTypeNames.push(compositeContentType.name); + + // make parentTabContentTypes to an array so we can push values + if (compositionGroup.parentTabContentTypes === null || compositionGroup.parentTabContentTypes === undefined) { + compositionGroup.parentTabContentTypes = []; + } + + // push id to array of merged composite content types + compositionGroup.parentTabContentTypes.push(compositeContentType.id); + + // push group before placeholder tab + contentType.groups.unshift(compositionGroup); + + } + + }); + + // sort all groups by sortOrder property + contentType.groups = $filter('orderBy')(contentType.groups, 'sortOrder'); + + return contentType; + + }, + + splitCompositeContentType: function (contentType, compositeContentType) { + + var groups = []; + + angular.forEach(contentType.groups, function(contentTypeGroup){ + + if( contentTypeGroup.tabState !== "init" ) { + + var idIndex = contentTypeGroup.parentTabContentTypes.indexOf(compositeContentType.id); + var nameIndex = contentTypeGroup.parentTabContentTypeNames.indexOf(compositeContentType.name); + var groupIndex = contentType.groups.indexOf(contentTypeGroup); + + + if( idIndex !== -1 ) { + + var properties = []; + + // remove all properties from composite content type + angular.forEach(contentTypeGroup.properties, function(property){ + if(property.contentTypeId !== compositeContentType.id) { + properties.push(property); + } + }); + + // set new properties array to properties + contentTypeGroup.properties = properties; + + // remove composite content type name and id from inherited arrays + contentTypeGroup.parentTabContentTypes.splice(idIndex, 1); + contentTypeGroup.parentTabContentTypeNames.splice(nameIndex, 1); + + // remove inherited state if there are no inherited properties + if(contentTypeGroup.parentTabContentTypes.length === 0) { + contentTypeGroup.inherited = false; + } + + // remove group if there are no properties left + if(contentTypeGroup.properties.length > 1) { + //contentType.groups.splice(groupIndex, 1); + groups.push(contentTypeGroup); + } + + } else { + groups.push(contentTypeGroup); + } + + } else { + groups.push(contentTypeGroup); + } + + // update sort order on properties + contentTypeGroup.properties = contentTypeHelperService.updatePropertiesSortOrder(contentTypeGroup.properties); + + }); + + contentType.groups = groups; + + }, + + updatePropertiesSortOrder: function (properties) { + + var sortOrder = 0; + + angular.forEach(properties, function(property) { + if( !property.inherited && property.propertyState !== "init") { + property.sortOrder = sortOrder; + } + sortOrder++; + }); + + return properties; + + }, + + getTemplatePlaceholder: function() { + + var templatePlaceholder = { + "name": "", + "icon": "icon-layout", + "alias": "templatePlaceholder", + "placeholder": true + }; + + return templatePlaceholder; + + }, + + insertDefaultTemplatePlaceholder: function(defaultTemplate) { + + // get template placeholder + var templatePlaceholder = contentTypeHelperService.getTemplatePlaceholder(); + + // add as default template + defaultTemplate = templatePlaceholder; + + return defaultTemplate; + + }, + + insertTemplatePlaceholder: function(array) { + + // get template placeholder + var templatePlaceholder = contentTypeHelperService.getTemplatePlaceholder(); + + // add as selected item + array.push(templatePlaceholder); + + return array; + + }, + + insertChildNodePlaceholder: function (array, name, icon, id) { + + var placeholder = { + "name": name, + "icon": icon, + "id": id + }; + + array.push(placeholder); + + } + + }; + + return contentTypeHelperService; +} +angular.module('umbraco.services').factory('contentTypeHelper', contentTypeHelper); + +/** +* @ngdoc service +* @name umbraco.services.cropperHelper +* @description A helper object used for dealing with image cropper data +**/ +function cropperHelper(umbRequestHelper, $http) { + var service = { + + /** + * @ngdoc method + * @name umbraco.services.cropperHelper#configuration + * @methodOf umbraco.services.cropperHelper + * + * @description + * Returns a collection of plugins available to the tinyMCE editor + * + */ + configuration: function (mediaTypeAlias) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "imageCropperApiBaseUrl", + "GetConfiguration", + [{ mediaTypeAlias: mediaTypeAlias}])), + 'Failed to retrieve tinymce configuration'); + }, + + + //utill for getting either min/max aspect ratio to scale image after + calculateAspectRatioFit : function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) { + var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ]; + + if(maximize){ + ratio = Math.max(ratio[0], ratio[1]); + }else{ + ratio = Math.min(ratio[0], ratio[1]); + } + + return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; + }, + + //utill for scaling width / height given a ratio + calculateSizeToRatio : function(srcWidth, srcHeight, ratio) { + return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio}; + }, + + scaleToMaxSize : function(srcWidth, srcHeight, maxSize) { + + var retVal = {height: srcHeight, width: srcWidth}; + + if(srcWidth > maxSize ||srcHeight > maxSize){ + var ratio = [maxSize / srcWidth, maxSize / srcHeight ]; + ratio = Math.min(ratio[0], ratio[1]); + + retVal.height = srcHeight * ratio; + retVal.width = srcWidth * ratio; + } + + return retVal; + }, + + //returns a ng-style object with top,left,width,height pixel measurements + //expects {left,right,top,bottom} - {width,height}, {width,height}, int + //offset is just to push the image position a number of pixels from top,left + convertToStyle : function(coordinates, originalSize, viewPort, offset){ + + var coordinates_px = service.coordinatesToPixels(coordinates, originalSize, offset); + var _offset = offset || 0; + + var x = 1 - (coordinates.x1 + Math.abs(coordinates.x2)); + var left_of_x = originalSize.width * x; + var ratio = viewPort.width / left_of_x; + + var style = { + position: "absolute", + top: -(coordinates_px.y1*ratio)+ _offset, + left: -(coordinates_px.x1* ratio)+ _offset, + width: Math.floor(originalSize.width * ratio), + height: Math.floor(originalSize.height * ratio), + originalWidth: originalSize.width, + originalHeight: originalSize.height, + ratio: ratio + }; + + return style; + }, + + + coordinatesToPixels : function(coordinates, originalSize, offset){ + + var coordinates_px = { + x1: Math.floor(coordinates.x1 * originalSize.width), + y1: Math.floor(coordinates.y1 * originalSize.height), + x2: Math.floor(coordinates.x2 * originalSize.width), + y2: Math.floor(coordinates.y2 * originalSize.height) + }; + + return coordinates_px; + }, + + pixelsToCoordinates : function(image, width, height, offset){ + + var x1_px = Math.abs(image.left-offset); + var y1_px = Math.abs(image.top-offset); + + var x2_px = image.width - (x1_px + width); + var y2_px = image.height - (y1_px + height); + + + //crop coordinates in % + var crop = {}; + crop.x1 = x1_px / image.width; + crop.y1 = y1_px / image.height; + crop.x2 = x2_px / image.width; + crop.y2 = y2_px / image.height; + + for(var coord in crop){ + if(crop[coord] < 0){ + crop[coord] = 0; + } + } + + return crop; + }, + + centerInsideViewPort : function(img, viewport){ + var left = viewport.width/ 2 - img.width / 2, + top = viewport.height / 2 - img.height / 2; + + return {left: left, top: top}; + }, + + alignToCoordinates : function(image, center, viewport){ + + var min_left = (image.width) - (viewport.width); + var min_top = (image.height) - (viewport.height); + + var c_top = -(center.top * image.height) + (viewport.height / 2); + var c_left = -(center.left * image.width) + (viewport.width / 2); + + if(c_top < -min_top){ + c_top = -min_top; + } + if(c_top > 0){ + c_top = 0; + } + if(c_left < -min_left){ + c_left = -min_left; + } + if(c_left > 0){ + c_left = 0; + } + return {left: c_left, top: c_top}; + }, + + + syncElements : function(source, target){ + target.height(source.height()); + target.width(source.width()); + + target.css({ + "top": source[0].offsetTop, + "left": source[0].offsetLeft + }); + } + }; + + return service; +} + +angular.module('umbraco.services').factory('cropperHelper', cropperHelper); + +/** + * @ngdoc service + * @name umbraco.services.dataTypeHelper + * @description A helper service for data types + **/ +function dataTypeHelper() { + + var dataTypeHelperService = { + + createPreValueProps: function(preVals) { + + var preValues = []; + + for (var i = 0; i < preVals.length; i++) { + preValues.push({ + hideLabel: preVals[i].hideLabel, + alias: preVals[i].key, + description: preVals[i].description, + label: preVals[i].label, + view: preVals[i].view, + value: preVals[i].value + }); + } + + return preValues; + + }, + + rebindChangedProperties: function (origContent, savedContent) { + + //a method to ignore built-in prop changes + var shouldIgnore = function (propName) { + return _.some(["notifications", "ModelState"], function (i) { + return i === propName; + }); + }; + //check for changed built-in properties of the content + for (var o in origContent) { + + //ignore the ones listed in the array + if (shouldIgnore(o)) { + continue; + } + + if (!_.isEqual(origContent[o], savedContent[o])) { + origContent[o] = savedContent[o]; + } + } + } + + }; + + return dataTypeHelperService; +} angular.module('umbraco.services').factory('dataTypeHelper', dataTypeHelper); /** * @ngdoc service @@ -2734,89 +2734,89 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati }; } angular.module('umbraco.services').factory('formHelper', formHelper); -angular.module('umbraco.services') - .factory('gridService', function ($http, $q){ - - var configPath = Umbraco.Sys.ServerVariables.umbracoUrls.gridConfig; - var service = { - getGridEditors: function () { - return $http.get(configPath); - } - }; - - return service; - - }); - -angular.module('umbraco.services') - .factory('helpService', function ($http, $q){ - var helpTopics = {}; - - var defaultUrl = "http://our.umbraco.org/rss/help"; - var tvUrl = "http://umbraco.tv/feeds/help"; - - function getCachedHelp(url){ - if(helpTopics[url]){ - return helpTopics[cacheKey]; - }else{ - return null; - } - } - - function setCachedHelp(url, data){ - helpTopics[url] = data; - } - - function fetchUrl(url){ - var deferred = $q.defer(); - var found = getCachedHelp(url); - - if(found){ - deferred.resolve(found); - }else{ - - var proxyUrl = "dashboard/feedproxy.aspx?url=" + url; - $http.get(proxyUrl).then(function(data){ - var feed = $(data.data); - var topics = []; - - $('item', feed).each(function (i, item) { - var topic = {}; - topic.thumbnail = $(item).find('thumbnail').attr('url'); - topic.title = $("title", item).text(); - topic.link = $("guid", item).text(); - topic.description = $("description", item).text(); - topics.push(topic); - }); - - setCachedHelp(topics); - deferred.resolve(topics); - }); - } - - return deferred.promise; - } - - - - var service = { - findHelp: function (args) { - var url = service.getUrl(defaultUrl, args); - return fetchUrl(url); - }, - - findVideos: function (args) { - var url = service.getUrl(tvUrl, args); - return fetchUrl(url); - }, - - getUrl: function(url, args){ - return url + "?" + $.param(args); - } - }; - - return service; +angular.module('umbraco.services') + .factory('gridService', function ($http, $q){ + + var configPath = Umbraco.Sys.ServerVariables.umbracoUrls.gridConfig; + var service = { + getGridEditors: function () { + return $http.get(configPath); + } + }; + + return service; + + }); +angular.module('umbraco.services') + .factory('helpService', function ($http, $q){ + var helpTopics = {}; + + var defaultUrl = "http://our.umbraco.org/rss/help"; + var tvUrl = "http://umbraco.tv/feeds/help"; + + function getCachedHelp(url){ + if(helpTopics[url]){ + return helpTopics[cacheKey]; + }else{ + return null; + } + } + + function setCachedHelp(url, data){ + helpTopics[url] = data; + } + + function fetchUrl(url){ + var deferred = $q.defer(); + var found = getCachedHelp(url); + + if(found){ + deferred.resolve(found); + }else{ + + var proxyUrl = "dashboard/feedproxy.aspx?url=" + url; + $http.get(proxyUrl).then(function(data){ + var feed = $(data.data); + var topics = []; + + $('item', feed).each(function (i, item) { + var topic = {}; + topic.thumbnail = $(item).find('thumbnail').attr('url'); + topic.title = $("title", item).text(); + topic.link = $("guid", item).text(); + topic.description = $("description", item).text(); + topics.push(topic); + }); + + setCachedHelp(topics); + deferred.resolve(topics); + }); + } + + return deferred.promise; + } + + + + var service = { + findHelp: function (args) { + var url = service.getUrl(defaultUrl, args); + return fetchUrl(url); + }, + + findVideos: function (args) { + var url = service.getUrl(tvUrl, args); + return fetchUrl(url); + }, + + getUrl: function(url, args){ + return url + "?" + $.param(args); + } + }; + + return service; + }); /** * @ngdoc service @@ -2938,291 +2938,291 @@ angular.module('umbraco.services') } }; }); -/** -* @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;x0){ - 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; - } - }; -} +/** +* @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;x0){ + 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.imageHelper -* @deprecated -**/ -function imageHelper(umbRequestHelper, mediaHelper) { - return { - /** - * @ngdoc function - * @name umbraco.services.imageHelper#getImagePropertyValue - * @methodOf umbraco.services.imageHelper - * @function - * - * @deprecated - */ - getImagePropertyValue: function (options) { - return mediaHelper.getImagePropertyValue(options); - }, - /** - * @ngdoc function - * @name umbraco.services.imageHelper#getThumbnail - * @methodOf umbraco.services.imageHelper - * @function - * - * @deprecated - */ - getThumbnail: function (options) { - return mediaHelper.getThumbnail(options); - }, - - /** - * @ngdoc function - * @name umbraco.services.imageHelper#scaleToMaxSize - * @methodOf umbraco.services.imageHelper - * @function - * - * @deprecated - */ - scaleToMaxSize: function (maxSize, width, height) { - return mediaHelper.scaleToMaxSize(maxSize, width, height); - }, - - /** - * @ngdoc function - * @name umbraco.services.imageHelper#getThumbnailFromPath - * @methodOf umbraco.services.imageHelper - * @function - * - * @deprecated - */ - getThumbnailFromPath: function (imagePath) { - return mediaHelper.getThumbnailFromPath(imagePath); - }, - - /** - * @ngdoc function - * @name umbraco.services.imageHelper#detectIfImageByExtension - * @methodOf umbraco.services.imageHelper - * @function - * - * @deprecated - */ - detectIfImageByExtension: function (imagePath) { - return mediaHelper.detectIfImageByExtension(imagePath); - } - }; -} +/** +* @ngdoc service +* @name umbraco.services.imageHelper +* @deprecated +**/ +function imageHelper(umbRequestHelper, mediaHelper) { + return { + /** + * @ngdoc function + * @name umbraco.services.imageHelper#getImagePropertyValue + * @methodOf umbraco.services.imageHelper + * @function + * + * @deprecated + */ + getImagePropertyValue: function (options) { + return mediaHelper.getImagePropertyValue(options); + }, + /** + * @ngdoc function + * @name umbraco.services.imageHelper#getThumbnail + * @methodOf umbraco.services.imageHelper + * @function + * + * @deprecated + */ + getThumbnail: function (options) { + return mediaHelper.getThumbnail(options); + }, + + /** + * @ngdoc function + * @name umbraco.services.imageHelper#scaleToMaxSize + * @methodOf umbraco.services.imageHelper + * @function + * + * @deprecated + */ + scaleToMaxSize: function (maxSize, width, height) { + return mediaHelper.scaleToMaxSize(maxSize, width, height); + }, + + /** + * @ngdoc function + * @name umbraco.services.imageHelper#getThumbnailFromPath + * @methodOf umbraco.services.imageHelper + * @function + * + * @deprecated + */ + getThumbnailFromPath: function (imagePath) { + return mediaHelper.getThumbnailFromPath(imagePath); + }, + + /** + * @ngdoc function + * @name umbraco.services.imageHelper#detectIfImageByExtension + * @methodOf umbraco.services.imageHelper + * @function + * + * @deprecated + */ + detectIfImageByExtension: function (imagePath) { + return mediaHelper.detectIfImageByExtension(imagePath); + } + }; +} angular.module('umbraco.services').factory('imageHelper', imageHelper); // This service was based on OpenJS library available in BSD License // http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php @@ -3547,325 +3547,325 @@ function keyboardService($window, $timeout) { return keyboardManagerService; } angular.module('umbraco.services').factory('keyboardService', ['$window', '$timeout', keyboardService]); -(function () { - 'use strict'; - - function listViewHelper(localStorageService) { - - var firstSelectedIndex = 0; - var localStorageKey = "umblistViewLayout"; - - function getLayout(nodeId, availableLayouts) { - - var storedLayouts = []; - - if (localStorageService.get(localStorageKey)) { - storedLayouts = localStorageService.get(localStorageKey); - } - - if (storedLayouts && storedLayouts.length > 0) { - for (var i = 0; storedLayouts.length > i; i++) { - var layout = storedLayouts[i]; - if (layout.nodeId === nodeId) { - return setLayout(nodeId, layout, availableLayouts); - } - } - - } - - return getFirstAllowedLayout(availableLayouts); - - } - - function setLayout(nodeId, selectedLayout, availableLayouts) { - - var activeLayout = {}; - var layoutFound = false; - - for (var i = 0; availableLayouts.length > i; i++) { - var layout = availableLayouts[i]; - if (layout.path === selectedLayout.path) { - activeLayout = layout; - layout.active = true; - layoutFound = true; - } else { - layout.active = false; - } - } - - if (!layoutFound) { - activeLayout = getFirstAllowedLayout(availableLayouts); - } - - saveLayoutInLocalStorage(nodeId, activeLayout); - - return activeLayout; - - } - - function saveLayoutInLocalStorage(nodeId, selectedLayout) { - var layoutFound = false; - var storedLayouts = []; - - if (localStorageService.get(localStorageKey)) { - storedLayouts = localStorageService.get(localStorageKey); - } - - if (storedLayouts.length > 0) { - for (var i = 0; storedLayouts.length > i; i++) { - var layout = storedLayouts[i]; - if (layout.nodeId === nodeId) { - layout.path = selectedLayout.path; - layoutFound = true; - } - } - } - - if (!layoutFound) { - var storageObject = { - "nodeId": nodeId, - "path": selectedLayout.path - }; - storedLayouts.push(storageObject); - } - - localStorageService.set(localStorageKey, storedLayouts); - - } - - function getFirstAllowedLayout(layouts) { - - var firstAllowedLayout = {}; - - for (var i = 0; layouts.length > i; i++) { - var layout = layouts[i]; - if (layout.selected === true) { - firstAllowedLayout = layout; - break; - } - } - - return firstAllowedLayout; - } - - function selectHandler(selectedItem, selectedIndex, items, selection, $event) { - - var start = 0; - var end = 0; - var item = null; - - if ($event.shiftKey === true) { - - if (selectedIndex > firstSelectedIndex) { - - start = firstSelectedIndex; - end = selectedIndex; - - for (; end >= start; start++) { - item = items[start]; - selectItem(item, selection); - } - - } else { - - start = firstSelectedIndex; - end = selectedIndex; - - for (; end <= start; start--) { - item = items[start]; - selectItem(item, selection); - } - - } - - } else { - - if (selectedItem.selected) { - deselectItem(selectedItem, selection); - } else { - selectItem(selectedItem, selection); - } - - firstSelectedIndex = selectedIndex; - - } - - } - - function selectItem(item, selection) { - var isSelected = false; - for (var i = 0; selection.length > i; i++) { - var selectedItem = selection[i]; - if (item.id === selectedItem.id) { - isSelected = true; - } - } - if (!isSelected) { - selection.push({ id: item.id }); - item.selected = true; - } - } - - function deselectItem(item, selection) { - for (var i = 0; selection.length > i; i++) { - var selectedItem = selection[i]; - if (item.id === selectedItem.id) { - selection.splice(i, 1); - item.selected = false; - } - } - } - - function clearSelection(items, folders, selection) { - - var i = 0; - - selection.length = 0; - - if (angular.isArray(items)) { - for (i = 0; items.length > i; i++) { - var item = items[i]; - item.selected = false; - } - } - - if (angular.isArray(items)) { - for (i = 0; folders.length > i; i++) { - var folder = folders[i]; - folder.selected = false; - } - } - } - - function selectAllItems(items, selection, $event) { - - var checkbox = $event.target; - var clearSelection = false; - - if (!angular.isArray(items)) { - return; - } - - selection.length = 0; - - for (var i = 0; i < items.length; i++) { - - var item = items[i]; - - if (checkbox.checked) { - selection.push({ id: item.id }); - } else { - clearSelection = true; - } - - item.selected = checkbox.checked; - - } - - if (clearSelection) { - selection.length = 0; - } - - } - - function isSelectedAll(items, selection) { - - var numberOfSelectedItem = 0; - - for (var itemIndex = 0; items.length > itemIndex; itemIndex++) { - var item = items[itemIndex]; - - for (var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) { - var selectedItem = selection[selectedIndex]; - - if (item.id === selectedItem.id) { - numberOfSelectedItem++; - } - } - - } - - if (numberOfSelectedItem === items.length) { - return true; - } - - } - - function setSortingDirection(col, direction, options) { - return options.orderBy.toUpperCase() === col.toUpperCase() && options.orderDirection === direction; - } - - function setSorting(field, allow, options) { - if (allow) { - options.orderBy = field; - - if (options.orderDirection === "desc") { - options.orderDirection = "asc"; - } else { - options.orderDirection = "desc"; - } - } - } - - //This takes in a dictionary of Ids with Permissions and determines - // the intersect of all permissions to return an object representing the - // listview button permissions - function getButtonPermissions(unmergedPermissions, currentIdsWithPermissions) { - - if (currentIdsWithPermissions == null) { - currentIdsWithPermissions = {}; - } - - //merge the newly retrieved permissions to the main dictionary - _.each(unmergedPermissions, function (value, key, list) { - currentIdsWithPermissions[key] = value; - }); - - //get the intersect permissions - var arr = []; - _.each(currentIdsWithPermissions, function (value, key, list) { - arr.push(value); - }); - - //we need to use 'apply' to call intersection with an array of arrays, - //see: http://stackoverflow.com/a/16229480/694494 - var intersectPermissions = _.intersection.apply(_, arr); - - return { - canCopy: _.contains(intersectPermissions, 'O'), //Magic Char = O - canCreate: _.contains(intersectPermissions, 'C'), //Magic Char = C - canDelete: _.contains(intersectPermissions, 'D'), //Magic Char = D - canMove: _.contains(intersectPermissions, 'M'), //Magic Char = M - canPublish: _.contains(intersectPermissions, 'U'), //Magic Char = U - canUnpublish: _.contains(intersectPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish) - }; - } - - var service = { - getLayout: getLayout, - getFirstAllowedLayout: getFirstAllowedLayout, - setLayout: setLayout, - saveLayoutInLocalStorage: saveLayoutInLocalStorage, - selectHandler: selectHandler, - selectItem: selectItem, - deselectItem: deselectItem, - clearSelection: clearSelection, - selectAllItems: selectAllItems, - isSelectedAll: isSelectedAll, - setSortingDirection: setSortingDirection, - setSorting: setSorting, - getButtonPermissions: getButtonPermissions - }; - - return service; - - } - - - angular.module('umbraco.services').factory('listViewHelper', listViewHelper); - - -})(); +(function () { + 'use strict'; + + function listViewHelper(localStorageService) { + + var firstSelectedIndex = 0; + var localStorageKey = "umblistViewLayout"; + + function getLayout(nodeId, availableLayouts) { + + var storedLayouts = []; + + if (localStorageService.get(localStorageKey)) { + storedLayouts = localStorageService.get(localStorageKey); + } + + if (storedLayouts && storedLayouts.length > 0) { + for (var i = 0; storedLayouts.length > i; i++) { + var layout = storedLayouts[i]; + if (layout.nodeId === nodeId) { + return setLayout(nodeId, layout, availableLayouts); + } + } + + } + + return getFirstAllowedLayout(availableLayouts); + + } + + function setLayout(nodeId, selectedLayout, availableLayouts) { + + var activeLayout = {}; + var layoutFound = false; + + for (var i = 0; availableLayouts.length > i; i++) { + var layout = availableLayouts[i]; + if (layout.path === selectedLayout.path) { + activeLayout = layout; + layout.active = true; + layoutFound = true; + } else { + layout.active = false; + } + } + + if (!layoutFound) { + activeLayout = getFirstAllowedLayout(availableLayouts); + } + + saveLayoutInLocalStorage(nodeId, activeLayout); + + return activeLayout; + + } + + function saveLayoutInLocalStorage(nodeId, selectedLayout) { + var layoutFound = false; + var storedLayouts = []; + + if (localStorageService.get(localStorageKey)) { + storedLayouts = localStorageService.get(localStorageKey); + } + + if (storedLayouts.length > 0) { + for (var i = 0; storedLayouts.length > i; i++) { + var layout = storedLayouts[i]; + if (layout.nodeId === nodeId) { + layout.path = selectedLayout.path; + layoutFound = true; + } + } + } + + if (!layoutFound) { + var storageObject = { + "nodeId": nodeId, + "path": selectedLayout.path + }; + storedLayouts.push(storageObject); + } + + localStorageService.set(localStorageKey, storedLayouts); + + } + + function getFirstAllowedLayout(layouts) { + + var firstAllowedLayout = {}; + + for (var i = 0; layouts.length > i; i++) { + var layout = layouts[i]; + if (layout.selected === true) { + firstAllowedLayout = layout; + break; + } + } + + return firstAllowedLayout; + } + + function selectHandler(selectedItem, selectedIndex, items, selection, $event) { + + var start = 0; + var end = 0; + var item = null; + + if ($event.shiftKey === true) { + + if (selectedIndex > firstSelectedIndex) { + + start = firstSelectedIndex; + end = selectedIndex; + + for (; end >= start; start++) { + item = items[start]; + selectItem(item, selection); + } + + } else { + + start = firstSelectedIndex; + end = selectedIndex; + + for (; end <= start; start--) { + item = items[start]; + selectItem(item, selection); + } + + } + + } else { + + if (selectedItem.selected) { + deselectItem(selectedItem, selection); + } else { + selectItem(selectedItem, selection); + } + + firstSelectedIndex = selectedIndex; + + } + + } + + function selectItem(item, selection) { + var isSelected = false; + for (var i = 0; selection.length > i; i++) { + var selectedItem = selection[i]; + if (item.id === selectedItem.id) { + isSelected = true; + } + } + if (!isSelected) { + selection.push({ id: item.id }); + item.selected = true; + } + } + + function deselectItem(item, selection) { + for (var i = 0; selection.length > i; i++) { + var selectedItem = selection[i]; + if (item.id === selectedItem.id) { + selection.splice(i, 1); + item.selected = false; + } + } + } + + function clearSelection(items, folders, selection) { + + var i = 0; + + selection.length = 0; + + if (angular.isArray(items)) { + for (i = 0; items.length > i; i++) { + var item = items[i]; + item.selected = false; + } + } + + if (angular.isArray(items)) { + for (i = 0; folders.length > i; i++) { + var folder = folders[i]; + folder.selected = false; + } + } + } + + function selectAllItems(items, selection, $event) { + + var checkbox = $event.target; + var clearSelection = false; + + if (!angular.isArray(items)) { + return; + } + + selection.length = 0; + + for (var i = 0; i < items.length; i++) { + + var item = items[i]; + + if (checkbox.checked) { + selection.push({ id: item.id }); + } else { + clearSelection = true; + } + + item.selected = checkbox.checked; + + } + + if (clearSelection) { + selection.length = 0; + } + + } + + function isSelectedAll(items, selection) { + + var numberOfSelectedItem = 0; + + for (var itemIndex = 0; items.length > itemIndex; itemIndex++) { + var item = items[itemIndex]; + + for (var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) { + var selectedItem = selection[selectedIndex]; + + if (item.id === selectedItem.id) { + numberOfSelectedItem++; + } + } + + } + + if (numberOfSelectedItem === items.length) { + return true; + } + + } + + function setSortingDirection(col, direction, options) { + return options.orderBy.toUpperCase() === col.toUpperCase() && options.orderDirection === direction; + } + + function setSorting(field, allow, options) { + if (allow) { + options.orderBy = field; + + if (options.orderDirection === "desc") { + options.orderDirection = "asc"; + } else { + options.orderDirection = "desc"; + } + } + } + + //This takes in a dictionary of Ids with Permissions and determines + // the intersect of all permissions to return an object representing the + // listview button permissions + function getButtonPermissions(unmergedPermissions, currentIdsWithPermissions) { + + if (currentIdsWithPermissions == null) { + currentIdsWithPermissions = {}; + } + + //merge the newly retrieved permissions to the main dictionary + _.each(unmergedPermissions, function (value, key, list) { + currentIdsWithPermissions[key] = value; + }); + + //get the intersect permissions + var arr = []; + _.each(currentIdsWithPermissions, function (value, key, list) { + arr.push(value); + }); + + //we need to use 'apply' to call intersection with an array of arrays, + //see: http://stackoverflow.com/a/16229480/694494 + var intersectPermissions = _.intersection.apply(_, arr); + + return { + canCopy: _.contains(intersectPermissions, 'O'), //Magic Char = O + canCreate: _.contains(intersectPermissions, 'C'), //Magic Char = C + canDelete: _.contains(intersectPermissions, 'D'), //Magic Char = D + canMove: _.contains(intersectPermissions, 'M'), //Magic Char = M + canPublish: _.contains(intersectPermissions, 'U'), //Magic Char = U + canUnpublish: _.contains(intersectPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish) + }; + } + + var service = { + getLayout: getLayout, + getFirstAllowedLayout: getFirstAllowedLayout, + setLayout: setLayout, + saveLayoutInLocalStorage: saveLayoutInLocalStorage, + selectHandler: selectHandler, + selectItem: selectItem, + deselectItem: deselectItem, + clearSelection: clearSelection, + selectAllItems: selectAllItems, + isSelectedAll: isSelectedAll, + setSortingDirection: setSortingDirection, + setSorting: setSorting, + getButtonPermissions: getButtonPermissions + }; + + return service; + + } + + + angular.module('umbraco.services').factory('listViewHelper', listViewHelper); + + +})(); angular.module('umbraco.services') .factory('localizationService', function ($http, $q, eventsService, $window, $filter, userService) { @@ -4192,377 +4192,377 @@ function macroService() { angular.module('umbraco.services').factory('macroService', macroService); -/** -* @ngdoc service -* @name umbraco.services.mediaHelper -* @description A helper object used for dealing with media items -**/ -function mediaHelper(umbRequestHelper) { - - //container of fileresolvers - var _mediaFileResolvers = {}; - - return { - /** - * @ngdoc function - * @name umbraco.services.mediaHelper#getImagePropertyValue - * @methodOf umbraco.services.mediaHelper - * @function - * - * @description - * Returns the file path associated with the media property if there is one - * - * @param {object} options Options object - * @param {object} options.mediaModel The media object to retrieve the image path from - * @param {object} options.imageOnly Optional, if true then will only return a path if the media item is an image - */ - getMediaPropertyValue: function (options) { - if (!options || !options.mediaModel) { - throw "The options objet does not contain the required parameters: mediaModel"; - } - - //combine all props, TODO: we really need a better way then this - var props = []; - if (options.mediaModel.properties) { - props = options.mediaModel.properties; - } else { - $(options.mediaModel.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 (angular.isString(item.value) && item.value.indexOf(mediaRoot) === 0) { - return true; - } - - return false; - }); - - if (!imageProp) { - return ""; - } - - var mediaVal; - - //our default images might store one or many images (as csv) - var split = imageProp.value.split(','); - var self = this; - mediaVal = _.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 (mediaVal.length && mediaVal.length > 0) { - if (!options.imageOnly || (options.imageOnly === true && mediaVal[0].isImage)) { - return mediaVal[0].file; - } - } - - return ""; - }, - - /** - * @ngdoc function - * @name umbraco.services.mediaHelper#getImagePropertyValue - * @methodOf umbraco.services.mediaHelper - * @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 && !options.mediaModel)) { - throw "The options objet does not contain the required parameters: imageModel"; - } - - //required to support backwards compatibility. - options.mediaModel = options.imageModel ? options.imageModel : options.mediaModel; - - options.imageOnly = true; - - return this.getMediaPropertyValue(options); - }, - /** - * @ngdoc function - * @name umbraco.services.mediaHelper#getThumbnail - * @methodOf umbraco.services.mediaHelper - * @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 ""; - }, - - registerFileResolver: function(propertyEditorAlias, func){ - _mediaFileResolvers[propertyEditorAlias] = func; - }, - - /** - * @ngdoc function - * @name umbraco.services.mediaHelper#resolveFileFromEntity - * @methodOf umbraco.services.mediaHelper - * @function - * - * @description - * Gets the media file url for a media entity returned with the entityResource - * - * @param {object} mediaEntity A media Entity returned from the entityResource - * @param {boolean} thumbnail Whether to return the thumbnail url or normal url - */ - resolveFileFromEntity : function(mediaEntity, thumbnail) { - - if (!angular.isObject(mediaEntity.metaData)) { - throw "Cannot resolve the file url from the mediaEntity, it does not contain the required metaData"; - } - - var values = _.values(mediaEntity.metaData); - for (var i = 0; i < values.length; i++) { - var val = values[i]; - if (angular.isObject(val) && val.PropertyEditorAlias) { - for (var resolver in _mediaFileResolvers) { - if (val.PropertyEditorAlias === resolver) { - //we need to format a property variable that coincides with how the property would be structured - // if it came from the mediaResource just to keep things slightly easier for the file resolvers. - var property = { value: val.Value }; - - return _mediaFileResolvers[resolver](property, mediaEntity, thumbnail); - } - } - } - } - - return ""; - }, - - /** - * @ngdoc function - * @name umbraco.services.mediaHelper#resolveFile - * @methodOf umbraco.services.mediaHelper - * @function - * - * @description - * Gets the media file url for a media object returned with the mediaResource - * - * @param {object} mediaEntity A media Entity returned from the entityResource - * @param {boolean} thumbnail Whether to return the thumbnail url or normal url - */ - /*jshint loopfunc: true */ - resolveFile : function(mediaItem, thumbnail){ - - function iterateProps(props){ - var res = null; - for(var resolver in _mediaFileResolvers) { - var property = _.find(props, function(prop){ return prop.editor === resolver; }); - if(property){ - res = _mediaFileResolvers[resolver](property, mediaItem, thumbnail); - break; - } - } - - return res; - } - - //we either have properties raw on the object, or spread out on tabs - var result = ""; - if(mediaItem.properties){ - result = iterateProps(mediaItem.properties); - }else if(mediaItem.tabs){ - for(var tab in mediaItem.tabs) { - if(mediaItem.tabs[tab].properties){ - result = iterateProps(mediaItem.tabs[tab].properties); - if(result){ - break; - } - } - } - } - return result; - }, - - /*jshint loopfunc: true */ - hasFilePropertyType : function(mediaItem){ - function iterateProps(props){ - var res = false; - for(var resolver in _mediaFileResolvers) { - var property = _.find(props, function(prop){ return prop.editor === resolver; }); - if(property){ - res = true; - break; - } - } - return res; - } - - //we either have properties raw on the object, or spread out on tabs - var result = false; - if(mediaItem.properties){ - result = iterateProps(mediaItem.properties); - }else if(mediaItem.tabs){ - for(var tab in mediaItem.tabs) { - if(mediaItem.tabs[tab].properties){ - result = iterateProps(mediaItem.tabs[tab].properties); - if(result){ - break; - } - } - } - } - return result; - }, - - /** - * @ngdoc function - * @name umbraco.services.mediaHelper#scaleToMaxSize - * @methodOf umbraco.services.mediaHelper - * @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.mediaHelper#getThumbnailFromPath - * @methodOf umbraco.services.mediaHelper - * @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) { - - //If the path is not an image we cannot get a thumb - if (!this.detectIfImageByExtension(imagePath)) { - return null; - } - - //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.mediaHelper#detectIfImageByExtension - * @methodOf umbraco.services.mediaHelper - * @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) { - - if (!imagePath) { - return false; - } - - var lowered = imagePath.toLowerCase(); - var ext = lowered.substr(lowered.lastIndexOf(".") + 1); - return ("," + Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes + ",").indexOf("," + ext + ",") !== -1; - }, - - /** - * @ngdoc function - * @name umbraco.services.mediaHelper#formatFileTypes - * @methodOf umbraco.services.mediaHelper - * @function - * - * @description - * Returns a string with correctly formated file types for ng-file-upload - * - * @param {string} file types, ex: jpg,png,tiff - */ - formatFileTypes: function(fileTypes) { - - var fileTypesArray = fileTypes.split(','); - var newFileTypesArray = []; - - for (var i = 0; i < fileTypesArray.length; i++) { - var fileType = fileTypesArray[i]; - - if (fileType.indexOf(".") !== 0) { - fileType = ".".concat(fileType); - } - - newFileTypesArray.push(fileType); - } - - return newFileTypesArray.join(","); - - } - - }; -}angular.module('umbraco.services').factory('mediaHelper', mediaHelper); +/** +* @ngdoc service +* @name umbraco.services.mediaHelper +* @description A helper object used for dealing with media items +**/ +function mediaHelper(umbRequestHelper) { + + //container of fileresolvers + var _mediaFileResolvers = {}; + + return { + /** + * @ngdoc function + * @name umbraco.services.mediaHelper#getImagePropertyValue + * @methodOf umbraco.services.mediaHelper + * @function + * + * @description + * Returns the file path associated with the media property if there is one + * + * @param {object} options Options object + * @param {object} options.mediaModel The media object to retrieve the image path from + * @param {object} options.imageOnly Optional, if true then will only return a path if the media item is an image + */ + getMediaPropertyValue: function (options) { + if (!options || !options.mediaModel) { + throw "The options objet does not contain the required parameters: mediaModel"; + } + + //combine all props, TODO: we really need a better way then this + var props = []; + if (options.mediaModel.properties) { + props = options.mediaModel.properties; + } else { + $(options.mediaModel.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 (angular.isString(item.value) && item.value.indexOf(mediaRoot) === 0) { + return true; + } + + return false; + }); + + if (!imageProp) { + return ""; + } + + var mediaVal; + + //our default images might store one or many images (as csv) + var split = imageProp.value.split(','); + var self = this; + mediaVal = _.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 (mediaVal.length && mediaVal.length > 0) { + if (!options.imageOnly || (options.imageOnly === true && mediaVal[0].isImage)) { + return mediaVal[0].file; + } + } + + return ""; + }, + + /** + * @ngdoc function + * @name umbraco.services.mediaHelper#getImagePropertyValue + * @methodOf umbraco.services.mediaHelper + * @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 && !options.mediaModel)) { + throw "The options objet does not contain the required parameters: imageModel"; + } + + //required to support backwards compatibility. + options.mediaModel = options.imageModel ? options.imageModel : options.mediaModel; + + options.imageOnly = true; + + return this.getMediaPropertyValue(options); + }, + /** + * @ngdoc function + * @name umbraco.services.mediaHelper#getThumbnail + * @methodOf umbraco.services.mediaHelper + * @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 ""; + }, + + registerFileResolver: function(propertyEditorAlias, func){ + _mediaFileResolvers[propertyEditorAlias] = func; + }, + + /** + * @ngdoc function + * @name umbraco.services.mediaHelper#resolveFileFromEntity + * @methodOf umbraco.services.mediaHelper + * @function + * + * @description + * Gets the media file url for a media entity returned with the entityResource + * + * @param {object} mediaEntity A media Entity returned from the entityResource + * @param {boolean} thumbnail Whether to return the thumbnail url or normal url + */ + resolveFileFromEntity : function(mediaEntity, thumbnail) { + + if (!angular.isObject(mediaEntity.metaData)) { + throw "Cannot resolve the file url from the mediaEntity, it does not contain the required metaData"; + } + + var values = _.values(mediaEntity.metaData); + for (var i = 0; i < values.length; i++) { + var val = values[i]; + if (angular.isObject(val) && val.PropertyEditorAlias) { + for (var resolver in _mediaFileResolvers) { + if (val.PropertyEditorAlias === resolver) { + //we need to format a property variable that coincides with how the property would be structured + // if it came from the mediaResource just to keep things slightly easier for the file resolvers. + var property = { value: val.Value }; + + return _mediaFileResolvers[resolver](property, mediaEntity, thumbnail); + } + } + } + } + + return ""; + }, + + /** + * @ngdoc function + * @name umbraco.services.mediaHelper#resolveFile + * @methodOf umbraco.services.mediaHelper + * @function + * + * @description + * Gets the media file url for a media object returned with the mediaResource + * + * @param {object} mediaEntity A media Entity returned from the entityResource + * @param {boolean} thumbnail Whether to return the thumbnail url or normal url + */ + /*jshint loopfunc: true */ + resolveFile : function(mediaItem, thumbnail){ + + function iterateProps(props){ + var res = null; + for(var resolver in _mediaFileResolvers) { + var property = _.find(props, function(prop){ return prop.editor === resolver; }); + if(property){ + res = _mediaFileResolvers[resolver](property, mediaItem, thumbnail); + break; + } + } + + return res; + } + + //we either have properties raw on the object, or spread out on tabs + var result = ""; + if(mediaItem.properties){ + result = iterateProps(mediaItem.properties); + }else if(mediaItem.tabs){ + for(var tab in mediaItem.tabs) { + if(mediaItem.tabs[tab].properties){ + result = iterateProps(mediaItem.tabs[tab].properties); + if(result){ + break; + } + } + } + } + return result; + }, + + /*jshint loopfunc: true */ + hasFilePropertyType : function(mediaItem){ + function iterateProps(props){ + var res = false; + for(var resolver in _mediaFileResolvers) { + var property = _.find(props, function(prop){ return prop.editor === resolver; }); + if(property){ + res = true; + break; + } + } + return res; + } + + //we either have properties raw on the object, or spread out on tabs + var result = false; + if(mediaItem.properties){ + result = iterateProps(mediaItem.properties); + }else if(mediaItem.tabs){ + for(var tab in mediaItem.tabs) { + if(mediaItem.tabs[tab].properties){ + result = iterateProps(mediaItem.tabs[tab].properties); + if(result){ + break; + } + } + } + } + return result; + }, + + /** + * @ngdoc function + * @name umbraco.services.mediaHelper#scaleToMaxSize + * @methodOf umbraco.services.mediaHelper + * @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.mediaHelper#getThumbnailFromPath + * @methodOf umbraco.services.mediaHelper + * @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) { + + //If the path is not an image we cannot get a thumb + if (!this.detectIfImageByExtension(imagePath)) { + return null; + } + + //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.mediaHelper#detectIfImageByExtension + * @methodOf umbraco.services.mediaHelper + * @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) { + + if (!imagePath) { + return false; + } + + var lowered = imagePath.toLowerCase(); + var ext = lowered.substr(lowered.lastIndexOf(".") + 1); + return ("," + Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes + ",").indexOf("," + ext + ",") !== -1; + }, + + /** + * @ngdoc function + * @name umbraco.services.mediaHelper#formatFileTypes + * @methodOf umbraco.services.mediaHelper + * @function + * + * @description + * Returns a string with correctly formated file types for ng-file-upload + * + * @param {string} file types, ex: jpg,png,tiff + */ + formatFileTypes: function(fileTypes) { + + var fileTypesArray = fileTypes.split(','); + var newFileTypesArray = []; + + for (var i = 0; i < fileTypesArray.length; i++) { + var fileType = fileTypesArray[i]; + + if (fileType.indexOf(".") !== 0) { + fileType = ".".concat(fileType); + } + + newFileTypesArray.push(fileType); + } + + return newFileTypesArray.join(","); + + } + + }; +}angular.module('umbraco.services').factory('mediaHelper', mediaHelper); /** * @ngdoc service @@ -4641,741 +4641,741 @@ function umbracoMenuActions($q, treeService, $location, navigationService, appSt } angular.module('umbraco.services').factory('umbracoMenuActions', umbracoMenuActions); -/** - * @ngdoc service - * @name umbraco.services.navigationService - * - * @requires $rootScope - * @requires $routeParams - * @requires $log - * @requires $location - * @requires dialogService - * @requires treeService - * @requires sectionResource - * - * @description - * Service to handle the main application navigation. Responsible for invoking the tree - * Section navigation and search, and maintain their state for the entire application lifetime - * - */ -function navigationService($rootScope, $routeParams, $log, $location, $q, $timeout, $injector, dialogService, umbModelMapper, treeService, notificationsService, historyService, appState, angularHelper) { - - - //used to track the current dialog object - var currentDialog = null; - - //the main tree event handler, which gets assigned via the setupTreeEvents method - var mainTreeEventHandler = null; - //tracks the user profile dialog - var userDialog = null; - - function setMode(mode) { - switch (mode) { - case 'tree': - appState.setGlobalState("navMode", "tree"); - appState.setGlobalState("showNavigation", true); - appState.setMenuState("showMenu", false); - appState.setMenuState("showMenuDialog", false); - appState.setGlobalState("stickyNavigation", false); - appState.setGlobalState("showTray", false); - - //$("#search-form input").focus(); - break; - case 'menu': - appState.setGlobalState("navMode", "menu"); - appState.setGlobalState("showNavigation", true); - appState.setMenuState("showMenu", true); - appState.setMenuState("showMenuDialog", false); - appState.setGlobalState("stickyNavigation", true); - break; - case 'dialog': - appState.setGlobalState("navMode", "dialog"); - appState.setGlobalState("stickyNavigation", true); - appState.setGlobalState("showNavigation", true); - appState.setMenuState("showMenu", false); - appState.setMenuState("showMenuDialog", true); - break; - case 'search': - appState.setGlobalState("navMode", "search"); - appState.setGlobalState("stickyNavigation", false); - appState.setGlobalState("showNavigation", true); - appState.setMenuState("showMenu", false); - appState.setSectionState("showSearchResults", true); - appState.setMenuState("showMenuDialog", false); - - //TODO: This would be much better off in the search field controller listening to appState changes - $timeout(function() { - $("#search-field").focus(); - }); - - break; - default: - appState.setGlobalState("navMode", "default"); - appState.setMenuState("showMenu", false); - appState.setMenuState("showMenuDialog", false); - appState.setSectionState("showSearchResults", false); - appState.setGlobalState("stickyNavigation", false); - appState.setGlobalState("showTray", false); - - if (appState.getGlobalState("isTablet") === true) { - appState.setGlobalState("showNavigation", false); - } - - break; - } - } - - var service = { - - /** initializes the navigation service */ - init: function() { - - //keep track of the current section - initially this will always be undefined so - // no point in setting it now until it changes. - $rootScope.$watch(function () { - return $routeParams.section; - }, function (newVal, oldVal) { - appState.setSectionState("currentSection", newVal); - }); - - - }, - - /** - * @ngdoc method - * @name umbraco.services.navigationService#load - * @methodOf umbraco.services.navigationService - * - * @description - * Shows the legacy iframe and loads in the content based on the source url - * @param {String} source The URL to load into the iframe - */ - loadLegacyIFrame: function (source) { - $location.path("/" + appState.getSectionState("currentSection") + "/framed/" + encodeURIComponent(source)); - }, - - /** - * @ngdoc method - * @name umbraco.services.navigationService#changeSection - * @methodOf umbraco.services.navigationService - * - * @description - * Changes the active section to a given section alias - * If the navigation is 'sticky' this will load the associated tree - * and load the dashboard related to the section - * @param {string} sectionAlias The alias of the section - */ - changeSection: function(sectionAlias, force) { - setMode("default-opensection"); - - if (force && appState.getSectionState("currentSection") === sectionAlias) { - appState.setSectionState("currentSection", ""); - } - - appState.setSectionState("currentSection", sectionAlias); - this.showTree(sectionAlias); - - $location.path(sectionAlias); - }, - - /** - * @ngdoc method - * @name umbraco.services.navigationService#showTree - * @methodOf umbraco.services.navigationService - * - * @description - * Displays the tree for a given section alias but turning on the containing dom element - * only changes if the section is different from the current one - * @param {string} sectionAlias The alias of the section to load - * @param {Object} syncArgs Optional object of arguments for syncing the tree for the section being shown - */ - showTree: function (sectionAlias, syncArgs) { - if (sectionAlias !== appState.getSectionState("currentSection")) { - appState.setSectionState("currentSection", sectionAlias); - - if (syncArgs) { - this.syncTree(syncArgs); - } - } - setMode("tree"); - }, - - showTray: function () { - appState.setGlobalState("showTray", true); - }, - - hideTray: function () { - appState.setGlobalState("showTray", false); - }, - - /** - Called to assign the main tree event handler - this is called by the navigation controller. - TODO: Potentially another dev could call this which would kind of mung the whole app so potentially there's a better way. - */ - setupTreeEvents: function(treeEventHandler) { - mainTreeEventHandler = treeEventHandler; - - //when a tree is loaded into a section, we need to put it into appState - mainTreeEventHandler.bind("treeLoaded", function(ev, args) { - appState.setTreeState("currentRootNode", args.tree); - }); - - //when a tree node is synced this event will fire, this allows us to set the currentNode - mainTreeEventHandler.bind("treeSynced", function (ev, args) { - - if (args.activate === undefined || args.activate === true) { - //set the current selected node - appState.setTreeState("selectedNode", args.node); - //when a node is activated, this is the same as clicking it and we need to set the - //current menu item to be this node as well. - appState.setMenuState("currentNode", args.node); - } - }); - - //this reacts to the options item in the tree - mainTreeEventHandler.bind("treeOptionsClick", function(ev, args) { - ev.stopPropagation(); - ev.preventDefault(); - - //Set the current action node (this is not the same as the current selected node!) - appState.setMenuState("currentNode", args.node); - - if (args.event && args.event.altKey) { - args.skipDefault = true; - } - - service.showMenu(ev, args); - }); - - mainTreeEventHandler.bind("treeNodeAltSelect", function(ev, args) { - ev.stopPropagation(); - ev.preventDefault(); - - args.skipDefault = true; - service.showMenu(ev, args); - }); - - //this reacts to tree items themselves being clicked - //the tree directive should not contain any handling, simply just bubble events - mainTreeEventHandler.bind("treeNodeSelect", function (ev, args) { - var n = args.node; - ev.stopPropagation(); - ev.preventDefault(); - - if (n.metaData && n.metaData["jsClickCallback"] && angular.isString(n.metaData["jsClickCallback"]) && n.metaData["jsClickCallback"] !== "") { - //this is a legacy tree node! - var jsPrefix = "javascript:"; - var js; - if (n.metaData["jsClickCallback"].startsWith(jsPrefix)) { - js = n.metaData["jsClickCallback"].substr(jsPrefix.length); - } - else { - js = n.metaData["jsClickCallback"]; - } - try { - var func = eval(js); - //this is normally not necessary since the eval above should execute the method and will return nothing. - if (func != null && (typeof func === "function")) { - func.call(); - } - } - catch(ex) { - $log.error("Error evaluating js callback from legacy tree node: " + ex); - } - } - else if (n.routePath) { - //add action to the history service - historyService.add({ name: n.name, link: n.routePath, icon: n.icon }); - - //put this node into the tree state - appState.setTreeState("selectedNode", args.node); - //when a node is clicked we also need to set the active menu node to this node - appState.setMenuState("currentNode", args.node); - - //not legacy, lets just set the route value and clear the query string if there is one. - $location.path(n.routePath).search(""); - } - else if (args.element.section) { - $location.path(args.element.section).search(""); - } - - service.hideNavigation(); - }); - }, - /** - * @ngdoc method - * @name umbraco.services.navigationService#syncTree - * @methodOf umbraco.services.navigationService - * - * @description - * Syncs a tree with a given path, returns a promise - * The path format is: ["itemId","itemId"], and so on - * so to sync to a specific document type node do: - *
-         * navigationService.syncTree({tree: 'content', path: ["-1","123d"], forceReload: true});
-         * 
- * @param {Object} args arguments passed to the function - * @param {String} args.tree the tree alias to sync to - * @param {Array} args.path the path to sync the tree to - * @param {Boolean} args.forceReload optional, specifies whether to force reload the node data from the server even if it already exists in the tree currently - * @param {Boolean} args.activate optional, specifies whether to set the synced node to be the active node, this will default to true if not specified - */ - syncTree: function (args) { - if (!args) { - throw "args cannot be null"; - } - if (!args.path) { - throw "args.path cannot be null"; - } - if (!args.tree) { - throw "args.tree cannot be null"; - } - - if (mainTreeEventHandler) { - //returns a promise - return mainTreeEventHandler.syncTree(args); - } - - //couldn't sync - return angularHelper.rejectedPromise(); - }, - - /** - Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to - have to set an active tree and then sync, the new API does this in one method by using syncTree - */ - _syncPath: function(path, forceReload) { - if (mainTreeEventHandler) { - mainTreeEventHandler.syncTree({ path: path, forceReload: forceReload }); - } - }, - - //TODO: This should return a promise - reloadNode: function(node) { - if (mainTreeEventHandler) { - mainTreeEventHandler.reloadNode(node); - } - }, - - //TODO: This should return a promise - reloadSection: function(sectionAlias) { - if (mainTreeEventHandler) { - mainTreeEventHandler.clearCache({ section: sectionAlias }); - mainTreeEventHandler.load(sectionAlias); - } - }, - - /** - Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to - have to set an active tree and then sync, the new API does this in one method by using syncTreePath - */ - _setActiveTreeType: function (treeAlias, loadChildren) { - if (mainTreeEventHandler) { - mainTreeEventHandler._setActiveTreeType(treeAlias, loadChildren); - } - }, - - /** - * @ngdoc method - * @name umbraco.services.navigationService#hideTree - * @methodOf umbraco.services.navigationService - * - * @description - * Hides the tree by hiding the containing dom element - */ - hideTree: function() { - - if (appState.getGlobalState("isTablet") === true && !appState.getGlobalState("stickyNavigation")) { - //reset it to whatever is in the url - appState.setSectionState("currentSection", $routeParams.section); - setMode("default-hidesectiontree"); - } - - }, - - /** - * @ngdoc method - * @name umbraco.services.navigationService#showMenu - * @methodOf umbraco.services.navigationService - * - * @description - * Hides the tree by hiding the containing dom element. - * This always returns a promise! - * - * @param {Event} event the click event triggering the method, passed from the DOM element - */ - showMenu: function(event, args) { - - var deferred = $q.defer(); - var self = this; - - treeService.getMenu({ treeNode: args.node }) - .then(function(data) { - - //check for a default - //NOTE: event will be undefined when a call to hideDialog is made so it won't re-load the default again. - // but perhaps there's a better way to deal with with an additional parameter in the args ? it works though. - if (data.defaultAlias && !args.skipDefault) { - - var found = _.find(data.menuItems, function(item) { - return item.alias = data.defaultAlias; - }); - - if (found) { - - //NOTE: This is assigning the current action node - this is not the same as the currently selected node! - appState.setMenuState("currentNode", args.node); - - //ensure the current dialog is cleared before creating another! - if (currentDialog) { - dialogService.close(currentDialog); - } - - var dialog = self.showDialog({ - node: args.node, - action: found, - section: appState.getSectionState("currentSection") - }); - - //return the dialog this is opening. - deferred.resolve(dialog); - return; - } - } - - //there is no default or we couldn't find one so just continue showing the menu - - setMode("menu"); - - appState.setMenuState("currentNode", args.node); - appState.setMenuState("menuActions", data.menuItems); - appState.setMenuState("dialogTitle", args.node.name); - - //we're not opening a dialog, return null. - deferred.resolve(null); - }); - - return deferred.promise; - }, - - /** - * @ngdoc method - * @name umbraco.services.navigationService#hideMenu - * @methodOf umbraco.services.navigationService - * - * @description - * Hides the menu by hiding the containing dom element - */ - hideMenu: function() { - //SD: Would we ever want to access the last action'd node instead of clearing it here? - appState.setMenuState("currentNode", null); - appState.setMenuState("menuActions", []); - setMode("tree"); - }, - - /** Executes a given menu action */ - executeMenuAction: function (action, node, section) { - - if (!action) { - throw "action cannot be null"; - } - if (!node) { - throw "node cannot be null"; - } - if (!section) { - throw "section cannot be null"; - } - - if (action.metaData && action.metaData["actionRoute"] && angular.isString(action.metaData["actionRoute"])) { - //first check if the menu item simply navigates to a route - var parts = action.metaData["actionRoute"].split("?"); - $location.path(parts[0]).search(parts.length > 1 ? parts[1] : ""); - this.hideNavigation(); - return; - } - else if (action.metaData && action.metaData["jsAction"] && angular.isString(action.metaData["jsAction"])) { - - //we'll try to get the jsAction from the injector - var menuAction = action.metaData["jsAction"].split('.'); - if (menuAction.length !== 2) { - - //if it is not two parts long then this most likely means that it's a legacy action - var js = action.metaData["jsAction"].replace("javascript:", ""); - //there's not really a different way to acheive this except for eval - eval(js); - } - else { - var menuActionService = $injector.get(menuAction[0]); - if (!menuActionService) { - throw "The angular service " + menuAction[0] + " could not be found"; - } - - var method = menuActionService[menuAction[1]]; - - if (!method) { - throw "The method " + menuAction[1] + " on the angular service " + menuAction[0] + " could not be found"; - } - - method.apply(this, [{ - //map our content object to a basic entity to pass in to the menu handlers, - //this is required for consistency since a menu item needs to be decoupled from a tree node since the menu can - //exist standalone in the editor for which it can only pass in an entity (not tree node). - entity: umbModelMapper.convertToEntityBasic(node), - action: action, - section: section, - treeAlias: treeService.getTreeAlias(node) - }]); - } - } - else { - service.showDialog({ - node: node, - action: action, - section: section - }); - } - }, - - /** - * @ngdoc method - * @name umbraco.services.navigationService#showUserDialog - * @methodOf umbraco.services.navigationService - * - * @description - * Opens the user dialog, next to the sections navigation - * template is located in views/common/dialogs/user.html - */ - showUserDialog: function () { - // hide tray and close help dialog - if (service.helpDialog) { - service.helpDialog.close(); - } - service.hideTray(); - - if (service.userDialog) { - service.userDialog.close(); - service.userDialog = undefined; - } - - service.userDialog = dialogService.open( - { - template: "views/common/dialogs/user.html", - modalClass: "umb-modal-left", - show: true - }); - - return service.userDialog; - }, - - /** - * @ngdoc method - * @name umbraco.services.navigationService#showUserDialog - * @methodOf umbraco.services.navigationService - * - * @description - * Opens the user dialog, next to the sections navigation - * template is located in views/common/dialogs/user.html - */ - showHelpDialog: function () { - // hide tray and close user dialog - service.hideTray(); - if (service.userDialog) { - service.userDialog.close(); - } - - if(service.helpDialog){ - service.helpDialog.close(); - service.helpDialog = undefined; - } - - service.helpDialog = dialogService.open( - { - template: "views/common/dialogs/help.html", - modalClass: "umb-modal-left", - show: true - }); - - return service.helpDialog; - }, - - /** - * @ngdoc method - * @name umbraco.services.navigationService#showDialog - * @methodOf umbraco.services.navigationService - * - * @description - * Opens a dialog, for a given action on a given tree node - * uses the dialogService to inject the selected action dialog - * into #dialog div.umb-panel-body - * the path to the dialog view is determined by: - * "views/" + current tree + "/" + action alias + ".html" - * The dialog controller will get passed a scope object that is created here with the properties: - * scope.currentNode = the selected tree node - * scope.currentAction = the selected menu item - * so that the dialog controllers can use these properties - * - * @param {Object} args arguments passed to the function - * @param {Scope} args.scope current scope passed to the dialog - * @param {Object} args.action the clicked action containing `name` and `alias` - */ - showDialog: function(args) { - - if (!args) { - throw "showDialog is missing the args parameter"; - } - if (!args.action) { - throw "The args parameter must have an 'action' property as the clicked menu action object"; - } - if (!args.node) { - throw "The args parameter must have a 'node' as the active tree node"; - } - - //ensure the current dialog is cleared before creating another! - if (currentDialog) { - dialogService.close(currentDialog); - currentDialog = null; - } - - setMode("dialog"); - - //NOTE: Set up the scope object and assign properties, this is legacy functionality but we have to live with it now. - // we should be passing in currentNode and currentAction using 'dialogData' for the dialog, not attaching it to a scope. - // This scope instance will be destroyed by the dialog so it cannot be a scope that exists outside of the dialog. - // If a scope instance has been passed in, we'll have to create a child scope of it, otherwise a new root scope. - var dialogScope = args.scope ? args.scope.$new() : $rootScope.$new(); - dialogScope.currentNode = args.node; - dialogScope.currentAction = args.action; - - //the title might be in the meta data, check there first - if (args.action.metaData["dialogTitle"]) { - appState.setMenuState("dialogTitle", args.action.metaData["dialogTitle"]); - } - else { - appState.setMenuState("dialogTitle", args.action.name); - } - - var templateUrl; - var iframe; - - if (args.action.metaData["actionUrl"]) { - templateUrl = args.action.metaData["actionUrl"]; - iframe = true; - } - else if (args.action.metaData["actionView"]) { - templateUrl = args.action.metaData["actionView"]; - iframe = false; - } - else { - - //by convention we will look into the /views/{treetype}/{action}.html - // for example: /views/content/create.html - - //we will also check for a 'packageName' for the current tree, if it exists then the convention will be: - // for example: /App_Plugins/{mypackage}/backoffice/{treetype}/create.html - - var treeAlias = treeService.getTreeAlias(args.node); - var packageTreeFolder = treeService.getTreePackageFolder(treeAlias); - - if (!treeAlias) { - throw "Could not get tree alias for node " + args.node.id; - } - - if (packageTreeFolder) { - templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath + - "/" + packageTreeFolder + - "/backoffice/" + treeAlias + "/" + args.action.alias + ".html"; - } - else { - templateUrl = "views/" + treeAlias + "/" + args.action.alias + ".html"; - } - - iframe = false; - } - - //TODO: some action's want to launch a new window like live editing, we support this in the menu item's metadata with - // a key called: "actionUrlMethod" which can be set to either: Dialog, BlankWindow. Normally this is always set to Dialog - // if a URL is specified in the "actionUrl" metadata. For now I'm not going to implement launching in a blank window, - // though would be v-easy, just not sure we want to ever support that? - - var dialog = dialogService.open( - { - container: $("#dialog div.umb-modalcolumn-body"), - //The ONLY reason we're passing in scope to the dialogService (which is legacy functionality) is - // for backwards compatibility since many dialogs require $scope.currentNode or $scope.currentAction - // to exist - scope: dialogScope, - inline: true, - show: true, - iframe: iframe, - modalClass: "umb-dialog", - template: templateUrl, - - //These will show up on the dialog controller's $scope under dialogOptions - currentNode: args.node, - currentAction: args.action, - }); - - //save the currently assigned dialog so it can be removed before a new one is created - currentDialog = dialog; - return dialog; - }, - - /** - * @ngdoc method - * @name umbraco.services.navigationService#hideDialog - * @methodOf umbraco.services.navigationService - * - * @description - * hides the currently open dialog - */ - hideDialog: function (showMenu) { - - setMode("default"); - - if(showMenu){ - this.showMenu(undefined, { skipDefault: true, node: appState.getMenuState("currentNode") }); - } - }, - /** - * @ngdoc method - * @name umbraco.services.navigationService#showSearch - * @methodOf umbraco.services.navigationService - * - * @description - * shows the search pane - */ - showSearch: function() { - setMode("search"); - }, - /** - * @ngdoc method - * @name umbraco.services.navigationService#hideSearch - * @methodOf umbraco.services.navigationService - * - * @description - * hides the search pane - */ - hideSearch: function() { - setMode("default-hidesearch"); - }, - /** - * @ngdoc method - * @name umbraco.services.navigationService#hideNavigation - * @methodOf umbraco.services.navigationService - * - * @description - * hides any open navigation panes and resets the tree, actions and the currently selected node - */ - hideNavigation: function() { - appState.setMenuState("menuActions", []); - setMode("default"); - } - }; - - return service; -} - -angular.module('umbraco.services').factory('navigationService', navigationService); +/** + * @ngdoc service + * @name umbraco.services.navigationService + * + * @requires $rootScope + * @requires $routeParams + * @requires $log + * @requires $location + * @requires dialogService + * @requires treeService + * @requires sectionResource + * + * @description + * Service to handle the main application navigation. Responsible for invoking the tree + * Section navigation and search, and maintain their state for the entire application lifetime + * + */ +function navigationService($rootScope, $routeParams, $log, $location, $q, $timeout, $injector, dialogService, umbModelMapper, treeService, notificationsService, historyService, appState, angularHelper) { + + + //used to track the current dialog object + var currentDialog = null; + + //the main tree event handler, which gets assigned via the setupTreeEvents method + var mainTreeEventHandler = null; + //tracks the user profile dialog + var userDialog = null; + + function setMode(mode) { + switch (mode) { + case 'tree': + appState.setGlobalState("navMode", "tree"); + appState.setGlobalState("showNavigation", true); + appState.setMenuState("showMenu", false); + appState.setMenuState("showMenuDialog", false); + appState.setGlobalState("stickyNavigation", false); + appState.setGlobalState("showTray", false); + + //$("#search-form input").focus(); + break; + case 'menu': + appState.setGlobalState("navMode", "menu"); + appState.setGlobalState("showNavigation", true); + appState.setMenuState("showMenu", true); + appState.setMenuState("showMenuDialog", false); + appState.setGlobalState("stickyNavigation", true); + break; + case 'dialog': + appState.setGlobalState("navMode", "dialog"); + appState.setGlobalState("stickyNavigation", true); + appState.setGlobalState("showNavigation", true); + appState.setMenuState("showMenu", false); + appState.setMenuState("showMenuDialog", true); + break; + case 'search': + appState.setGlobalState("navMode", "search"); + appState.setGlobalState("stickyNavigation", false); + appState.setGlobalState("showNavigation", true); + appState.setMenuState("showMenu", false); + appState.setSectionState("showSearchResults", true); + appState.setMenuState("showMenuDialog", false); + + //TODO: This would be much better off in the search field controller listening to appState changes + $timeout(function() { + $("#search-field").focus(); + }); + + break; + default: + appState.setGlobalState("navMode", "default"); + appState.setMenuState("showMenu", false); + appState.setMenuState("showMenuDialog", false); + appState.setSectionState("showSearchResults", false); + appState.setGlobalState("stickyNavigation", false); + appState.setGlobalState("showTray", false); + + if (appState.getGlobalState("isTablet") === true) { + appState.setGlobalState("showNavigation", false); + } + + break; + } + } + + var service = { + + /** initializes the navigation service */ + init: function() { + + //keep track of the current section - initially this will always be undefined so + // no point in setting it now until it changes. + $rootScope.$watch(function () { + return $routeParams.section; + }, function (newVal, oldVal) { + appState.setSectionState("currentSection", newVal); + }); + + + }, + + /** + * @ngdoc method + * @name umbraco.services.navigationService#load + * @methodOf umbraco.services.navigationService + * + * @description + * Shows the legacy iframe and loads in the content based on the source url + * @param {String} source The URL to load into the iframe + */ + loadLegacyIFrame: function (source) { + $location.path("/" + appState.getSectionState("currentSection") + "/framed/" + encodeURIComponent(source)); + }, + + /** + * @ngdoc method + * @name umbraco.services.navigationService#changeSection + * @methodOf umbraco.services.navigationService + * + * @description + * Changes the active section to a given section alias + * If the navigation is 'sticky' this will load the associated tree + * and load the dashboard related to the section + * @param {string} sectionAlias The alias of the section + */ + changeSection: function(sectionAlias, force) { + setMode("default-opensection"); + + if (force && appState.getSectionState("currentSection") === sectionAlias) { + appState.setSectionState("currentSection", ""); + } + + appState.setSectionState("currentSection", sectionAlias); + this.showTree(sectionAlias); + + $location.path(sectionAlias); + }, + + /** + * @ngdoc method + * @name umbraco.services.navigationService#showTree + * @methodOf umbraco.services.navigationService + * + * @description + * Displays the tree for a given section alias but turning on the containing dom element + * only changes if the section is different from the current one + * @param {string} sectionAlias The alias of the section to load + * @param {Object} syncArgs Optional object of arguments for syncing the tree for the section being shown + */ + showTree: function (sectionAlias, syncArgs) { + if (sectionAlias !== appState.getSectionState("currentSection")) { + appState.setSectionState("currentSection", sectionAlias); + + if (syncArgs) { + this.syncTree(syncArgs); + } + } + setMode("tree"); + }, + + showTray: function () { + appState.setGlobalState("showTray", true); + }, + + hideTray: function () { + appState.setGlobalState("showTray", false); + }, + + /** + Called to assign the main tree event handler - this is called by the navigation controller. + TODO: Potentially another dev could call this which would kind of mung the whole app so potentially there's a better way. + */ + setupTreeEvents: function(treeEventHandler) { + mainTreeEventHandler = treeEventHandler; + + //when a tree is loaded into a section, we need to put it into appState + mainTreeEventHandler.bind("treeLoaded", function(ev, args) { + appState.setTreeState("currentRootNode", args.tree); + }); + + //when a tree node is synced this event will fire, this allows us to set the currentNode + mainTreeEventHandler.bind("treeSynced", function (ev, args) { + + if (args.activate === undefined || args.activate === true) { + //set the current selected node + appState.setTreeState("selectedNode", args.node); + //when a node is activated, this is the same as clicking it and we need to set the + //current menu item to be this node as well. + appState.setMenuState("currentNode", args.node); + } + }); + + //this reacts to the options item in the tree + mainTreeEventHandler.bind("treeOptionsClick", function(ev, args) { + ev.stopPropagation(); + ev.preventDefault(); + + //Set the current action node (this is not the same as the current selected node!) + appState.setMenuState("currentNode", args.node); + + if (args.event && args.event.altKey) { + args.skipDefault = true; + } + + service.showMenu(ev, args); + }); + + mainTreeEventHandler.bind("treeNodeAltSelect", function(ev, args) { + ev.stopPropagation(); + ev.preventDefault(); + + args.skipDefault = true; + service.showMenu(ev, args); + }); + + //this reacts to tree items themselves being clicked + //the tree directive should not contain any handling, simply just bubble events + mainTreeEventHandler.bind("treeNodeSelect", function (ev, args) { + var n = args.node; + ev.stopPropagation(); + ev.preventDefault(); + + if (n.metaData && n.metaData["jsClickCallback"] && angular.isString(n.metaData["jsClickCallback"]) && n.metaData["jsClickCallback"] !== "") { + //this is a legacy tree node! + var jsPrefix = "javascript:"; + var js; + if (n.metaData["jsClickCallback"].startsWith(jsPrefix)) { + js = n.metaData["jsClickCallback"].substr(jsPrefix.length); + } + else { + js = n.metaData["jsClickCallback"]; + } + try { + var func = eval(js); + //this is normally not necessary since the eval above should execute the method and will return nothing. + if (func != null && (typeof func === "function")) { + func.call(); + } + } + catch(ex) { + $log.error("Error evaluating js callback from legacy tree node: " + ex); + } + } + else if (n.routePath) { + //add action to the history service + historyService.add({ name: n.name, link: n.routePath, icon: n.icon }); + + //put this node into the tree state + appState.setTreeState("selectedNode", args.node); + //when a node is clicked we also need to set the active menu node to this node + appState.setMenuState("currentNode", args.node); + + //not legacy, lets just set the route value and clear the query string if there is one. + $location.path(n.routePath).search(""); + } + else if (args.element.section) { + $location.path(args.element.section).search(""); + } + + service.hideNavigation(); + }); + }, + /** + * @ngdoc method + * @name umbraco.services.navigationService#syncTree + * @methodOf umbraco.services.navigationService + * + * @description + * Syncs a tree with a given path, returns a promise + * The path format is: ["itemId","itemId"], and so on + * so to sync to a specific document type node do: + *
+         * navigationService.syncTree({tree: 'content', path: ["-1","123d"], forceReload: true});
+         * 
+ * @param {Object} args arguments passed to the function + * @param {String} args.tree the tree alias to sync to + * @param {Array} args.path the path to sync the tree to + * @param {Boolean} args.forceReload optional, specifies whether to force reload the node data from the server even if it already exists in the tree currently + * @param {Boolean} args.activate optional, specifies whether to set the synced node to be the active node, this will default to true if not specified + */ + syncTree: function (args) { + if (!args) { + throw "args cannot be null"; + } + if (!args.path) { + throw "args.path cannot be null"; + } + if (!args.tree) { + throw "args.tree cannot be null"; + } + + if (mainTreeEventHandler) { + //returns a promise + return mainTreeEventHandler.syncTree(args); + } + + //couldn't sync + return angularHelper.rejectedPromise(); + }, + + /** + Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to + have to set an active tree and then sync, the new API does this in one method by using syncTree + */ + _syncPath: function(path, forceReload) { + if (mainTreeEventHandler) { + mainTreeEventHandler.syncTree({ path: path, forceReload: forceReload }); + } + }, + + //TODO: This should return a promise + reloadNode: function(node) { + if (mainTreeEventHandler) { + mainTreeEventHandler.reloadNode(node); + } + }, + + //TODO: This should return a promise + reloadSection: function(sectionAlias) { + if (mainTreeEventHandler) { + mainTreeEventHandler.clearCache({ section: sectionAlias }); + mainTreeEventHandler.load(sectionAlias); + } + }, + + /** + Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to + have to set an active tree and then sync, the new API does this in one method by using syncTreePath + */ + _setActiveTreeType: function (treeAlias, loadChildren) { + if (mainTreeEventHandler) { + mainTreeEventHandler._setActiveTreeType(treeAlias, loadChildren); + } + }, + + /** + * @ngdoc method + * @name umbraco.services.navigationService#hideTree + * @methodOf umbraco.services.navigationService + * + * @description + * Hides the tree by hiding the containing dom element + */ + hideTree: function() { + + if (appState.getGlobalState("isTablet") === true && !appState.getGlobalState("stickyNavigation")) { + //reset it to whatever is in the url + appState.setSectionState("currentSection", $routeParams.section); + setMode("default-hidesectiontree"); + } + + }, + + /** + * @ngdoc method + * @name umbraco.services.navigationService#showMenu + * @methodOf umbraco.services.navigationService + * + * @description + * Hides the tree by hiding the containing dom element. + * This always returns a promise! + * + * @param {Event} event the click event triggering the method, passed from the DOM element + */ + showMenu: function(event, args) { + + var deferred = $q.defer(); + var self = this; + + treeService.getMenu({ treeNode: args.node }) + .then(function(data) { + + //check for a default + //NOTE: event will be undefined when a call to hideDialog is made so it won't re-load the default again. + // but perhaps there's a better way to deal with with an additional parameter in the args ? it works though. + if (data.defaultAlias && !args.skipDefault) { + + var found = _.find(data.menuItems, function(item) { + return item.alias = data.defaultAlias; + }); + + if (found) { + + //NOTE: This is assigning the current action node - this is not the same as the currently selected node! + appState.setMenuState("currentNode", args.node); + + //ensure the current dialog is cleared before creating another! + if (currentDialog) { + dialogService.close(currentDialog); + } + + var dialog = self.showDialog({ + node: args.node, + action: found, + section: appState.getSectionState("currentSection") + }); + + //return the dialog this is opening. + deferred.resolve(dialog); + return; + } + } + + //there is no default or we couldn't find one so just continue showing the menu + + setMode("menu"); + + appState.setMenuState("currentNode", args.node); + appState.setMenuState("menuActions", data.menuItems); + appState.setMenuState("dialogTitle", args.node.name); + + //we're not opening a dialog, return null. + deferred.resolve(null); + }); + + return deferred.promise; + }, + + /** + * @ngdoc method + * @name umbraco.services.navigationService#hideMenu + * @methodOf umbraco.services.navigationService + * + * @description + * Hides the menu by hiding the containing dom element + */ + hideMenu: function() { + //SD: Would we ever want to access the last action'd node instead of clearing it here? + appState.setMenuState("currentNode", null); + appState.setMenuState("menuActions", []); + setMode("tree"); + }, + + /** Executes a given menu action */ + executeMenuAction: function (action, node, section) { + + if (!action) { + throw "action cannot be null"; + } + if (!node) { + throw "node cannot be null"; + } + if (!section) { + throw "section cannot be null"; + } + + if (action.metaData && action.metaData["actionRoute"] && angular.isString(action.metaData["actionRoute"])) { + //first check if the menu item simply navigates to a route + var parts = action.metaData["actionRoute"].split("?"); + $location.path(parts[0]).search(parts.length > 1 ? parts[1] : ""); + this.hideNavigation(); + return; + } + else if (action.metaData && action.metaData["jsAction"] && angular.isString(action.metaData["jsAction"])) { + + //we'll try to get the jsAction from the injector + var menuAction = action.metaData["jsAction"].split('.'); + if (menuAction.length !== 2) { + + //if it is not two parts long then this most likely means that it's a legacy action + var js = action.metaData["jsAction"].replace("javascript:", ""); + //there's not really a different way to acheive this except for eval + eval(js); + } + else { + var menuActionService = $injector.get(menuAction[0]); + if (!menuActionService) { + throw "The angular service " + menuAction[0] + " could not be found"; + } + + var method = menuActionService[menuAction[1]]; + + if (!method) { + throw "The method " + menuAction[1] + " on the angular service " + menuAction[0] + " could not be found"; + } + + method.apply(this, [{ + //map our content object to a basic entity to pass in to the menu handlers, + //this is required for consistency since a menu item needs to be decoupled from a tree node since the menu can + //exist standalone in the editor for which it can only pass in an entity (not tree node). + entity: umbModelMapper.convertToEntityBasic(node), + action: action, + section: section, + treeAlias: treeService.getTreeAlias(node) + }]); + } + } + else { + service.showDialog({ + node: node, + action: action, + section: section + }); + } + }, + + /** + * @ngdoc method + * @name umbraco.services.navigationService#showUserDialog + * @methodOf umbraco.services.navigationService + * + * @description + * Opens the user dialog, next to the sections navigation + * template is located in views/common/dialogs/user.html + */ + showUserDialog: function () { + // hide tray and close help dialog + if (service.helpDialog) { + service.helpDialog.close(); + } + service.hideTray(); + + if (service.userDialog) { + service.userDialog.close(); + service.userDialog = undefined; + } + + service.userDialog = dialogService.open( + { + template: "views/common/dialogs/user.html", + modalClass: "umb-modal-left", + show: true + }); + + return service.userDialog; + }, + + /** + * @ngdoc method + * @name umbraco.services.navigationService#showUserDialog + * @methodOf umbraco.services.navigationService + * + * @description + * Opens the user dialog, next to the sections navigation + * template is located in views/common/dialogs/user.html + */ + showHelpDialog: function () { + // hide tray and close user dialog + service.hideTray(); + if (service.userDialog) { + service.userDialog.close(); + } + + if(service.helpDialog){ + service.helpDialog.close(); + service.helpDialog = undefined; + } + + service.helpDialog = dialogService.open( + { + template: "views/common/dialogs/help.html", + modalClass: "umb-modal-left", + show: true + }); + + return service.helpDialog; + }, + + /** + * @ngdoc method + * @name umbraco.services.navigationService#showDialog + * @methodOf umbraco.services.navigationService + * + * @description + * Opens a dialog, for a given action on a given tree node + * uses the dialogService to inject the selected action dialog + * into #dialog div.umb-panel-body + * the path to the dialog view is determined by: + * "views/" + current tree + "/" + action alias + ".html" + * The dialog controller will get passed a scope object that is created here with the properties: + * scope.currentNode = the selected tree node + * scope.currentAction = the selected menu item + * so that the dialog controllers can use these properties + * + * @param {Object} args arguments passed to the function + * @param {Scope} args.scope current scope passed to the dialog + * @param {Object} args.action the clicked action containing `name` and `alias` + */ + showDialog: function(args) { + + if (!args) { + throw "showDialog is missing the args parameter"; + } + if (!args.action) { + throw "The args parameter must have an 'action' property as the clicked menu action object"; + } + if (!args.node) { + throw "The args parameter must have a 'node' as the active tree node"; + } + + //ensure the current dialog is cleared before creating another! + if (currentDialog) { + dialogService.close(currentDialog); + currentDialog = null; + } + + setMode("dialog"); + + //NOTE: Set up the scope object and assign properties, this is legacy functionality but we have to live with it now. + // we should be passing in currentNode and currentAction using 'dialogData' for the dialog, not attaching it to a scope. + // This scope instance will be destroyed by the dialog so it cannot be a scope that exists outside of the dialog. + // If a scope instance has been passed in, we'll have to create a child scope of it, otherwise a new root scope. + var dialogScope = args.scope ? args.scope.$new() : $rootScope.$new(); + dialogScope.currentNode = args.node; + dialogScope.currentAction = args.action; + + //the title might be in the meta data, check there first + if (args.action.metaData["dialogTitle"]) { + appState.setMenuState("dialogTitle", args.action.metaData["dialogTitle"]); + } + else { + appState.setMenuState("dialogTitle", args.action.name); + } + + var templateUrl; + var iframe; + + if (args.action.metaData["actionUrl"]) { + templateUrl = args.action.metaData["actionUrl"]; + iframe = true; + } + else if (args.action.metaData["actionView"]) { + templateUrl = args.action.metaData["actionView"]; + iframe = false; + } + else { + + //by convention we will look into the /views/{treetype}/{action}.html + // for example: /views/content/create.html + + //we will also check for a 'packageName' for the current tree, if it exists then the convention will be: + // for example: /App_Plugins/{mypackage}/backoffice/{treetype}/create.html + + var treeAlias = treeService.getTreeAlias(args.node); + var packageTreeFolder = treeService.getTreePackageFolder(treeAlias); + + if (!treeAlias) { + throw "Could not get tree alias for node " + args.node.id; + } + + if (packageTreeFolder) { + templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath + + "/" + packageTreeFolder + + "/backoffice/" + treeAlias + "/" + args.action.alias + ".html"; + } + else { + templateUrl = "views/" + treeAlias + "/" + args.action.alias + ".html"; + } + + iframe = false; + } + + //TODO: some action's want to launch a new window like live editing, we support this in the menu item's metadata with + // a key called: "actionUrlMethod" which can be set to either: Dialog, BlankWindow. Normally this is always set to Dialog + // if a URL is specified in the "actionUrl" metadata. For now I'm not going to implement launching in a blank window, + // though would be v-easy, just not sure we want to ever support that? + + var dialog = dialogService.open( + { + container: $("#dialog div.umb-modalcolumn-body"), + //The ONLY reason we're passing in scope to the dialogService (which is legacy functionality) is + // for backwards compatibility since many dialogs require $scope.currentNode or $scope.currentAction + // to exist + scope: dialogScope, + inline: true, + show: true, + iframe: iframe, + modalClass: "umb-dialog", + template: templateUrl, + + //These will show up on the dialog controller's $scope under dialogOptions + currentNode: args.node, + currentAction: args.action, + }); + + //save the currently assigned dialog so it can be removed before a new one is created + currentDialog = dialog; + return dialog; + }, + + /** + * @ngdoc method + * @name umbraco.services.navigationService#hideDialog + * @methodOf umbraco.services.navigationService + * + * @description + * hides the currently open dialog + */ + hideDialog: function (showMenu) { + + setMode("default"); + + if(showMenu){ + this.showMenu(undefined, { skipDefault: true, node: appState.getMenuState("currentNode") }); + } + }, + /** + * @ngdoc method + * @name umbraco.services.navigationService#showSearch + * @methodOf umbraco.services.navigationService + * + * @description + * shows the search pane + */ + showSearch: function() { + setMode("search"); + }, + /** + * @ngdoc method + * @name umbraco.services.navigationService#hideSearch + * @methodOf umbraco.services.navigationService + * + * @description + * hides the search pane + */ + hideSearch: function() { + setMode("default-hidesearch"); + }, + /** + * @ngdoc method + * @name umbraco.services.navigationService#hideNavigation + * @methodOf umbraco.services.navigationService + * + * @description + * hides any open navigation panes and resets the tree, actions and the currently selected node + */ + hideNavigation: function() { + appState.setMenuState("menuActions", []); + setMode("default"); + } + }; + + return service; +} + +angular.module('umbraco.services').factory('navigationService', navigationService); /** * @ngdoc service @@ -5677,43 +5677,43 @@ angular.module('umbraco.services') return service; }); -(function() { - 'use strict'; - - function overlayHelper() { - - var numberOfOverlays = 0; - - function registerOverlay() { - numberOfOverlays++; - return numberOfOverlays; - } - - function unregisterOverlay() { - numberOfOverlays--; - return numberOfOverlays; - } - - function getNumberOfOverlays() { - return numberOfOverlays; - } - - var service = { - numberOfOverlays: numberOfOverlays, - registerOverlay: registerOverlay, - unregisterOverlay: unregisterOverlay, - getNumberOfOverlays: getNumberOfOverlays - }; - - return service; - - } - - - angular.module('umbraco.services').factory('overlayHelper', overlayHelper); - - -})(); +(function() { + 'use strict'; + + function overlayHelper() { + + var numberOfOverlays = 0; + + function registerOverlay() { + numberOfOverlays++; + return numberOfOverlays; + } + + function unregisterOverlay() { + numberOfOverlays--; + return numberOfOverlays; + } + + function getNumberOfOverlays() { + return numberOfOverlays; + } + + var service = { + numberOfOverlays: numberOfOverlays, + registerOverlay: registerOverlay, + unregisterOverlay: unregisterOverlay, + getNumberOfOverlays: getNumberOfOverlays + }; + + return service; + + } + + + angular.module('umbraco.services').factory('overlayHelper', overlayHelper); + + +})(); /** * @ngdoc service @@ -9125,450 +9125,450 @@ angular.module('umbraco.services').factory('umbDataFormatter', umbDataFormatter) -/** - * @ngdoc service - * @name umbraco.services.windowResizeListener - * @function - * - * @description - * A single window resize listener... we don't want to have more than one in theory to ensure that - * there aren't too many events raised. This will debounce the event with 100 ms intervals and force - * a $rootScope.$apply when changed and notify all listeners - * - */ -function windowResizeListener($rootScope) { - - var WinReszier = (function () { - var registered = []; - var inited = false; - var resize = _.debounce(function(ev) { - notify(); - }, 100); - var notify = function () { - var h = $(window).height(); - var w = $(window).width(); - //execute all registrations inside of a digest - $rootScope.$apply(function() { - for (var i = 0, cnt = registered.length; i < cnt; i++) { - registered[i].apply($(window), [{ width: w, height: h }]); - } - }); - }; - return { - register: function (fn) { - registered.push(fn); - if (inited === false) { - $(window).bind('resize', resize); - inited = true; - } - }, - unregister: function (fn) { - var index = registered.indexOf(fn); - if (index > -1) { - registered.splice(index, 1); - } - } - }; - }()); - - return { - - /** - * Register a callback for resizing - * @param {Function} cb - */ - register: function (cb) { - WinReszier.register(cb); - }, - - /** - * Removes a registered callback - * @param {Function} cb - */ - unregister: function(cb) { - WinReszier.unregister(cb); - } - - }; -} +/** + * @ngdoc service + * @name umbraco.services.windowResizeListener + * @function + * + * @description + * A single window resize listener... we don't want to have more than one in theory to ensure that + * there aren't too many events raised. This will debounce the event with 100 ms intervals and force + * a $rootScope.$apply when changed and notify all listeners + * + */ +function windowResizeListener($rootScope) { + + var WinReszier = (function () { + var registered = []; + var inited = false; + var resize = _.debounce(function(ev) { + notify(); + }, 100); + var notify = function () { + var h = $(window).height(); + var w = $(window).width(); + //execute all registrations inside of a digest + $rootScope.$apply(function() { + for (var i = 0, cnt = registered.length; i < cnt; i++) { + registered[i].apply($(window), [{ width: w, height: h }]); + } + }); + }; + return { + register: function (fn) { + registered.push(fn); + if (inited === false) { + $(window).bind('resize', resize); + inited = true; + } + }, + unregister: function (fn) { + var index = registered.indexOf(fn); + if (index > -1) { + registered.splice(index, 1); + } + } + }; + }()); + + return { + + /** + * Register a callback for resizing + * @param {Function} cb + */ + register: function (cb) { + WinReszier.register(cb); + }, + + /** + * Removes a registered callback + * @param {Function} cb + */ + unregister: function(cb) { + WinReszier.unregister(cb); + } + + }; +} angular.module('umbraco.services').factory('windowResizeListener', windowResizeListener); -/** - * @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, '/'); - } 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 ""; - } - - 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 += ""; - } - - 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("") + 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; - } - }; -} +/** + * @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, '/'); + } 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 ""; + } + + 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 += ""; + } + + 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("") + 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; + } + }; +} angular.module('umbraco.services').factory('xmlhelper', xmlhelper); })(); \ No newline at end of file diff --git a/OurUmbraco.Site/umbraco_client/Installer/css/all.css b/OurUmbraco.Site/umbraco_client/Installer/css/all.css index 5a6b52d4..6fdebdea 100644 --- a/OurUmbraco.Site/umbraco_client/Installer/css/all.css +++ b/OurUmbraco.Site/umbraco_client/Installer/css/all.css @@ -1,1327 +1,1327 @@ -body{ - margin:0; - color:#fff; - font:13px/16px Tahoma, Geneva, sans-serif; - background:#191919; - min-width:1000px; - position:relative; -} -img{border:0;} -a{ - color:#333; - text-decoration:none; -} -a:hover{text-decoration:underline;} -input, -textarea, -select{ - font:100% arial,sans-serif; - vertical-align:middle; -} -form,fieldset{ - margin:0; - padding:0; - border-style:none; -} -header, -footer, -article, -section, -hgroup, -nav, -figure{display: block;} -/* all page */ -#wrapper{ - width:100%; - overflow:hidden; - position:relative; - z-index:10; -} -/* bg page */ -#wrapper .bg-l{ - position:absolute; - top:0; - left:0; - width:1%; - height:1050px; -} -.bg-main { - position:absolute; - top: 0; - left: 0; - width:100%; - height:100%; - z-index:1; -} -.bg-main .color1, -.bg-main .color2, -.bg-main .color3, -.bg-main .color4, -.bg-main .color5 { - position:absolute; - top: 0; - left: 0; - width:100%; - height:1050px; -} -.bg-main .color1 .bg-c, -.bg-main .color2 .bg-c, -.bg-main .color3 .bg-c, -.bg-main .color4 .bg-c, -.bg-main .color5 .bg-c { - position:absolute; - top: 0; - left: 0; - width:100%; - height:1050px; -} - -/* Color 1 */ -.bg-main .color1 { background:url(../images/bg-normal-repeat.jpg) repeat-x;} - -.bg-main .color1 .bg-c { background:url(../images/bg-normal.jpg) no-repeat 50% 0;} -/* Color 2 */ -.bg-main .color2 { background:#2e1427 url(../images/bg-simple-repeat.jpg) repeat-x;} - -.bg-main .color2 .bg-c { background:url(../images/bg-simple.jpg) no-repeat 50% 0;} -/* Color 3 */ -.bg-main .color3 { background:#01242d url(../images/bg-blog-repeat.jpg) repeat-x;} - -.bg-main .color3 .bg-c { background:url(../images/bg-blog.jpg) no-repeat 50% 0;} -/* Color 4 */ -.bg-main .color4 { background:#201c01 url(../images/bg-normal-repeat.jpg) repeat-x;} - -.bg-main .color4 .bg-c { background:url(../images/bg-bhuiness.jpg) no-repeat 50% 0;} -/* Color 5 */ -.bg-main .color5 { background:#3b0d03 url(../images/bg-personal-repeat.jpg) repeat-x;} - -.bg-main .color5 .bg-c { background:url(../images/bg-personal.jpg) no-repeat 50% 0;} -.wholder:after { - clear: both; - content:""; - display: block; -} -.wholder{ - width:100%; - min-height:1000px; - position:relative; - padding:0 0 50px; -} -* html .wholder{height:1200px;} -/* header */ -#header{ - width:100%; - overflow:hidden; - height:98px; - background:url(../images/bg-header.png) repeat-x; -} -#header .holder{ - width:960px; - overflow:hidden; - margin:0 auto; -} -.logo{ - display:block; - width:177px; - margin:0 auto; - font-size:0; - line-height:0; - padding:14px 0 0; -} -.logo a{ - display:block; - width:177px; - height:53px; - overflow:hidden; - text-indent:-99999px; - background:url(../images/logo.gif) no-repeat; -} -/* all content */ -#main:after { - clear: both; - content:""; - display: block; -} -#main{ - width:960px; - margin:0 auto; -} -/* tabset */ -.tabset{ - width:100%; - overflow:hidden; - padding:2px 0 0; -} -.tabset .b{ - width:100%; - overflow:hidden; - height:1px; - font-size:0; - line-height:0; - background:url(../images/sep1.png) repeat-x; -} -.tabset ul{ - width:1003px; - overflow:hidden; - margin:0 -43px 0 0; - padding:0 0 2px; - list-style:none; -} -.tabset ul li{ - float:left; - padding:0 43px 17px 0; - font:bold 21px/23px Arial,Helvetica,sans-serif; - letter-spacing:-1px; - color:#f27021; - position:relative; -} -.tabset ul li.disable{color:#666;} -.tabset ul li em{ - position:absolute; - left:13px; - bottom:0; - font-size:0; - line-height:0; - width:14px; - height:11px; -} -.tabset ul li.active {color:#fff;} -.tabset ul li.active em{background:url(../images/bul1.png) no-repeat;} -/* content */ -.content:after { - clear: both; - content:""; - display: block; -} -.content{ - width:100%; - padding:20px 0 0; -} -.tab:after { - clear: both; - content:""; - display: block; -} -.tab{width:100%;} -.content h1{ - margin:0 0 26px; - font-size:36px; - line-height:40px; - font-weight:normal; - color:#fff; -} -.content .container{ - overflow:hidden; - height:1%; - padding:0 100px 13px 5px; -} -.content h2{ - margin:0 0 11px; - font-size:20px; - line-height:24px; - color:#fff; - font-weight:normal; -} -.content p{margin:0 0 13px;} -.content p strong{color:#fff;} -/* text list */ -.text-list{ - overflow:hidden; - width:100%; - margin:0; - padding:3px 0 20px; - list-style:none; -} -.text-list li{ - overflow:hidden; - height:1%; - vertical-align:top; - padding:0 0 4px; -} -.text-list li strong{ - float:left; - display:inline; - margin:0 3px 0 0; - width:13px; - color:#fff; -} -* html .text-list li strong{margin:0;} -.text-list li span{ - display:block; - overflow:hidden; - height:1%; -} -.content .enjoy{ - display:block; - padding:0 0 14px; - font-size:20px; - line-height:24px; - color:#fff; - font-weight:normal; -} -/* btn box */ -.btn-box{ - width:100%; - overflow:hidden; -} -.btn-box .t{ - width:100%; - overflow:hidden; - height:1px; - font-size:0; - line-height:0; - background:url(../images/sep1.png) repeat-x; -} -.btn-box .btn{margin:13px 0 0;} -.btn{ - float:left; - overflow:hidden; - cursor:pointer; -} -.btn:hover{border:none;} -.btn span{ - float:left; - overflow:hidden; - text-indent:-9999px; -} -.btn-get{height:59px;} -.btn-get span{ - height:118px; - width:230px; - background:url(../images/btn-get.png) no-repeat; -} -.btn-get:hover span{margin:-59px 0 0;} -.accept-hold{ - width:100%; - overflow:hidden; - margin:-2px 0 0; - padding:0 0 7px; -} -.content .accept-hold h2{margin:0 0 10px;} -.content .accept-hold p{ - font-size:15px; - line-height:20px; -} -.content h3{ - margin:0 0 18px; - font-size:15px; - line-height:20px; - color:#fff; - font-weight: normal; -} -.btn-accept, -.btn-continue, -.btn-back{height:42px;} -.btn-accept span{ - height:84px; - width:215px; - background:url(../images/btn-accept.png) no-repeat; -} -.btn-back span{ - height:84px; - width:90px; - background:url(../images/btn-back.png) no-repeat; -} - -.btn-accept:hover span, -.btn-continue:hover span, -.btn-back:hover span{margin:-42px 0 0;} - -/* database */ -.database-hold{ - width:100%; - overflow:hidden; -} -.content .database-hold .container{padding-bottom:0;} -/* step */ -.step{ - width:100%; - overflow:hidden; - padding:11px 0 3px; -} -/* mini tabset */ -.mini-tabset{ - width:100%; - overflow:hidden; - margin:0; - padding:2px 0 9px; - list-style:none; -} -.mini-tabset li{ - float:left; - padding:0 12px 0 0; -} -.mini-tabset li a{ - float:left; - height:42px; - overflow:hidden; - cursor:pointer; -} -.mini-tabset li a:hover{position:relative;} -.mini-tabset li a span{ - float:left; - height:84px; - overflow:hidden; - text-indent:-9999px; -} -.mini-tabset li.btn-yes a span{ - width:79px; - background:url(../images/btn-yes.png) no-repeat; -} -.mini-tabset li.btn-no a span{ - width:73px; - background:url(../images/btn-no.png) no-repeat; -} -.mini-tabset li a:hover span{margin:-42px 0 0;} -.step .row, -.instruction-hold .row{ - overflow:hidden; - height:1%; - padding:0 0 8px; -} -.instruction-hold a -{ - color: #eeffcc; - text-decoration: underline; -} -.step .sel{ - width:310px; - display:inline; -} -.step .select{padding:0 0 8px 139px !important;} -.instruction-hold{ - width:100%; - overflow:hidden; - padding:2px 0 25px; -} -.instruction-hold label{ - float:left; - width:135px; - padding:5px 3px 0 0; - font-weight:bold; - color:#fff; -} -.instruction-hold .row span{ - float:left; - width:298px; - height:27px; - padding:0 6px; - background:url(../images/bg-inp.png) no-repeat; -} - -.instruction-hold .row span.textarea -{ - height:108px; - background:url(../images/bg-inp-big.png) no-repeat; -} -.instruction-hold .error span{background:url(../images/bg-inp-error.png) no-repeat;} -.instruction-hold .text{ - float:left; - width:298px; - outline:none; - background:none; - border:none; - overflow:hidden; - font-size:13px; - line-height:16px; - height:16px; - padding:5px 0 6px; - position:relative; -} -.instruction-hold .textarea -{ - height:95px; -} -.btn-box .btn-install { - float:left; - margin:13px 0 0; - width:99px; - height:42px; - position: relative; - overflow:hidden; -} -.btn-box .btn-install span { - display: block; - width:99px; - height:42px; - text-indent:-9999px; - overflow:hidden; - background: url(../images/btn-install.png) no-repeat; -} -.btn-box .btn-install:hover { border: none; } -.btn-box .btn-install:hover span { - margin: -42px 0 0; - padding:42px 0 0; -} -.btn-link{ - display:block; - width:100%; - overflow:hidden; -} -.btn-link a{ - float:left; - font-size:17px; - line-height:20px; - text-decoration:underline; - color:#fff; -} -.btn-link a:hover{text-decoration:none;} -/* loader */ -.loader{ - width:100%; - overflow:hidden; - padding:13px 0 14px; -} -.loader .hold{ - width:100%; - overflow:hidden; -} -.loader img{float:left;} -.loader .hold span{ - float:left; - font-size:12px; - line-height:14px; - color:#fff; - padding:4px 0 0 7px; -} -.loader strong{ - display:block; - color:#fff; - padding:17px 0 0; -} -.progress-bar { - width:369px; - height:22px !important; - overflow:hidden; - float:left; -} -.ui-progressbar-value { background-image: url(../images/pbar-ani.gif) } -.btn-continue span{ - width:122px; - height:84px; - background:url(../images/btn-continue.png) no-repeat; -} -/* create box */ -.create-hold{ - width:100%; - overflow:hidden; - margin:-1px 0 0; - padding:0 0 13px; -} -.content .create-hold p{ - margin:0; - line-height:20px; -} -/* instruction box */ -.instruction-hold .check-hold{ - overflow:hidden; - height:1%; - padding:10px 0 3px 139px; -} -.instruction-hold .chk{ - float:left; - display:inline; - width:14px; - height:14px; - margin:2px 8px 0 0; - padding:0; -} -.instruction-hold .check-hold label{ - float:left; - width:auto; - line-height:16px; - color:#ffffff; - font-weight:normal; - padding:0; -} -.btn-box .btn-create { - float:left; - margin:13px 0 0; - width:145px; - height:42px; - position:relative; - overflow:hidden; -} -.btn-box .btn-create span { - display: block; - width:145px; - height:42px; - overflow:hidden; - text-indent: -9999px; - background:url(../images/btn-create.png) no-repeat; -} -.btn-box .btn-create:hover { border: none; } -.btn-box .btn-create:hover span { - padding: 42px 0 0; - margin: -42px 0 0; -} -.validaing, -.invalidaing, .basevalidaing{ - float:left !Important; - display:inline; - margin:6px 0 0 10px !Important; - padding:0 0 2px 28px !Important; - line-height:13px !Important; - font-weight:bold !Important; - color:#fff !Important; - background-color: none !Important; -} - -.validaing -{ - background:url(../images/ico-validaing.png) no-repeat !Important; -} - -.invalidaing{ - margin:6px 0 0 12px !Important; - padding:0 0 2px 26px !Important; - background:url(../images/ico-invalidaing.png) no-repeat !Important; -} - - -/* add navigation */ -.add-nav:after { - clear: both; - content:""; - display: block; -} -.add-nav{ - width:100%; - position:relative; - margin:-19px 0 0; -} -.add-nav ul{ - margin:0; - padding:0; - list-style:none; -} -.add-nav ul li{ - float:left; - padding:0 4px 0 32px; -} -.add-nav ul li a { - display:block; - width:150px; - height:212px; - position:relative; - cursor:pointer; -} -.add-nav ul li a img{ - position:absolute; - top: 0; - left: 0; -} -.add-nav ul li a span{ - position:absolute; - top:-15px; - left:-99999px; - display:none; -} -.add-nav ul li.hover a span{ - display:block; - left:-15px; -} -.add-nav ul li em{ - position:absolute; - top:236px; - width:16px; - height:13px; - overflow:hidden; - font-size:0; - line-height:0; -} -.add-nav ul li.add-simple em{left:166px;} -.add-nav ul li.add-blog em{left:319px;} -.add-nav ul li.add-personal em{left:471px;} -.add-nav ul li.add-buisness em{left:624px;} -.add-nav ul li.add-thanks em{left:776px;} -.add-nav ul li:hover em, -.add-nav ul li.hover em{background:url(../images/bul3.png) no-repeat;} -.add-nav ul li:hover .drop-hold, -.add-nav ul li.hover .drop-hold{display:block;} -/* drop down */ -.drop-hold{ - width:708px; - overflow:hidden; - position:absolute; - top:209px; - left:50%; - margin:0 0 0 -355px; - padding:40px 0 0; - display:none; -} -.drop-hold .c:after { - clear: both; - content:""; - display: block; -} -.drop-hold .c{ - width:620px; - min-height:107px; - padding:16px 44px; - background:url(../images/bg-drop-c.gif) repeat-y; -} -* html .drop-hold .c{height:107px;} -.drop-hold .t, -.drop-hold .b{ - width:100%; - overflow:hidden; - height:10px; - font-size:0; - line-height:0; -} -.drop-hold .t{background:url(../images/bg-drop-t.png) no-repeat;} -.drop-hold .b{background:url(../images/bg-drop-b.png) no-repeat;} -.drop-hold .title{ - width:100%; - overflow:hidden; - padding:0 0 17px; -} -.drop-hold .title span{ - display:block; - font-size:20px; - line-height:24px; - color:#3e0e03; -} -.drop-hold .hold{ - width:100%; - overflow:hidden; - color:#3e0e03; -} -.drop-hold .box{ - float:left; - width:280px; - padding:0 8px 0 0; -} -.add-nav .box ul{ - margin:0; - padding:0; - list-style:none; - width:100%; - overflow:hidden; -} -.add-nav .box ul li{ - overflow:hidden; - height:1%; - vertical-align:top; - float:none; - padding:0 0 4px 13px; - color:#3e0e03; - background:url(../images/bul2.gif) no-repeat 0 7px; -} - -/* basic starterkit grid*/ -ul.kits { - list-style: none; -} - -.thumbnails li { - float: left; - width: 256px; - margin: 0 30px 0 0; - background: #1f1f1f; - padding: 18px 22px; - border-radius: 10px; - position: relative; -} - -.thumbnails li .overlay { - position: absolute; - bottom: 30px; - right: 32px; - background: #3388cc; - height: 16px; - color: #fff; - border-radius: 3px; - padding: 7px 15px; - cursor: pointer; - outline: 0; - box-shadow: 0px 1px 4px 0px rgba(0,0,0,0.05); - display: inline; - -moz-transition: background-color .25s ease-in-out; - -webkit-transition: background-color .25s ease-in-out; - -o-transition: background-color .25s ease-in-out; - -ms-transition: background-color .25s ease-in-out; - transition: background-color .25s ease-in-out; -} - -.thumbnails li .overlay:hover { - background: #4398dc; -} - - -.thumbnails li.add-cwsstart { - margin: 0 -} - -.thumbnails li img { - width: 100%; - height: 143px; - background-color: #fff -} - - - -.kits:after { - clear: both; - content:""; - display: block; -} - -.declineKit { - color: #fff; - text-decoration: underline; - font-size: 14px; - margin-top: 20px; - display: block; - float: left; -} - -/* Light box */ - -.zoom-in { - background: #1f1f1f url(../Images/zoom-in.png) no-repeat 7px 7px; - width: 16px; - height: 16px; - position: absolute; - bottom: 30px; - left: 32px; - text-indent: -9999px; - border-radius: 3px; - padding: 7px; - -moz-transition: background-color .25s ease-in-out; - -webkit-transition: background-color .25s ease-in-out; - -o-transition: background-color .25s ease-in-out; - -ms-transition: background-color .25s ease-in-out; - transition: background-color .25s ease-in-out; -} - -.zoom-in:hover { - background-color: #333 -} - -div.lb { - display: none; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - text-align: center; - z-index: 100 -} - -div.lb img { - position: absolute; - top: 10px; - left: 50%; - width: 960px; - height: auto; - margin-left: -480px; /* Half the width */ -} - -div.lb:target { - display: block; -} - -/** IE doesn't support :target, so we use CSS expressions **/ -div.lb { - display: expression((document.location.toString().split('#').slice(1) == this.id)?'block':'none'); -} - - - -/* gallery */ -.gallery:after { - clear: both; - content:""; - display: block; -} -.gallery{ - width:795px; - margin:0 0 0 17px; - position:relative; - min-height:300px; - padding:0 54px 0 77px; -} -* html .gallery{height:300px;} -.gallery .hold{ - width:100%; - position:relative; - padding:18px 0; -} -.gallery .gal-box{ - width:99999px; - position:relative; -} -.gallery .box { - float:left; - width:796px; - background: none !important; -} -.gallery .box ul{ - float:left; - display:inline; - margin:5px -18px 0 10px; - padding:0; - list-style:none; -} -.gallery .box ul li { - float:left; - width:152px; - height:129px; - position:relative; - padding:5px 48px 29px 0; -} -.gallery .box .image-hold { - float:left; - width:152px; - height: 129px; - margin: 0 -100px -100px 0; - position:relative; -} -.gallery .box .image-hold .faik-mask { - display: block; -} -.gallery .box .image{ - position:absolute; - top:0; - left:0; - padding:7px 9px 19px 9px; -} -.gallery .box .image img { - display:block; - position:relative; -} -.gallery .box .faik-mask-ie6 { - position:absolute; - top:-9999px; - left:-9999px; -} -.gallery .btn-prev, -.gallery .btn-next{ - position:absolute; - top:152px; - height:30px; - width:30px; - overflow:hidden; - outline:none; -} -.gallery .btn-prev:hover, -.gallery .btn-next:hover{border:none;} -.gallery .btn-prev{left:0;} -.gallery .btn-next{right:0;} -.gallery .btn-prev span, -.gallery .btn-next span{ - float:left; - width:30px; - height:60px; - overflow:hidden; - text-indent:-9999px; -} -.gallery .btn-prev span{background:url(../images/btn-prev.png) no-repeat;} -.gallery .btn-next span{background:url(../images/btn-next.png) no-repeat;} -.gallery .btn-prev:hover span, -.gallery .btn-next:hover span{margin:-30px 0 0;} -/* gallery drop */ -.gal-drop { - position:absolute; - top:7px; - left:9px; - width:178px; -} -* html .gal-drop { - margin: -2px 0 0 -} -.gal-drop a{ - display:block; - width:100%; - overflow:hidden; -} -.gal-drop a:hover{border:none;} -.gal-drop a span{ - float:left; - width:178px; - overflow:hidden; - text-indent:-99999px; -} -.gal-drop .btn-preview{height:69px;} -.gal-drop .btn-preview span{ - height:138px; - background:url(../images/btn-preview.png) no-repeat; -} -.gal-drop .btn-preview:hover span{margin:-69px 0 0;} -.gal-drop .btn-install-gal{height:68px;} -.gal-drop .btn-install-gal span{ - height:138px; - background:url(../images/btn-install-gal.png) no-repeat; -} -.gal-drop .btn-install-gal:hover span{margin:-68px 0 0;} -/* paging */ -.paging{ - width:100%; - overflow:hidden; - position:relative; -} -.w1{ - float:left; - position:relative; - left:50%; -} -.w2{ - float:left; - position:relative; - left:-50%; -} -.paging span{ - float:left; - font-size:14px; - line-height:22px; - color:#fff; - padding:0 16px 0 0; -} -.paging ul{ - float:left; - margin:0; - padding:0 90px 0 0; - list-style:none; -} -.paging ul li{ - float:left; - padding:4px 7px 4px 7px; -} -.paging ul li a{ - display:block; - font-size:11px; - line-height:13px; - font-weight:bold; - color:#fff; - width:16px; - height:16px; - padding:2px 0 0 2px; - text-align:center; - background:url(../images/bg-paging.png) no-repeat; -} -.paging ul li a:hover, -.paging ul li.active a{ - font-size:13px; - line-height:16px; - width:22px; - height:21px; - margin:-2px -2px -4px -3px; - position:relative; - color:#000; - padding:2px 0 0 1px; - text-decoration:none; - background:url(../images/bg-paging-h.png) no-repeat; -} -.container .alt{padding-top:7px;} -/* lightbox */ -.lightbox{ - width:870px; - overflow:hidden; - position:absolute; - top:-9999px; - left:-9999px; - padding:10px; - margin:0; - z-index: 999; -} -.lightbox .t, -.lightbox .b{ - width:100%; - overflow:hidden; - height:8px; - font-size:0; - line-height:0; -} -.lightbox .t{background:url(../images/bg-lightbox-t.png) no-repeat;} -.lightbox .b{background:url(../images/bg-lightbox-b.png) no-repeat;} -.lightbox .c{ - width:824px; - overflow:hidden; - background:#ededed; - padding:9px 23px 35px 23px; -} -.lightbox .heading{ - width:100%; - overflow:hidden; - padding:0 0 11px; -} -.lightbox .title{ - display:block; - font-size:26px; - line-height:28px; - color:#000; - font-weight:bold; - padding:0 0 7px; -} -.lightbox .create{ - display:block; - font-size:14px; - line-height:18px; - color:#5f5f5f; -} -.lightbox .create a{ - font-weight:bold; - color:#5f5f5f; -} -.lightbox .carusel{ - width:100%; - overflow:hidden; - border-top:1px solid #a5a5a5; - border-bottom:1px solid #a5a5a5; - padding:13px 0 15px; - position:relative; -} -.lightbox .carusel ul{ - width:99999px; - margin:0; - padding:0; - list-style:none; -} -.lightbox .carusel ul li{ - float:left; - padding:0 8px 0 0; -} -.lightbox .carusel ul li img{display:block;} -.lightbox .btn-box{padding-top:27px;} -.lightbox .btn-install{ - display:block; - width:99px; - height:42px; - overflow:hidden; - text-indent:-99999px; - margin:0; - background:url(../images/btn-install.png) no-repeat; -} -.lightbox .btn-install:hover{background:url(../images/btn-install-hover.png) no-repeat;} -.lightbox .btn-close{ - position:absolute; - top:0; - right:0; - width:30px; - height:30px; - overflow:hidden; - text-indent:-99999px; - z-index:100; - background:url(../images/btn-close.png) no-repeat; -} -.btn-web{ - width:100%; - overflow:hidden; - margin:0; - padding:15px 0 20px; - list-style:none; -} -.btn-web li{ - float:left; - padding:0 13px 0 0; -} -.btn-web li a{ - float:left; - height:42px; - overflow:hidden; - cursor:pointer; -} -.btn-web li a:hover{border:none;} -.btn-web li a span{ - float:left; - height:84px; - overflow:hidden; - text-indent:-99999px; -} -.btn-web li.btn-set a span{ - width:248px; - background:url(../images/btn-set.png) no-repeat; -} -.btn-web li.btn-preview-web a span{ - width:257px; - background:url(../images/btn-preview-web.png) no-repeat; -} -.btn-web li a:hover span{margin:-42px 0 0;} -/* three columns */ -.threcol{ - width:100%; - overflow:hidden; -} -.threcol .t{ - width:100%; - overflow:hidden; - height:1px; - font-size:0; - line-height:0; - background:url(../images/sep1.png) repeat-x; -} -.threcol .hold{ - width:100%; - overflow:hidden; - padding:24px 0 0; -} -.content .threcol h2{ - margin:0 0 11px; - font-size:20px; - line-height:24px; - font-weight:bold; - color:#fff; -} -.col1{ - float:left; - width:225px; - padding:0 57px 0 3px; -} -.content .threcol p{ - margin:0 0 20px; - line-height:20px; -} -.content .threcol p span{display:block;} -.content .threcol p a{ - color:#fff; - text-decoration:underline; -} -.content .threcol p a:hover{text-decoration:none;} -/* links box */ -.links{ - width:100%; - overflow:hidden; -} -.links ul{ - width:100%; - overflow:hidden; - margin:0; - padding:6px 0 14px; - list-style:none; -} -.links ul li{ - overflow:hidden; - height:1%; - vertical-align:top; - padding:0 0 9px 11px; - font-size:14px; - line-height:16px; - background:url(../images/bul4.gif) no-repeat 0 5px; -} -.links ul li a{ - color:#fff; - text-decoration:underline; -} -.links ul li a:hover{text-decoration:none;} -.col2{ - float:left; - width:301px; - padding:0 60px 0 0; -} -.col3{ - float:left; - width:295px; -} -.box-software{ - width:100%; - overflow:hidden; -} -.content .box-software p{ - font-size:12px; - line-height:15px; - margin:0 0 15px; -} -.box-software p span{font-size:11px;} - - -.ui-selectmenu { - border: none !important; - display:block !important; - width:310px !important; - height: 29px; - overflow:hidden; - font-size:13px; - line-height:29px; - background:none; -} -.ui-selectmenu:focus { - outline: 1px dotted #000; -} -.ui-selectmenu-status { - float:left; - height: 29px; - width:266px; - padding:0 10px; - color: #000; - font-weight:normal; - background:url(../images/select-left-2.png) no-repeat; -} -.ui-state-default .ui-icon { - position:static; - float:right; - width:24px; - height: 29px; - background:url(../images/select-button.png) no-repeat; -} -.ui-selectmenu-menu { - position:absolute; - top: 0; - left: 0; - visibility: hidden; - border-radius: 0 !important; - -moz-border-radius: 0 !important; - -webkit-border-radius: 0 !important; -} -.ui-selectmenu-open { - visibility: visible; -} -.ui-selectmenu-menu li { - font-weight:normal !important; - border: none !important; -} - -.summary li{margin-bottom: 20px; display: block;} -.summary a{color: White;display:block;font-weight:bold;} - -.error {padding:0 100px 13px 5px} -.error p{margin:0 0 13px;padding-right:100px;} -.error p strong {font-size:14px;display:block;} - -/* db step refactor */ -.database-option, footer.installbtn -{ - display:none; -} - -.database-hold{ - width:100%; - overflow:hidden; -} - -.step.rendering-engine input[type="radio"] { - margin: 0px 5px 0px 5px; -} - -.step.rendering-engine > div { - line-height: 25px; -} - -.upgrade-report { - padding: 10px; - border-collapse: separate; - border-spacing: inherit; - line-height: 20px; - background: #361919; - background: rgba(255, 255, 255, 0.15); - -moz-border-radius: 20px; - -webkit-border-radius: 20px; - border-radius: 20px; - width: 920px; -} - -.upgrade-report td { - padding-bottom: 5px; - padding-left: 5px; - padding-right: 10px; -} -.upgrade-report td.msg { - vertical-align: middle; -} - -.upgrade-report td.icon span { - margin-top: 5px; - display: block; +body{ + margin:0; + color:#fff; + font:13px/16px Tahoma, Geneva, sans-serif; + background:#191919; + min-width:1000px; + position:relative; +} +img{border:0;} +a{ + color:#333; + text-decoration:none; +} +a:hover{text-decoration:underline;} +input, +textarea, +select{ + font:100% arial,sans-serif; + vertical-align:middle; +} +form,fieldset{ + margin:0; + padding:0; + border-style:none; +} +header, +footer, +article, +section, +hgroup, +nav, +figure{display: block;} +/* all page */ +#wrapper{ + width:100%; + overflow:hidden; + position:relative; + z-index:10; +} +/* bg page */ +#wrapper .bg-l{ + position:absolute; + top:0; + left:0; + width:1%; + height:1050px; +} +.bg-main { + position:absolute; + top: 0; + left: 0; + width:100%; + height:100%; + z-index:1; +} +.bg-main .color1, +.bg-main .color2, +.bg-main .color3, +.bg-main .color4, +.bg-main .color5 { + position:absolute; + top: 0; + left: 0; + width:100%; + height:1050px; +} +.bg-main .color1 .bg-c, +.bg-main .color2 .bg-c, +.bg-main .color3 .bg-c, +.bg-main .color4 .bg-c, +.bg-main .color5 .bg-c { + position:absolute; + top: 0; + left: 0; + width:100%; + height:1050px; +} + +/* Color 1 */ +.bg-main .color1 { background:url(../images/bg-normal-repeat.jpg) repeat-x;} + +.bg-main .color1 .bg-c { background:url(../images/bg-normal.jpg) no-repeat 50% 0;} +/* Color 2 */ +.bg-main .color2 { background:#2e1427 url(../images/bg-simple-repeat.jpg) repeat-x;} + +.bg-main .color2 .bg-c { background:url(../images/bg-simple.jpg) no-repeat 50% 0;} +/* Color 3 */ +.bg-main .color3 { background:#01242d url(../images/bg-blog-repeat.jpg) repeat-x;} + +.bg-main .color3 .bg-c { background:url(../images/bg-blog.jpg) no-repeat 50% 0;} +/* Color 4 */ +.bg-main .color4 { background:#201c01 url(../images/bg-normal-repeat.jpg) repeat-x;} + +.bg-main .color4 .bg-c { background:url(../images/bg-bhuiness.jpg) no-repeat 50% 0;} +/* Color 5 */ +.bg-main .color5 { background:#3b0d03 url(../images/bg-personal-repeat.jpg) repeat-x;} + +.bg-main .color5 .bg-c { background:url(../images/bg-personal.jpg) no-repeat 50% 0;} +.wholder:after { + clear: both; + content:""; + display: block; +} +.wholder{ + width:100%; + min-height:1000px; + position:relative; + padding:0 0 50px; +} +* html .wholder{height:1200px;} +/* header */ +#header{ + width:100%; + overflow:hidden; + height:98px; + background:url(../images/bg-header.png) repeat-x; +} +#header .holder{ + width:960px; + overflow:hidden; + margin:0 auto; +} +.logo{ + display:block; + width:177px; + margin:0 auto; + font-size:0; + line-height:0; + padding:14px 0 0; +} +.logo a{ + display:block; + width:177px; + height:53px; + overflow:hidden; + text-indent:-99999px; + background:url(../images/logo.gif) no-repeat; +} +/* all content */ +#main:after { + clear: both; + content:""; + display: block; +} +#main{ + width:960px; + margin:0 auto; +} +/* tabset */ +.tabset{ + width:100%; + overflow:hidden; + padding:2px 0 0; +} +.tabset .b{ + width:100%; + overflow:hidden; + height:1px; + font-size:0; + line-height:0; + background:url(../images/sep1.png) repeat-x; +} +.tabset ul{ + width:1003px; + overflow:hidden; + margin:0 -43px 0 0; + padding:0 0 2px; + list-style:none; +} +.tabset ul li{ + float:left; + padding:0 43px 17px 0; + font:bold 21px/23px Arial,Helvetica,sans-serif; + letter-spacing:-1px; + color:#f27021; + position:relative; +} +.tabset ul li.disable{color:#666;} +.tabset ul li em{ + position:absolute; + left:13px; + bottom:0; + font-size:0; + line-height:0; + width:14px; + height:11px; +} +.tabset ul li.active {color:#fff;} +.tabset ul li.active em{background:url(../images/bul1.png) no-repeat;} +/* content */ +.content:after { + clear: both; + content:""; + display: block; +} +.content{ + width:100%; + padding:20px 0 0; +} +.tab:after { + clear: both; + content:""; + display: block; +} +.tab{width:100%;} +.content h1{ + margin:0 0 26px; + font-size:36px; + line-height:40px; + font-weight:normal; + color:#fff; +} +.content .container{ + overflow:hidden; + height:1%; + padding:0 100px 13px 5px; +} +.content h2{ + margin:0 0 11px; + font-size:20px; + line-height:24px; + color:#fff; + font-weight:normal; +} +.content p{margin:0 0 13px;} +.content p strong{color:#fff;} +/* text list */ +.text-list{ + overflow:hidden; + width:100%; + margin:0; + padding:3px 0 20px; + list-style:none; +} +.text-list li{ + overflow:hidden; + height:1%; + vertical-align:top; + padding:0 0 4px; +} +.text-list li strong{ + float:left; + display:inline; + margin:0 3px 0 0; + width:13px; + color:#fff; +} +* html .text-list li strong{margin:0;} +.text-list li span{ + display:block; + overflow:hidden; + height:1%; +} +.content .enjoy{ + display:block; + padding:0 0 14px; + font-size:20px; + line-height:24px; + color:#fff; + font-weight:normal; +} +/* btn box */ +.btn-box{ + width:100%; + overflow:hidden; +} +.btn-box .t{ + width:100%; + overflow:hidden; + height:1px; + font-size:0; + line-height:0; + background:url(../images/sep1.png) repeat-x; +} +.btn-box .btn{margin:13px 0 0;} +.btn{ + float:left; + overflow:hidden; + cursor:pointer; +} +.btn:hover{border:none;} +.btn span{ + float:left; + overflow:hidden; + text-indent:-9999px; +} +.btn-get{height:59px;} +.btn-get span{ + height:118px; + width:230px; + background:url(../images/btn-get.png) no-repeat; +} +.btn-get:hover span{margin:-59px 0 0;} +.accept-hold{ + width:100%; + overflow:hidden; + margin:-2px 0 0; + padding:0 0 7px; +} +.content .accept-hold h2{margin:0 0 10px;} +.content .accept-hold p{ + font-size:15px; + line-height:20px; +} +.content h3{ + margin:0 0 18px; + font-size:15px; + line-height:20px; + color:#fff; + font-weight: normal; +} +.btn-accept, +.btn-continue, +.btn-back{height:42px;} +.btn-accept span{ + height:84px; + width:215px; + background:url(../images/btn-accept.png) no-repeat; +} +.btn-back span{ + height:84px; + width:90px; + background:url(../images/btn-back.png) no-repeat; +} + +.btn-accept:hover span, +.btn-continue:hover span, +.btn-back:hover span{margin:-42px 0 0;} + +/* database */ +.database-hold{ + width:100%; + overflow:hidden; +} +.content .database-hold .container{padding-bottom:0;} +/* step */ +.step{ + width:100%; + overflow:hidden; + padding:11px 0 3px; +} +/* mini tabset */ +.mini-tabset{ + width:100%; + overflow:hidden; + margin:0; + padding:2px 0 9px; + list-style:none; +} +.mini-tabset li{ + float:left; + padding:0 12px 0 0; +} +.mini-tabset li a{ + float:left; + height:42px; + overflow:hidden; + cursor:pointer; +} +.mini-tabset li a:hover{position:relative;} +.mini-tabset li a span{ + float:left; + height:84px; + overflow:hidden; + text-indent:-9999px; +} +.mini-tabset li.btn-yes a span{ + width:79px; + background:url(../images/btn-yes.png) no-repeat; +} +.mini-tabset li.btn-no a span{ + width:73px; + background:url(../images/btn-no.png) no-repeat; +} +.mini-tabset li a:hover span{margin:-42px 0 0;} +.step .row, +.instruction-hold .row{ + overflow:hidden; + height:1%; + padding:0 0 8px; +} +.instruction-hold a +{ + color: #eeffcc; + text-decoration: underline; +} +.step .sel{ + width:310px; + display:inline; +} +.step .select{padding:0 0 8px 139px !important;} +.instruction-hold{ + width:100%; + overflow:hidden; + padding:2px 0 25px; +} +.instruction-hold label{ + float:left; + width:135px; + padding:5px 3px 0 0; + font-weight:bold; + color:#fff; +} +.instruction-hold .row span{ + float:left; + width:298px; + height:27px; + padding:0 6px; + background:url(../images/bg-inp.png) no-repeat; +} + +.instruction-hold .row span.textarea +{ + height:108px; + background:url(../images/bg-inp-big.png) no-repeat; +} +.instruction-hold .error span{background:url(../images/bg-inp-error.png) no-repeat;} +.instruction-hold .text{ + float:left; + width:298px; + outline:none; + background:none; + border:none; + overflow:hidden; + font-size:13px; + line-height:16px; + height:16px; + padding:5px 0 6px; + position:relative; +} +.instruction-hold .textarea +{ + height:95px; +} +.btn-box .btn-install { + float:left; + margin:13px 0 0; + width:99px; + height:42px; + position: relative; + overflow:hidden; +} +.btn-box .btn-install span { + display: block; + width:99px; + height:42px; + text-indent:-9999px; + overflow:hidden; + background: url(../images/btn-install.png) no-repeat; +} +.btn-box .btn-install:hover { border: none; } +.btn-box .btn-install:hover span { + margin: -42px 0 0; + padding:42px 0 0; +} +.btn-link{ + display:block; + width:100%; + overflow:hidden; +} +.btn-link a{ + float:left; + font-size:17px; + line-height:20px; + text-decoration:underline; + color:#fff; +} +.btn-link a:hover{text-decoration:none;} +/* loader */ +.loader{ + width:100%; + overflow:hidden; + padding:13px 0 14px; +} +.loader .hold{ + width:100%; + overflow:hidden; +} +.loader img{float:left;} +.loader .hold span{ + float:left; + font-size:12px; + line-height:14px; + color:#fff; + padding:4px 0 0 7px; +} +.loader strong{ + display:block; + color:#fff; + padding:17px 0 0; +} +.progress-bar { + width:369px; + height:22px !important; + overflow:hidden; + float:left; +} +.ui-progressbar-value { background-image: url(../images/pbar-ani.gif) } +.btn-continue span{ + width:122px; + height:84px; + background:url(../images/btn-continue.png) no-repeat; +} +/* create box */ +.create-hold{ + width:100%; + overflow:hidden; + margin:-1px 0 0; + padding:0 0 13px; +} +.content .create-hold p{ + margin:0; + line-height:20px; +} +/* instruction box */ +.instruction-hold .check-hold{ + overflow:hidden; + height:1%; + padding:10px 0 3px 139px; +} +.instruction-hold .chk{ + float:left; + display:inline; + width:14px; + height:14px; + margin:2px 8px 0 0; + padding:0; +} +.instruction-hold .check-hold label{ + float:left; + width:auto; + line-height:16px; + color:#ffffff; + font-weight:normal; + padding:0; +} +.btn-box .btn-create { + float:left; + margin:13px 0 0; + width:145px; + height:42px; + position:relative; + overflow:hidden; +} +.btn-box .btn-create span { + display: block; + width:145px; + height:42px; + overflow:hidden; + text-indent: -9999px; + background:url(../images/btn-create.png) no-repeat; +} +.btn-box .btn-create:hover { border: none; } +.btn-box .btn-create:hover span { + padding: 42px 0 0; + margin: -42px 0 0; +} +.validaing, +.invalidaing, .basevalidaing{ + float:left !Important; + display:inline; + margin:6px 0 0 10px !Important; + padding:0 0 2px 28px !Important; + line-height:13px !Important; + font-weight:bold !Important; + color:#fff !Important; + background-color: none !Important; +} + +.validaing +{ + background:url(../images/ico-validaing.png) no-repeat !Important; +} + +.invalidaing{ + margin:6px 0 0 12px !Important; + padding:0 0 2px 26px !Important; + background:url(../images/ico-invalidaing.png) no-repeat !Important; +} + + +/* add navigation */ +.add-nav:after { + clear: both; + content:""; + display: block; +} +.add-nav{ + width:100%; + position:relative; + margin:-19px 0 0; +} +.add-nav ul{ + margin:0; + padding:0; + list-style:none; +} +.add-nav ul li{ + float:left; + padding:0 4px 0 32px; +} +.add-nav ul li a { + display:block; + width:150px; + height:212px; + position:relative; + cursor:pointer; +} +.add-nav ul li a img{ + position:absolute; + top: 0; + left: 0; +} +.add-nav ul li a span{ + position:absolute; + top:-15px; + left:-99999px; + display:none; +} +.add-nav ul li.hover a span{ + display:block; + left:-15px; +} +.add-nav ul li em{ + position:absolute; + top:236px; + width:16px; + height:13px; + overflow:hidden; + font-size:0; + line-height:0; +} +.add-nav ul li.add-simple em{left:166px;} +.add-nav ul li.add-blog em{left:319px;} +.add-nav ul li.add-personal em{left:471px;} +.add-nav ul li.add-buisness em{left:624px;} +.add-nav ul li.add-thanks em{left:776px;} +.add-nav ul li:hover em, +.add-nav ul li.hover em{background:url(../images/bul3.png) no-repeat;} +.add-nav ul li:hover .drop-hold, +.add-nav ul li.hover .drop-hold{display:block;} +/* drop down */ +.drop-hold{ + width:708px; + overflow:hidden; + position:absolute; + top:209px; + left:50%; + margin:0 0 0 -355px; + padding:40px 0 0; + display:none; +} +.drop-hold .c:after { + clear: both; + content:""; + display: block; +} +.drop-hold .c{ + width:620px; + min-height:107px; + padding:16px 44px; + background:url(../images/bg-drop-c.gif) repeat-y; +} +* html .drop-hold .c{height:107px;} +.drop-hold .t, +.drop-hold .b{ + width:100%; + overflow:hidden; + height:10px; + font-size:0; + line-height:0; +} +.drop-hold .t{background:url(../images/bg-drop-t.png) no-repeat;} +.drop-hold .b{background:url(../images/bg-drop-b.png) no-repeat;} +.drop-hold .title{ + width:100%; + overflow:hidden; + padding:0 0 17px; +} +.drop-hold .title span{ + display:block; + font-size:20px; + line-height:24px; + color:#3e0e03; +} +.drop-hold .hold{ + width:100%; + overflow:hidden; + color:#3e0e03; +} +.drop-hold .box{ + float:left; + width:280px; + padding:0 8px 0 0; +} +.add-nav .box ul{ + margin:0; + padding:0; + list-style:none; + width:100%; + overflow:hidden; +} +.add-nav .box ul li{ + overflow:hidden; + height:1%; + vertical-align:top; + float:none; + padding:0 0 4px 13px; + color:#3e0e03; + background:url(../images/bul2.gif) no-repeat 0 7px; +} + +/* basic starterkit grid*/ +ul.kits { + list-style: none; +} + +.thumbnails li { + float: left; + width: 256px; + margin: 0 30px 0 0; + background: #1f1f1f; + padding: 18px 22px; + border-radius: 10px; + position: relative; +} + +.thumbnails li .overlay { + position: absolute; + bottom: 30px; + right: 32px; + background: #3388cc; + height: 16px; + color: #fff; + border-radius: 3px; + padding: 7px 15px; + cursor: pointer; + outline: 0; + box-shadow: 0px 1px 4px 0px rgba(0,0,0,0.05); + display: inline; + -moz-transition: background-color .25s ease-in-out; + -webkit-transition: background-color .25s ease-in-out; + -o-transition: background-color .25s ease-in-out; + -ms-transition: background-color .25s ease-in-out; + transition: background-color .25s ease-in-out; +} + +.thumbnails li .overlay:hover { + background: #4398dc; +} + + +.thumbnails li.add-cwsstart { + margin: 0 +} + +.thumbnails li img { + width: 100%; + height: 143px; + background-color: #fff +} + + + +.kits:after { + clear: both; + content:""; + display: block; +} + +.declineKit { + color: #fff; + text-decoration: underline; + font-size: 14px; + margin-top: 20px; + display: block; + float: left; +} + +/* Light box */ + +.zoom-in { + background: #1f1f1f url(../Images/zoom-in.png) no-repeat 7px 7px; + width: 16px; + height: 16px; + position: absolute; + bottom: 30px; + left: 32px; + text-indent: -9999px; + border-radius: 3px; + padding: 7px; + -moz-transition: background-color .25s ease-in-out; + -webkit-transition: background-color .25s ease-in-out; + -o-transition: background-color .25s ease-in-out; + -ms-transition: background-color .25s ease-in-out; + transition: background-color .25s ease-in-out; +} + +.zoom-in:hover { + background-color: #333 +} + +div.lb { + display: none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + text-align: center; + z-index: 100 +} + +div.lb img { + position: absolute; + top: 10px; + left: 50%; + width: 960px; + height: auto; + margin-left: -480px; /* Half the width */ +} + +div.lb:target { + display: block; +} + +/** IE doesn't support :target, so we use CSS expressions **/ +div.lb { + display: expression((document.location.toString().split('#').slice(1) == this.id)?'block':'none'); +} + + + +/* gallery */ +.gallery:after { + clear: both; + content:""; + display: block; +} +.gallery{ + width:795px; + margin:0 0 0 17px; + position:relative; + min-height:300px; + padding:0 54px 0 77px; +} +* html .gallery{height:300px;} +.gallery .hold{ + width:100%; + position:relative; + padding:18px 0; +} +.gallery .gal-box{ + width:99999px; + position:relative; +} +.gallery .box { + float:left; + width:796px; + background: none !important; +} +.gallery .box ul{ + float:left; + display:inline; + margin:5px -18px 0 10px; + padding:0; + list-style:none; +} +.gallery .box ul li { + float:left; + width:152px; + height:129px; + position:relative; + padding:5px 48px 29px 0; +} +.gallery .box .image-hold { + float:left; + width:152px; + height: 129px; + margin: 0 -100px -100px 0; + position:relative; +} +.gallery .box .image-hold .faik-mask { + display: block; +} +.gallery .box .image{ + position:absolute; + top:0; + left:0; + padding:7px 9px 19px 9px; +} +.gallery .box .image img { + display:block; + position:relative; +} +.gallery .box .faik-mask-ie6 { + position:absolute; + top:-9999px; + left:-9999px; +} +.gallery .btn-prev, +.gallery .btn-next{ + position:absolute; + top:152px; + height:30px; + width:30px; + overflow:hidden; + outline:none; +} +.gallery .btn-prev:hover, +.gallery .btn-next:hover{border:none;} +.gallery .btn-prev{left:0;} +.gallery .btn-next{right:0;} +.gallery .btn-prev span, +.gallery .btn-next span{ + float:left; + width:30px; + height:60px; + overflow:hidden; + text-indent:-9999px; +} +.gallery .btn-prev span{background:url(../images/btn-prev.png) no-repeat;} +.gallery .btn-next span{background:url(../images/btn-next.png) no-repeat;} +.gallery .btn-prev:hover span, +.gallery .btn-next:hover span{margin:-30px 0 0;} +/* gallery drop */ +.gal-drop { + position:absolute; + top:7px; + left:9px; + width:178px; +} +* html .gal-drop { + margin: -2px 0 0 +} +.gal-drop a{ + display:block; + width:100%; + overflow:hidden; +} +.gal-drop a:hover{border:none;} +.gal-drop a span{ + float:left; + width:178px; + overflow:hidden; + text-indent:-99999px; +} +.gal-drop .btn-preview{height:69px;} +.gal-drop .btn-preview span{ + height:138px; + background:url(../images/btn-preview.png) no-repeat; +} +.gal-drop .btn-preview:hover span{margin:-69px 0 0;} +.gal-drop .btn-install-gal{height:68px;} +.gal-drop .btn-install-gal span{ + height:138px; + background:url(../images/btn-install-gal.png) no-repeat; +} +.gal-drop .btn-install-gal:hover span{margin:-68px 0 0;} +/* paging */ +.paging{ + width:100%; + overflow:hidden; + position:relative; +} +.w1{ + float:left; + position:relative; + left:50%; +} +.w2{ + float:left; + position:relative; + left:-50%; +} +.paging span{ + float:left; + font-size:14px; + line-height:22px; + color:#fff; + padding:0 16px 0 0; +} +.paging ul{ + float:left; + margin:0; + padding:0 90px 0 0; + list-style:none; +} +.paging ul li{ + float:left; + padding:4px 7px 4px 7px; +} +.paging ul li a{ + display:block; + font-size:11px; + line-height:13px; + font-weight:bold; + color:#fff; + width:16px; + height:16px; + padding:2px 0 0 2px; + text-align:center; + background:url(../images/bg-paging.png) no-repeat; +} +.paging ul li a:hover, +.paging ul li.active a{ + font-size:13px; + line-height:16px; + width:22px; + height:21px; + margin:-2px -2px -4px -3px; + position:relative; + color:#000; + padding:2px 0 0 1px; + text-decoration:none; + background:url(../images/bg-paging-h.png) no-repeat; +} +.container .alt{padding-top:7px;} +/* lightbox */ +.lightbox{ + width:870px; + overflow:hidden; + position:absolute; + top:-9999px; + left:-9999px; + padding:10px; + margin:0; + z-index: 999; +} +.lightbox .t, +.lightbox .b{ + width:100%; + overflow:hidden; + height:8px; + font-size:0; + line-height:0; +} +.lightbox .t{background:url(../images/bg-lightbox-t.png) no-repeat;} +.lightbox .b{background:url(../images/bg-lightbox-b.png) no-repeat;} +.lightbox .c{ + width:824px; + overflow:hidden; + background:#ededed; + padding:9px 23px 35px 23px; +} +.lightbox .heading{ + width:100%; + overflow:hidden; + padding:0 0 11px; +} +.lightbox .title{ + display:block; + font-size:26px; + line-height:28px; + color:#000; + font-weight:bold; + padding:0 0 7px; +} +.lightbox .create{ + display:block; + font-size:14px; + line-height:18px; + color:#5f5f5f; +} +.lightbox .create a{ + font-weight:bold; + color:#5f5f5f; +} +.lightbox .carusel{ + width:100%; + overflow:hidden; + border-top:1px solid #a5a5a5; + border-bottom:1px solid #a5a5a5; + padding:13px 0 15px; + position:relative; +} +.lightbox .carusel ul{ + width:99999px; + margin:0; + padding:0; + list-style:none; +} +.lightbox .carusel ul li{ + float:left; + padding:0 8px 0 0; +} +.lightbox .carusel ul li img{display:block;} +.lightbox .btn-box{padding-top:27px;} +.lightbox .btn-install{ + display:block; + width:99px; + height:42px; + overflow:hidden; + text-indent:-99999px; + margin:0; + background:url(../images/btn-install.png) no-repeat; +} +.lightbox .btn-install:hover{background:url(../images/btn-install-hover.png) no-repeat;} +.lightbox .btn-close{ + position:absolute; + top:0; + right:0; + width:30px; + height:30px; + overflow:hidden; + text-indent:-99999px; + z-index:100; + background:url(../images/btn-close.png) no-repeat; +} +.btn-web{ + width:100%; + overflow:hidden; + margin:0; + padding:15px 0 20px; + list-style:none; +} +.btn-web li{ + float:left; + padding:0 13px 0 0; +} +.btn-web li a{ + float:left; + height:42px; + overflow:hidden; + cursor:pointer; +} +.btn-web li a:hover{border:none;} +.btn-web li a span{ + float:left; + height:84px; + overflow:hidden; + text-indent:-99999px; +} +.btn-web li.btn-set a span{ + width:248px; + background:url(../images/btn-set.png) no-repeat; +} +.btn-web li.btn-preview-web a span{ + width:257px; + background:url(../images/btn-preview-web.png) no-repeat; +} +.btn-web li a:hover span{margin:-42px 0 0;} +/* three columns */ +.threcol{ + width:100%; + overflow:hidden; +} +.threcol .t{ + width:100%; + overflow:hidden; + height:1px; + font-size:0; + line-height:0; + background:url(../images/sep1.png) repeat-x; +} +.threcol .hold{ + width:100%; + overflow:hidden; + padding:24px 0 0; +} +.content .threcol h2{ + margin:0 0 11px; + font-size:20px; + line-height:24px; + font-weight:bold; + color:#fff; +} +.col1{ + float:left; + width:225px; + padding:0 57px 0 3px; +} +.content .threcol p{ + margin:0 0 20px; + line-height:20px; +} +.content .threcol p span{display:block;} +.content .threcol p a{ + color:#fff; + text-decoration:underline; +} +.content .threcol p a:hover{text-decoration:none;} +/* links box */ +.links{ + width:100%; + overflow:hidden; +} +.links ul{ + width:100%; + overflow:hidden; + margin:0; + padding:6px 0 14px; + list-style:none; +} +.links ul li{ + overflow:hidden; + height:1%; + vertical-align:top; + padding:0 0 9px 11px; + font-size:14px; + line-height:16px; + background:url(../images/bul4.gif) no-repeat 0 5px; +} +.links ul li a{ + color:#fff; + text-decoration:underline; +} +.links ul li a:hover{text-decoration:none;} +.col2{ + float:left; + width:301px; + padding:0 60px 0 0; +} +.col3{ + float:left; + width:295px; +} +.box-software{ + width:100%; + overflow:hidden; +} +.content .box-software p{ + font-size:12px; + line-height:15px; + margin:0 0 15px; +} +.box-software p span{font-size:11px;} + + +.ui-selectmenu { + border: none !important; + display:block !important; + width:310px !important; + height: 29px; + overflow:hidden; + font-size:13px; + line-height:29px; + background:none; +} +.ui-selectmenu:focus { + outline: 1px dotted #000; +} +.ui-selectmenu-status { + float:left; + height: 29px; + width:266px; + padding:0 10px; + color: #000; + font-weight:normal; + background:url(../images/select-left-2.png) no-repeat; +} +.ui-state-default .ui-icon { + position:static; + float:right; + width:24px; + height: 29px; + background:url(../images/select-button.png) no-repeat; +} +.ui-selectmenu-menu { + position:absolute; + top: 0; + left: 0; + visibility: hidden; + border-radius: 0 !important; + -moz-border-radius: 0 !important; + -webkit-border-radius: 0 !important; +} +.ui-selectmenu-open { + visibility: visible; +} +.ui-selectmenu-menu li { + font-weight:normal !important; + border: none !important; +} + +.summary li{margin-bottom: 20px; display: block;} +.summary a{color: White;display:block;font-weight:bold;} + +.error {padding:0 100px 13px 5px} +.error p{margin:0 0 13px;padding-right:100px;} +.error p strong {font-size:14px;display:block;} + +/* db step refactor */ +.database-option, footer.installbtn +{ + display:none; +} + +.database-hold{ + width:100%; + overflow:hidden; +} + +.step.rendering-engine input[type="radio"] { + margin: 0px 5px 0px 5px; +} + +.step.rendering-engine > div { + line-height: 25px; +} + +.upgrade-report { + padding: 10px; + border-collapse: separate; + border-spacing: inherit; + line-height: 20px; + background: #361919; + background: rgba(255, 255, 255, 0.15); + -moz-border-radius: 20px; + -webkit-border-radius: 20px; + border-radius: 20px; + width: 920px; +} + +.upgrade-report td { + padding-bottom: 5px; + padding-left: 5px; + padding-right: 10px; +} +.upgrade-report td.msg { + vertical-align: middle; +} + +.upgrade-report td.icon span { + margin-top: 5px; + display: block; } \ No newline at end of file diff --git a/OurUmbraco.Site/umbraco_client/Installer/css/form.css b/OurUmbraco.Site/umbraco_client/Installer/css/form.css index 2f1485b1..6985e5dc 100644 --- a/OurUmbraco.Site/umbraco_client/Installer/css/form.css +++ b/OurUmbraco.Site/umbraco_client/Installer/css/form.css @@ -1,77 +1,77 @@ -.outtaHere { - position:absolute; - left:-3000px; -} -/* Selects */ -.selectArea { - position: relative; - height: 29px; - float:left; - color:#000; - font-size:13px; - line-height:29px; -} -.selectArea .left { - position: absolute; - top: 0; - left: 0; - width:15px; - height:29px; - background: url(../images/select-left.png) no-repeat; - display: block; -} -.selectArea a.selectButton { - position: absolute; - top: 0; - right: 0; - width:310px; - height:29px; - background: url(../images/select-button.png) no-repeat; -} -.selectArea .center{ - height: 29px; - line-height:28px; - display:block; - margin:0 24px 0 15px; - background: url(../images/select-center.png) repeat-x; -} -.selectArea .center img { - float:left; -} -/*Selects drop-down*/ -.optionsDivInvisible, -.optionsDivVisible { - position: absolute; - background-color: #fff; - border: 1px solid #896e68; - display: block; - z-index: 30; - font-size: 11px; -} -.optionsDivInvisible {display: none;} -.optionsDivVisible ul { - margin:0; - padding:0; - overflow:hidden; - width:100%; - list-style: none; -} -.optionsDivVisible ul li { - float:left; - width:100%; -} -.optionsDivVisible a { - color: #000; - overflow:hidden; - text-decoration: none; - display: block; - height:1%; - padding: 3px 5px; -} -.optionsDivVisible a img { - border:none; - float:left; -} -.optionsDivVisible a:hover { - text-decoration:underline; +.outtaHere { + position:absolute; + left:-3000px; +} +/* Selects */ +.selectArea { + position: relative; + height: 29px; + float:left; + color:#000; + font-size:13px; + line-height:29px; +} +.selectArea .left { + position: absolute; + top: 0; + left: 0; + width:15px; + height:29px; + background: url(../images/select-left.png) no-repeat; + display: block; +} +.selectArea a.selectButton { + position: absolute; + top: 0; + right: 0; + width:310px; + height:29px; + background: url(../images/select-button.png) no-repeat; +} +.selectArea .center{ + height: 29px; + line-height:28px; + display:block; + margin:0 24px 0 15px; + background: url(../images/select-center.png) repeat-x; +} +.selectArea .center img { + float:left; +} +/*Selects drop-down*/ +.optionsDivInvisible, +.optionsDivVisible { + position: absolute; + background-color: #fff; + border: 1px solid #896e68; + display: block; + z-index: 30; + font-size: 11px; +} +.optionsDivInvisible {display: none;} +.optionsDivVisible ul { + margin:0; + padding:0; + overflow:hidden; + width:100%; + list-style: none; +} +.optionsDivVisible ul li { + float:left; + width:100%; +} +.optionsDivVisible a { + color: #000; + overflow:hidden; + text-decoration: none; + display: block; + height:1%; + padding: 3px 5px; +} +.optionsDivVisible a img { + border:none; + float:left; +} +.optionsDivVisible a:hover { + text-decoration:underline; } \ No newline at end of file diff --git a/OurUmbraco.Site/umbraco_client/Installer/css/lt7.css b/OurUmbraco.Site/umbraco_client/Installer/css/lt7.css index 88bfc8ab..97d7edfe 100644 --- a/OurUmbraco.Site/umbraco_client/Installer/css/lt7.css +++ b/OurUmbraco.Site/umbraco_client/Installer/css/lt7.css @@ -1,174 +1,174 @@ -* html body{width: expression(document.documentElement.clientWidth < 1000 ? "1000px" : "auto");} -#header{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-header.png', sizingmethod='scale'); -} -.tabset .b{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/sep1.png', sizingmethod='scale'); -} -.tabset ul li.active em{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bul1.png', sizingmethod='crop'); -} -.btn-box .t{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/sep1.png', sizingmethod='scale'); -} -.btn-get span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-get.png', sizingmethod='crop'); -} -.btn-accept span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-accept.png', sizingmethod='crop'); -} -.mini-tabset li.btn-yes a span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-yes.png', sizingmethod='crop'); -} -.mini-tabset li.btn-no a span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-no.png', sizingmethod='crop'); -} -.selectArea a.selectButton{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/select-button.png', sizingmethod='crop'); -} -.selectArea .left{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/select-left.png', sizingmethod='crop'); -} -.selectArea .center{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/select-center.png', sizingmethod='scale'); -} -.instruction-hold .row span{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-inp.png', sizingmethod='crop'); -} -.btn-continue span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-continue.png', sizingmethod='crop'); -} -.instruction-hold .error span{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-inp-error.png', sizingmethod='crop'); -} -.validaing{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/ico-validaing.png', sizingmethod='crop') !important; -} -.invalidaing{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/ico-invalidaing.png', sizingmethod='crop') !important; -} -.drop-hold .t{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-drop-t.png', sizingmethod='crop'); -} -.drop-hold .b{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-drop-b.png', sizingmethod='crop'); -} -.add-nav ul li.hover em{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bul3.png', sizingmethod='crop'); -} -.gallery .btn-prev span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-prev.png', sizingmethod='crop'); -} -.gallery .btn-next span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-next.png', sizingmethod='crop'); -} -.gal-drop .btn-preview span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-preview.png', sizingmethod='crop'); -} -.gal-drop .btn-install-gal span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-install-gal.png', sizingmethod='crop'); -} -.paging ul li a{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-paging.png', sizingmethod='crop'); -} -.paging ul li a:hover, -.paging ul li.active a{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-paging-h.png', sizingmethod='crop'); -} -.lightbox .t{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-lightbox-t.png', sizingmethod='crop'); -} -.lightbox .b{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-lightbox-b.png', sizingmethod='crop'); -} -.lightbox .btn-install{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-install.png', sizingmethod='crop'); -} -.lightbox .btn-install:hover{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-install-hover.png', sizingmethod='crop'); -} -.lightbox .btn-close{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-close.png', sizingmethod='crop'); -} -.btn-web li.btn-set a span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-set.png', sizingmethod='crop'); -} -.btn-web li.btn-preview-web a span{ - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-preview-web.png', sizingmethod='crop'); -} -.threcol .t{ - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/sep1.png', sizingmethod='scale'); -} -.gallery .box { - filter: none !important; -} -.btn-box .btn-install span { - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-install.png', sizingmethod='crop'); -} -.ui-selectmenu-status { - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/select-left-2.png', sizingmethod='crop'); -} -.ui-state-default .ui-icon { - cursor: pointer; - background:url(../images/none.gif); - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/select-button.png', sizingmethod='crop'); -} -.btn-box .btn-create span { - background:url(../images/none.gif); - cursor:pointer; - filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-create.png', sizingmethod='crop'); +* html body{width: expression(document.documentElement.clientWidth < 1000 ? "1000px" : "auto");} +#header{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-header.png', sizingmethod='scale'); +} +.tabset .b{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/sep1.png', sizingmethod='scale'); +} +.tabset ul li.active em{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bul1.png', sizingmethod='crop'); +} +.btn-box .t{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/sep1.png', sizingmethod='scale'); +} +.btn-get span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-get.png', sizingmethod='crop'); +} +.btn-accept span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-accept.png', sizingmethod='crop'); +} +.mini-tabset li.btn-yes a span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-yes.png', sizingmethod='crop'); +} +.mini-tabset li.btn-no a span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-no.png', sizingmethod='crop'); +} +.selectArea a.selectButton{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/select-button.png', sizingmethod='crop'); +} +.selectArea .left{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/select-left.png', sizingmethod='crop'); +} +.selectArea .center{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/select-center.png', sizingmethod='scale'); +} +.instruction-hold .row span{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-inp.png', sizingmethod='crop'); +} +.btn-continue span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-continue.png', sizingmethod='crop'); +} +.instruction-hold .error span{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-inp-error.png', sizingmethod='crop'); +} +.validaing{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/ico-validaing.png', sizingmethod='crop') !important; +} +.invalidaing{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/ico-invalidaing.png', sizingmethod='crop') !important; +} +.drop-hold .t{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-drop-t.png', sizingmethod='crop'); +} +.drop-hold .b{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-drop-b.png', sizingmethod='crop'); +} +.add-nav ul li.hover em{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bul3.png', sizingmethod='crop'); +} +.gallery .btn-prev span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-prev.png', sizingmethod='crop'); +} +.gallery .btn-next span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-next.png', sizingmethod='crop'); +} +.gal-drop .btn-preview span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-preview.png', sizingmethod='crop'); +} +.gal-drop .btn-install-gal span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-install-gal.png', sizingmethod='crop'); +} +.paging ul li a{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-paging.png', sizingmethod='crop'); +} +.paging ul li a:hover, +.paging ul li.active a{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-paging-h.png', sizingmethod='crop'); +} +.lightbox .t{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-lightbox-t.png', sizingmethod='crop'); +} +.lightbox .b{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/bg-lightbox-b.png', sizingmethod='crop'); +} +.lightbox .btn-install{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-install.png', sizingmethod='crop'); +} +.lightbox .btn-install:hover{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-install-hover.png', sizingmethod='crop'); +} +.lightbox .btn-close{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-close.png', sizingmethod='crop'); +} +.btn-web li.btn-set a span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-set.png', sizingmethod='crop'); +} +.btn-web li.btn-preview-web a span{ + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-preview-web.png', sizingmethod='crop'); +} +.threcol .t{ + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/sep1.png', sizingmethod='scale'); +} +.gallery .box { + filter: none !important; +} +.btn-box .btn-install span { + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-install.png', sizingmethod='crop'); +} +.ui-selectmenu-status { + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/select-left-2.png', sizingmethod='crop'); +} +.ui-state-default .ui-icon { + cursor: pointer; + background:url(../images/none.gif); + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/select-button.png', sizingmethod='crop'); +} +.btn-box .btn-create span { + background:url(../images/none.gif); + cursor:pointer; + filter: progid:dximagetransform.microsoft.alphaimageloader(src='../umbraco_client/installer/images/btn-create.png', sizingmethod='crop'); } \ No newline at end of file diff --git a/OurUmbraco.Site/umbraco_client/Installer/css/reset.css b/OurUmbraco.Site/umbraco_client/Installer/css/reset.css index f9c75305..ca6e8c94 100644 --- a/OurUmbraco.Site/umbraco_client/Installer/css/reset.css +++ b/OurUmbraco.Site/umbraco_client/Installer/css/reset.css @@ -1,52 +1,52 @@ -/* v1.0 | 20080212 */ - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, font, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td { - margin: 0; - padding: 0; - border: 0; - outline: 0; - font-size: 100%; - vertical-align: baseline; - background: transparent; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} - -/* remember to define focus styles! */ -:focus { - outline: 0; -} - -/* remember to highlight inserts somehow! */ -ins { - text-decoration: none; -} -del { - text-decoration: line-through; -} - -/* tables still need 'cellspacing="0"' in the markup */ -table { - border-collapse: collapse; - border-spacing: 0; -} +/* v1.0 | 20080212 */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +/* remember to define focus styles! */ +:focus { + outline: 0; +} + +/* remember to highlight inserts somehow! */ +ins { + text-decoration: none; +} +del { + text-decoration: line-through; +} + +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: collapse; + border-spacing: 0; +} diff --git a/OurUmbraco/OurUmbraco.csproj b/OurUmbraco/OurUmbraco.csproj index 1e576bf3..56f86f90 100644 --- a/OurUmbraco/OurUmbraco.csproj +++ b/OurUmbraco/OurUmbraco.csproj @@ -38,7 +38,7 @@ ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\businesslogic.dll True @@ -50,11 +50,11 @@ ..\packages\ClientDependency-Mvc5.1.8.0.0\lib\net45\ClientDependency.Core.Mvc.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\cms.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\controls.dll True @@ -86,7 +86,7 @@ ..\packages\ImageProcessor.Web.4.5.3.0\lib\net45\ImageProcessor.Web.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\interfaces.dll True @@ -170,7 +170,7 @@ ..\packages\semver.1.1.2\lib\net451\Semver.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\SQLCE4Umbraco.dll True @@ -240,35 +240,35 @@ ..\packages\UmbracoCms.Core.7.4.2\lib\TidyNet.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\Umbraco.Core.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.DataLayer.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.editorControls.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.MacroEngines.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.providers.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\Umbraco.Web.UI.dll True - + ..\packages\UmbracoCms.Core.7.4.2\lib\UmbracoExamine.dll True