Files
ERP/sophal/js/widget/widget.slickgrid.js

2914 lines
74 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() {
/* global Slick: true */
"use strict";
var ui = angular.module('axelor.ui');
//used to keep track of columns by x-path
function setDummyCols(element, cols) {
var e = $('<div>').appendTo(element).hide();
_.each(cols, function(col, i) {
$('<span class="slick-dummy-column">')
.data('column', col)
.attr('x-path', col.xpath)
.appendTo(e);
});
}
function makeFilterCombo(input, selection, callback) {
var data = _.map(selection, function(item){
return {
key: item.value,
value: item.title
};
});
function update(item) {
var filter = {};
item = item || {};
input.val(item.value || '');
filter[input.data('columnId')] = item.key || '';
callback(filter);
}
input.autocomplete({
minLength: 0,
source: data,
focus: function(event, ui) {
return false;
},
select: function(event, ui) {
update(ui.item);
return false;
}
}).keyup(function(e){
return false;
}).keydown(function(e){
switch(e.keyCode) {
case 8: // backspace
case 46: // delete
update(null);
}
}).click(function(){
input.autocomplete("search", "");
});
$("<i class='fa fa-caret-down combo-icon'></i>").appendTo(input.parent()).click(function () {
input.focus();
input.autocomplete("search", "");
});
}
var Editor = function(args) {
var element;
var column = args.column;
var scope;
var external;
var form = $(args.container)
.parents('[ui-slick-grid]:first')
.find('[ui-slick-editors]:first');
element = form.find('[x-field="'+ column.field +'"]');
scope = form.data('$scope');
if (!element.parent().is('td.form-item'))
element = element.parent();
element.data('$parent', element.parent());
element.data('$editorForm', form);
external = element.is('.text-item,.html-item');
this.init = function() {
var container = $(args.container);
if (external) {
container = container.parents('.ui-dialog-content:first,.view-container:first').first();
$(document).on('mousedown.slick-external', function (e) {
if (element.is(e.target) || element.find(e.target).length > 0) {
return;
}
args.grid.getEditorLock().commitCurrentEdit();
});
}
element.css('display', 'inline-block')
.addClass('slick-external-editor')
.appendTo(container)
.hide();
if (!element.data('keydown.nav')) {
element.data('keydown.nav', true);
element.bind("keydown.nav", function (e) {
switch (e.keyCode) {
case 37: // LEFT
case 39: // RIGHT
case 38: // UP
case 40: // DOWN
e.stopImmediatePropagation();
break;
case 9: // TAB
if (external) {
args.grid.onKeyDown.notify(args.grid.getActiveCell(), e);
return false;
}
}
});
element.bind('close:slick-editor', function(e) {
var grid = args.grid;
var lock = grid.getEditorLock();
if (lock.isActive()) {
lock.commitCurrentEdit();
}
});
}
};
this.shouldWait = function () {
var es = element.scope();
if (es && es.field && es.field.onChange) {
return true;
}
return false;
};
this.destroy = function() {
scope.$lastEditor = this;
element.appendTo(element.data('$parent') || form)
.removeData('$parent')
.removeData('$editorForm');
element.trigger("hide:slick-editor");
element.parent().zIndex('');
$(document).off('mousedown.slick-external');
};
this.position = function(pos) {
//XXX: ui-dialog issue
var zIndex = element.parents('.slickgrid:first').zIndex();
if (zIndex) {
element.parent().zIndex(zIndex);
}
if (external) {
setTimeout(adjustExternal);
}
};
function adjustExternal() {
var container = $(args.container);
var parent = element.data('$parent') || element;
var zIndex = (parent.parents('.slickgrid:first').zIndex() || 0) + container.zIndex();
element.css({
border: 0,
width: container.width(),
zIndex: zIndex + 1
});
element.css('position', 'absolute');
element.position({
my: 'left top',
at: 'left top',
of: args.container
});
// focus html editor
if (element.is('.html-item')) {
element.find('[contenteditable]').focus();
}
}
function focus() {
// Firefox throws exception if element is hidden
if (element.is(':hidden')) return;
if (element.is(':input')) {
element.focus().select();
} else {
element.find(':input:first').focus().select();
}
if (element.is('[x-cell-css*=select-item]') && element.scope().showSelection) {
element.scope().showSelection(300);
}
}
this.focus = function() {
_.delay(focus);
};
this.loadValue = function(item) {
var that = this,
record = scope.record || {},
current = item || { id: 0 },
updated = false;
if ((!current.id || current.id < 0) && (current[column.field] === undefined)) {
var defaults = scope.$events.onNew ? scope.record : scope.defaultValues;
current[column.field] = (defaults||{})[column.field] || null;
}
var changed = (record.id !== item.id || record.version !== current.version);
if (changed) {
scope.editRecord(current);
} else {
record[column.field] = current[column.field];
updated = true;
}
setTimeout(function(){
if (updated) {
scope.$applyAsync();
}
element.show();
that.focus();
});
};
this.serializeValue = function() {
var record = scope.record || {};
var value = record[column.field];
return value === undefined ? "" : value;
};
this.applyValue = function(item, state) {
if (item.id === undefined) {
item = _.extend(item, scope.record);
}
item[column.field] = state;
item.$dirty = true;
if (item.id === undefined) {
args.grid.onCellChange.notify(args.grid.getActiveCell());
}
};
this.isValueChanged = function() {
// force change event on spinner widget
element.find('.ui-spinner-input').trigger('grid:check', args.item);
element.find('.ui-mask').trigger('grid:check');
var record = scope.record || {};
var current = args.item || { id: 0 };
var v1 = record[column.field];
var v2 = current[column.field];
var changed = !angular.equals(v1, v2);
if (changed && element.scope().isRequired() && !v1) {
current[column.field] = v1;
}
return changed;
};
this.validate = function() {
return {
valid: !element.hasClass('ng-invalid'),
msg: null
};
};
this.init();
};
var Formatters = {
"string": function(field, value, context) {
return _.escapeHTML(ui.formatters.string(field, value, context));
},
"integer": function(field, value) {
return ui.formatters.integer(field, value);
},
"decimal": function(field, value, context) {
return ui.formatters.decimal(field, value, context);
},
"boolean": function(field, value) {
return value ? '<i class="fa fa-check"></i>' : "<i class='fa'></i>";
},
"duration": function(field, value) {
return ui.formatDuration(field, value);
},
"date": function(field, value) {
return value ? moment(value).format('DD/MM/YYYY') : "";
},
"time": function(field, value) {
return value ? value : "";
},
"datetime": function(field, value) {
return value ? moment(value).format('DD/MM/YYYY HH:mm') : "";
},
"one-to-one": function(field, value) {
var text = (value||{})[field.targetName];
return text ? _.escapeHTML(text) : "";
},
"many-to-one": function(field, value) {
var text = (value||{})[field.targetName];
return text ? _.escapeHTML(text) : "";
},
"one-to-many": function(field, value) {
return value ? '(' + value.map(i => s.concat(i["fullName"].split("-")[0])) + ')' : "";
},
"many-to-many": function(field, value) {
return value ? '(' + value.length + ')' : "";
},
"button": function(field, value, context, grid) {
var elem;
var icon = field.icon || (field.widgetAttrs || {}).icon;
var isIcon = icon && icon.indexOf('fa-') === 0;
var css = isIcon ? "slick-icon-button fa " + icon : "slick-img-button";
var help = field.help || field.title;
var handler = grid.scope.handler;
var rec = context;
if (field.jsonField && rec) {
rec = angular.fromJson(rec[field.jsonField]);
}
if (field.readonlyIf && axelor.$eval(grid.scope, field.readonlyIf, _.extend({}, handler._context, rec))) {
css += " readonly disabled";
}
if(isIcon) {
elem = '<a href="javascript: void(0)" tabindex="-1"';
if (help) {
elem += ' title="' + _.escapeHTML(help) + '"';
}
elem += '><i class="' + css + '"></i></a>';
} else if (icon) {
elem = '<img class="' + css + '" src="' + icon + '"';
if (help) {
elem += ' title="' + _.escapeHTML(help) + '"';
}
elem += '>';
} else {
return "";
}
return elem;
},
"progress": function(field, value) {
var props = ui.ProgressMixin.compute(field, value);
return '<div class="progress ' + props.css + '" style="height: 18px; margin: 0; margin-top: 1px;">'+
'<div class="bar" style="width: ' + props.width +'%;"></div>'+
'</div>';
},
"selection": function(field, value) {
var cmp = field.type === "integer" ? function(a, b) { return a == b ; } : _.isEqual;
var findSelect = function (v) {
var val = field.type === "integer" ? v : _.unescapeHTML(v);
var res = _.extend({}, _.find(field.selectionList, function(item) {
return cmp(item.value, val);
}));
if (_.isString(res.title)) {
res.title = _.escapeHTML(res.title);
}
return res;
};
if (value && field.widget === 'MultiSelect') {
var items = value.split(/\s*,\s*/).map(findSelect).map(function (res) {
return '<span class="label label-primary"><span class="tag-text">'+res.title+'</span></span>';
});
return '<span class="tag-select">' + items.join(' ') + '</span>';
}
var res = findSelect(value);
var text = res.title;
if (field.widget === 'ImageSelect' && res.icon) {
var image = "<img style='max-height: 24px;' src='" + (res.icon || res.value) + "'>";
return field.labels === false ? image : image + " " + text;
}
return text;
},
"url": function(field, value) {
return '<a target="_blank" ng-show="text" href="' + _.escapeHTML(value) + '">' + _.escapeHTML(value) + '</a>';
},
"icon": function(field, value, dataContext, grid) {
if (value && value.indexOf("fa-") > -1) {
return '<i class="slick-icon ' + value + '"></i>';
}
return Formatters.button(field, value, dataContext, grid);
},
"jsonRef": function (field, value) {
var val = _.extend({}, value);
var id = val.id;
if (!id || id < 0) return '';
delete val.model;
delete val.version;
delete val.id;
var vals = _.flatten([id, _.values(val)]);
return '[' + vals.join(', ') + ']';
},
"json": function(field, value) {
if (!value || !field.jsonFields || field.jsonFields.length === 0) return "";
var that = this;
var items = [];
var json = angular.fromJson(value);
field.jsonFields.forEach(function (item) {
if (json[item.name] === undefined || json[item.name] === null) return;
var value = json[item.name];
var type = item.selection ? 'selection' : item.type;
if (item.widget === 'json-ref-select') type = 'jsonRef';
var func = that[type];
if (func) {
value = func(item, value);
}
items.push('<strong>' + item.title + '</strong>: ' + value);
});
return items.join(' &bull; ');
}
};
Formatters.text = Formatters.string;
function totalsFormatter(totals, columnDef) {
var field = columnDef.descriptor;
if (["integer", "long", "decimal"].indexOf(field.type) === -1) {
return "";
}
var vals = totals[field.aggregate || 'sum'] || {};
var val = vals[field.name];
var formatter = Formatters[field.type];
if (formatter) {
return formatter(field, val);
}
return val;
}
function Factory(grid) {
this.grid = grid;
}
_.extend(Factory.prototype, {
getEditor : function(col) {
var field = col.descriptor;
if (!field || field.readonly || col.forEdit === false) {
return null;
}
if (field.type == 'binary') {
return null;
}
if (!col.editor) {
col.editor = Editor;
}
return col.editor;
},
getFormatter: function(col) {
return _.bind(this.formatter, this);
},
formatter: function(row, cell, value, columnDef, dataContext) {
var field = columnDef.descriptor || {},
attrs = _.extend({}, field, field.widgetAttrs),
widget = attrs.widget || "",
type = attrs.type;
if (widget === 'json-field' || attrs.json) {
return Formatters.json(field, value);
}
if (attrs.jsonPath && attrs.jsonField) {
var jsonValue = dataContext[attrs.name];
if (jsonValue === undefined) {
jsonValue = dataContext[attrs.jsonField];
if (jsonValue) {
jsonValue = angular.fromJson(jsonValue);
value = jsonValue[attrs.jsonPath];
}
} else if (jsonValue && attrs.target) { // relational field value
value = angular.fromJson(jsonValue);
}
}
if (widget === "Progress" || widget === "progress" || widget === "SelectProgress") {
type = "progress";
}
if (_.isArray(field.selectionList) && widget !== "SelectProgress") {
type = "selection";
}
if (typeof value === 'string') {
value = axelor.sanitize(value).replace('&lt;', '<').replace('&gt;', '>').replace('&amp;', '&');
}
if (type === "button" || type === "progress") {
return Formatters[type](field, value, dataContext, this.grid);
}
if (["Url", "url", "duration"].indexOf(widget) > 0) {
type = widget.toLowerCase();
}
if (widget.toLowerCase() === "image" || widget.toLowerCase() === "binary-link" || (type === "binary" && field.name === "image")) {
var url = null;
if (field.target === "com.axelor.meta.db.MetaFile") {
if (value) {
url = ui.makeImageURL("com.axelor.meta.db.MetaFile", "content", (value.id || value), undefined, this.grid.handler);
}
if (url && widget.toLowerCase() === "binary-link") {
return '<a href="' + url + '" download="' + value.fileName + '">' + value.fileName + '</a>';
}
} else {
url = ui.makeImageURL(this.grid.handler._model, field.name, dataContext, undefined, this.grid.handler) + "&image=true";
}
return url ? '<img src="' + url + '" style="height: 21px;margin-top: -2px;">' : '';
}
if (widget.toLowerCase() === "html") {
return value ? '<span>' + value + '</span>' : '';
}
// try to get dotted field value from related object
if ((value === null || value === undefined) && field.name && field.name.indexOf('.') > -1) {
var path = field.name.split('.');
var val = dataContext || {};
var idx = 0;
while (val && idx < path.length) {
val = val[path[idx++]];
}
if (idx === path.length) {
value = val;
}
}
var fn = Formatters[type];
if (fn) {
value = fn(field, value, dataContext, this.grid);
} else if (_.isString(value)) {
value = _.escapeHTML(value);
}
if (value === null || value === undefined || (_.isObject(value) && _.isEmpty(value))) {
return "";
}
return value;
},
formatProgress: function(field, value) {
var props = ui.ProgressMixin.compute(field, value);
return '<div class="progress ' + props.css + '" style="height: 18px; margin: 0; margin-top: 1px;">'+
'<div class="bar" style="width: ' + props.width +'%;"></div>'+
'</div>';
},
formatDecimal: function(field, value) {
var scale = field.scale || 2,
num = +(value);
if (num) {
return num.toFixed(scale);
}
return value;
},
formatButton: function(field, value, columnDef) {
return '<img class="slick-img-button" src="' + field.icon + '">';
}
});
var Grid = function(scope, element, attrs, ViewService, ActionService) {
var noFilter = scope.$eval('noFilter');
if (_.isString(noFilter)) {
noFilter = noFilter === 'true';
}
this.compile = function(template) {
return ViewService.compile(template)(scope.$new());
};
this.newActionHandler = function(scope, element, options) {
return ActionService.handler(scope, element, options);
};
this.scope = scope;
this.element = element;
this.attrs = attrs;
this.handler = scope.handler;
this.showFilters = !noFilter;
this.$oldValues = null;
this.grid = this.parse(scope.view);
};
function buttonScope(scope) {
var btnScope = scope.$new();
var handler = scope.handler;
btnScope._dataSource = handler._dataSource;
btnScope.editRecord = function (record) {};
btnScope.reload = function () {
if ((handler.field||{}).target) {
handler.$parent.reload();
}
return handler.onRefresh();
};
if ((handler.field||{}).target) {
btnScope.onSave = function () {
return handler.$parent.onSave.call(handler.$parent, {
callOnSave: false,
wait: false
});
};
}
return btnScope;
}
Grid.prototype.parse = function(view) {
var that = this,
scope = this.scope,
handler = scope.handler,
dataView = scope.dataView,
element = this.element;
scope.fields_view = {};
var cols = [];
var allColsHasWidth = true;
_.each(view.items, function(item) {
var field = handler.fields[item.name] || {},
path = handler.formPath, type;
type = (item.widgetAttrs||{}).type || field.type || item.serverType || item.type || 'string';
field = _.extend({}, field, item, {type: type});
scope.fields_view[item.name] = field;
path = path ? path + '.' + item.name : item.name;
if (type === 'field' || field.selection) {
type = 'string';
}
if (!item.width) {
allColsHasWidth = false;
}
var sortable = view.sortable !== undefined && field.sortable === undefined
? view.sortable !== false
: field.sortable !== false;
switch (field.type) {
case 'field': // dummy field
case 'icon':
case 'button':
case 'one-to-many':
case 'many-to-many':
sortable = false;
break;
}
if (field.transient || field.json || field.encrypted) {
sortable = false;
}
if (field.type == "button") {
if (scope.selector) return;
field.image = field.title;
field.handler = that.newActionHandler(buttonScope(scope), element, {
action: field.onClick
});
}
if (field.type == "button" || field.type == "icon") {
item.title = "&nbsp;";
item.width = field.width || 32;
}
var column = {
name: item.title || field.title || item.autoTitle || _.chain(item.name).humanize().titleize().value(),
id: item.name,
field: item.name,
toolTip: item.help,
forEdit: item.forEdit,
descriptor: field,
sortable: sortable,
width: parseInt(item.width) || null,
hasWidth: item.width ? true : false,
cssClass: type,
headerCssClass: type,
xpath: path
};
var minWidth = view.colWidth || 100;
column.minWidth = Math.min(minWidth, column.width || minWidth);
column._title = column.name;
var css = [type];
if (item.forEdit !== false) {
if (!field.readonly) {
css.push('slick-cell-editable');
}
if (field.required) {
css.push('slick-cell-required');
}
}
column.cssClass = css.join(' ');
cols.push(column);
if (field.aggregate) {
column.groupTotalsFormatter = totalsFormatter;
}
if (field.type === "button" || field.type === "boolean" || field.type === "icon") {
return;
}
var menus = [sortable ? {
iconImage: "lib/slickgrid/images/sort-asc.gif",
title: _t("Sort Ascending"),
command: "sort-asc"
} : null, sortable ? {
iconImage: "lib/slickgrid/images/sort-desc.gif",
title: _t("Sort Descending"),
command: "sort-desc"
} : null, sortable ? {
separator: true
} : null, view.editable ? null : {
title: _t("Group by") + " <i>" + column.name + "</i>",
command: "group-by"
}, view.editable ? null : {
title: _t("Ungroup"),
command: "ungroup"
}, view.editable ? null : {
separator: true
}, {
title: _t("Hide") + " <i>" + column.name + "</i>",
command: "hide"
}];
menus = _.compact(menus);
column.header = {
menu: {
items: menus,
position: function($menu, $button) {
$menu.css('top', 0)
.css('left', 0);
$menu.position({
my: 'left top',
at: 'left bottom',
of: $button
});
}
}
};
});
// if all columns are fixed width, add a dummy column
if (allColsHasWidth) {
cols.push({
name: "&nbsp;"
});
}
// create edit column
var editColumn = null;
if (view.editIcon && (!scope.selector || scope.selector === "checkbox") && (!handler.hasPermission || handler.hasPermission('write'))) {
editColumn = new EditIconColumn({
onClick: function (e, args) {
if (e.isDefaultPrevented()) {
return;
}
e.preventDefault();
var elem = $(e.target);
if (elem.is('.fa-minus') && handler) {
return handler.dataView.deleteItem(0);
}
if (handler && handler.onEdit) {
handler.waitForActions(function () {
args.grid.setActiveCell(args.row, args.cell);
handler.$applyAsync(function () {
handler.onEdit(true);
});
});
}
}
});
cols.unshift(editColumn.getColumnDefinition());
}
// create checkbox column
var selectColumn = null;
if (scope.selector) {
selectColumn = new Slick.CheckboxSelectColumn({
cssClass: "slick-cell-checkboxsel",
multiSelect: scope.selector !== "single"
});
cols.unshift(_.extend(selectColumn.getColumnDefinition(), {
headerCssClass: "slick-cell-checkboxsel"
}));
}
// add column for re-ordering rows
this._canMove = view.canMove && view.orderBy === "sequence" && !view.groupBy;
if (this._canMove) {
cols.push({
id: "_move_column",
name: "",
width: 32,
behavior: "selectAndMove",
selectable: false,
resizable: false,
cssClass: "fa fa-bars move-icon",
canMove: function () {
if (handler.isReadonly && handler.isReadonly()) {
return false;
}
return true;
}
});
}
var factory = new Factory(this);
var options = {
rowHeight: Math.max(view.rowHeight || 26, 26),
editable: view.editable && !axelor.device.mobile,
editorFactory: factory,
formatterFactory: factory,
enableCellNavigation: true,
enableColumnReorder: false,
fullWidthRows: true,
multiColumnSort: true,
showHeaderRow: this.showFilters,
multiSelect: scope.selector !== "single",
explicitInitialization: true
};
var grid = new Slick.Grid(element, dataView, cols, options);
this.cols = cols;
this.grid = grid;
this._selectColumn = selectColumn;
this._editColumn = editColumn;
element.show();
element.data('grid', grid);
// delegate some methods to handler scope
handler.showColumn = _.bind(this.showColumn, this);
handler.resetColumns = _.bind(this.resetColumns, this);
handler.setColumnTitle = _.bind(this.setColumnTitle, this);
handler.getVisibleCols = _.bind(this.getVisibleCols, this);
// set dummy columns to apply attrs if grid is not initialized yet
setDummyCols(element, this.cols);
function adjustSize(event, force) {
scope.ajaxStop(function () {
var forceAdjust = force || handler._isPopup;
if (forceAdjust || element.is(':visible')) {
that.adjustSize(forceAdjust);
if (forceAdjust) {
setTimeout(grid.autosizeColumns, 100);
}
}
});
}
scope.$on('grid:adjust-size', function (e, viewScope) {
adjustSize(e, viewScope === handler);
});
scope.$onAdjust(adjustSize, 100); // handle global events
scope.$callWhen(function () {
return element.is(':visible');
}, adjustSize, 100);
element.addClass('slickgrid-empty');
this.doInit = _.once(function doInit() {
this._doInit(view);
element.removeClass('slickgrid-empty');
}.bind(this));
handler._dataSource.on('change', function (e, records, page) {
element.toggleClass('slickgrid-empty-message', page && page.size === 0);
});
var emptyMessage = handler.$emptyMessage || _t("No records found.");
element.append($("<div class='slickgrid-empty-text'>").hide().text(emptyMessage));
return grid;
};
Grid.prototype._doInit = function(view) {
var that = this,
grid = this.grid,
scope = this.scope,
handler = this.scope.handler,
dataView = this.scope.dataView,
element = this.element;
var headerMenu = new Slick.Plugins.HeaderMenu({
buttonImage: "lib/slickgrid/images/down.gif"
});
var rowMoveManager = new Slick.RowMoveManager({
cancelEditOnDrag: true
});
grid.setSelectionModel(new Slick.RowSelectionModel());
grid.registerPlugin(new Slick.Data.GroupItemMetadataProvider());
grid.registerPlugin(headerMenu);
if (this._selectColumn) {
grid.registerPlugin(this._selectColumn);
}
if (this._editColumn) {
grid.registerPlugin(this._editColumn);
}
if (this._canMove) {
grid.registerPlugin(rowMoveManager);
}
// performance tweaks
var _containerH = 0;
var _containerW = 0;
var _resizeCanvas = grid.resizeCanvas;
grid.resizeCanvas = _.debounce(function() {
var w = element.width(),
h = element.height();
if (element.is(':hidden') || (w === _containerW && h === _containerH)) {
return;
}
_containerW = w;
_containerH = h;
_resizeCanvas.call(grid);
}, 100);
grid.init();
this.$$initialized = true;
// end performance tweaks
dataView.$syncSelection = function(old, oldIds, focus) {
var selection = dataView.mapIdsToRows(oldIds || []);
// if saving o2m items, we may get negative oldIds, consider reselecting old selection
if (old && oldIds && old.length === 1 && oldIds.length === 1 && oldIds[0] < 0) {
dataView.getItem(old[0]).selected = true;
}
if (!focus) {
_.each(dataView.getItems(), function (item, i) {
if (item.selected) {
selection.push(i);
}
});
}
selection = _.unique(selection);
grid.setSelectedRows(selection);
if (selection.length === 0 && !grid.getEditorLock().isActive()) {
grid.setActiveCell(null);
} else if (focus) {
grid.setActiveCell(_.first(selection), 1);
grid.focus();
}
};
dataView.$setSelection = function(selection, focus) {
var rows = selection || [];
grid.setSelectedRows(rows);
if (selection.length === 0 && !grid.getEditorLock().isActive()) {
grid.setActiveCell(null);
} else if (focus) {
grid.setActiveCell(_.first(selection), 1);
grid.focus();
}
};
if (this._canMove) {
dataView.$resequence = _.bind(this._resequence, this);
}
// register grid event handlers
this.subscribe(grid.onSort, this.onSort);
this.subscribe(grid.onSelectedRowsChanged, this.onSelectionChanged);
this.subscribe(grid.onClick, this.onItemClick);
this.subscribe(grid.onDblClick, this.onItemDblClick);
this.subscribe(grid.onKeyDown, this.onKeyDown);
this.subscribe(grid.onCellChange, this.onCellChange);
this.subscribe(grid.onBeforeEditCell, this.onBeforeEditCell);
// register dataView event handlers
this.subscribe(dataView.onRowCountChanged, this.onRowCountChanged);
this.subscribe(dataView.onRowsChanged, this.onRowsChanged);
// register header menu event handlers
this.subscribe(headerMenu.onBeforeMenuShow, this.onBeforeMenuShow);
this.subscribe(headerMenu.onCommand, this.onMenuCommand);
// register row move handlers
this.subscribe(rowMoveManager.onBeforeMoveRows, this.onBeforeMoveRows);
this.subscribe(rowMoveManager.onMoveRows, this.onMoveRows);
// hilite support
var getItemMetadata = dataView.getItemMetadata;
dataView.getItemMetadata = function (row) {
var item = grid.getDataItem(row);
if (item && !item.$style) {
that.hilite(row);
}
var meta = getItemMetadata.apply(dataView, arguments);
var my = that.getItemMetadata(row);
if (my && meta && meta.cssClasses) {
my.cssClasses += " " + meta.cssClasses;
}
return meta || my;
};
this.subscribe(grid.onCellChange, function (e, args) {
that.hilite(args.row);
grid.invalidateRow(args.row);
grid.render();
});
function setFilterCols() {
if (!that.showFilters) {
return;
}
var filters = {};
var filtersRow = $(grid.getHeaderRow());
function updateFilters(event) {
/* jshint validthis: true */
var elem = $(this);
if (elem.is('.ui-autocomplete-input')) {
return;
}
filters[$(this).data('columnId')] = $(this).val().trim();
handler._simpleFilters = filters;
}
function clearFilters() {
filters = {};
filtersRow.find(":input").val("");
handler._simpleFilters = null;
}
handler.clearFilters = clearFilters;
filtersRow.on('keyup', ':input', updateFilters);
filtersRow.on('keypress', ':input', function(event){
if (event.keyCode === 13) {
updateFilters.call(this, event);
scope.handler.filter(filters, that.advanceFilter);
}
});
function _setInputs(cols) {
_.each(cols, function(col){
if (!col.xpath || col.descriptor.type === 'button' || col.descriptor.json || col.descriptor.encrypted) return;
var header = grid.getHeaderRowColumn(col.id),
input = $('<input type="text">').data("columnId", col.id).val(filters[col.id]).appendTo(header),
field = col.descriptor || {};
input.on("change", function () {
input.attr('placeholder', input.is(':focus') ? _t('Search...') : null);
});
input.on("focus", function () {
input.attr('placeholder', _t('Search...'));
});
input.on("blur", function () {
input.attr('placeholder', '');
});
if (_.isArray(field.selectionList)) {
makeFilterCombo(input, field.selectionList, function(filter){
_.extend(filters, filter);
});
}
});
}
var _setColumns = grid.setColumns;
grid.setColumns = function(columns) {
_setColumns.apply(grid, arguments);
_setInputs(columns);
};
_setInputs(that.cols);
}
setFilterCols();
var onInit = scope.onInit();
if (_.isFunction(onInit)) {
onInit(grid, this);
}
if (view.groupBy) {
this.groupBy(view.groupBy);
}
setTimeout(function () {
grid.setColumns(that.getVisibleCols());
});
if (scope.$parent._viewResolver) {
scope.$parent._viewResolver.resolve(view, element);
}
scope.$on("cancel:grid-edit", function(e) {
if (that.$oldValues && that.canSave()){
dataView.beginUpdate();
dataView.setItems(that.$oldValues);
dataView.endUpdate();
that.$oldValues = null;
that.clearDirty();
grid.invalidateAllRows();
grid.render();
e.preventDefault();
}
});
scope.$on("on:new", function(e) {
that.$oldValues = null;
that.clearDirty();
that.resetColumns();
});
scope.$on("on:edit", function(e, record) {
if (record && record.id > 0) {
that.$oldValues = null;
that.clearDirty();
that.resetColumns();
}
});
scope.$on("on:advance-filter", function (e, criteria) {
if (e.targetScope === handler) {
that.advanceFilter = criteria;
}
});
scope.$on("on:context-field-change", function (e, data) {
that.contextField = data && data.field ? data.field.name : null;
that.contextValue = data.value;
that.resetColumns();
});
scope.$on("on:before-save", function(e) {
// only for editable grid
if (!that.editable) {
return;
}
var row = null;
var lock = grid.getEditorLock();
if (lock.isActive()) {
lock.commitCurrentEdit();
row = grid.getDataItem(grid.getDataLength() - 1); // to check if adding new row
}
if (grid.getActiveCell() && that.focusInvalidCell(grid.getActiveCell())) {
e.preventDefault();
showErrorNotice();
return false;
}
var beforeSavePending = that.__beforeSavePending || (row && row.id === 0);
that.__beforeSavePending = false;
function showErrorNotice() {
var args = that.grid.getActiveCell() || {};
var col = that.getColumn(args.cell);
if (!col || !col.xpath) {
return;
}
var name = col.name;
if (that.handler.field &&
that.handler.field.title) {
name = that.handler.field.title + "[" + args.row +"] / " + name;
}
var items = "<ul><li>" + name + "</li></ul>";
axelor.notify.error(items, {
title: _t("The following fields are invalid:")
});
}
var node = that.element.find('.slick-cell-required:empty,.slick-cell > .ng-invalid').first();
if (node.parent().is('.slick-cell')) {
node = node.parent();
}
if (node.length) {
that.grid.setActiveNode(node[0]);
that.grid.editActiveCell();
e.preventDefault();
showErrorNotice();
return false;
}
if (!that.isDirty() || !beforeSavePending || that.saveChanges()) {
return;
}
if (!that.editorScope || that.editorScope.isValid()) {
return;
}
if (that.editorForm && that.editorForm.is(":hidden")) {
return;
}
var args = that.grid.getActiveCell();
if (args) {
that.focusInvalidCell(args);
showErrorNotice();
} else {
var item = that.editorScope.record;
if (item && item.id === 0) {
// new row was canceled
return;
}
axelor.dialogs.error(_t('There are some invalid rows.'));
}
e.preventDefault();
return false;
});
scope.$timeout(function () {
that.zIndexFix();
grid.invalidate();
grid.autosizeColumns();
// focus first filter input
if (!axelor.device.mobile && !that.element.parent().is('.portlet-grid')) {
that.element
.find('.slick-headerrow:first input[type=text]:first')
.focus()
.select();
}
});
var scrollTop;
this.subscribe(grid.onScroll, function (e, args) {
scrollTop = args.scrollTop;
});
$(document).on("click",".fa-check", function () {
grid.re
$(this).parent().parent().parent().removeClass("hilite-info-text");
$(this).parent().parent().parent().removeClass("hilite-warning-text");
$(this).parent().parent().parent().addClass("hilite-success-text");
$(this).parent().parent().siblings(".l11").text("Accepté")
});
function resetScroll() {
if (scrollTop) {
setTimeout(function () {
that.element.children('.slick-viewport').scrollTop(scrollTop);
setTimeout(function() {
grid.invalidateAllRows();
grid.render();
}, 100);
});
}
}
scope.$on('dom:attach', resetScroll);
scope.$on('tab:select', resetScroll);
var onColumnsResized = false;
this.subscribe(grid.onColumnsResized, function (e, args) {
onColumnsResized = true;
});
scope.$on('grid:adjust-columns', function () {
if (!onColumnsResized) {
grid.autosizeColumns();
}
});
scope.$on('$destroy', function () {
if (that.editorForm) {
that.editorForm.remove();
}
if (that.editorScope) {
that.editorScope.$destroy();
}
grid.destroy();
});
};
Grid.prototype.subscribe = function(event, handler) {
event.subscribe(_.bind(handler, this));
};
Grid.prototype.zIndexFix = function() {
//XXX: ui-dialog issue (filter row)
var zIndex = this.element.parents('.ui-dialog:first').zIndex();
if (zIndex) {
this.element.find('.slick-headerrow-column').zIndex(zIndex);
}
};
Grid.prototype.adjustSize = function(force) {
if (!this.grid || (!force && this.element.is(':hidden')) || this.grid.getEditorLock().isActive()) {
return;
}
this.doInit();
if (this.grid.getCanvasNode() && !this.grid.getCanvasNode().hasChildNodes()) {
this.grid.invalidate();
}
this.adjustToScreen();
this.grid.resizeCanvas();
this.zIndexFix();
};
Grid.prototype.adjustToScreen = function() {
var compact = this.__compact;
var mobile = axelor.device.small;
if (!compact && !mobile) {
return;
}
if (mobile && compact) {
return;
}
this.__compact = mobile;
_.each(this.cols, function (col, i) {
var field = col.descriptor || {};
if (field.hidden) {
return;
}
}, this);
};
Grid.prototype.getColumn = function(indexOrName) {
var cols = this.grid.getColumns(),
index = indexOrName;
if (_.isString(index)) {
index = this.grid.getColumnIndex(index);
}
return cols[index];
};
Grid.prototype.showColumn = function(name, show) {
var that = this,
grid = this.grid,
cols = this.cols;
this.visibleCols = this.visibleCols || _.pluck(this.getVisibleCols(), 'id');
show = _.isUndefined(show) ? true : show;
var visible = [],
current = [];
_.each(cols, function(col){
if (col.id != name && _.contains(that.visibleCols, col.id))
return visible.push(col.id);
if (col.id == name && show)
return visible.push(name);
});
this.visibleCols = visible;
if (!this.$$initialized) {
return;
}
current = _.filter(cols, function(col) {
return _.contains(visible, col.id);
});
grid.setColumns(current);
grid.getViewport().rightPx = 0;
grid.resizeCanvas();
grid.autosizeColumns();
this.zIndexFix();
};
Grid.prototype.getVisibleCols = function(reset) {
var visible = reset ? [] : (this.visibleCols || []);
if (visible.length === 0) {
var contextField = this.contextField;
var contextValue = this.contextValue;
return this.cols.filter(function (col) {
var desc = col.descriptor||{};
if (desc.contextField) {
return !desc.hidden
&& desc.contextField === contextField
&& desc.contextFieldValue === contextValue;
}
return !desc.hidden;
});
}
return this.cols.filter(function (col) {
return visible.length ? _.contains(visible, col.id) : true;
});
};
Grid.prototype.resetColumns = function() {
var grid = this.grid,
cols = this.getVisibleCols(true);
this.visibleCols = _.pluck(cols, 'id');
grid.setColumns(cols);
grid.getViewport().rightPx = 0;
grid.resizeCanvas();
grid.autosizeColumns();
};
Grid.prototype.setColumnTitle = function(name, title) {
if (this.$$initialized) {
return this.grid.updateColumnHeader(name, title);
}
var col = this.getColumn(name);
if (col && title) {
col.name = title;
}
};
Grid.prototype.getItemMetadata = function(row) {
var item = this.grid.getDataItem(row);
if (item && item.$style) {
return {
cssClasses: item.$style
};
}
return null;
};
Grid.prototype.hilite = function (row, field) {
var view = this.scope.view,
record = this.grid.getDataItem(row),
params = null;
if (!view || !record || record.__group || record.__groupTotals) {
return null;
}
if (!field) {
_.each(this.scope.fields_view, function (item) {
if (item.hilites) this.hilite(row, item);
}, this);
}
var hilites = field ? field.hilites : view.hilites;
if (!hilites || hilites.length === 0) {
return null;
}
record.$style = null;
var ctx = record || {};
if (this.handler._context) {
ctx = _.extend({}, this.handler._context, ctx);
}
for (var i = 0; i < hilites.length; i++) {
params = hilites[i];
var condition = params.condition,
styles = null,
pass = false;
try {
pass = axelor.$eval(this.scope, condition, ctx);
} catch (e) {
}
if (!pass && field) {
styles = record.$styles || (record.$styles = {});
styles[field.name] = null;
}
if (!pass) {
continue;
}
if (field) {
styles = record.$styles || (record.$styles = {});
styles[field.name] = params.css;
} else {
record.$style = params.css;
}
break;
}
};
Grid.prototype.onBeforeMenuShow = function(event, args) {
var menu = args.menu;
if (!menu || !menu.items || !this.visibleCols) {
return;
}
menu.items = _.filter(menu.items, function(item) {
return item.command !== 'show';
});
_.each(this.cols, function(col) {
if (_.contains(this.visibleCols, col.id)) return;
menu.items.push({
title: _t('Show') + " <i>" + col.name + "</i>",
command: 'show',
field: col.field
});
}, this);
};
Grid.prototype.onMenuCommand = function(event, args) {
var grid = this.grid;
if (args.command === 'sort-asc' ||
args.command == 'sort-desc') {
var opts = {
grid: grid,
multiColumnSort: true,
sortCols: [{
sortCol: args.column,
sortAsc: args.command === 'sort-asc'
}]
};
return grid.onSort.notify(opts, event, grid);
}
var groups, index;
if (args.command === 'group-by') {
groups = this._groups || [];
index = groups.indexOf(args.column.field);
if (index === -1) {
groups.push(args.column.field);
}
return this.groupBy(groups);
}
if (args.command === 'ungroup') {
groups = this._groups || [];
index = groups.indexOf(args.column.field);
if (index > -1) {
groups.splice(index, 1);
}
return this.groupBy(groups);
}
if (args.command === 'hide') {
return this.showColumn(args.column.field, false);
}
if (args.command === 'show') {
return this.showColumn(args.item.field, true);
}
};
Grid.prototype.onBeforeEditCell = function(event, args) {
if (this.$oldValues === null) {
this.$oldValues = [];
var n = 0;
while (n < this.grid.getDataLength()) {
var item = this.grid.getDataItem(n++);
if (item && item.id) {
this.$oldValues.push(_.clone(item));
}
}
}
if (args.item && args.item._original === undefined) {
args.item._original = _.clone(args.item);
}
};
Grid.prototype.onKeyDown = function(e, args) {
var that = this,
grid = this.grid,
lock = grid.getEditorLock();
if (e.which === $.ui.keyCode.ENTER && $(e.target).is('textarea,[contenteditable]')) {
return;
}
if (e.isDefaultPrevented()){
e.stopImmediatePropagation();
return false;
}
if (!e.isBlocked && (blocked() || !lock.isActive())) {
return false;
}
function blockCallback(blocked) {
if (blocked && e.which === $.ui.keyCode.TAB) {
setTimeout(function(){
var cell = e.shiftKey ? that.findPrevEditable(args.row, args.cell)
: that.findNextEditable(args.row, args.cell);
if (cell) {
grid.setActiveCell(cell.row, cell.cell);
grid.editActiveCell();
}
});
}
}
function blocked() {
if (that.isDirty() && axelor.blockUI(blockCallback)) {
grid.focus();
e.stopImmediatePropagation();
return true;
}
return false;
}
function commitChanges() {
if (lock.commitCurrentEdit() && !blocked()) {
return true;
}
return false;
}
function focusCell(row, cell) {
grid.setActiveCell(row, cell);
// make sure cell has focus RM-3938
setTimeout(function () {
grid.editActiveCell();
});
}
// firefox & IE fails to trigger onChange
if ((axelor.browser.mozilla || axelor.browser.msie) &&
(e.which === $.ui.keyCode.TAB || e.which === $.ui.keyCode.ENTER)) {
var editor = grid.getCellEditor(),
target = $(e.target);
if (editor.isValueChanged()) {
target.change();
}
setTimeout(function(){
target.blur();
});
}
var handled = false;
if (e.which === $.ui.keyCode.TAB) {
var cell = e.shiftKey ? this.findPrevEditable(args.row, args.cell) :
this.findNextEditable(args.row, args.cell);
if (commitChanges() && cell && cell.row > args.row && this.isDirty()) {
args.item = null;
this.scope.waitForActions(function () {
that.scope.waitForActions(function () {
that.addNewRow(args);
});
});
} else if (cell) {
focusCell(cell.row, cell.cell);
}
handled = true;
}
if (e.which === $.ui.keyCode.ENTER) {
if (e.ctrlKey) {
if (!this.saveChanges(args)) {
this.focusInvalidCell(args);
}
} else {
if (!lock.commitCurrentEdit()) {
this.focusInvalidCell(args);
}
grid.focus();
}
grid.focus();
handled = true;
}
if (e.which === $.ui.keyCode.ESCAPE) {
grid.focus();
}
if (handled) {
e.stopImmediatePropagation();
return false;
}
};
Grid.prototype.isCellEditable = function(row, cell) {
var cols = this.grid.getColumns(),
col = cols[cell];
if (!col || col.id === "_edit_column" || col.id === "_move_column" || col.id === "_checkbox_selector") {
return false;
}
var field = col.descriptor || {};
var form = this.editorForm;
if (field.type === 'button' || (field.name && field.name.indexOf('.') > -1)) {
return false;
}
if (!form) {
return !field.readonly;
}
var current = this.grid.getActiveCell();
if (current && current.row === row) {
var item = this.element.find('[x-field=' + field.name + ']:first');
if (item.length) {
return !item.scope().isReadonly();
}
}
return !field.readonly;
};
Grid.prototype.findNextEditable = function(posY, posX) {
var grid = this.grid,
cols = grid.getColumns(),
args = {row: posY, cell: posX + 1};
while (args.cell < cols.length) {
if (this.isCellEditable(args.row, args.cell)) {
return args;
}
args.cell += 1;
}
if (grid.getDataItem(args.row)) {
args.row += 1;
}
args.cell = 0;
while (args.cell <= posX) {
if (this.isCellEditable(args.row, args.cell)) {
return args;
}
args.cell += 1;
}
return null;
};
Grid.prototype.findPrevEditable = function(posY, posX) {
var grid = this.grid,
cols = grid.getColumns(),
args = {row: posY, cell: posX - 1};
while (args.cell > -1) {
if (this.isCellEditable(args.row, args.cell)) {
return args;
}
args.cell -= 1;
}
if (args.row > 0) {
args.row -= 1;
}
args.cell = cols.length - 1;
while (args.cell >= posX) {
if (this.isCellEditable(args.row, args.cell)) {
return args;
}
args.cell -= 1;
}
return null;
};
Grid.prototype.saveChanges = function(args, callback) {
// onBeforeSave may cause recursion
if (this._saveChangesRunning) {
return;
}
var that = this;
var grid = this.grid;
var lock = grid.getEditorLock();
var force = arguments[2];
if (!force &&
((lock.isActive() && !lock.commitCurrentEdit()) ||
(this.editorScope && !this.editorScope.isValid()))) {
return false;
}
this._saveChangesRunning = true;
if (this.editorScope) {
this.editorScope.$emit("on:before-save", this.editorScope.record);
}
var params = arguments;
this.scope.waitForActions(function () {
that.__saveChanges.apply(that, params);
that._saveChangesRunning = false;
}, 100);
return true;
};
Grid.prototype.__saveChanges = function(args, callback) {
var that = this;
var grid = this.grid;
if (!args) {
args = _.extend({ row: 0, cell: 0 }, grid.getActiveCell());
args.item = grid.getDataItem(args.row);
}
var ds = this.handler._dataSource;
var data = this.scope.dataView;
var records = [];
records = _.map(data.getItems(), function(rec) {
var res = {};
for(var key in rec) {
var val = rec[key];
if (_.isString(val) && val.trim() === "")
val = null;
res[key] = val;
}
if (res.id === 0) {
res.id = null;
}
if (res.$dirty && _.isUndefined(res.version)) {
res.version = res.$version;
}
return res;
});
function focus() {
grid.setActiveCell(args.row, args.cell);
grid.focus();
if (callback) {
that.handler.waitForActions(callback);
}
}
var onBeforeSave = this.scope.onBeforeSave(),
onAfterSave = this.scope.onAfterSave();
if (onBeforeSave && onBeforeSave(records) === false) {
return setTimeout(focus, 200);
}
// prevent cache
var saveDS = ds;
var handler = this.handler || {};
if (handler.field && handler.field.target) {
saveDS = ds._new(ds._model, {
domain: ds._domain,
context: ds._context
});
}
records = _.where(records, { $dirty: true });
if (records.length === 0) {
return setTimeout(focus, 200);
}
var fields = handler.selectFields ? handler.selectFields() : undefined;
return saveDS.saveAll(records, fields).success(function(records, page) {
if (data.getItemById(0)) {
data.deleteItem(0);
}
if (onAfterSave) {
onAfterSave(records, page);
}
setTimeout(focus);
});
};
Grid.prototype.canSave = function() {
return this.editorScope && this.editorScope.isValid() && this.isDirty();
};
Grid.prototype.isDirty = function(row) {
var grid = this.grid;
var item;
if (row === null || row === undefined) {
var n = 0;
while (n < grid.getDataLength()) {
item = grid.getDataItem(n);
if (item && item.$dirty) {
return true;
}
n ++;
}
} else {
item = grid.getDataItem(row);
if (item && item.$dirty) {
return true;
}
}
return false;
};
Grid.prototype.__markHandlerDirty = function () {
if (this.scope.handler && this.scope.handler.$dirtyGrid) {
this.$gid = this.$gid || _.uniqueId('grid');
this.scope.handler.$dirtyGrid(this.$gid, this.isDirty());
}
};
Grid.prototype.markDirty = function(row, field) {
var grid = this.grid,
dataView = this.scope.dataView,
hash = grid.getCellCssStyles("highlight") || {},
items = hash[row] || {};
items[field] = "dirty";
hash[row] = items;
var record = dataView.getItem(row);
if (this.handler.$$ensureIds && record && !record.id) {
this.handler.$$ensureIds([record]);
}
grid.setCellCssStyles("highlight", hash);
grid.invalidateAllRows();
grid.render();
this.__markHandlerDirty();
this.__beforeSavePending = true;
};
Grid.prototype.clearDirty = function(row) {
var grid = this.grid,
hash = grid.getCellCssStyles("highlight") || {};
if (row === null || row === undefined) {
hash = {};
} else {
delete hash[row];
}
grid.setCellCssStyles("highlight", hash);
grid.invalidateAllRows();
grid.render();
this.__markHandlerDirty();
this.__beforeSavePending = false;
};
Grid.prototype.focusInvalidCell = function(args) {
var grid = this.grid,
formCtrl = this.editorForm.children('form').data('$formController'),
error = formCtrl.$error || {};
if (this.editorForm.is(':hidden') && _.isEmpty(this.editorScope.record)) {
return false;
}
for(var name in error) {
var errors = error[name] || [];
if (errors.length) {
name = errors[0].$name;
var cell = grid.getColumnIndex(name);
if (cell > -1) {
grid.setActiveCell(args.row, cell);
grid.editActiveCell();
return true;
}
}
}
return false;
};
Grid.prototype.addNewRow = function (args) {
var self = this,
scope = this.scope,
grid = this.grid,
dataView = scope.dataView,
lock = grid.getEditorLock();
if (lock.isActive()) {
lock.commitCurrentEdit();
}
args.row = Math.max(0, args.row);
args.cell = Math.max(0, args.cell);
var cell = self.findNextEditable(args.row, 0);
function addRow(defaults) {
var args = { row: grid.getDataLength(), cell: 0 };
var item = _.extend({ id: 0 }, defaults);
grid.invalidateRow(dataView.length);
dataView.addItem(item);
self.scope.waitForActions(function () {
cell = self.findNextEditable(args.row, args.cell);
if (cell) {
grid.focus();
grid.setActiveCell(cell.row, cell.cell);
setTimeout(function () {
grid.editActiveCell();
}, 200);
}
}, 100);
}
function focus() {
grid.focus();
grid.setActiveCell(cell.row, cell.cell);
if (grid.getDataLength() > cell.row) {
return grid.editActiveCell();
}
if (!self.canAdd()) {
return;
}
self.editorScope.doOnNew();
self.scope.waitForActions(function () {
self.scope.waitForActions(function () {
addRow(self.editorScope.record);
});
}, 100);
}
if (args.item || grid.getDataLength() === 0) {
return focus();
}
var saved = self.saveChanges(args, function () {
cell.row += 1;
focus();
});
if (!saved) {
self.focusInvalidCell(args);
}
};
Grid.prototype.canEdit = function () {
var handler = this.handler || {};
if (!this.editable) return false;
if (handler.canEdit && !handler.canEdit()) return false;
if (handler.isReadonly && handler.isReadonly()) return false;
return true;
};
Grid.prototype.canAdd = function () {
var handler = this.handler || {};
if (!this.editable) return false;
if (handler.isReadonly && handler.isReadonly()) return false;
return handler.canNew && handler.canNew();
};
Grid.prototype.setEditors = function(form, formScope, forEdit) {
var grid = this.grid,
data = this.scope.dataView,
element = this.element;
this.editable = forEdit = forEdit === undefined ? true : forEdit;
grid.setOptions({
editable: !axelor.device.mobile,
asyncEditorLoading: false,
editorLock: new Slick.EditorLock()
});
form.prependTo(element).hide();
formScope.onChangeNotify = function(scope, values) {
var item, editor, cell = grid.getActiveCell();
if (!cell || formScope.record !== scope.record) {
return;
}
item = grid.getDataItem(cell.row);
if (item) {
editor = grid.getCellEditor();
if (grid.getEditorLock().isActive()) {
grid.getEditorLock().commitCurrentEdit();
}
item = _.extend(item, values);
// update dotted fields
_.filter(grid.getColumns(), function (col) {
return col.field && col.field.indexOf('.') > -1;
}).forEach(function (col) {
var path = col.field.split('.');
var val = item || {};
var idx = 0;
while (val && idx < path.length) {
val = val[path[idx++]];
}
if (idx === path.length && val !== undefined) {
item[col.field] = val;
}
});
grid.updateRowCount();
grid.invalidateRow(cell.row);
grid.render();
grid.setActiveCell(cell.row, cell.cell);
if (editor) {
grid.focus();
}
}
};
formScope.onNewHandler = function (event) {
};
formScope.doOnNew = function () {
if (formScope.defaultValues === null) {
formScope.defaultValues = {};
_.each(formScope.fields, function (field, name) {
if (field.defaultValue !== undefined) {
formScope.defaultValues[name] = field.defaultValue;
}
});
}
var values = angular.copy(formScope.defaultValues);
var args = grid.getActiveCell();
formScope.editRecord(values);
formScope.$applyAsync();
if (!formScope.$events.onNew) {
return;
}
var handler = formScope.$events.onNew;
var lock = grid.getEditorLock();
if (lock.isActive()) {
lock.commitCurrentEdit();
}
var promise = handler();
promise.then(function () {
grid.focus();
grid.editActiveCell();
});
};
// delegate isDirty to the dataView
data.canSave = _.bind(this.canSave, this);
data.saveChanges = _.bind(this.saveChanges, this);
var that = this;
var onNew = this.handler.onNew;
if (onNew) {
this.handler.onNew = function () {
var lock = that.grid.getEditorLock();
if (that.editable) {
if (lock.isActive()) {
lock.commitCurrentEdit();
}
var cell = that.findNextEditable(that.grid.getDataLength() - 1, 0) || { row: that.grid.getDataLength(), cell: 0 };
that.grid.focus();
that.grid.setActiveCell(cell.row, cell.cell);
that.grid.editActiveCell();
return that.scope.$timeout(function () {
return that.addNewRow(cell);
});
}
return onNew.apply(that.handler, arguments);
};
}
if (!forEdit) {
formScope.setEditable(false);
}
this.editorForm = form;
this.editorScope = formScope;
this.editorForEdit = forEdit;
};
Grid.prototype.onSelectionChanged = function(event, args) {
var grid = this.grid;
var activeCell = grid.getActiveCell();
var selectedRows = args.rows || [];
if (activeCell && selectedRows.indexOf(activeCell.row) === -1) {
grid.resetActiveCell();
}
if (this.handler.onSelectionChanged) {
this.handler.onSelectionChanged(event, args);
}
this.element.find(' > .slick-viewport > .grid-canvas > .slick-row')
.removeClass('selected')
.find(' > .slick-cell.selected')
.parent()
.each(function () {
$(this).addClass('selected');
});
};
Grid.prototype.onCellChange = function(event, args) {
var grid = this.grid,
cols = grid.getColumns(),
name = cols[args.cell].field;
var es = this.editorScope;
if (es.record && es.record.version === undefined) {
es.record.version = es.record.$version;
}
if (es.isDirty()) {
this.markDirty(args.row, name);
}
};
Grid.prototype.onSort = function(event, args) {
if (this.canSave())
return;
if (this.handler.onSort)
this.handler.onSort(event, args);
};
Grid.prototype.onBeforeMoveRows = function (event, args) {
var data = this.scope.dataView;
for (var i = 0; i < args.rows.length; i++) {
// no point in moving before or after itself
if (args.rows[i] == args.insertBefore || args.rows[i] == args.insertBefore - 1) {
event.stopPropagation();
return false;
}
}
return true;
};
Grid.prototype._resequence = function (items) {
var min = _.min(_.map(items, function (item) {
return item.sequence || 0;
}));
for (var i = 0; i < items.length; i++) {
var last = items[i].sequence;
var next = min++;
if (items[i].sequence !== next) {
items[i].sequence = next;
items[i].$dirty = true;
}
}
return items;
};
Grid.prototype.onMoveRows = function (event, args) {
var grid = this.grid;
var dataView = this.scope.dataView;
var rows = args.rows;
var items = dataView.getItems();
var insertBefore = args.insertBefore;
var left = items.slice(0, insertBefore);
var right = items.slice(insertBefore, items.length);
var extractedRows = [];
rows.sort(function(a, b) { return a - b; });
var i;
for (i = 0; i < rows.length; i++) {
extractedRows.push(items[rows[i]]);
}
rows.reverse();
for (i = 0; i < rows.length; i++) {
var row = rows[i];
if (row < insertBefore) {
left.splice(row, 1);
} else {
right.splice(row - insertBefore, 1);
}
}
items = left.concat(extractedRows.concat(right));
var selectedRows = [];
for (i = 0; i < rows.length; i++) {
selectedRows.push(left.length + i);
}
// resequence
this._resequence(items);
function resetSelection() {
grid.setActiveCell(selectedRows[0], 0);
grid.setSelectedRows(selectedRows);
}
dataView.beginUpdate();
dataView.setItems(items);
dataView.endUpdate();
resetSelection();
grid.render();
var that = this;
this.scope.$timeout(function () {
dataView.$isResequencing = true;
var saved = that.saveChanges(null, function() {
delete dataView.$isResequencing;
resetSelection();
}, true);
if (saved === false) {
delete dataView.$isResequencing;
}
});
};
Grid.prototype.onButtonClick = function(event, args) {
if ($(event.srcElement || event.target).is('.readonly') || this._buttonClickRunning) {
event.stopImmediatePropagation();
return false;
}
var grid = this.grid;
var data = this.scope.dataView;
var cols = this.getColumn(args.cell);
var field = (cols || {}).descriptor || {};
// set selection
grid.setSelectedRows([args.row]);
grid.setActiveCell(args.row, args.cell);
if (field.handler) {
this._buttonClickRunning = true;
var handlerScope = this.scope.handler;
var model = handlerScope._model;
var record = data.getItem(args.row) || {};
var that = this;
// defer record access so that any pending changes are applied
Object.defineProperty(field.handler.scope, 'record', {
enumerable: true,
configurable: true,
get: function () {
return data.getItem(args.row) || {};
}
});
if(field.prompt) {
field.handler.prompt = field.prompt;
}
field.handler.scope.getContext = function() {
var context = _.extend({
_model: model
}, record);
var current = handlerScope.dataView.getItem(_.first(handlerScope.selection));
if (handlerScope.field && handlerScope.field.target) {
context._parent = handlerScope.getContext();
}
if (context.id === 0) {
context.id = null;
}
if (current && current.id > 0 && (!context.id || context.id < 0)) {
context.id = current.id;
}
return context;
};
var old = _.extend({}, record);
field.handler.onClick().then(function(res){
delete that._buttonClickRunning;
var current = field.handler.scope.record;
if (handlerScope.setValue && !handlerScope._dataSource.equals(old, current)) {
current.version = current.version === undefined ? current.$version : current.version;
handlerScope.setValue(data.getItems(), true);
}
grid.invalidateRows([args.row]);
grid.render();
let active = $(".ui-widget-content.active");
if(field.handler.action == "action-purchase-order-supplier-line-method-refuse"){
active.removeClass("hilite-info-text hilite-success-text");
active.addClass("hilite-warning-text");
active.children(".l11").text("Non retenu")
active.children(".l13").text(new Date().toLocaleDateString("fr-FR"))
}else if(field.handler.action == "action-purchase-order-supplier-line-method-accept"){
active.removeClass("hilite-info-text hilite-warning-text");
active.addClass("hilite-success-text");
active.children(".l11").text("Accepté")
active.children(".l13").text(new Date().toLocaleDateString("fr-FR"))
}
}, function () {
delete that._buttonClickRunning;
});
}
};
Grid.prototype.onItemClick = function(event, args) {
var that = this;
var waitCallback = function (done) {
setTimeout(function () {
that.handler.waitForActions(function () {
that.__onItemClick(event, args);
if (done) {
done();
}
}, 100);
}, 100);
};
var lock = this.grid.getEditorLock();
if (lock.isActive()) {
lock.commitCurrentEdit();
if (this.editorScope &&
this.editorScope.$lastEditor &&
this.editorScope.$lastEditor.shouldWait()) {
return waitCallback;
}
}
// prevent edit if some action is still in progress
if (this.isDirty() && axelor.blockUI()) {
return waitCallback;
}
return this.__onItemClick(event, args);
};
Grid.prototype.__onItemClick = function(event, args) {
// prevent edit if some action is still in progress
if (this.isDirty() && axelor.blockUI()) {
return;
}
var source = $(event.target);
if (source.is('img.slick-img-button,i.slick-icon-button')) {
return this.onButtonClick(event, args);
}
// checkbox column
if (this.scope.selector && args.cell === 0) {
return false;
}
//XXX: hack to show popup grid (selector and editable in conflict?)
if (this.scope.selector && this.editable) {
var col = this.grid.getColumns()[args.cell] || {},
field = col.descriptor || {};
if (col.forEdit !== false &&
(field.type === 'one-to-many' || field.type === 'many-to-many' || this.scope.selector === 'checkbox')) {
this.grid.setActiveCell(args.row, args.cell);
this.grid.editActiveCell();
event.preventDefault();
event.stopImmediatePropagation();
return false;
}
}
if (!this.scope.selector && this.canEdit()) {
return this.grid.setActiveCell();
}
if (this.handler.onItemClick) {
this.handler.onItemClick(event, args);
}
};
Grid.prototype.onItemDblClick = function(event, args) {
if ($(event.srcElement || event.target).is('img.slick-img-button,i.slick-icon-button')) {
return this.onButtonClick(event, args);
}
var col = this.grid.getColumns()[args.cell];
if (col.id === '_edit_column') return;
var item = this.grid.getDataItem(args.row) || {};
if (item.__group || item.__groupTotals) {
return;
}
if (!this.handler.field && this.canSave())
return;
var selected = this.grid.getSelectedRows() || [];
if (selected.length === 0) {
this.grid.setSelectedRows([args.row]);
}
if (this.handler.onItemDblClick)
this.handler.onItemDblClick(event, args);
event.stopImmediatePropagation();
};
Grid.prototype.onRowCountChanged = function(event, args) {
this.grid.updateRowCount();
this.grid.render();
};
Grid.prototype.onRowsChanged = function(event, args) {
var grid = this.grid,
data = this.scope.dataView,
forEdit = this.editorForEdit;
if (!this.isDirty()) {
this.clearDirty();
}
grid.invalidateRows(args.rows);
grid.render();
};
Grid.prototype.groupBy = function(names) {
var grid = this.grid,
data = this.scope.dataView,
cols = this.grid.getColumns();
var aggregators = _.map(cols, function(col) {
var field = col.descriptor;
if (!field) return null;
if (field.aggregate === "sum") {
return new Slick.Data.Aggregators.Sum(field.name);
}
if (field.aggregate === "avg") {
return new Slick.Data.Aggregators.Avg(field.name);
}
if (field.aggregate === "min") {
return new Slick.Data.Aggregators.Min(field.name);
}
if (field.aggregate === "max") {
return new Slick.Data.Aggregators.Max(field.name);
}
});
aggregators = _.compact(aggregators);
var all = names;
if (_.isString(all)) {
all = all.split(/\s*,\s*/);
}
var fields = _.compact(_.pluck(this.cols, 'descriptor'));
var grouping = _.map(all, function(name) {
var that = this;
var field = _.findWhere(fields, { name: name });
return {
getter: function(item) {
var value = item[name];
var formatter = Formatters[field.selection ? 'selection' : field.type];
if (field.jsonPath && field.jsonField) {
var jsonValue = item[field.jsonField];
if (jsonValue) {
jsonValue = angular.fromJson(jsonValue);
value = jsonValue[field.jsonPath];
}
}
return (formatter ? formatter(field, value, item, grid) : value) || _t('N/A');
},
formatter: function(g) {
var title = field.title + ": " + g.value;
return '<span class="slick-group-text">' + title + '</span>' + ' ' +
'<span class="slick-group-count">' + _t("({0} items)", g.count) + '</span>';
},
aggregators: aggregators,
aggregateCollapsed: false
};
}, this);
this._groups = all;
data.setGrouping(grouping);
};
function EditIconColumn(options) {
var _grid;
var _self = this;
var _handler = new Slick.EventHandler();
var _opts = _.extend({
onClick: angular.noop
}, options);
function init(grid) {
_grid = grid;
_handler.subscribe(grid.onClick, handleClick);
}
function handleClick(e, args) {
if (_grid.getColumns()[args.cell].id !== '_edit_column' || !$(e.target).is("i")) {
return;
}
return _opts.onClick(e, args);
}
function destroy() {
_handler.unsubscribeAll();
}
function editFormatter(row, cell, value, columnDef, dataContext) {
if (!dataContext || !dataContext.id) return '<i class="fa fa-minus"></i>';
return '<i class="fa fa-pencil"></i>';
}
function getColumnDefinition() {
return {
id : '_edit_column',
name : "<span class='slick-column-name'>&nbsp;</span>",
field : "edit",
width : 24,
resizable : false,
sortable : false,
cssClass: 'edit-icon',
formatter : editFormatter
};
}
$.extend(this, {
"init": init,
"destroy": destroy,
"getColumnDefinition": getColumnDefinition
});
}
ui.directive('uiSlickEditors', function() {
return {
restrict: 'EA',
replace: true,
controller: ['$scope', '$element', 'DataSource', 'ViewService', function($scope, $element, DataSource, ViewService) {
ui.ViewCtrl($scope, DataSource, ViewService);
ui.FormViewCtrl.call(this, $scope, $element);
$scope.setEditable();
$scope.onShow = function(viewPromise) {
};
var _getContext = $scope.getContext;
$scope.getContext = function() {
var context = _getContext();
var handler = $scope.handler || {};
var current = handler.dataView.getItem(_.first(handler.selection));
if (context && handler.field && handler.field.target) {
context._parent = handler.getContext();
}
if (context.id === 0) {
context.id = null;
}
if (current && current.id > 0 && (!context.id || context.id < 0)) {
context.id = current.id;
}
return context;
};
$scope.show();
}],
link: function(scope, element, attrs) {
var grid = null;
scope.canWatch = function () {
return true;
};
},
template: '<div ui-view-form x-handler="true" ui-watch-if="canWatch()"></div>'
};
});
ui.directive('uiSlickGrid', ['ViewService', 'ActionService', function(ViewService, ActionService) {
var types = {
'one-to-many' : 'one-to-many-inline',
'many-to-many' : 'many-to-many-inline'
};
function makeForm(scope, model, items, fields, forEdit, onNew) {
var _fields = fields || {},
_items = [];
_.each(items, function(item) {
var field = _fields[item.name] || item,
type = types[field.type];
// force lite html widget
if (item.widget && item.widget.toLowerCase() === 'html') {
item.lite = true;
}
if (!type && !forEdit) {
item.forEdit = false;
return;
}
var params = _.extend({}, item, { showTitle: false });
if (type) {
params.widget = type;
params.canEdit = forEdit;
}
_items.push(params);
});
var schema = {
cols: _items.length,
colWidths: '=',
viewType : 'form',
onNew: onNew,
items: _items
};
scope._viewParams = {
model: model,
views: [schema]
};
return ViewService.compile('<div ui-slick-editors></div>')(scope);
}
return {
restrict: 'EA',
replace: false,
scope: {
'view' : '=',
'dataView' : '=',
'handler' : '=',
'selector' : '@',
'editable' : '@',
'noFilter' : '@',
'onInit' : '&',
'onBeforeSave' : '&',
'onAfterSave' : '&'
},
link: function(scope, element, attrs) {
var grid = null,
schema = null,
handler = scope.handler,
initialized = false;
function doInit() {
if (initialized || !schema || !scope.dataView) return;
initialized = true;
if (attrs.editable === "false") {
schema.editable = false;
}
scope.selector = attrs.selector;
scope.noFilter = attrs.noFilter;
if (axelor.config["view.grid.selection"] === "checkbox" && !scope.selector) {
scope.selector = "checkbox";
}
var forEdit = schema.editable || false,
canEdit = schema.editable || false,
hasMulti = false;
hasMulti = _.find(schema.items, function(item) {
var field = handler.fields[item.name] || {};
return _.str.endsWith(field.type, '-many');
});
if (hasMulti) {
canEdit = true;
}
var form = null,
formScope = null;
if (handler.field && handler.field.onNew) {
schema.onNew = handler.field.onNew;
}
if (canEdit) {
formScope = scope.$new();
form = makeForm(formScope, handler._model, schema.items, handler.fields, forEdit, schema.onNew);
}
if (forEdit) {
element.addClass('slickgrid-editable');
}
grid = new Grid(scope, element, attrs, ViewService, ActionService);
if (form) {
formScope.grid = grid;
grid.setEditors(form, formScope, forEdit);
}
if (!handler._isPopup && schema.inlineHelp && !axelor.config["user.noHelp"]) {
addHelp(schema.inlineHelp);
}
// handle pending attrs change on dashlets
if (handler.$$pendingAttrs) {
_.each(handler.$$pendingAttrs, function (itemAttrs, itemName) {
_.each(itemAttrs, function (attrValue, attrName) {
switch (attrName) {
case 'hidden':
grid.showColumn(itemName, !attrValue);
break;
case 'title':
grid.setColumnTitle(itemName, attrValue);
break;
}
});
});
handler.$$pendingAttrs = undefined;
}
grid.adjustSize();
}
function addHelp(helpItem) {
var helpElem = $('<div>')
.css('visibility', 'hidden')
.addClass('help-item alert')
.html(helpItem.text);
var css = helpItem.css || '';
if (css.indexOf('alert-') === -1) {
css = (css + ' alert-info').trim();
}
helpElem.addClass(css);
helpElem.appendTo('body');
element.css('top', helpElem.height() + 36 + 40);
setTimeout(function () {
element.before(helpElem.css('visibility', ''));
});
}
element.addClass('slickgrid').hide();
var unwatch = scope.$watch("view.loaded", function gridSchemaWatch(viewLoaded) {
if (!viewLoaded || !scope.dataView) {
return;
}
unwatch();
schema = scope.view;
var field = handler.field || {};
if (field.canMove !== undefined) {
schema.canMove = field.canMove;
}
if (field.editable !== undefined) {
schema.editable = field.editable;
}
schema.rowHeight = field.rowHeight || schema.rowHeight;
schema.orderBy = field.orderBy || schema.orderBy;
schema.groupBy = field.groupBy || schema.groupBy;
schema.groupBy = (schema.editable || schema.groupBy === "false") ? false : schema.groupBy;
schema.canMassUpdate = !!_.find(schema.items, function (item) { return item.massUpdate; });
element.show();
doInit();
});
}
};
}]);
})();