diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js b/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js index 150bbab7cc..9be27d585a 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js @@ -87,10 +87,10 @@ Umbraco.Sys.registerNamespace("Umbraco.Application"); //mimic the API of the legacy tree var tree = { setActiveTreeType : function(treeType){ - navService.syncTree(null, treeType, null); + navService.setActiveTreeType(treeType); }, syncTree : function(path,forceReload){ - navService.syncPath(path); + navService.syncPath(path, forceReload); }, getActionNode: function () { //need to replicate the legacy tree node diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js index 5c8c856796..13203372b5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbsections.directive.js @@ -13,6 +13,14 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se scope.maxSections = 7; scope.overflowingSections = 0; scope.sections = []; + scope.nav = navigationService; + + +/* + scope.$watch("currentSection", function (newVal, oldVal) { + scope.currentSection = newVal; + }); +*/ function loadSections(){ sectionResource.getSections() 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 111b343019..cb98f33bf4 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 @@ -14,8 +14,6 @@ angular.module("umbraco.directives") scope: { section: '@', treealias: '@', - path: '@', - activetree: '@', showoptions: '@', showheader: '@', cachekey: '@', @@ -26,7 +24,7 @@ angular.module("umbraco.directives") //config var hideheader = (attrs.showheader === 'false') ? true : false; var hideoptions = (attrs.showoptions === 'false') ? "hide-options" : ""; - + var template = '
'; @@ -53,6 +51,9 @@ angular.module("umbraco.directives") // 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 enable/disable delete animations var enableDeleteAnimations = false; @@ -64,10 +65,36 @@ angular.module("umbraco.directives") } } + function setupExternalEvents() { + if (scope.eventhandler) { + + scope.eventhandler.clearCache = function(treeAlias){ + treeService.clearCache(treeAlias); + }; + + scope.eventhandler.syncPath = function(path, forceReload){ + if(!angular.isArray(path)){ + path = path.split(','); + } + + path = _.filter(path, function(item){ return (item !== "init" && item !== "-1"); }); + + //if we have a active tree, we sync based on that. + var root = activeTree ? activeTree : scope.tree.root; + + //tell the tree to sync the children below the root + syncTree(root, path, forceReload); + }; + + scope.eventhandler.setActiveTreeType = function(treeAlias){ + activeTree = _.find(scope.tree.root.children, function(node){ return node.metaData.treeAlias === treeAlias; }); + }; + } + } + /** 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 @@ -94,6 +121,31 @@ angular.module("umbraco.directives") } } + function syncTree(node, array, forceReload) { + if(!node || !array || array.length === 0){ + return; + } + + scope.loadChildren(node, forceReload) + .then(function(children){ + var next = _.where(children, {id: array[0]}); + if(next && next.length > 0){ + + if(array.length > 0){ + array.splice(0,1); + }else{ + + } + + if(array.length === 0){ + scope.currentNode = next[0]; + } + + syncTree(next[0], array, forceReload); + } + }); + } + /** 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. @@ -107,6 +159,35 @@ angular.module("umbraco.directives") } }; + /* 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 }); + + if (node.hasChildren && (forceReload || !node.children || (angular.isArray(node.children) && 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 @@ -115,7 +196,7 @@ angular.module("umbraco.directives") 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 @@ -132,7 +213,7 @@ angular.module("umbraco.directives") //watch for section changes scope.$watch("section", function (newVal, oldVal) { - + if(!scope.tree){ loadTree(); } @@ -146,28 +227,10 @@ angular.module("umbraco.directives") loadTree(); //store the new section to be loaded as the last section + //clear any active trees to reset lookups lastSection = newVal; - } - - }); - - //watch for path changes - scope.$watch("path", function (newVal, oldVal) { - - //resetting the path destroys the tree - if(newVal && newVal !== oldVal){ - scope.tree = null; - } - - }); - - //watch for active tree changes - scope.$watch("activetree", function (newVal, oldVal) { - - if (newVal && newVal !== oldVal) { - scope.tree = null; - //only reload the tree data and Dom if the newval is different from the old one - } + activeTree = undefined; + } }); //When the user logs in @@ -176,6 +239,8 @@ angular.module("umbraco.directives") if (data.lastUserId !== data.user.id) { treeService.clearCache(); scope.tree = null; + + setupExternalEvents(); loadTree(); } }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js index dc63e7e7be..32d0ddcce7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js @@ -27,15 +27,14 @@ angular.module("umbraco.directives") section: '@', cachekey: '@', eventhandler: '=', - path: '@', + currentNode:'=', node:'=', - activetree:'@', tree:'=' }, - template: '
- * navigationService.syncTree("content", "nodeTypes", [-1,1023,3453]);
+ * navigationService.syncPath(["-1","123d"], true);
*
- * @param {string} sectionAlias The alias of the section the tree should load data from
- * @param {string} treeAlias The alias of tree to auto-expand
- * @param {array} path array of ascendant ids, ex: [,1023,1243] (loads a specific document type into the settings tree)
+ * @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
*/
- syncTree: function (sectionAlias, treeAlias, path) {
- //TODO: investicate if we need to halt watch triggers
- //and instead pause them and then manually tell the tree to digest path changes
- //as this might be a bit heavy loading
- if(sectionAlias){
- this.ui.currentSection = sectionAlias;
- }
- if(treeAlias){
- this.ui.currentTree = treeAlias;
- }
- if(path){
- this.ui.currentPath = path;
- }
+ syncPath: function (path, forceReload) {
+ if(this.ui.treeEventHandler){
+ this.ui.treeEventHandler.syncPath(path,forceReload);
+ }
},
- /* this is to support the legacy ways to sync the tree, so you can do it in 2 steps
- For all new operations, its recommend to just use syncTree()
- */
- syncPath: function (path) {
- //TODO: investicate if we need to halt watch triggers
- //and instead pause them and then manually tell the tree to digest path changes
- //as this might be a bit heavy loading
- if(!angular.isArray(path)){
- path = path.split(",");
- }
-
- this.ui.currentPath = path;
+ setActiveTreeType: function (treeAlias) {
+ if(this.ui.treeEventHandler){
+ this.ui.treeEventHandler.setActiveTreeType(treeAlias);
+ }
},
/**
@@ -249,10 +335,13 @@ angular.module('umbraco.services')
* Hides the tree by hiding the containing dom element
*/
hideTree: function () {
- if (!this.ui.stickyNavigation) {
- this.ui.currentSection = "";
+
+ if (this.ui.tablet && !this.ui.stickyNavigation) {
+ //reset it to whatever is in the url
+ this.ui.currentSection = $routeParams.section;
setMode("default-hidesectiontree");
}
+
},
/**
@@ -552,7 +641,7 @@ angular.module('umbraco.services')
*/
hideNavigation: function () {
this.ui.actions = [];
- this.ui.currentNode = undefined;
+ //this.ui.currentNode = undefined;
setMode("default");
}
};
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js
index b299ece618..d9e7a8c97b 100644
--- a/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js
@@ -16,113 +16,49 @@ function NavigationController($scope,$rootScope, $location, $log, $routeParams,
// 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;
+ //wire up the screensize and tree mode detection
+ $scope.nav.init();
+
+ //the tree event handler i used to subscribe to the main tree click events
+ $scope.treeEventHandler = $({});
+ $scope.nav.setupTreeEvents($scope.treeEventHandler, $scope);
+
+ //keep track of
$scope.$watch(function () {
//watch the route parameters section
return $routeParams.section;
}, function(newVal, oldVal) {
- $scope.currentSection = newVal;
+ $scope.nav.ui.currentSection = newVal;
});
+
//trigger search with a hotkey:
keyboardService.bind("ctrl+shift+s", function(){
$scope.nav.showSearch();
});
- //the tree event handler i used to subscribe to the main tree click events
- $scope.treeEventHandler = $({});
$scope.selectedId = navigationService.currentId;
-
//This reacts to clicks passed to the body element which emits a global call to close all dialogs
$rootScope.$on("closeDialogs", function (event) {
if (navigationService.ui.stickyNavigation) {
- navigationService.hideNavigation();
+ navigationService.hideNavigation();
angularHelper.safeApply($scope);
}
});
//this reacts to the options item in the tree
- $scope.treeEventHandler.bind("treeOptionsClick", function (ev, args) {
- ev.stopPropagation();
- ev.preventDefault();
-
- $scope.currentNode = args.node;
- args.scope = $scope;
-
- if(args.event && args.event.altKey){
- args.skipDefault = true;
- }
-
- navigationService.showMenu(ev, args);
- });
-
- $scope.treeEventHandler.bind("treeNodeAltSelect", function (ev, args) {
- ev.stopPropagation();
- ev.preventDefault();
-
- $scope.currentNode = args.node;
- args.scope = $scope;
-
- args.skipDefault = true;
- navigationService.showMenu(ev, args);
- });
-
-
- //this reacts to the options item in the tree
- $scope.searchShowMenu = function (ev, args) {
-
+ //todo, migrate to nav service
+ $scope.searchShowMenu = function (ev, args) {
$scope.currentNode = args.node;
args.scope = $scope;
//always skip default
args.skipDefault = true;
-
navigationService.showMenu(ev, args);
};
- //this reacts to tree items themselves being clicked
- //the tree directive should not contain any handling, simply just bubble events
- $scope.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!
- 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 });
- //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("");
- }
-
- navigationService.hideNavigation();
- });
-
-
/** Opens a dialog but passes in this scope instance to be used for the dialog */
$scope.openDialog = function (currentNode, action, currentSection) {
navigationService.showDialog({
diff --git a/src/Umbraco.Web.UI.Client/src/views/directives/umb-navigation.html b/src/Umbraco.Web.UI.Client/src/views/directives/umb-navigation.html
index 0c8287bed0..3a337f1922 100644
--- a/src/Umbraco.Web.UI.Client/src/views/directives/umb-navigation.html
+++ b/src/Umbraco.Web.UI.Client/src/views/directives/umb-navigation.html
@@ -1,7 +1,8 @@