Files
ERP/sophal/js/view/view.popup.js

512 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 ui = angular.module("axelor.ui");
EditorCtrl.$inject = ['$scope', '$element', 'DataSource', 'ViewService', '$q'];
function EditorCtrl($scope, $element, DataSource, ViewService, $q) {
var parent = $scope.$parent;
$scope._viewParams = parent._viewParams;
$scope.editorCanSave = parent.editorCanSave;
$scope.editorCanReload = parent.editorCanReload;
ui.ViewCtrl.call(this, $scope, DataSource, ViewService);
ui.FormViewCtrl.call(this, $scope, $element);
var closeCallback = null;
var originalEdit = $scope.edit;
var originalShow = $scope.show;
var recordVersion = -1;
var canClose = false;
var isClosed = true;
$scope.show = function(record, callback) {
originalShow();
if (_.isFunction(record)) {
callback = record;
record = null;
}
closeCallback = callback;
isClosed = false;
recordVersion = record ? record.version : -1;
if (recordVersion === undefined && record) {
recordVersion = record.$version;
}
this.edit(record);
};
function doEdit(record, fireOnLoad) {
if (record && record.id > 0 && (!_.isNumber(record.version) || !record.$fetched)) {
$scope.doRead(record.id).success(function(rec) {
if (record.$dirty) {
rec = _.extend({}, rec, record);
}
originalEdit(rec, fireOnLoad);
});
} else {
originalEdit(record, fireOnLoad);
}
canClose = false;
}
var parentCanEditTarget = null;
$scope.canEditTarget = function () {
if (parentCanEditTarget === null) {
var parent = $scope.$parent;
var func = parent.canEditTarget;
while (parent && func === $scope.canEditTarget) {
parent = parent.$parent;
func = parent.canEditTarget;
}
parentCanEditTarget = func || angular.noop;
}
return parentCanEditTarget() !== false;
};
var isEditable = $scope.isEditable;
$scope.isEditable = function () {
var id = ($scope.record || {}).id;
var perm = id > 0 ? 'write' : 'create';
if (parent.isReadonly && parent.isReadonly()) return false;
return $scope.hasPermission(perm)
&& (id > 0 ? $scope.canEditTarget() : true)
&& isEditable.call($scope);
};
var canEdit = $scope.canEdit;
$scope.canEdit = function() {
return $scope.canEditTarget() && canEdit.call($scope);
};
$scope.edit = function(record, fireOnLoad) {
if (isClosed) return;
$scope._viewPromise.then(function(){
doEdit(record, fireOnLoad);
$scope.setEditable(!$scope.$parent.$$readonly);
});
};
function isChanged() {
if ($scope.isDirty()) return true;
var record = $scope.record || {};
var version = record.version;
return recordVersion !== version || record.$forceDirty;
}
function canOK() {
if (isClosed) return false;
return isChanged();
}
function onOK() {
var record = $scope.record;
function close(value, forceSelect) {
if (value && (forceSelect || canOK())) {
value.$fetched = true;
value.selected = true;
$scope.$parent.select(value);
}
canClose = true;
$element.dialog('close');
if ($scope.editorCanReload) {
$scope.$parent.parentReload();
}
if (closeCallback && value) {
closeCallback(value);
}
closeCallback = null;
isClosed = true;
}
var event = $scope.$broadcast('on:before-save', record);
if (event.defaultPrevented) {
if (event.error) {
axelor.dialogs.error(event.error);
}
return;
}
// wait for onChange actions
$scope.waitForActions(function() {
if ($scope.editorCanSave && isChanged()) {
if (record.id < 0)
record.id = null;
return $scope.onSave({force: true}).then(function(record, page) {
// wait for onSave actions
$scope.waitForActions(function(){
close(record, true);
});
});
}
if ($scope.isValid()) {
close(record);
} else if ($scope.showErrorNotice) {
$scope.showErrorNotice();
}
}, 100);
}
$scope.onOK = function() {
$scope.$timeout(onOK, 10);
};
$scope.onBeforeClose = function(event, ui) {
if (canClose || !$scope.isDirty()) {
$scope.$evalAsync(function () {
$scope.edit(null, false);
});
return true;
}
event.preventDefault();
$scope.confirmDirty(function(){
canClose = true;
$element.dialog('close');
});
};
$scope.onHotKey = function (e, action) {
if (action === "save") {
$(e.target).blur().focus();
$scope.onOK();
}
$scope.$applyAsync();
return false;
};
}
SelectorCtrl.$inject = ['$scope', '$element', 'DataSource', 'ViewService'];
function SelectorCtrl($scope, $element, DataSource, ViewService) {
var parent = $scope.$parent;
$scope._viewParams = parent._viewParams;
$scope.getDomain = parent.getDomain;
ui.ViewCtrl.call(this, $scope, DataSource, ViewService);
ui.GridViewCtrl.call(this, $scope, $element);
var searchLimit = (parent.field||{}).searchLimit || 0;
if (searchLimit > 0) {
$scope._dataSource._page.limit = searchLimit;
}
function doFilter() {
$scope.filter($scope.getDomain());
}
var initialized = false;
var origShow = $scope.show;
$scope.show = function() {
origShow();
if (initialized) {
doFilter();
}
};
var _getContext = $scope.getContext;
$scope.getContext = function() {
// selector popup should return parent's context
if ($scope.$parent && $scope.$parent.getContext) {
return $scope.$parent.getContext();
}
return _getContext();
};
$scope.onItemClick = function(e, args) {
$scope.$applyAsync($scope.onOK.bind($scope));
};
var origOnShow = $scope.onShow;
$scope.onShow = function(viewPromise) {
viewPromise.then(function(){
var view = $scope.schema;
var field = $scope.field || $scope.$parent.field;
if (field) {
view.orderBy = field.orderBy || view.orderBy;
}
$element.dialog('open');
initialized = true;
origOnShow(viewPromise);
});
};
$scope.onOK = function() {
var selection = _.map($scope.selection, function(index){
return $scope.dataView.getItem(index);
});
if (!_.isEmpty(selection)) {
$scope.$applyAsync(function () {
$scope.$parent.select(selection);
$scope.selection = [];
});
}
$element.dialog('close');
};
$scope.onCreate = function () {
$element.dialog('close');
$scope.$parent.onNew();
};
$scope.canNew = function () {
return $scope.hasPermission('create') && $scope.$parent.canNew();
};
}
ui.directive('uiDialogSize', function() {
return function (scope, element, attrs) {
// use only with dialogs
if (attrs.uiDialog === undefined && !element.hasClass('ui-dialog-content')) {
return;
}
var loaded = false;
var addMaximizeButton = _.once(function () {
var elemDialog = element.parent();
var elemTitle = elemDialog.find('.ui-dialog-title');
var elemButton = $('<a href="#" class="ui-dialog-titlebar-max"><i class="fa fa-expand"></i></a>')
.click(function (e) {
$(this).children('i').toggleClass('fa-expand fa-compress');
elemDialog.toggleClass('maximized');
axelor.$adjustSize();
setTimeout(function () {
scope.$broadcast('grid:adjust-columns');
}, 350);
return false;
}).insertAfter(elemTitle);
// remove maximized state on close
element.on('dialogclose', function(e, ui) {
elemTitle.parent().find('i.fa-compress').toggleClass('fa-expand fa-compress');
elemDialog.removeClass('maximized');
});
var params = (scope._viewParams || {}).params || {};
if (params['popup.maximized']) {
elemButton.click();
}
});
var addCollapseButton = _.once(function () {
var elemDialog = element.parent();
var elemTitle = elemDialog.find('.ui-dialog-title');
$('<a href="#" class="ui-dialog-titlebar-collapse"><i class="fa fa-chevron-up"></i></a>')
.click(function (e) {
$(this).children('i').toggleClass('fa-chevron-up fa-chevron-down');
elemDialog.toggleClass('collapsed');
axelor.$adjustSize();
return false;
}).insertAfter(elemTitle);
// remove maximized and collapsed states on close
element.on('dialogclose', function(e, ui) {
elemTitle.parent().find('i.fa-compress').toggleClass('fa-expand fa-compress');
elemTitle.parent().find('i.fa-chevron-down').toggleClass('fa-chevron-down fa-chevron-up');
elemDialog.removeClass('maximized collapsed');
});
});
function doAdjust() {
element.dialog('open');
element.scrollTop(0);
setTimeout(doFocus);
if (scope._afterPopupShow) {
scope._afterPopupShow();
}
}
function doShow() {
addMaximizeButton();
addCollapseButton();
if (loaded) {
return setTimeout(doAdjust);
}
loaded = true;
scope.waitForActions(doAdjust);
}
function doFocus() {
var container = element.is('[ui-selector-popup]')
? element.find('.slick-headerrow')
: element;
var focusElem = container.find('input:tabbable');
if (focusElem.length == 0) {
focusElem = element.parent().find('.ui-dialog-buttonset').find(':tabbable');
}
if (focusElem[0]) {
focusElem[0].focus();
}
//XXX: ui-dialog issue
element.find('.slick-headerrow-column,.slickgrid,[ui-embedded-editor]').zIndex(element.zIndex());
element.find('.record-toolbar .btn').zIndex(element.zIndex()+1);
}
// a flag used by evalScope to detect popup (see form.base.js)
scope._isPopup = true;
scope._doShow = function(viewPromise) {
if (viewPromise && viewPromise.then) {
viewPromise.then(doShow);
} else {
doShow();
}
};
scope._setTitle = function (title) {
if (title) {
element.closest('.ui-dialog').find('.ui-dialog-title').text(title);
}
};
scope.adjustSize = function() {
};
};
});
ui.directive('uiEditorPopup', function() {
return {
restrict: 'EA',
controller: EditorCtrl,
scope: {},
link: function(scope, element, attrs) {
scope.onShow = function(viewPromise) {
scope._doShow(viewPromise);
};
scope.$watch('schema.title', function popupTitleWatch(title) {
scope._setTitle(title);
});
element.scroll(function (e) {
$(document).trigger('adjust:scroll', element);
});
var onNewHandler = scope.onNewHandler;
scope.onNewHandler = function (event) {
if (scope.isPopupOpen) {
return onNewHandler.apply(scope, arguments);
}
};
scope.isPopupOpen = true;
setTimeout(function () {
var isOpen = false;
element.on('dialogclose', function (e) {
isOpen = false;
scope.waitForActions(function () {
scope.isPopupOpen = isOpen;
scope.$$popupStack.pop(1);
}, 2000); // delay couple of seconds to that popup can cleanup
});
element.on('dialogopen', function (e) {
scope.isPopupOpen = isOpen = true;
scope.$$popupStack.push(1);
scope.$applyAsync();
});
});
},
replace: true,
template:
'<div ui-dialog ui-dialog-size x-on-ok="onOK" x-on-before-close="onBeforeClose" ui-watch-if="isPopupOpen">'+
'<div ui-view-form x-handler="this"></div>'+
'</div>'
};
});
ui.directive('uiSelectorPopup', function(){
return {
restrict: 'EA',
controller: SelectorCtrl,
scope: {
selectMode: "@"
},
link: function(scope, element, attrs) {
var onShow = scope.onShow;
scope.onShow = function (viewPromise) {
if (scope.clearFilters) {
scope.clearFilters();
scope.selection = [];
}
onShow(viewPromise);
scope._doShow(viewPromise);
};
scope.$watch('schema.title', function popupTitleWatch(title){
scope._setTitle(title);
});
var btnOK = null;
function buttonState(count) {
if (btnOK === null) {
btnOK = element.siblings('.ui-dialog-buttonpane').find('.btn:last');
}
return btnOK.attr('disabled', !count || count <= 0);
}
scope.$watch('selection.length', buttonState);
setTimeout(function(){
var footer = element.closest('.ui-dialog').find('.ui-dialog-buttonpane'),
header = element.closest('.ui-dialog').find('.ui-dialog-titlebar'),
pager = element.find('.record-pager'),
buttons = element.find('.ui-dialog-buttonset-left');
header.find('.ui-dialog-title').after(pager);
footer.prepend(buttons);
footer.find('.button-ok').html(_t("Select"));
});
},
replace: true,
template:
'<div ui-dialog ui-dialog-size x-on-ok="onOK">'+
'<div ui-view-grid x-view="schema" x-data-view="dataView" x-handler="this" x-editable="false" x-selector="{{selectMode}}"></div>'+
'<div ui-record-pager></div>'+
'<div class="ui-dialog-buttonset-left pull-left" ng-show="canNew()">'+
'<button class="btn" ng-click="onCreate()" x-translate>Create</button>'+
'</div>'+
'</div>'
};
});
})();