
options.src + '/components/**/*.js' options.src + '/components/**/*.css' gulp.watch(options.src + '/components/**/*.css', function(event) { if(isOnlyChange(event)) { browserSync.reload(event.path); } else { gulp.start('inject'); } }); gulp.watch(options.src + '/components/**/*.js', function(event) { if(isOnlyChange(event)) { gulp.start('scripts'); } else { gulp.start('inject'); } }); gulp.watch(options.src + '/components/**/*.html', function(event) { browserSync.reload(event.path); }); <div class="container" ng-mouseleave="closeMenu($event)"> <div class="navbar-header"> <button type="button" class="navbar-toggle" ng-click="collapseMenu($event)"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="link-kukuri" href="#" ui-sref="{{::sref}}" data-letters="{{::name}}">{{::name}}</a> </div> <div id="navbar" class="collapse navbar-collapse" aria-expanded="false" ng-class="navCollapsed"> <ul class="nav navbar-nav navbar-right"> <li ng-repeat="items in navbar" class="{{::menuClass(items.name, 'firstLevel')}} list-status"> <a href="#" ng-if="!items.name.pop" ui-sref="{{items.state}}" ng-mouseenter="closeOnMoveMenu()">{{items.name}}</a> <a href="#" ng-if="items.name.pop" class="dropdown-toggle dropdown-toggle-firstLevel" dropdown-toggle aria-expanded="false" ng-click="expandMenu($event)" ng-mouseenter="expandMenu($event)" ng-mouseleave="closeSubMenu($event)"> {{::items.name[0]}}<b class="caret"></b> </a> <ul ng-if="items.name.pop" class="dropdown-menu" ng-include="'submenu.template'"></ul> </li> </ul> </div> </div> <script type="text/ng-template" id="submenu.template"> <li ng-repeat="items in items.name" ng-if="$index !== 0" class="{{::menuClass(items.name)}} sub-menu"> <a href="#" class="sub-link" ng-if="!items.name.pop" ui-sref="{{::items.state}}" ng-mouseenter="closeOnMoveSubMenu($event)"> {{::items.name}}</a> <a href="#" ng-if="items.name.pop" class="dropdown-toggle" data-toggle="dropdown" ng-click="expandSubMenu($event)" ng-mouseenter="expandSubMenu($event)"> {{::items.name[0]}} </a> <ul ng-if="items.name.pop" class="dropdown-menu" ng-include="'submenu.template'"> </ul> </li> </script> 'use strict'; (function () { angular.module('navbar', ['ui.router']); })(); 'use strict'; (function () { angular.module('navbar') .directive('navbar', function ($document, $state, navbarList, navPermission) { return { restrict: 'A', scope: { name: '@', sref: '@' }, templateUrl: '/components/navbar.directive/navbar.template.html', link: function (scope, elem) { var openedMenu = null, openedSubMenu = null, username = navPermission.getUser($state.params); // DOM bootstrap elem.addClass('navbar navbar-inverse navbar-fixed-top'); elem.attr('role', 'navigation'); // scope if(username) { navPermission.acceptPermission(navbarList.list, username); } scope.navbar = navbarList.list; // / scope.collapseMenu = function ($event) { var navbar = elem.find('#navbar'), expanded = navbar.hasClass('in'); navbar.attr('aria-expanded', !expanded); scope.navCollapsed = (expanded) ? '' : 'in'; closeAllMenu(); stopBubleAndPropagation($event); }; // , scope.menuClass = function (item, level) { var status = false, activePage = getActivePage($state.current.name), currentPage = (item.pop) ? item[0] : item, classList = (level === 'firstLevel') ? 'dropdown dropdown-firstLevel ' : 'menu-item dropdown dropdown-submenu ', activeClass = (currentPage === activePage || isActive(item, activePage, status) ) ? 'menu-active' : ''; if(item.pop) { return classList + activeClass; } else { return activeClass; } }; // () function getActivePage(state, currentList) { var name; if(!currentList) { currentList = scope.navbar; } for(var i = (currentList[0].name) ? 0 : 1; i < currentList.length; i++) { if(currentList[i].state === state) { return currentList[i].name; } else if(currentList[i].name.pop) { name = getActivePage(state, currentList[i].name); } } return name; } // , function isActive (item, activePage, status) { if(item.pop) { for(var i = 1; i < item.length; i++) { if(item[i].name.pop) { status = isActive(item[i].name, activePage, status); } else if(item[i].name === activePage) { return true; } } } else if(item === activePage) { return true; } return status; } // ( , .. ) scope.expandMenu = function ($event) { var clickedElem = $($event.currentTarget), parentClicked = $($event.currentTarget.parentElement), expanded = clickedElem.attr('aria-expanded'), isOpened = parentClicked.hasClass('open'), attrExpanded = (expanded === 'false'), allOpenedMenu = parentClicked.parent().find('.open'), smallWindow = window.innerWidth < 768, eventMouseEnter = $event.type === 'mouseenter', subMenuAll = elem.find('.dropdown-submenu'); if(!smallWindow || !eventMouseEnter) { allOpenedMenu.removeClass('open'); clickedElem.attr('aria-expanded', attrExpanded); if(isOpened && !eventMouseEnter) { parentClicked.removeClass('open'); } else { parentClicked.addClass('open'); openedMenu = clickedElem; //** } } subMenuAll.removeClass('dropdown-submenu-small dropdown-submenu-big'); if(smallWindow) { subMenuAll.addClass('dropdown-submenu-small'); } else { subMenuAll.addClass('dropdown-submenu-big'); } stopBubleAndPropagation($event); }; // scope.closeOnMoveMenu = function () { var smallWindow = window.innerWidth < 768; if(openedMenu && !smallWindow) { var clickedLink = openedMenu, clickedElement = clickedLink.parent(); clickedElement.removeClass('open'); clickedLink.attr('aria-expanded', false); openedMenu = null; } }; // ( 92 ) scope.expandSubMenu = function ($event) { var elemClicked = $($event.currentTarget.parentElement), smallWindow = window.innerWidth < 768, eMouseEnter = $event.type === 'mouseenter', sameElement = elemClicked.hasClass('open'); if(!smallWindow || !eMouseEnter) { // if(!sameElement && !eMouseEnter || !eMouseEnter || !sameElement) { elemClicked.parent().find('.open').removeClass('open'); } if(!sameElement) { elemClicked.addClass('open'); openedSubMenu = elemClicked; } } stopBubleAndPropagation($event); }; // ( :)) scope.closeOnMoveSubMenu = function ($event) { var smallWindow = window.innerWidth < 768; if(openedSubMenu && !smallWindow) { var clickedElement = openedSubMenu, savedList = clickedElement.parent(), currentList = $($event.target).parent().parent(); if(savedList[0] === currentList[0]) { clickedElement.removeClass('open'); openedSubMenu = null; } } }; scope.closeMenu = closeMenu; // var $body = $document.find('html'); elem.bind('$destroy', function() { $body.unbind(); // }); // - $body.bind('click', closeMenu); function closeMenu ($event) { var elemClicked = $event.relatedTarget || $event.target; if(isClickOutNavbar(elemClicked)) { closeAllMenu(); } } // , , function isClickOutNavbar(elem) { if($(elem).hasClass('dropdown-firstLevel')) { return false; } if(elem.parentElement !== null) { return isClickOutNavbar(elem.parentElement); } else { return true; } } // function closeAllMenu() { elem.find('.open').removeClass('open'); elem.find('[aria-expanded=true]').attr('aria-expanded', false); } // function stopBubleAndPropagation($event) { $event.stopPropagation(); $event.preventDefault(); } } }; }); })(); 'use strict'; (function () { angular.module('navbar') .provider('navbarList', function () { var list = []; // this.add = function (obj) { // if(obj.location) { if(obj.location.place.length !== obj.location.priority.length || !obj.location.place.pop || !obj.location.priority.pop) { console.log('Warning! Bad location params for menu "' + obj.name + '". Skip item'); return; } } // if(!obj.location) { var name = obj.name; for(var i = 0; i < list.length; i++) { // var currentName = (list[i].name.pop) ? list[i].name[0] : list[i].name; if(currentName === name) { console.log('Warning! Duplicate menu "' + name + '". Skip item'); return; } } list.push(obj); list.sort(sortByPriority); return; } // , var place = obj.location.place.shift(), priority = obj.location.priority.shift(); for(i = 0; i < list.length; i++) { // , i JS var currentSubName = (list[i].name.pop) ? list[i].name[0] : null; if(place === currentSubName) { list[i].name = changeExistPart(obj, list[i].name); if(priority !== list[i].priority) { console.log('Warning! Priority of menu "' + list[i].name + '" has been changed from "' + list[i].priority + '" to "' + priority + '"'); list[i].priority = priority; list.sort(sortByPriority); } return; } currentName = list[i].name; if(place === currentName) { console.log('Warning! Duplicate submenu "' + place + '". Skip item'); return; } } // , list.push( { name: [place, makeOriginalPart(obj)], priority: priority } ); list.sort(sortByPriority); }; // function changeExistPart(obj, list) { var place = obj.location.place.shift(), priority = obj.location.priority.shift(), // searchName = (place) ? place : obj.name; for(var i = 1; i < list.length; i++) { var currentName = (list[i].name.pop) ? list[i].name[0] : list[i].name; if(searchName === currentName) { if(!list[i].name.pop || (!place && list[i].name.pop) ) { console.log('Warning! Duplicate menu "' + searchName + '". Skip item'); return list; } else { list[i].name = changeExistPart(obj, list[i].name); if(priority !== list[i].priority) { console.log('Warning! Priority of menu "' + list[i].name + '" has been changed from "' + list[i].priority + '" to "' + priority + '"'); list[i].priority = priority; list.sort(sortByPriority); } return list; } } } if(!place) { delete obj.location; list.push(obj); } else { list.push({ name: [place, makeOriginalPart(obj)], priority: priority }); } list.sort(sortByPriority); return list; } // , function makeOriginalPart (obj) { var place = obj.location.place.shift(), priority = obj.location.priority.shift(); if(place) { var menu = { priority: priority, name: [place, makeOriginalPart(obj)] }; } else { delete obj.location; menu = obj; } return menu; } // function sortByPriority(a, b) { return a.priority - b.priority; } // angularJS this.$get = function () { return { list: list, add: this.add }; }; }); })(); 'use strict'; (function () { angular.module('navbar') .provider('navbarList', function () { var list = []; this.add = addMenu; function addMenu(obj, nestedMenu, currentList) { if(currentList) { list = currentList; } else if(list.length < 1) { list.push(makeOriginalPart(obj)); return; } if(!obj.location || !obj.location.place) { // . place==priority isDuplicate(obj.name, list); list.push(obj); list.sort(sortByPriority); return; } else if(obj.location.place.length > 0){ var searchName = obj.location.place.shift(), priority = (obj.location.priority) ? obj.location.priority.shift() : null; for(var i = (nestedMenu) ? 1 : 0; i < list.length; i++) { var currentName = (list[i].name.pop) ? list[i].name[0] :list[i].name; if(currentName === searchName) { if(list[i].name.pop) { // if(!nestedMenu) { nestedMenu = [list]; } var sublistName = list[i].name.shift(); list[i].name.sort(sortByPriority); list[i].name.unshift(sublistName); list[i].name.priority = priority; // nestedMenu.push(list[i].name); addMenu(obj, nestedMenu, list[i].name); return; } else { console.log('Warning! Duplicate menu', currentName); } } } if(nestedMenu) { var last = nestedMenu.length - 1; nestedMenu[last].push({ name: [searchName, makeOriginalPart(obj, null, nestedMenu[last]) ], priority: priority }); } } else { last = nestedMenu.length - 1; nestedMenu[last].push(makeOriginalPart(obj, null, nestedMenu[last])); } if(nestedMenu) { // changeExistPart nestedMenu[nestedMenu.length - 1].sort(sortByPriority); list = changeExistPart(nestedMenu); } else { if(priority) { // . place==priority obj.location.priority.unshift(priority); } obj.location.place.unshift(searchName); list.push(makeOriginalPart(obj, null, list)); list.sort(sortByPriority); } } function changeExistPart(nestedMenu) { if(nestedMenu.length > 1) { var subList = nestedMenu.pop(), priority = subList.priority, searchName = subList[0], last = nestedMenu.length - 1; for(var i = 1; i < nestedMenu[last].length; i++) { var currentName = (nestedMenu[last][i].name.pop) ? nestedMenu[last][i].name[0] : ''; if(searchName === currentName){ nestedMenu[last][i].name = subList; nestedMenu[last][i].priority = priority; return changeExistPart(nestedMenu); } } return changeExistPart(nestedMenu); // . } else { return nestedMenu[0]; } } function makeOriginalPart(obj, menu, currentList){ if(!menu) { isDuplicate(obj.name, currentList); menu = { name: obj.name, priority: obj.priority, state: obj.state, permissions: obj.permissions }; } if(obj.location.place.length > 0) { var currentLocation = obj.location.place.pop(), priority = (obj.location.priority) ? obj.location.priority.pop() : null, currentMenu = { priority: priority, name: [currentLocation, menu] }; return makeOriginalPart(obj, currentMenu); } else { return menu; } } function isDuplicate(name, list) { if(!list || list.length < 1) { return; } for(var i = (list[0].name) ? 0 : 1; i < list.length; i++) { var currentName = (list[i].name.pop) ? list[i].name[0] : list[i].name; if(currentName === name) { console.log('Warning! Duplicate menu', currentName); } } } function sortByPriority(a, b) { return a.priority - b.priority; } this.$get = function () { return { list: list, add: this.add }; }; }); })(); 'use strict'; (function () { angular.module('navbar') .factory('navPermission', function (Permission, $q) { // function getUser(params) { var users = Permission.roleValidations, names = Object.keys(users), promisesArr = []; for(var i = 0; i < names.length; i++) { var current = names[i], validUser = $q.when( users[current](params) ); promisesArr.push(validUser); } return $q.all(promisesArr).then(function (users) { for(var i = 0; i < users.length; i++) { if(users[i]) { return names[i]; } } return null; }); } // , , - function acceptPermission (list, username) { if(!username.then) { return changeList(list, username); } else { return username.then(function (username) { return changeList(list, username); }); } } // , function changeList(list, username) { for(var i = (list[0].name) ? 0 : 1; i < list.length; i++) { if(list[i].permissions) { if(list[i].permissions.except) { var except = list[i].permissions.except; for(var j = 0; j < except.length; j++) { if(except[j] === username) { list.splice(i--, 1); } } } else if(list[i].permissions.only) { var only = list[i].permissions.only, accessDenided = true; for(j = 0; j < only.length; j++) { if(only[j] === username) { accessDenided = false; } } if(accessDenided) { list.splice(i--, 1); } } } else if(list[i].name.pop) { list[i].name = changeList( list[i].name, username); if(list[i].name.length === 1 ) { list.splice(i--, 1); } } } return list; } // return { getUser: getUser, acceptPermission: acceptPermission }; }); })(); .config(function ($stateProvider, navbarListProvider) { // $stateProvider .state('persianCat', { url: '/ ', templateUrl: 'app/cats/persianCat.html', controller: 'persianCatCtrl', permissions: { except: ['anonymous', 'banned'], redirectTo: 'login' } }); // navbarListProvider.add({ state: 'persianCat', name: ' ', permissions: { except: ['anonymous', 'banned'] }, priority: 20, location: { place: [' ', '', ''], priority: [10, 10, 10] } }); }); 'use strict'; (function() { angular.module('navbar') .config(function ($stateProvider, navbarListProvider) { // state $stateProvider.decorator('state', function (obj) { var menu = obj.menu, permissions = (obj.data) ? obj.data.permissions : null; // - if(!menu) { return; } menu.state = obj.name; // if(permissions) { menu.permissions = {}; if(permissions.except) { menu.permissions.except = permissions.except; } else if(permissions.only) { menu.permissions.only = permissions.only; } else { delete menu.permissions; } } // menu navbarListProvider.add(menu); }); }); })(); .config(function ($stateProvider) { $stateProvider .state('persianCat', { url: '/ ', templateUrl: 'app/cats/persianCat.html', controller: 'persianCatCtrl', permissions: { except: ['anonymous', 'banned'], redirectTo: 'login' }, menu: { name: ' ', priority: 20, location: { place: [' ', '', ''], priority: [10, 10, 10] } } }); }); Source: https://habr.com/ru/post/259347/
All Articles