Re-formats the code structure of nav service and umbtree to be easier to read, adds better error checking to treeservice

This commit is contained in:
Shannon
2013-11-08 09:07:28 +11:00
parent 367cdde35e
commit ef40c76363
3 changed files with 451 additions and 435 deletions

View File

@@ -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 = '<ul class="umb-tree ' + hideoptions + '">' +
'<li class="root">';
compile: function(element, attrs) {
//config
var hideheader = (attrs.showheader === 'false') ? true : false;
var hideoptions = (attrs.showoptions === 'false') ? "hide-options" : "";
if(!hideheader){
template +='<div>' +
'<h5><a href="#/{{section}}" ng-click="select(this, tree.root, $event)" on-right-click="altSelect(this, tree.root, $event)" class="root-link">{{tree.name}}</a></h5>' +
'<a href class="umb-options" ng-hide="tree.root.isContainer || !tree.root.menuUrl" ng-click="options(this, tree.root, $event)" ng-swipe-right="options(this, tree.root, $event)"><i></i><i></i><i></i></a>' +
'</div>';
}
template += '<ul>' +
'<umb-tree-item ng-repeat="child in tree.root.children" eventhandler="eventhandler" path="{{path}}" activetree="{{activetree}}" node="child" current-node="currentNode" tree="child" section="{{section}}" ng-animate="animation()"></umb-tree-item>' +
'</ul>' +
var template = '<ul class="umb-tree ' + hideoptions + '">' +
'<li class="root">';
if (!hideheader) {
template += '<div>' +
'<h5><a href="#/{{section}}" ng-click="select(this, tree.root, $event)" on-right-click="altSelect(this, tree.root, $event)" class="root-link">{{tree.name}}</a></h5>' +
'<a href class="umb-options" ng-hide="tree.root.isContainer || !tree.root.menuUrl" ng-click="options(this, tree.root, $event)" ng-swipe-right="options(this, tree.root, $event)"><i></i><i></i><i></i></a>' +
'</div>';
}
template += '<ul>' +
'<umb-tree-item ng-repeat="child in tree.root.children" eventhandler="eventhandler" path="{{path}}" activetree="{{activetree}}" node="child" current-node="currentNode" tree="child" section="{{section}}" ng-animate="animation()"></umb-tree-item>' +
'</ul>' +
'</li>' +
'</ul>';
'</ul>';
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();
}
});
};
}
};
});
angular.module("umbraco.directives").directive('umbTree', umbTreeDirective);

View File

@@ -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:
* <pre>
@@ -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);

View File

@@ -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;