### # Copyright (C) 2014 Andrey Antukh # Copyright (C) 2014 Jesús Espino Garcia # Copyright (C) 2014 David Barragán Merino # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # # File: modules/nav.coffee ### taiga = @.taiga groupBy = @.taiga.groupBy bindOnce = @.taiga.bindOnce timeout = @.taiga.timeout module = angular.module("taigaNavMenu", []) ############################################################################# ## Projects Navigation ############################################################################# class ProjectsNavigationController extends taiga.Controller @.$inject = ["$scope", "$rootScope", "$tgResources", "$tgNavUrls", "$projectUrl"] constructor: (@scope, @rootscope, @rs, @navurls, @projectUrl) -> promise = @.loadInitialData() promise.then null, -> console.log "FAIL" # TODO # Listen when someone wants to reload all the projects @scope.$on "projects:reload", => @.loadInitialData() # Listen when someone has reloaded a project @scope.$on "project:loaded", (ctx, project) => @.loadInitialData() loadInitialData: -> return @rs.projects.listByMember(@rootscope.user?.id).then (projects) => for project in projects project.url = @projectUrl.get(project) @scope.projects = projects @scope.filteredProjects = projects @scope.filterText = "" return projects newProject: -> @scope.$apply () => @rootscope.$broadcast("projects:create") filterProjects: (text) -> @scope.filteredProjects = _.filter @scope.projects, (project) -> project.name.toLowerCase().indexOf(text) > -1 @scope.filterText = text @rootscope.$broadcast("projects:filtered") module.controller("ProjectsNavigationController", ProjectsNavigationController) ProjectsNavigationDirective = ($rootscope, animationFrame, $timeout, tgLoader, $location, $compile, $template) -> baseTemplate = $template.get("project/project-navigation-base.html", true) projectsTemplate = $template.get("project/project-navigation-list.html", true) overlay = $(".projects-nav-overlay") loadingStart = 0 hideMenu = () -> if overlay.is(':visible') difftime = new Date().getTime() - loadingStart timeoutValue = 0 if (difftime < 1000) timeoutValue = 1000 - timeoutValue timeout timeoutValue, -> overlay.one 'transitionend', () -> $(document.body) .removeClass("loading-project open-projects-nav closed-projects-nav") .css("overflow-x", "visible") overlay.hide() $(document.body).addClass("closed-projects-nav") tgLoader.disablePreventLoading() link = ($scope, $el, $attrs, $ctrls) -> $ctrl = $ctrls[0] $rootscope.$on("project:loaded", hideMenu) renderProjects = (projects) -> html = projectsTemplate({projects: projects}) $el.find(".projects-list").html(html) $scope.$emit("regenerate:project-pagination") render = (projects) -> $el.html($compile(baseTemplate())($scope)) renderProjects(projects) overlay.on 'click', () -> hideMenu() $(document).on 'keydown', (e) => code = if e.keyCode then e.keyCode else e.which if code == 27 hideMenu() $scope.$on "nav:projects-list:open", -> if !$(document.body).hasClass("open-projects-nav") animationFrame.add () => overlay.show() animationFrame.add( () => $(document.body).css("overflow-x", "hidden") () => $(document.body).toggleClass("open-projects-nav") ) $el.on "click", ".projects-list > li > a", (event) -> # HACK: to solve a problem with the loader when the next url # is equal to the current one target = angular.element(event.currentTarget) nextUrl = target.prop("href") currentUrl = $location.absUrl() if nextUrl == currentUrl hideMenu() return # END HACK $(document.body).addClass('loading-project') tgLoader.preventLoading() loadingStart = new Date().getTime() $el.on "click", ".create-project-button", (event) -> event.preventDefault() $ctrl.newProject() $el.on "keyup", ".search-project", (event) -> target = angular.element(event.currentTarget) $ctrl.filterProjects(target.val()) $scope.$on "projects:filtered", -> renderProjects($scope.filteredProjects) $scope.$watch "projects", (projects) -> render(projects) if projects? return { require: ["tgProjectsNav"] controller: ProjectsNavigationController link: link } module.directive("tgProjectsNav", ["$rootScope", "animationFrame", "$timeout", "tgLoader", "$tgLocation", "$compile", "$tgTemplate", ProjectsNavigationDirective]) ############################################################################# ## Project ############################################################################# ProjectMenuDirective = ($log, $compile, $auth, $rootscope, $tgAuth, $location, $navUrls, $config, $template) -> menuEntriesTemplate = $template.get("project/project-menu.html", true) mainTemplate = _.template(""" """) # If the last page was kanban or backlog and # the new one is the task detail or the us details # this method preserve the last section name. getSectionName = ($el, sectionName, project) -> oldSectionName = $el.find("a.active").parent().attr("id")?.replace("nav-", "") if sectionName == "backlog-kanban" if oldSectionName in ["backlog", "kanban"] sectionName = oldSectionName else if project.is_backlog_activated && !project.is_kanban_activated sectionName = "backlog" else if !project.is_backlog_activated && project.is_kanban_activated sectionName = "kanban" return sectionName renderMainMenu = ($el) -> html = mainTemplate({}) $el.html(html) # WARNING: this code has traces of slighty hacky parts # This rerenders and compiles the navigation when ng-view # content loaded signal is raised using inner scope. renderMenuEntries = ($el, targetScope, project={}) -> container = $el.find(".menu-container") sectionName = getSectionName($el, targetScope.section, project) ctx = { user: $auth.getUser(), project: project, feedbackEnabled: $config.get("feedbackEnabled") } dom = $compile(menuEntriesTemplate(ctx))(targetScope) dom.find("a.active").removeClass("active") dom.find("#nav-#{sectionName} > a").addClass("active") container.replaceWith(dom) videoConferenceUrl = (project) -> if project.videoconferences == "appear-in" baseUrl = "https://appear.in/" else if project.videoconferences == "talky" baseUrl = "https://talky.io/" else return "" if project.videoconferences_salt url = "#{project.slug}-#{project.videoconferences_salt}" else url = "#{project.slug}" return baseUrl + url link = ($scope, $el, $attrs, $ctrl) -> renderMainMenu($el) project = null $el.on "click", ".logo", (event) -> event.preventDefault() target = angular.element(event.currentTarget) $rootscope.$broadcast("nav:projects-list:open") $el.on "click", ".user-settings .avatar", (event) -> event.preventDefault() $el.find(".user-settings .popover").popover().open() $el.on "click", ".logout", (event) -> event.preventDefault() $auth.logout() $scope.$apply -> $location.path($navUrls.resolve("login")) $el.on "click", "#nav-search > a", (event) -> event.preventDefault() $rootscope.$broadcast("search-box:show", project) $el.on "click", ".feedback", (event) -> event.preventDefault() $rootscope.$broadcast("feedback:show") $scope.$on "projects:loaded", (listener) -> $el.addClass("hidden") listener.stopPropagation() $scope.$on "project:loaded", (ctx, newProject) -> project = newProject if $el.hasClass("hidden") $el.removeClass("hidden") project.videoconferenceUrl = videoConferenceUrl(project) renderMenuEntries($el, ctx.targetScope, project) return {link: link} module.directive("tgProjectMenu", ["$log", "$compile", "$tgAuth", "$rootScope", "$tgAuth", "$tgLocation", "$tgNavUrls", "$tgConfig", "$tgTemplate", ProjectMenuDirective])