/* * Axelor Business Solutions * * Copyright (C) 2005-2019 Axelor (). * * 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 . */ (function() { "use strict"; var ui = angular.module("axelor.ui"); ui.OneToManyCtrl = OneToManyCtrl; ui.OneToManyCtrl.$inject = ['$scope', '$element', 'DataSource', 'ViewService']; function OneToManyCtrl($scope, $element, DataSource, ViewService, initCallback) { ui.RefFieldCtrl.call(this, $scope, $element, DataSource, ViewService, function(){ ui.GridViewCtrl.call(this, $scope, $element); $scope.editorCanSave = false; $scope.selectEnable = false; if (initCallback) { initCallback(); } }); var embedded = null, detailView = null; $scope.createNestedEditor = function() { embedded = $('
'); embedded.attr('x-title', $element.attr('x-title')); embedded = ViewService.compile(embedded)($scope); embedded.hide(); $element.append(embedded); embedded.data('$rel', $element.children('.slickgrid:first').children('.slick-viewport')); return embedded; }; var _showNestedEditor = $scope.showNestedEditor; $scope.showNestedEditor = function(show, record) { _showNestedEditor(show, record); if (embedded) { embedded.data('$rel').hide(); var formScope = embedded.scope(); formScope._viewPromise.then(function () { formScope.edit(record); }); } return embedded; }; $scope.showDetailView = function() { if (detailView == null) { detailView = $('
').attr('x-title', $element.attr('x-title')); detailView = ViewService.compile(detailView)($scope); detailView.data('$rel', $()); detailView.data('$scope').isDetailView = true; $element.after(detailView); } var es = detailView.data('$scope'); detailView.toggle(es.visible = !es.visible); }; $scope.select = function(value) { // if items are same, no need to set values if ($scope._dataSource.equals(value, $scope.getValue())) { return; } var items = _.chain([value]).flatten(true).compact().value(); var records = _.map($scope.getItems(), _.clone); _.each(records, function(item) { item.selected = false; }); _.each($scope.itemsPending, function (item) { var find = _.find(records, function(rec) { if (rec.id && rec.id == item.id) { return true; } var a = _.omit(item, 'id', 'version'); var b = _.omit(rec, 'id', 'version'); return $scope._dataSource.equals(a, b); }); if (!find) { records.push(item); } }); _.each(items, function(item){ item = _.clone(item); var find = _.find(records, function(rec){ var id1 = rec.id || rec.$id; var id2 = item.id || item.$id; return id1 && id1 === id2; }); if (find) { _.extend(find, item); } else { records.push(item); } }); records = $scope.$$ensureIds(records); _.each(records, function(rec){ if (rec.id <= 0) rec.id = null; }); if ($scope.dataView.$resequence) { $scope.dataView.$resequence(records); } var callOnChange = $scope.dataView.$isResequencing !== true; $scope.itemsPending = records; $scope.setValue(records, callOnChange); $scope.$timeout(function() { $scope.$broadcast('grid:changed'); }); }; var _setItems = $scope.setItems; $scope.setItems = function(items) { _setItems(items); $scope.itemsPending = []; if (embedded !== null) { embedded.data('$scope').onClose(); } if (detailView !== null) if (items === null || _.isEmpty(items)) detailView.hide(); else detailView.show(); }; $scope.removeItems = function(items, fireOnChange) { var all, ids, records; if (_.isEmpty(items)) return; all = _.isArray(items) ? items : [items]; ids = _.map(all, function(item) { return _.isNumber(item) ? item : item.id; }); records = _.filter($scope.getItems(), function(item) { return ids.indexOf(item.id) === -1; }); $scope.setValue(records, fireOnChange); $scope.$applyAsync(); }; $scope.removeSelected = function(selection) { var selected, items; if (_.isEmpty(selection)) return; selected = _.map(selection, function (i) { return $scope.dataView.getItem(i).id; }); items = _.filter($scope.getItems(), function (item) { return selected.indexOf(item.id) === -1; }); // remove selected from data view _.each(selected, function (id) { if (id && id > -1) $scope.dataView.deleteItem(id); }); $scope.dataView.$setSelection([]); $scope.setValue(items, true); $scope.$applyAsync(); }; $scope.canEditTarget = function () { return $scope.canEdit() && $scope.attr('canEdit') !== false; }; $scope.canShowEdit = function () { var selected = $scope.selection.length ? $scope.selection[0] : null; return selected !== null && $scope.canEdit(); }; $scope.canShowView = function () { if ($scope.canShowEdit()) return false; var selected = $scope.selection.length ? $scope.selection[0] : null; return selected !== null && $scope.canView(); }; $scope.canEdit = function () { return $scope.attr('canEdit') !== false && $scope.canView(); }; var _canRemove = $scope.canRemove; $scope.canRemove = function () { var selected = $scope.selection.length ? $scope.selection[0] : null; return _canRemove() && selected !== null; }; $scope.canCopy = function () { if (!$scope.field || !($scope.field.widgetAttrs||{}).canCopy) return false; if (!$scope.canNew()) return false; if (!$scope.selection || $scope.selection.length !== 1) return false; var record = $scope.dataView.getItem(_.first($scope.selection)); return !!record; }; $scope.onCopy = function() { var ds = $scope._dataSource; var index = _.first($scope.selection); var item = $scope.dataView.getItem(index); var doSelect = function (record) { if (!record.id) $scope.$$ensureIds([record]); $scope.select([record]); $scope.$timeout(function () { $scope.dataView.$setSelection([$scope.dataView.getLength() - 1], true); }, 100); }; if (item && item.id > 0) { ds.copy(item.id).success(doSelect); } else if (item) { item = angular.copy(item); item.id = undefined; item.$id = undefined; doSelect(item); } }; $scope.onEdit = function() { var selected = $scope.selection.length ? $scope.selection[0] : null; if (selected !== null) { var record = $scope.dataView.getItem(selected); $scope.showEditor(record); } }; $scope.onRemove = function() { if (this.isReadonly()) { return; } axelor.dialogs.confirm(_t("Do you really want to delete the selected record(s)?"), function(confirmed){ if (confirmed && $scope.selection && $scope.selection.length) $scope.removeSelected($scope.selection); }); }; $scope.onSummary = function() { var selected = $scope.getSelectedRecord(); if (selected) { $scope.showNestedEditor(true, selected); } }; $scope.viewCanCopy = function () { return this.hasPermission("create") && !this.isDisabled() && !this.isReadonly() && this.canCopy(); }; $scope.viewCanExport = function () { if (!$scope.field || !($scope.field.widgetAttrs||{}).canExport) return false; return this.hasPermission("export") && this.canExport(); }; $scope.getSelectedRecord = function() { var selected = _.first($scope.selection || []); if (_.isUndefined(selected)) return null; return $scope.dataView.getItem(selected); }; var _onSelectionChanged = $scope.onSelectionChanged; $scope.onSelectionChanged = function(e, args) { _onSelectionChanged(e, args); var field = $scope.field, record = $scope.record || {}, selection = $scope.selection || []; record.$many = record.$many || (record.$many = {}); record.$many[field.name] = selection.length ? $scope.getItems : null; if (detailView === null) { return; } $scope.$timeout(function() { var dvs = detailView.scope(); var rec = $scope.getSelectedRecord() || {}; detailView.show(); if (!dvs.record || (dvs.record.id !== rec.id)) { dvs.edit(rec); } }); }; $scope.onItemDblClick = function(event, args) { if($scope.canView()){ $scope.onEdit(); $scope.$applyAsync(); } }; (function (scope) { var dummyId = 0; function ensureIds(records, clone) { var items = []; angular.forEach(records, function(record){ var item = clone ? angular.copy(record, {}) : (record || {}); if (!item.id) { item.id = item.$id || (item.$id = --dummyId); } items.push(item); }); return items; } function fetchData() { var items = scope.getValue(); return scope.fetchData(items, function(records){ scope.setItems(ensureIds(records, true)); }); } scope.$$ensureIds = ensureIds; scope.$$fetchData = fetchData; })($scope); $scope.reload = function() { return $scope.$$fetchData(); }; $scope.filter = function() { }; $scope.onSort = function(event, args) { //TODO: implement client side sorting (prevent losing O2M changes). if ($scope.isDirty() && !$scope.editorCanSave) { return; } var records = $scope.getItems(); if (records == null || records.length === 0) return; for (var i = 0; i < records.length; i++) { var item = records[i]; if (!item.id || item.id <= 0) { return; } } var sortBy = []; angular.forEach(args.sortCols, function(column){ var field = column.sortCol.descriptor; var name = column.sortCol.field; if (field.jsonField) { if (field.type === 'many-to-one' && field.targetName) { name = name + "." + field.targetName; } name += '::' + ('integer,boolean,decimal'.indexOf(field.type) > -1 ? field.type : 'text'); } var spec = column.sortAsc ? name : '-' + name; sortBy.push(spec); }); var ids = _.pluck(records, 'id'); var criterion = { 'fieldName': 'id', 'operator': 'in', 'value': ids }; var fields = $scope.selectFields(); var filter = { operator: 'and', criteria: [criterion] }; $scope.selection = []; $scope._dataSource.search({ filter: filter, fields: fields, sortBy: sortBy, archived: true, limit: -1, domain: null, context: null }); }; $scope.onShow = function(viewPromise) { }; $scope.show(); } ui.ManyToManyCtrl = ManyToManyCtrl; ui.ManyToManyCtrl.$inject = ['$scope', '$element', 'DataSource', 'ViewService']; function ManyToManyCtrl($scope, $element, DataSource, ViewService) { OneToManyCtrl.call(this, $scope, $element, DataSource, ViewService, function(){ $scope.editorCanSave = true; $scope.selectEnable = true; }); var _setValue = $scope.setValue; $scope.setValue = function(value, trigger) { var compact = _.map(value, function(item) { return { id: item.id, $version: item.version }; }); _setValue(compact, trigger); }; } ui.formInput('OneToMany', { css: 'one2many-item', transclude: true, showTitle: false, collapseIfEmpty: true, controller: OneToManyCtrl, link: function(scope, element, attrs, model) { scope.ngModel = model; scope.title = attrs.title; scope.formPath = scope.formPath ? scope.formPath + "." + attrs.name : attrs.name; var doRenderUnwatch = null; var doViewPromised = false; var validate = model.$validators.valid || function () { return true; }; model.$validators.valid = function(modelValue, viewValue) { if (scope.isRequired() && _.isEmpty(viewValue)) return false; return validate.call(model.$validators, viewValue); }; if (scope.field.requiredIf) { scope.$watch("attr('required')", function o2mRequiredWatch(required) { if (required !== undefined) { model.$validate(); } }); } if (scope.field.validIf) { scope.$watch("attr('valid')", function o2mValidWatch(valid) { if (valid !== undefined) { model.$setValidity('invalid', valid); } }); } function doRender() { if (doRenderUnwatch) { return; } doRenderUnwatch = scope.$watch(function o2mFetchWatch() { if (!isVisible() || !doViewPromised) { return; } doRenderUnwatch(); doRenderUnwatch = null; scope.$$fetchData(); }); } function isVisible() { return !element.is(':hidden'); } scope._viewPromise.then(function () { doViewPromised = true; if (doRenderUnwatch) { doRenderUnwatch(); doRenderUnwatch = null; doRender(); } }); model.$render = doRender; var adjustHeight = false; var adjustSize = (function() { var rowSize = +(scope.field.rowHeight) || 26; var minSize = 56; var elem = element; if (elem.is('.panel-related')) { elem = element.children('.panel-body'); minSize = 28; } else if (scope.$hasPanels) { minSize += 28; } if (elem.is('.picker-input')) { elem = null; } var inc = 0, height = +(scope.field.height) || 10; var maxSize = (rowSize * height) + minSize; var unwatch = scope.$watch('schema', function (schema) { if (schema) { unwatch(); unwatch = null; if (schema.rowHeight && schema.rowHeight !== rowSize) { rowSize = schema.rowHeight; maxSize = (rowSize * height) + minSize; } } }); return function(value) { inc = arguments[1] || inc; var count = Math.max(_.size(value), scope.dataView.getLength()) + inc, height = minSize; if (count > 0) { height = (rowSize * count) + (minSize + rowSize); } if (elem && adjustHeight) { elem.css('min-height', count ? Math.min(height, maxSize) : (minSize + 26)); } axelor.$adjustSize(); }; })(); var collapseIfEmpty = this.collapseIfEmpty; scope.$watch(attrs.ngModel, function o2mAdjustWatch(value){ if (!value) { // clear data view scope.dataView.setItems([]); } if (collapseIfEmpty) { adjustSize(value); } }); function deleteItemsById(id) { var items = scope.dataView.getItems() || []; while (items.length > 0) { var item = _.findWhere(items, {id: id}); var index = _.indexOf(items, item); if (index === -1) { break; } items.splice(index, 1); } return items; } scope.onGridInit = function(grid, inst) { var editIcon = scope.canView() || (!scope.isReadonly() && scope.canEdit()); var editable = grid.getOptions().editable && !axelor.device.mobile; adjustHeight = true; if (editable) { element.addClass('inline-editable'); scope.$on('on:new', function(event){ var items = deleteItemsById(0); if (items.length === 0) { scope.dataView.setItems([]); grid.setSelectedRows([]); } }); scope.$watch("isReadonly()", function o2mReadonlyWatch(readonly) { grid.setOptions({ editable: !readonly && scope.canEdit() }); var _editIcon = scope.canView() || (!readonly && scope.canEdit()); if (_editIcon != editIcon) { inst.showColumn('_edit_column', editIcon = _editIcon); } }); adjustSize(scope.getValue(), 1); } else { adjustSize(scope.getValue()); } inst.showColumn('_edit_column', editIcon); grid.onAddNewRow.subscribe(function (e, args) { var items = scope.getValue() || []; var rows = grid.getDataLength(); adjustSize(items, rows - items.length + 1); }); scope.dataView.onRowCountChanged.subscribe(function (e, args) { adjustSize(); }); if (!(scope._viewParams || {}).summaryView || scope.field.widget === "MasterDetail") { return; } var col = { id: '_summary', name: ' ', sortable: false, resizable: false, width: 16, formatter: function(row, cell, value, columnDef, dataContext) { return ''; } }; var cols = grid.getColumns(); cols.splice(0, 0, col); grid.setColumns(cols); grid.onClick.subscribe(function(e, args) { if ($(e.target).is('.fa-caret-right')) scope.$timeout(function(){ grid.setSelectedRows([args.row]); grid.setActiveCell(args.row, args.cell); scope.$timeout(function () { scope.onSummary(); }); }); }); }; scope.onGridBeforeSave = function(records) { if (!scope.editorCanSave) { deleteItemsById(0); scope.select(records); return false; } return true; }; scope.onGridAfterSave = function(records, args) { if (scope.editorCanSave) { scope.select(records); } }; scope.isDisabled = function() { return this.isReadonly(); }; var field = scope.field; if (field.widget === 'MasterDetail') { setTimeout(function(){ scope.showDetailView(); }); } scope.$watch("attr('title')", function o2mTitleWatch(title){ scope.title = title; }); }, template_editable: null, template_readonly: null, template: "
" + "" + "
" + "
" }); ui.formInput('ManyToMany', 'OneToMany', { css : 'many2many-item', controller: ManyToManyCtrl }); var panelRelatedTemplate = ""; ui.formInput('PanelOneToMany', 'OneToMany', { template_editable: null, template_readonly: null, template: panelRelatedTemplate }); ui.formInput('PanelManyToMany', 'ManyToMany', { template_editable: null, template_readonly: null, template: panelRelatedTemplate }); ui.formInput('TagSelect', 'ManyToMany', 'MultiSelect', { css : 'many2many-tags', showTitle: true, init: function(scope) { this._super(scope); var nameField = scope.field.targetName || 'id'; scope.parse = function(value) { return value; }; scope.formatItem = function(item) { if (item && scope._items && item.id in scope._items) { item = scope._items[item.id]; } return item ? item[nameField] : item; }; scope.getItems = function() { return _.pluck(this.getSelection(), "value"); }; var _select = scope.select; scope.select = function (value) { var res = _select.apply(scope, arguments); scope.itemsPending = []; return res; }; scope.handleClick = function(e, item) { if (scope.field['tag-edit'] && scope.onTagEdit && !scope.isReadonly()) { return scope.onTagEdit(e, item); } scope.showEditor(item); }; }, link: function(scope, element, attrs, model) { this._super.apply(this, arguments); var field = scope.field; // special case for json fields if (field.jsonField) { var nameField = scope.field.targetName; var ds = scope._dataSource._new(scope._dataSource._model); scope.$watch(attrs.ngModel, function m2mValueWatch(value, old) { scope._items = null; if (value && value.length && value[0][nameField] === undefined) { ds.search({ fields: ['id', nameField], domain: "id in :ids", context: { ids: _.pluck(value, 'id') } }).success(function (records) { scope._items = _.reduce(records, function(memo, item) { memo[item.id] = item; return memo; }, {}); model.$render(); }); } }, true); } }, link_editable: function(scope, element, attrs, model) { this._super.apply(this, arguments); var input = this.findInput(element); var field = scope.field; function create(term, popup) { scope.createOnTheFly(term, popup, function (record) { scope.select(record); setTimeout(function() { input.focus(); }); }); } scope.loadSelection = function(request, response) { if (!scope.canSelect()) { return response([]); } this.fetchSelection(request, function(items, page) { var term = request.term; var text = '' + term + ''; var canSelect = scope.canSelect() && (items.length < page.total || (request.term && items.length === 0)); if (field.create && term && scope.canNew()) { items.push({ label : _t('Create "{0}" and select...', text), click : function() { create(term); } }); items.push({ label : _t('Create "{0}"...', text), click : function() { create(term, true); } }); } if (canSelect) { items.push({ label : _t("Search more..."), click : function() { scope.showSelector(); } }); } if ((field.create === undefined || (field.create && !term)) && scope.canNew()) { items.push({ label: _t("Create..."), click: function() { scope.showPopupEditor(); } }); } response(items); }); }; scope.matchValues = function(a, b) { if (a === b) return true; if (!a) return false; if (!b) return false; return a.id === b.id; }; var _setValue = scope.setValue; scope.setValue = function(value, fireOnChange) { var items = _.map(value, function(item) { if (item.version === undefined) { return item; } var ver = item.version; var val = _.omit(item, "version"); val.$version = ver; return val; }); items = _.isEmpty(items) ? null : items; return _setValue.call(this, items, fireOnChange); }; var _handleSelect = scope.handleSelect; scope.handleSelect = function(e, ui) { if (ui.item.click) { setTimeout(function(){ input.val(""); }); ui.item.click.call(scope); return scope.$applyAsync(); } return _handleSelect.apply(this, arguments); }; var _removeItem = scope.removeItem; scope.removeItem = function(e, ui) { if (scope.attr('canRemove') === false) return; _removeItem.apply(this, arguments); }; if (scope.field && scope.field['tag-edit']) { scope.attachTagEditor(scope, element, attrs); } } }); ui.InlineOneToManyCtrl = InlineOneToManyCtrl; ui.InlineOneToManyCtrl.$inject = ['$scope', '$element', 'DataSource', 'ViewService']; function InlineOneToManyCtrl($scope, $element, DataSource, ViewService) { var field = $scope.field || $scope.getViewDef($element); var params = { model: field.target }; if (field.editor) { params.views = [{ type: 'grid', items: field.editor.items }]; } $scope._viewParams = params; OneToManyCtrl.call(this, $scope, $element, DataSource, ViewService, function(){ $scope.editorCanSave = false; $scope.selectEnable = false; }); } // used in panel form ui.formInput('InlineOneToMany', 'OneToMany', { showTitle: true, controller: InlineOneToManyCtrl, link: function(scope, element, attrs, model) { this._super.apply(this, arguments); scope.onGridInit = function() {}; scope.items = []; var showOnNew = (scope.field.editor||{}).showOnNew !== false; var unwatch = null; function canAdd() { return scope.hasPermission("write") && !scope.isDisabled() && scope.canNew(); } model.$render = function () { if (unwatch) { unwatch(); unwatch = null; } scope.items = model.$viewValue; if ((scope.items && scope.items.length > 0) || scope.$$readonly) { return; } scope.items = showOnNew && canAdd() ? [{}] : []; unwatch = scope.$watch('items[0]', function o2mFirstItemWatch(item, old) { if (!item) return; if (item.$changed) { unwatch(); model.$setViewValue(scope.items); } item.$changed = true; }, true); }; function isEmpty(record) { if (!record || _.isEmpty(record)) return true; var values = _.filter(record, function (value, name) { return !(/[\$_]/.test(name) || value === null || value === undefined); }); return values.length === 0; } function itemsChanged() { var items = scope.items; if (items && items.length > 0) { var changed = false; var values = _.filter(items, function (item) { if (item.$changed) { changed = true; } return !isEmpty(item); }); if (changed && values.length) { model.$setViewValue(values); } } } scope.$watch('items', itemsChanged, true); scope.$itemsChanged = itemsChanged; scope.$watch('$$readonly', function o2mReadonlyWatch(readonly, old) { if (readonly === undefined) return; var items = model.$viewValue; if (_.isEmpty(items)) { scope.items = (showOnNew && canAdd() && !readonly) ? [{}] : items; } }); scope.$copy = function (record) { return angular.copy(record); }; scope.addItem = function () { var items = scope.items || (scope.items = []); var item = _.last(items); if (items && items.length && isEmpty(item)) { return; } if (canAdd()) { items.push({}); } }; scope.removeItem = function (index) { var items = scope.items; items.splice(index, 1); var values = _.filter(items, function (item) { return !isEmpty(item); }); if (items.length === 0 && showOnNew) { scope.addItem(); } model.$setViewValue(values); }; scope.canRemove = function () { return scope.attr('canRemove') !== false; }; scope.setValidity = function (key, value, record) { if (arguments.length === 3) { record.$valid = value; value = _.all(scope.items, function (x) { return x.$valid; }); } model.$setValidity(key, value); }; scope.setExclusive = function (name, record) { _.each(scope.items, function (item) { if (record !== item) { item[name] = false; } }); }; }, template_readonly:function (scope) { var field = scope.field; var tmpl = (field.viewer || {}).template; if (!tmpl && field.editor && (field.editor.viewer || !field.targetName)) { tmpl = '
'; } if (!tmpl && field.targetName) { tmpl = '{{record.' + field.targetName + '}}'; } tmpl = tmpl || '{{record.id}}'; return "
" + "
" + tmpl + "
" + "
"; }, template_editable: function (scope) { return "
" + "
" + "
" + "" + "" + "" + "
" + "
" + "" + "
" + "
"; }, template: null }); //used in panel form ui.formInput('InlineManyToMany', 'InlineOneToMany', { }); // used in editable grid ui.formInput('OneToManyInline', 'OneToMany', { css : 'one2many-inline', collapseIfEmpty : false, link: function(scope, element, attrs, model) { this._super.apply(this, arguments); scope.onSort = function() { }; var field = scope.field; var input = element.children('input'); var grid = element.children('[ui-slick-grid]'); var container = null; var wrapper = $('
') .css("position", "absolute") .hide(); var render = model.$render, renderPending = false; model.$render = function() { if (wrapper.is(":visible")) { renderPending = false; render(); grid.trigger('adjust:size'); } else { renderPending = true; } }; scope.waitForActions(function(){ container = element.parents('.ui-dialog-content,.view-container').first(); grid.height(175).appendTo(wrapper); wrapper.height(175).appendTo(container); }); function adjust() { if (!wrapper.is(":visible")) return; if (axelor.device.small) { dropdownVisible = false; return wrapper.hide(); } wrapper.position({ my: "left top", at: "left bottom", of: element, within: container }) .zIndex(element.zIndex() + 1) .width(element.width()); } var dropdownVisible = false; scope.onDropdown = function () { dropdownVisible = !dropdownVisible; if (!dropdownVisible) { wrapper.hide(); return; } if (renderPending) { renderPending = false; render(); setTimeout(function () { axelor.$adjustSize(); }); } wrapper.show(); adjust(); }; scope.canDropdown = function () { return !axelor.device.small; }; scope.canShowAdd = function () { return dropdownVisible && scope.canEdit(); }; scope.canShowRemove = function () { return dropdownVisible && scope.canRemove() && !_.isEmpty(scope.selection); }; element.on("hide:slick-editor", function(e){ dropdownVisible = false; wrapper.hide(); }); scope.$onAdjust(adjust, 300); input.on('keydown', function (e) { if (e.keyCode === 40 && e.ctrlKey && !dropdownVisible) { scope.onDropdown(); } }); function hidePopup(e) { if (element.is(':hidden')) { return; } var all = element.add(wrapper); var elem = $(e.target); if (all.is(elem) || all.has(elem).length > 0) return; if (elem.zIndex() > element.parents('.slickgrid:first').zIndex()) return; if (elem.parents(".ui-dialog:first").zIndex() > element.parents('.slickgrid:first').zIndex()) return; element.trigger('close:slick-editor'); } $(document).on('mousedown.mini-grid', hidePopup); scope.$watch(attrs.ngModel, function o2mModelWatch(value) { var text = ""; if (value && value.length) text = "(" + value.length + ")"; input.val(text); }); scope.$watch('schema.loaded', function o2mSchemaWatch(viewLoaded) { var schema = scope.schema; if (schema && scope.attr('canEdit') === false) { schema.editIcon = false; } }); scope.$on("$destroy", function(e){ wrapper.remove(); $(document).off('mousedown.mini-grid', hidePopup); }); scope.canEdit = function () { return scope.hasPermission('create') && !scope.isReadonly() && scope.attr('canEdit') !== false; }; scope.canRemove = function() { return scope.hasPermission('create') && !scope.isReadonly() && scope.attr('canEdit') !== false; }; }, template_editable: null, template_readonly: null, template: ''+ ''+ ''+ ''+ ''+ ''+ ''+ '
'+ '
' }); ui.formInput('ManyToManyInline', 'OneToManyInline', { css : 'many2many-inline', controller: ManyToManyCtrl, link: function(scope, element, attrs, model) { this._super.apply(this, arguments); } }); })();