diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js index e044da4312..c6eaebb1c2 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbtree.directive.js @@ -3,346 +3,352 @@ * @name umbraco.directives.directive:umbTree * @restrict E **/ -angular.module("umbraco.directives") - .directive('umbTree', function ($compile, $log, $q, $rootScope, navigationService, treeService, notificationsService, $timeout) { - +function umbTreeDirective($compile, $log, $q, $rootScope, navigationService, treeService, notificationsService, $timeout) { + return { - restrict: 'E', - replace: true, - terminal: false, + restrict: 'E', + replace: true, + terminal: false, - scope: { - section: '@', - treealias: '@', - showoptions: '@', - showheader: '@', - cachekey: '@', - isdialog: '@', - eventhandler: '=' - }, + scope: { + section: '@', + treealias: '@', + showoptions: '@', + showheader: '@', + cachekey: '@', + isdialog: '@', + eventhandler: '=' + }, - compile: function (element, attrs) { - //config - var hideheader = (attrs.showheader === 'false') ? true : false; - var hideoptions = (attrs.showoptions === 'false') ? "hide-options" : ""; - - var template = '
'; - element.replaceWith(template); + element.replaceWith(template); - return function (scope, elem, attr, controller) { + return function(scope, elem, attr, controller) { - //flag to track the last loaded section when the tree 'un-loads'. We use this to determine if we should - // re-load the tree again. For example, if we hover over 'content' the content tree is shown. Then we hover - // outside of the tree and the tree 'un-loads'. When we re-hover over 'content', we don't want to re-load the - // entire tree again since we already still have it in memory. Of course if the section is different we will - // reload it. This saves a lot on processing if someone is navigating in and out of the same section many times - // since it saves on data retreival and DOM processing. - var lastSection = ""; - - //keeps track of the currently active tree being called by editors syncing - var activeTree; + //flag to track the last loaded section when the tree 'un-loads'. We use this to determine if we should + // re-load the tree again. For example, if we hover over 'content' the content tree is shown. Then we hover + // outside of the tree and the tree 'un-loads'. When we re-hover over 'content', we don't want to re-load the + // entire tree again since we already still have it in memory. Of course if the section is different we will + // reload it. This saves a lot on processing if someone is navigating in and out of the same section many times + // since it saves on data retreival and DOM processing. + var lastSection = ""; - //setup a default internal handler - if(!scope.eventhandler){ - scope.eventhandler = $({}); - } - - //flag to enable/disable delete animations - var enableDeleteAnimations = false; + //keeps track of the currently active tree being called by editors syncing + var activeTree; + + //setup a default internal handler + if (!scope.eventhandler) { + scope.eventhandler = $({}); + } + + //flag to enable/disable delete animations + var enableDeleteAnimations = false; - /** Helper function to emit tree events */ - function emitEvent(eventName, args) { - if (scope.eventhandler) { - $(scope.eventhandler).trigger(eventName, args); - } - } + /** Helper function to emit tree events */ - - /*this is the only external interface a tree has */ - function setupExternalEvents() { - if (scope.eventhandler) { - - scope.eventhandler.clearCache = function(section){ - treeService.clearCache({ section: section }); - }; - - scope.eventhandler.load = function(section){ - scope.section = section; - loadTree(); - }; - - scope.eventhandler.reloadNode = function(node){ - - if(!node){ - node = scope.currentNode; + function emitEvent(eventName, args) { + if (scope.eventhandler) { + $(scope.eventhandler).trigger(eventName, args); } - - if(node){ - scope.loadChildren(node, true); + } + + + /*this is the only external interface a tree has */ + + function setupExternalEvents() { + if (scope.eventhandler) { + + scope.eventhandler.clearCache = function(section) { + treeService.clearCache({ section: section }); + }; + + scope.eventhandler.load = function(section) { + scope.section = section; + loadTree(); + }; + + scope.eventhandler.reloadNode = function(node) { + + if (!node) { + node = scope.currentNode; + } + + if (node) { + scope.loadChildren(node, true); + } + }; + + scope.eventhandler.syncPath = function(path, forceReload) { + + if (angular.isString(path)) { + path = path.replace('"', '').split(','); + } + + //reset current node selection + scope.currentNode = undefined; + navigationService.ui.currentNode = undefined; + + //filter the path for root node ids + path = _.filter(path, function(item) { return (item !== "init" && item !== "-1"); }); + loadPath(path, forceReload); + }; + + scope.eventhandler.setActiveTreeType = function(treeAlias) { + loadActiveTree(treeAlias); + }; } - }; - - scope.eventhandler.syncPath = function(path, forceReload){ - - if(angular.isString(path)){ - path = path.replace('"', '').split(','); - } - - //reset current node selection - scope.currentNode = undefined; - navigationService.ui.currentNode = undefined; - - //filter the path for root node ids - path = _.filter(path, function(item){ return (item !== "init" && item !== "-1"); }); - loadPath(path, forceReload); - }; - - scope.eventhandler.setActiveTreeType = function(treeAlias){ - loadActiveTree(treeAlias); - }; - } - } - - - - - //helper to load a specific path on the active tree as soon as its ready - function loadPath(path, forceReload){ - function _load(tree, path, forceReload){ - syncTree(tree, path, forceReload); - } - - if(scope.activeTree){ - _load(scope.activeTree, path, forceReload); - }else{ - scope.eventhandler.one("activeTreeLoaded", function(e, args){ - _load(args.tree, path, forceReload); - }); - } - } - - //expands the first child with a tree alias as soon as the tree has loaded - function loadActiveTree(treeAlias){ - scope.activeTree = undefined; - - function _load(tree, alias){ - scope.activeTree = _.find(tree.children, function(node){ return node.metaData.treeAlias === treeAlias; }); - scope.activeTree.expanded = true; - - scope.loadChildren(scope.activeTree, false).then(function(){ - emitEvent("activeTreeLoaded", {tree: scope.activeTree}); - }); } - if(scope.tree){ - _load(scope.tree.root, treeAlias); - }else{ - scope.eventhandler.one("treeLoaded", function(e, args){ - _load(args.tree, treeAlias); - }); - } - } + //helper to load a specific path on the active tree as soon as its ready - /** Method to load in the tree data */ - function loadTree() { - if (!scope.loading && scope.section) { - scope.loading = true; + function loadPath(path, forceReload) { - //anytime we want to load the tree we need to disable the delete animations - enableDeleteAnimations = false; + function _load(tree, path, forceReload) { + syncTree(tree, path, forceReload); + } - //use $q.when because a promise OR raw data might be returned. - treeService.getTree({ section: scope.section, tree: scope.treealias, cacheKey: scope.cachekey, isDialog: scope.isdialog ? scope.isdialog : false }) - .then(function (data) { - //set the data once we have it - scope.tree = data; - - - - //do timeout so that it re-enables them after this digest - $timeout(function() { - //enable delete animations - enableDeleteAnimations = true; - },0,false); - - scope.loading = false; - - //set the root as the current active tree - scope.activeTree = scope.tree.root; - emitEvent("treeLoaded", {tree: scope.tree.root}); - - }, function (reason) { - scope.loading = false; - notificationsService.error("Tree Error", reason); + if (scope.activeTree) { + _load(scope.activeTree, path, forceReload); + } + else { + scope.eventhandler.one("activeTreeLoaded", function(e, args) { + _load(args.tree, path, forceReload); }); - } - } - - function syncTree(node, path, forceReload) { - if(!node || !path || path.length === 0){ - return; - } - - //we are directly above the changed node - var onParent = (path.length === 1); - var needsReload = true; - - node.expanded = true; - - //if we are not directly above, we will just try to locate - //the node and continue down the path - if(!onParent){ - //if we can find the next node in the path - var child = treeService.getChildNode(node, path[0]); - if(child){ - needsReload = false; - path.splice(0,1); - syncTree(child, path, forceReload); - } - } - - //if a reload is needed, all children will be loaded from server - if(needsReload){ - scope.loadChildren(node, forceReload) - .then(function(children){ - var child = treeService.getChildNode(node, path[0]); - - if(!onParent){ - path.splice(0,1); - syncTree(child, path, forceReload); - } - else { - navigationService.ui.currentNode = child; - scope.currentNode = child; - } - }); - } - } - - - - /** method to set the current animation for the node. - * This changes dynamically based on if we are changing sections or just loading normal tree data. - * When changing sections we don't want all of the tree-ndoes to do their 'leave' animations. - */ - scope.animation = function () { - if (enableDeleteAnimations && scope.tree && scope.tree.root && scope.tree.root.expanded) { - return { leave: 'tree-node-delete-leave' }; - } - else { - return {}; - } - }; - - /* helper to force reloading children of a tree node */ - scope.loadChildren = function(node, forceReload){ - var deferred = $q.defer(); - - //emit treeNodeExpanding event, if a callback object is set on the tree - emitEvent("treeNodeExpanding", {tree: scope.tree, node: node }); - - //standardising - if(!node.children){ - node.children = []; + } } - if (forceReload || (node.hasChildren && node.children.length === 0)) { - //get the children from the tree service - treeService.loadNodeChildren({ node: node, section: scope.section }) - .then(function(data) { - //emit expanded event - emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: data }); - enableDeleteAnimations = true; + //expands the first child with a tree alias as soon as the tree has loaded - deferred.resolve(data); + function loadActiveTree(treeAlias) { + scope.activeTree = undefined; + + function _load(tree, alias) { + scope.activeTree = _.find(tree.children, function(node) { return node.metaData.treeAlias === treeAlias; }); + scope.activeTree.expanded = true; + + scope.loadChildren(scope.activeTree, false).then(function() { + emitEvent("activeTreeLoaded", { tree: scope.activeTree }); }); + } + + if (scope.tree) { + _load(scope.tree.root, treeAlias); + } + else { + scope.eventhandler.one("treeLoaded", function(e, args) { + _load(args.tree, treeAlias); + }); + } } - else { - emitEvent("treeNodeExpanded", {tree: scope.tree, node: node, children: node.children }); + + + /** Method to load in the tree data */ + + function loadTree() { + if (!scope.loading && scope.section) { + scope.loading = true; + + //anytime we want to load the tree we need to disable the delete animations + enableDeleteAnimations = false; + + //use $q.when because a promise OR raw data might be returned. + treeService.getTree({ section: scope.section, tree: scope.treealias, cacheKey: scope.cachekey, isDialog: scope.isdialog ? scope.isdialog : false }) + .then(function(data) { + //set the data once we have it + scope.tree = data; + + + //do timeout so that it re-enables them after this digest + $timeout(function() { + //enable delete animations + enableDeleteAnimations = true; + }, 0, false); + + scope.loading = false; + + //set the root as the current active tree + scope.activeTree = scope.tree.root; + emitEvent("treeLoaded", { tree: scope.tree.root }); + + }, function(reason) { + scope.loading = false; + notificationsService.error("Tree Error", reason); + }); + } + } + + function syncTree(node, path, forceReload) { + if (!node || !path || path.length === 0) { + return; + } + + //we are directly above the changed node + var onParent = (path.length === 1); + var needsReload = true; + node.expanded = true; - enableDeleteAnimations = true; - deferred.resolve(node.children); + //if we are not directly above, we will just try to locate + //the node and continue down the path + if (!onParent) { + //if we can find the next node in the path + var child = treeService.getChildNode(node, path[0]); + if (child) { + needsReload = false; + path.splice(0, 1); + syncTree(child, path, forceReload); + } + } + + //if a reload is needed, all children will be loaded from server + if (needsReload) { + scope.loadChildren(node, forceReload) + .then(function(children) { + var child = treeService.getChildNode(node, path[0]); + + if (!onParent) { + path.splice(0, 1); + syncTree(child, path, forceReload); + } + else { + navigationService.ui.currentNode = child; + scope.currentNode = child; + } + }); + } } - return deferred.promise; + + /** method to set the current animation for the node. + * This changes dynamically based on if we are changing sections or just loading normal tree data. + * When changing sections we don't want all of the tree-ndoes to do their 'leave' animations. + */ + scope.animation = function() { + if (enableDeleteAnimations && scope.tree && scope.tree.root && scope.tree.root.expanded) { + return { leave: 'tree-node-delete-leave' }; + } + else { + return {}; + } + }; + + /* helper to force reloading children of a tree node */ + scope.loadChildren = function(node, forceReload) { + var deferred = $q.defer(); + + //emit treeNodeExpanding event, if a callback object is set on the tree + emitEvent("treeNodeExpanding", { tree: scope.tree, node: node }); + + //standardising + if (!node.children) { + node.children = []; + } + + if (forceReload || (node.hasChildren && node.children.length === 0)) { + //get the children from the tree service + treeService.loadNodeChildren({ node: node, section: scope.section }) + .then(function(data) { + //emit expanded event + emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: data }); + enableDeleteAnimations = true; + + deferred.resolve(data); + }); + } + else { + emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: node.children }); + node.expanded = true; + enableDeleteAnimations = true; + + deferred.resolve(node.children); + } + + return deferred.promise; + }; + + /** + Method called when the options button next to the root node is called. + The tree doesnt know about this, so it raises an event to tell the parent controller + about it. + */ + scope.options = function(e, n, ev) { + emitEvent("treeOptionsClick", { element: e, node: n, event: ev }); + }; + + /** + Method called when an item is clicked in the tree, this passes the + DOM element, the tree node object and the original click + and emits it as a treeNodeSelect element if there is a callback object + defined on the tree + */ + scope.select = function(e, n, ev) { + emitEvent("treeNodeSelect", { element: e, node: n, event: ev }); + }; + + scope.altSelect = function(e, n, ev) { + emitEvent("treeNodeAltSelect", { element: e, tree: scope.tree, node: n, event: ev }); + }; + + + //watch for section changes + scope.$watch("section", function(newVal, oldVal) { + + if (!scope.tree) { + loadTree(); + } + + if (!newVal) { + //store the last section loaded + lastSection = oldVal; + } + else if (newVal !== oldVal && newVal !== lastSection) { + //only reload the tree data and Dom if the newval is different from the old one + // and if the last section loaded is different from the requested one. + loadTree(); + + //store the new section to be loaded as the last section + //clear any active trees to reset lookups + lastSection = newVal; + activeTree = undefined; + } + }); + + //When the user logs in + scope.$on("authenticated", function(evt, data) { + //populate the tree if the user has changed + if (data.lastUserId !== data.user.id) { + treeService.clearCache(); + scope.tree = null; + + setupExternalEvents(); + loadTree(); + } + }); + }; + } + }; +} - /** - Method called when the options button next to the root node is called. - The tree doesnt know about this, so it raises an event to tell the parent controller - about it. - */ - scope.options = function (e, n, ev) { - emitEvent("treeOptionsClick", { element: e, node: n, event: ev }); - }; - - /** - Method called when an item is clicked in the tree, this passes the - DOM element, the tree node object and the original click - and emits it as a treeNodeSelect element if there is a callback object - defined on the tree - */ - scope.select = function(e,n,ev){ - emitEvent("treeNodeSelect", { element: e, node: n, event: ev }); - }; - - scope.altSelect = function(e,n,ev){ - emitEvent("treeNodeAltSelect", { element: e, tree: scope.tree, node: n, event: ev }); - }; - - - //watch for section changes - scope.$watch("section", function (newVal, oldVal) { - - if(!scope.tree){ - loadTree(); - } - - if (!newVal) { - //store the last section loaded - lastSection = oldVal; - }else if (newVal !== oldVal && newVal !== lastSection) { - //only reload the tree data and Dom if the newval is different from the old one - // and if the last section loaded is different from the requested one. - loadTree(); - - //store the new section to be loaded as the last section - //clear any active trees to reset lookups - lastSection = newVal; - activeTree = undefined; - } - }); - - //When the user logs in - scope.$on("authenticated", function (evt, data) { - //populate the tree if the user has changed - if (data.lastUserId !== data.user.id) { - treeService.clearCache(); - scope.tree = null; - - setupExternalEvents(); - loadTree(); - } - }); - - }; - } - }; - }); \ No newline at end of file +angular.module("umbraco.directives").directive('umbTree', umbTreeDirective); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index 89a8a1e971..c74eac6907 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -15,10 +15,8 @@ * Section navigation and search, and maintain their state for the entire application lifetime * */ +function navigationService($rootScope, $routeParams, $log, $location, $q, $timeout, dialogService, treeService, notificationsService, historyService) { -angular.module('umbraco.services') -.factory('navigationService', function ($rootScope, $routeParams, $log, $location, $q, $timeout, dialogService, treeService, notificationsService, historyService) { - var minScreenSize = 1100; //Define all sub-properties for the UI object here @@ -38,12 +36,13 @@ angular.module('umbraco.services') actions: undefined, currentDialog: undefined, dialogTitle: undefined, - + //a string/name reference for the currently set ui mode currentMode: "default" }; - - $rootScope.$on("closeDialogs", function(){}); + + $rootScope.$on("closeDialogs", function() { + }); function setTreeMode() { ui.tablet = ($(window).width() <= minScreenSize); @@ -52,57 +51,57 @@ angular.module('umbraco.services') function setMode(mode) { switch (mode) { - case 'tree': - ui.currentMode = "tree"; - ui.showNavigation = true; - ui.showContextMenu = false; - ui.showContextMenuDialog = false; - ui.stickyNavigation = false; - ui.showTray = false; - service.hideUserDialog(); - service.hideHelpDialog(); - //$("#search-form input").focus(); - break; - case 'menu': - ui.currentMode = "menu"; - ui.showNavigation = true; - ui.showContextMenu = true; - ui.showContextMenuDialog = false; - ui.stickyNavigation = true; - break; - case 'dialog': - ui.currentMode = "dialog"; - ui.stickyNavigation = true; - ui.showNavigation = true; - ui.showContextMenu = false; - ui.showContextMenuDialog = true; - break; - case 'search': - ui.currentMode = "search"; - ui.stickyNavigation = false; - ui.showNavigation = true; - ui.showContextMenu = false; - ui.showSearchResults = true; - ui.showContextMenuDialog = false; + case 'tree': + ui.currentMode = "tree"; + ui.showNavigation = true; + ui.showContextMenu = false; + ui.showContextMenuDialog = false; + ui.stickyNavigation = false; + ui.showTray = false; + service.hideUserDialog(); + service.hideHelpDialog(); + //$("#search-form input").focus(); + break; + case 'menu': + ui.currentMode = "menu"; + ui.showNavigation = true; + ui.showContextMenu = true; + ui.showContextMenuDialog = false; + ui.stickyNavigation = true; + break; + case 'dialog': + ui.currentMode = "dialog"; + ui.stickyNavigation = true; + ui.showNavigation = true; + ui.showContextMenu = false; + ui.showContextMenuDialog = true; + break; + case 'search': + ui.currentMode = "search"; + ui.stickyNavigation = false; + ui.showNavigation = true; + ui.showContextMenu = false; + ui.showSearchResults = true; + ui.showContextMenuDialog = false; - $timeout(function(){ - $("#search-field").focus(); - }); - - break; - default: - ui.currentMode = "default"; - ui.showContextMenu = false; - ui.showContextMenuDialog = false; - ui.showSearchResults = false; - ui.stickyNavigation = false; - ui.showTray = false; + $timeout(function() { + $("#search-field").focus(); + }); - if(ui.tablet){ - ui.showNavigation = false; - } + break; + default: + ui.currentMode = "default"; + ui.showContextMenu = false; + ui.showContextMenuDialog = false; + ui.showSearchResults = false; + ui.stickyNavigation = false; + ui.showTray = false; - break; + if (ui.tablet) { + ui.showNavigation = false; + } + + break; } } @@ -112,15 +111,15 @@ angular.module('umbraco.services') userDialog: undefined, ui: ui, - init: function(){ + init: function() { //TODO: detect tablet mode, subscribe to window resizing //for now we just hardcode it to non-tablet mode setTreeMode(); this.ui.currentSection = $routeParams.section; - $(window).bind("resize", function () { - setTreeMode(); + $(window).bind("resize", function() { + setTreeMode(); }); }, @@ -133,7 +132,7 @@ angular.module('umbraco.services') * 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) { + loadLegacyIFrame: function(source) { $location.path("/" + this.ui.currentSection + "/framed/" + encodeURIComponent(source)); }, @@ -148,16 +147,16 @@ angular.module('umbraco.services') * and load the dashboard related to the section * @param {string} sectionAlias The alias of the section */ - changeSection: function (sectionAlias, force) { + changeSection: function(sectionAlias, force) { setMode("default-opensection"); - - if(force && this.ui.currentSection === sectionAlias){ + + if (force && this.ui.currentSection === sectionAlias) { this.ui.currentSection = ""; } this.ui.currentSection = sectionAlias; this.showTree(sectionAlias); - + $location.path(sectionAlias); }, @@ -171,52 +170,52 @@ angular.module('umbraco.services') * only changes if the section is different from the current one * @param {string} sectionAlias The alias of the section the tree should load data from */ - showTree: function (sectionAlias, treeAlias, path) { + showTree: function(sectionAlias, treeAlias, path) { if (sectionAlias !== this.ui.currentSection) { this.ui.currentSection = sectionAlias; - if(treeAlias){ + if (treeAlias) { this.setActiveTreeType(treeAlias); } - if(path){ + if (path) { this.syncpath(path, true); } } setMode("tree"); }, - showTray: function () { + showTray: function() { ui.showTray = true; }, - hideTray: function () { + hideTray: function() { ui.showTray = false; }, //adding this to get clean global access to the main tree directive //there will only ever be one main tree event handler //we need to pass in the current scope for binding these actions - setupTreeEvents: function(treeEventHandler, scope){ + setupTreeEvents: function(treeEventHandler, scope) { this.ui.treeEventHandler = treeEventHandler; //this reacts to the options item in the tree - this.ui.treeEventHandler.bind("treeOptionsClick", function (ev, args) { + this.ui.treeEventHandler.bind("treeOptionsClick", function(ev, args) { ev.stopPropagation(); ev.preventDefault(); - + scope.currentNode = args.node; args.scope = scope; - if(args.event && args.event.altKey){ + if (args.event && args.event.altKey) { args.skipDefault = true; } service.showMenu(ev, args); }); - this.ui.treeEventHandler.bind("treeNodeAltSelect", function (ev, args) { + this.ui.treeEventHandler.bind("treeNodeAltSelect", function(ev, args) { ev.stopPropagation(); ev.preventDefault(); - + scope.currentNode = args.node; args.scope = scope; @@ -226,11 +225,11 @@ angular.module('umbraco.services') //this reacts to tree items themselves being clicked //the tree directive should not contain any handling, simply just bubble events - this.ui.treeEventHandler.bind("treeNodeSelect", function (ev, args) { + this.ui.treeEventHandler.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! @@ -253,14 +252,15 @@ angular.module('umbraco.services') $log.error("Error evaluating js callback from legacy tree node: " + ex); } } - else if(n.routePath){ + else if (n.routePath) { //add action to the history service historyService.add({ name: n.name, link: n.routePath, icon: n.icon }); //not legacy, lets just set the route value and clear the query string if there is one. - + ui.currentNode = n; $location.path(n.routePath).search(""); - } else if(args.element.section){ + } + else if (args.element.section) { $location.path(args.element.section).search(""); } @@ -269,11 +269,11 @@ angular.module('umbraco.services') }, /** * @ngdoc method - * @name umbraco.services.navigationService#syncTree + * @name umbraco.services.navigationService#syncPath * @methodOf umbraco.services.navigationService * * @description - * Syncs the tree with a given section alias and a given path + * Syncs the tree with a given path * The path format is: ["itemId","itemId"], and so on * so to sync to a specific document type node do: *
@@ -282,27 +282,27 @@ angular.module('umbraco.services')
* @param {array} path array of ascendant ids, ex: ["1023","1243"] (loads a specific document type into the settings tree)
* @param {bool} forceReload forces a reload of data from the server
*/
- syncPath: function (path, forceReload) {
- if(this.ui.treeEventHandler){
- this.ui.treeEventHandler.syncPath(path,forceReload);
+ syncPath: function(path, forceReload) {
+ if (this.ui.treeEventHandler) {
+ this.ui.treeEventHandler.syncPath(path, forceReload);
}
},
- reloadNode: function (node) {
- if(this.ui.treeEventHandler){
+ reloadNode: function(node) {
+ if (this.ui.treeEventHandler) {
this.ui.treeEventHandler.reloadNode(node);
}
},
- reloadSection: function (sectionAlias) {
- if(this.ui.treeEventHandler){
+ reloadSection: function(sectionAlias) {
+ if (this.ui.treeEventHandler) {
this.ui.treeEventHandler.clearCache({ section: sectionAlias });
this.ui.treeEventHandler.load(sectionAlias);
}
},
- setActiveTreeType: function (treeAlias) {
- if(this.ui.treeEventHandler){
+ setActiveTreeType: function(treeAlias) {
+ if (this.ui.treeEventHandler) {
this.ui.treeEventHandler.setActiveTreeType(treeAlias);
}
},
@@ -316,7 +316,7 @@ angular.module('umbraco.services')
* Sets a service variable as soon as the user hovers the navigation with the mouse
* used by the leaveTree method to delay hiding
*/
- enterTree: function (event) {
+ enterTree: function(event) {
service.active = true;
},
@@ -328,18 +328,18 @@ angular.module('umbraco.services')
* @description
* Hides navigation tree, with a short delay, is cancelled if the user moves the mouse over the tree again
*/
- leaveTree: function (event) {
+ 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){
+ if (!event) {
return;
}
- if(!service.touchDevice){
+ if (!service.touchDevice) {
service.active = false;
- $timeout(function(){
- if(!service.active){
+ $timeout(function() {
+ if (!service.active) {
service.hideTree();
}
}, 300);
@@ -354,7 +354,7 @@ angular.module('umbraco.services')
* @description
* Hides the tree by hiding the containing dom element
*/
- hideTree: function () {
+ hideTree: function() {
if (this.ui.tablet && !this.ui.stickyNavigation) {
//reset it to whatever is in the url
@@ -375,14 +375,14 @@ angular.module('umbraco.services')
*
* @param {Event} event the click event triggering the method, passed from the DOM element
*/
- showMenu: function (event, args) {
+ 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.
@@ -393,7 +393,7 @@ angular.module('umbraco.services')
});
if (found) {
-
+
self.ui.currentNode = args.node;
//ensure the current dialog is cleared before creating another!
if (self.ui.currentDialog) {
@@ -414,18 +414,18 @@ angular.module('umbraco.services')
}
//there is no default or we couldn't find one so just continue showing the menu
-
+
setMode("menu");
-
+
ui.actions = data.menuItems;
-
+
ui.currentNode = args.node;
ui.dialogTitle = args.node.name;
//we're not opening a dialog, return null.
deferred.resolve(null);
});
-
+
return deferred.promise;
},
@@ -437,7 +437,7 @@ angular.module('umbraco.services')
* @description
* Hides the menu by hiding the containing dom element
*/
- hideMenu: function () {
+ hideMenu: function() {
var selectedId = $routeParams.id;
this.ui.currentNode = undefined;
this.ui.actions = [];
@@ -454,7 +454,7 @@ angular.module('umbraco.services')
* Opens the user dialog, next to the sections navigation
* template is located in views/common/dialogs/user.html
*/
- showUserDialog: function () {
+ showUserDialog: function() {
service.userDialog = dialogService.open(
{
template: "views/common/dialogs/user.html",
@@ -474,7 +474,7 @@ angular.module('umbraco.services')
* Opens the user dialog, next to the sections navigation
* template is located in views/common/dialogs/user.html
*/
- showHelpDialog: function () {
+ showHelpDialog: function() {
service.helpDialog = dialogService.open(
{
template: "views/common/dialogs/help.html",
@@ -494,18 +494,18 @@ angular.module('umbraco.services')
* Hides the user dialog, next to the sections navigation
* template is located in views/common/dialogs/user.html
*/
- hideUserDialog: function () {
- if(service.userDialog){
+ hideUserDialog: function() {
+ if (service.userDialog) {
service.userDialog.close();
service.userDialog = undefined;
- }
+ }
},
- hideHelpDialog: function () {
- if(service.helpDialog){
+ hideHelpDialog: function() {
+ if (service.helpDialog) {
service.helpDialog.close();
service.helpDialog = undefined;
- }
+ }
},
/**
@@ -529,7 +529,7 @@ angular.module('umbraco.services')
* @param {Scope} args.scope current scope passed to the dialog
* @param {Object} args.action the clicked action containing `name` and `alias`
*/
- showDialog: function (args) {
+ showDialog: function(args) {
if (!args) {
throw "showDialog is missing the args parameter";
@@ -573,7 +573,7 @@ angular.module('umbraco.services')
iframe = false;
}
else {
-
+
//by convention we will look into the /views/{treetype}/{action}.html
// for example: /views/content/create.html
@@ -588,7 +588,7 @@ angular.module('umbraco.services')
}
if (packageTreeFolder) {
- templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
+ templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
"/" + packageTreeFolder +
"/umbraco/" + treeAlias + "/" + args.action.alias + ".html";
}
@@ -630,8 +630,8 @@ angular.module('umbraco.services')
* @description
* hides the currently open dialog
*/
- hideDialog: function () {
- this.showMenu(undefined, {skipDefault: true, node: this.ui.currentNode });
+ hideDialog: function() {
+ this.showMenu(undefined, { skipDefault: true, node: this.ui.currentNode });
},
/**
* @ngdoc method
@@ -641,7 +641,7 @@ angular.module('umbraco.services')
* @description
* shows the search pane
*/
- showSearch: function () {
+ showSearch: function() {
setMode("search");
},
/**
@@ -652,7 +652,7 @@ angular.module('umbraco.services')
* @description
* hides the search pane
*/
- hideSearch: function () {
+ hideSearch: function() {
setMode("default-hidesearch");
},
/**
@@ -663,7 +663,7 @@ angular.module('umbraco.services')
* @description
* hides any open navigation panes and resets the tree, actions and the currently selected node
*/
- hideNavigation: function () {
+ hideNavigation: function() {
this.ui.actions = [];
//this.ui.currentNode = undefined;
setMode("default");
@@ -671,4 +671,6 @@ angular.module('umbraco.services')
};
return service;
-});
+}
+
+angular.module('umbraco.services').factory('navigationService', navigationService);
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js
index 696170b88f..625dd3741a 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js
@@ -204,7 +204,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
/** Gets a child node by id */
getChildNode: function (treeNode, id) {
if (!treeNode.children) {
- throw "The current tree node has no assigned children, ensure it's children are loaded before calling this method";
+ return null;
}
var found = _.find(treeNode.children, function (child) {
return String(child.id) === String(id);
@@ -221,6 +221,10 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
}
//check each child of this node
+ if (!treeNode.children) {
+ return null;
+ }
+
for (var i = 0; i < treeNode.children.length; i++) {
if (treeNode.children[i].children && angular.isArray(treeNode.children[i].children) && treeNode.children[i].children.length > 0) {
//recurse
@@ -236,11 +240,15 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc
},
/** Gets the root node of the current tree type for a given tree node */
- getTreeRoot: function(treeNode) {
+ getTreeRoot: function (treeNode) {
+ if (!treeNode) {
+ throw "treeNode cannot be null";
+ }
+
//all root nodes have metadata key 'treeAlias'
var root = null;
var current = treeNode;
- while (root === null && current !== undefined) {
+ while (root === null && current) {
if (current.metaData && current.metaData["treeAlias"]) {
root = current;