Files
ERP/sophal/js/axelor.nav.js

610 lines
14 KiB
JavaScript

/*
* Axelor Business Solutions
*
* Copyright (C) 2005-2019 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* 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 <http://www.gnu.org/licenses/>.
*/
(function() {
"use strict";
var app = angular.module("axelor.app");
function useSingleTabOnly() {
return axelor.device.mobile
|| !!axelor.config['view.single.tab']
|| axelor.config['user.singleTab']
|| +(axelor.config["view.tabs.max"]) === 0
|| +(axelor.config["view.tabs.max"]) === 1;
}
app.factory('NavService', ['$location', 'MenuService', function($location, MenuService) {
var tabs = [];
var popups = [];
var selected = null;
var VIEW_TYPES = {
'list' : 'grid',
'edit' : 'form'
};
function findTab(key) {
return _.find(tabs, function(tab){
return tab.action == key;
});
}
function findTabTitle(tab) {
var first;
if (tab.title) {
return tab.title;
}
first = _.first(tab.views);
if (first) {
return first.title || first.name;
}
return tab.name || "Unknown";
}
function openView(view, options) {
if (view && (view.type || view.viewType) == 'html') {
var first = _.first(view.views) || view;
view.views = [{
name: first.name,
resource: first.resource,
title: first.title,
type: 'html'
}];
if ((view.params||{}).target === "_blank") {
var url = first.name || first.resource;
return setTimeout(function () {
window.open(url);
});
}
}
var closable = options && options.__tab_closable;
if (!closable && view.params && view.params.closable !== undefined) {
closable = view.params.closable;
}
view.closable = closable;
openTab(view, options);
}
function openTabByName(name, options) {
var tab = findTab(name);
if (tab) {
return openTab(tab, options);
}
return MenuService.action(name).success(function(result){
if (!result.data) {
return;
}
var view = result.data[0].view;
view.action = name;
return openView(view, options);
});
}
function openTabAsPopup(tab, options) {
popups.push(tab);
}
function openTab(tab, options) {
if (tab && tab.$popupParent) {
return openTabAsPopup(tab, options);
}
options = options || tab.options;
if (options && options.mode) {
tab.viewType = VIEW_TYPES[options.mode] || options.mode;
}
tab.options = options;
tab.title = tab.title || findTabTitle(tab);
if (tab.action && MenuService.updateTabStyle) {
MenuService.updateTabStyle(tab);
}
function __doSelect(found) {
var lastScope = (selected||{}).$viewScope || {};
if (lastScope.$locationChangeOff) {
lastScope.$locationChangeOff();
}
found.selected = true;
selected = found;
if (options && tab.$viewScope) {
var view = tab.$viewScope._views[tab.viewType],
promise = view ? view.deferred.promise : null;
if (promise) {
promise.then(function(viewScope) {
viewScope.setRouteOptions(options);
});
}
}
setTimeout(function(){
axelor.$adjustSize();
});
}
function __singleShow(found) {
tabs.length = 0;
tabs.push(found);
return __doSelect(found);
}
var found = findTab(tab.action);
if (useSingleTabOnly()) {
if (found) {
return __singleShow(found);
}
var last = _.last(tabs);
if (last) {
return closeTab(last, function () {
__singleShow(tab);
});
}
return __singleShow(tab);
}
if (!found) {
found = tab;
__closeUnusedTabs();
if (options && options.__tab_prepend) {
tabs.unshift(tab);
} else {
tabs.push(tab);
}
}
_.each(tabs, function(tab) {
tab.selected = false;
});
return __doSelect(found);
}
var MAX_TABS;
function __closeUnusedTabs() {
if (MAX_TABS === undefined) {
MAX_TABS = +(axelor.config["view.tabs.max"]) || -1;
}
if (MAX_TABS <= 0 || tabs.length < MAX_TABS) {
return;
}
var all = _.filter(tabs, function (tab) {
return !tab.selected && canCloseTab(tab);
});
var doClose = function doClose(tab) {
var index = _.indexOf(tabs, tab);
var vs = tab.$viewScope;
if (vs && vs.isDirty && vs.isDirty()) return;
if (vs && vs.$details && vs.$details.isDirty && vs.$details.isDirty()) return;
tabs.splice(index, 1);
};
for (var i = 0; i < all.length; i++) {
doClose(all[i]);
if (tabs.length === 0) selected = null;
if (tabs.length < MAX_TABS) break;
}
}
function __closeTab(tab, callback) {
var all = tab.$popupParent ? popups : tabs;
var index = _.indexOf(all, tab);
// remove tab
all.splice(index, 1);
if (tabs.length === 0) {
selected = null;
}
if (_.isFunction(callback)) {
callback();
}
if (tab.$popupParent) {
return;
}
if (tab.selected) {
if (index == tabs.length)
index -= 1;
_.each(all, function(tab){
tab.selected = false;
});
var select = all[index];
if (select) {
select.selected = true;
openTab(select);
} else {
$location.path('/');
}
}
}
function canCloseTab(tab) {
return tab.closable === undefined ? true : tab.closable;
}
function closeTab(tab, callback) {
var viewScope = tab.$viewScope;
if (viewScope && viewScope.confirmDirty) {
viewScope.confirmDirty(function(){
__closeTab(tab, callback);
});
} else {
__closeTab(tab, callback);
}
}
function closeTabs(selection) {
var all = _.flatten([selection], true);
function select(tab) {
if (!tab.selected) {
tab.selected = true;
openTab(tab);
}
}
function close(tab, ignore) {
var at = tabs.indexOf(tab);
if (at > -1) {
tabs.splice(at, 1);
}
closeTabs(_.difference(selection, [ignore, tab]));
if (tabs.length === 0) {
selected = null;
}
}
function doConfirm(tab, viewScope) {
return viewScope.confirmDirty(function(){
return close(tab);
}, function() {
close(null, tab);
viewScope.$applyAsync();
});
}
for (var i = 0; i < all.length; i++) {
var tab = all[i];
var viewScope = tab.$viewScope;
if (viewScope && viewScope.confirmDirty) {
select(tab);
return doConfirm(tab, viewScope);
}
return close(tab);
}
if (tabs.indexOf(selected) == -1) {
selected = null;
}
if (selected) {
return openTab(selected);
}
var first = _.first(tabs);
if (first && !first.selected) {
return openTab(first);
}
axelor.$adjustSize();
}
function closeTabOthers(current) {
var rest = _.filter(tabs, function(tab) {
return canCloseTab(tab) && tab !== current;
});
if (current && !current.selected) {
current.selected = true;
openTab(current);
}
return closeTabs(rest);
}
function closeTabAll() {
closeTabOthers();
}
function reloadTab(current) {
var viewScope = current.$viewScope;
if (viewScope) {
viewScope.$broadcast('on:tab-reload', current);
}
}
function getTabs() {
return tabs;
}
function getPopups() {
return popups;
}
function getSelected() {
return selected;
}
return {
openTabByName: openTabByName,
openTab: openTab,
openView: openView,
canCloseTab: canCloseTab,
reloadTab: reloadTab,
closeTab: closeTab,
closeTabOthers: closeTabOthers,
closeTabAll: closeTabAll,
getTabs: getTabs,
getPopups: getPopups,
getSelected: getSelected
};
}]);
NavCtrl.$inject = ['$scope', '$rootScope', '$location', 'NavService'];
function NavCtrl($scope, $rootScope, $location, NavService) {
$scope.singleTabOnly = useSingleTabOnly();
Object.defineProperty($scope, '$location', {
get: function() {
return $location;
}
});
Object.defineProperty($scope, 'navTabs', {
get: function() {
return NavService.getTabs();
}
});
Object.defineProperty($scope, 'navPopups', {
get: function() {
return NavService.getPopups();
}
});
Object.defineProperty($scope, 'selectedTab', {
get: function() {
return NavService.getSelected();
}
});
$scope.hasNabPopups = function () {
return $scope.navPopups && $scope.navPopups.length > 0;
};
$scope.menuClick = function(event, record) {
if (!record.action) {
return;
}
if (axelor.device.small) {
$("#offcanvas").removeClass("active");
}
$scope.openTabByName(record.action);
$scope.$applyAsync();
};
$scope.navClick = function(tab) {
$scope.openTab(tab);
$scope.$broadcast("on:nav-click", tab);
};
$scope.$on("on:update-route", update);
function update(event) {
var tab = $scope.selectedTab,
scope = event.targetScope;
if (!tab || !tab.action || scope !== tab.$viewScope || !scope.getRouteOptions) {
return;
}
if (tab.action.indexOf('$act') > -1) {
return;
}
var path = tab.action,
opts = scope.getRouteOptions(),
mode = opts.mode,
args = opts.args;
path = "/ds/" + path + "/" + mode;
args = _.filter(args, function(arg) {
return _.isNumber(args) || arg;
});
if (args.length) {
path += "/" + args.join("/");
}
if ($location.$$path !== path) {
$location.path(path);
$location.search(opts.query || "");
}
}
$scope.canCloseTab = function(tab) {
return NavService.canCloseTab(tab);
};
$scope.openTab = function(tab, options) {
return NavService.openTab(tab, options);
};
$scope.openTabByName = function(name, options) {
return NavService.openTabByName(name, options);
};
$scope.closeTab = function(tab, callback) {
var wasSelected = tab.selected;
if (NavService.canCloseTab(tab)) {
NavService.closeTab(tab, callback);
if ($scope.selectedTab && wasSelected) {
$scope.$broadcast("on:nav-click", $scope.selectedTab);
}
}
};
$scope.closeTabOthers = function(tab) {
var wasSelected = tab.selected;
NavService.closeTabOthers(tab);
if ($scope.selectedTab === tab && !wasSelected) {
$scope.$broadcast("on:nav-click", tab);
}
};
$scope.closeTabAll = function() {
return NavService.closeTabAll();
};
$scope.reloadTab = function(tab) {
return NavService.reloadTab(tab);
};
$scope.tabTitle = function(tab) {
var vs = tab.$viewScope || {};
if (vs.viewType === "form") {
return vs.viewTitle || tab.title;
}
return tab.title;
};
$scope.tabDirty = function(tab) {
var viewScope = tab.$viewScope;
if (viewScope && viewScope.isDirty) {
return viewScope.isDirty();
}
return false;
};
// expose common methods to $rootScope
$scope.$root.openTab = $scope.openTab;
$scope.$root.openTabByName = $scope.openTabByName;
$scope.$watch('selectedTab.viewType', function tabViewTypeWatch(viewType){
if (viewType) {
axelor.$adjustSize();
}
});
$scope.$watch('routePath', function routePathWatch(path) {
$scope.openHomeTab();
});
var confirm = _t('Current changes will be lost.');
function onbeforeunload(e) {
var tabs = $scope.navTabs || [];
for (var i = 0; i < tabs.length; i++) {
var vs = (tabs[i]||{}).$viewScope;
if (vs && vs.$$dirty) {
return confirm;
}
}
}
$(function () {
// menu toggle logic
var menuToggled = false;
var navigator = axelor.config["user.navigator"];
if (navigator !== 'hidden') {
$('#offcanvas-toggle').find('a').click(function (e) {
var active = ! $("#offcanvas").hasClass('inactive');
if (active && axelor.device.small) {
active = $("#offcanvas").hasClass('active');
}
$("#offcanvas").toggleClass("active", !active && axelor.device.small);
$("#offcanvas").toggleClass("inactive", active && !axelor.device.small);
if (!axelor.device.mobile) {
setTimeout(axelor.$adjustSize, 100);
}
});
}
$("#offcanvas,#offcanvas-toggle").toggleClass("hidden-menu", navigator === "hidden");
if (navigator === "collapse") {
$("#offcanvas").addClass("inactive");
}
$scope.ajaxStop(function () {
setTimeout(function () {
$("#offcanvas,#offcanvas-toggle").removeClass("hidden");
}, 100);
}, 100);
$(window).on('resize', _.debounce(function () {
$("#offcanvas").removeClass(axelor.device.small ? 'inactive' : 'active');
setTimeout(axelor.$adjustSize, 100);
}, 100));
// confirm dirty
$(window).on('beforeunload', onbeforeunload);
});
}
TabCtrl.$inject = ['$scope', '$location', '$routeParams'];
function TabCtrl($scope, $location, $routeParams) {
var homeAction = axelor.config["user.action"],
params = _.clone($routeParams),
search = _.clone($location.$$search);
var opts = {
mode: params.mode,
state: params.state,
search: search
};
if (homeAction === params.resource) {
_.extend(opts, {
__tab_prepend: true,
__tab_closable: false
});
}
if (params.resource) {
$scope.openTabByName(params.resource, opts);
}
}
app.controller("NavCtrl", NavCtrl);
app.controller("TabCtrl", TabCtrl);
})();