Last active
June 16, 2020 14:22
-
-
Save SiddiqueAhmad/d3766b3f11d823a09b8ea82760abbb5b to your computer and use it in GitHub Desktop.
related to selection based on dynamic select list
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Axelor Business Solutions | |
* | |
* Copyright (C) 2005-2020 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'); | |
var equals = angular.equals, | |
forEach = angular.forEach, | |
isArray = angular.isArray, | |
isObject = angular.isObject, | |
isDate = angular.isDate; | |
function dummyEquals(a, b) { | |
if (a === b) return true; | |
if (a === null || b === null) return false; | |
if (a !== a && b !== b) return true; // NaN === NaN | |
var keys = _.keys(a).filter(function (k) { return k.indexOf('$') === 0; }); | |
if (keys.length === 0) { | |
return true; | |
} | |
for (var i = 0; i < keys.length; i++) { | |
var k = keys[i]; | |
if (!equals(a[k], b[k])) { | |
return false; | |
} | |
} | |
return true; | |
} | |
function updateValues(source, target, itemScope, formScope) { | |
if (equals(source, target) && dummyEquals(source, target) && (!source || !source.$force)) { | |
return; | |
} | |
// handle json records | |
if (source && formScope && formScope._model === 'com.axelor.meta.db.MetaJsonRecord') { | |
if (source.attrs || source.id) { | |
source = source.id > 0 | |
? _.pick(source, 'jsonModel', 'name', 'attrs', 'id', 'version') | |
: _.pick(source, 'jsonModel', 'name', 'attrs'); | |
} | |
var values = source.attrs ? _.extend({}, JSON.parse(source.attrs)) : source; | |
var fix = function (rec) { | |
if (!rec) return rec; | |
if (_.isArray(rec)) return _.map(rec, fix); | |
if (rec.id > 0 && (rec.version || rec.attrs)) { | |
rec = _.pick(rec, 'id', 'name', 'selected'); | |
if (!rec.selected) delete rec.selected; | |
} | |
return rec; | |
}; | |
_.each(values, function (v, k) { | |
values[k] = fix(v); | |
}); | |
// if called from form fields | |
if (itemScope && itemScope.updateJsonValues) { | |
return itemScope.updateJsonValues(values); | |
} | |
// onNew or onSave from main form | |
var current = target && target.attrs ? JSON.parse(target.attrs) : {}; | |
if (source.attrs || !source.jsonModel) { | |
source.attrs = JSON.stringify(_.extend({}, current, values)); | |
} | |
} else if (itemScope && itemScope.updateJsonValues) { | |
return itemScope.updateJsonValues(source); | |
} | |
function compact(value) { | |
if (!value) return value; | |
if (value.version === undefined) return value; | |
if (!value.id) return value; | |
var res = _.extend(value); | |
res.$version = res.version; | |
res.version = undefined; | |
return res; | |
} | |
var changed = false; | |
forEach(source, function(value, key) { | |
var dest; | |
var newValue = value; | |
var oldValue = target[key]; | |
if (oldValue === newValue) { | |
return; | |
} | |
if (isArray(value)) { | |
dest = target[key] || []; | |
newValue = _.map(value, function(item) { | |
var found = _.find(dest, function(v){ | |
return item.id && v.id === item.id; | |
}); | |
if (_.has(item, "version") && item.id) item.$fetched = true; | |
if (found) { | |
var found_ = _.extend({}, found); | |
var changed_ = updateValues(item, found_); | |
changed = changed || changed_; | |
return changed_ ? found_ : found; | |
} | |
return item; | |
}); | |
} else if (isObject(value) && !isDate(value)) { | |
dest = target[key] || {}; | |
if (dest.id === value.id) { | |
if (_.isNumber(dest.version)) { | |
dest = _.extend({}, dest); | |
changed = updateValues(value, dest, itemScope, formScope) || changed; | |
} else { | |
dest.$updatedValues = value; | |
if (formScope) { | |
formScope.$broadcast('on:check-nested-values', value); | |
} | |
} | |
} else { | |
dest = compact(value); | |
} | |
newValue = dest; | |
} | |
if (!equals(oldValue, newValue)) { | |
changed = true; | |
target[key] = newValue; | |
} | |
}); | |
if (target && changed) { | |
target.$dirty = true; | |
} | |
return changed; | |
} | |
function handleError(scope, item, message) { | |
if (!item) { | |
return; | |
} | |
var ctrl = item.data('$ngModelController'); | |
if (!ctrl) { | |
return; | |
} | |
if (ctrl.$doReset) { | |
ctrl.$doReset(); | |
} | |
if (!message) { | |
ctrl.$doReset = null; | |
return; | |
} | |
var e = $('<span class="error"></span>').text(message); | |
var p = item.parent('.form-item'); | |
if (item.children(':first').is(':input,.input-append,.picker-input')) { | |
p.append(e); | |
} else { | |
p.prepend(e); | |
} | |
var clear = scope.$on('on:edit', function(){ | |
ctrl.$doReset(); | |
}); | |
function cleanUp(items) { | |
var idx = items.indexOf(ctrl.$doReset); | |
if (idx > -1) { | |
items.splice(idx, 1); | |
} | |
} | |
ctrl.$doReset = function(value) { | |
cleanUp(ctrl.$viewChangeListeners); | |
cleanUp(ctrl.$formatters); | |
ctrl.$setValidity('invalid', true); | |
ctrl.$doReset = null; | |
e.remove(); | |
clear(); | |
return value; | |
}; | |
if (!item.hasClass('readonly')) { | |
ctrl.$setValidity('invalid', false); | |
} | |
ctrl.$viewChangeListeners.push(ctrl.$doReset); | |
ctrl.$formatters.push(ctrl.$doReset); | |
} | |
function ActionHandler($scope, ViewService, options) { | |
if (!options || !options.action) | |
throw 'No action provided.'; | |
this.canSave = options.canSave; | |
this.name = options.name; | |
this.prompt = options.prompt; | |
this.action = options.action; | |
this.element = options.element || $(); | |
this.scope = $scope; | |
this.ws = ViewService; | |
this.viewType = $scope.viewType; | |
} | |
ActionHandler.prototype = { | |
constructor: ActionHandler, | |
onLoad : function() { | |
return this.handle(); | |
}, | |
onNew: function() { | |
return this.handle(); | |
}, | |
onSave: function() { | |
var self = this; | |
return this._fireBeforeSave().then(function() { | |
return self.handle(); | |
}); | |
}, | |
onTabSelect: function(unblocked) { | |
return this.onSelect.apply(this, arguments); | |
}, | |
onSelect: function(unblocked) { | |
var self = this; | |
var blockUI = this._blockUI; | |
if (unblocked) { | |
this._blockUI = angular.noop; | |
} | |
function reset() { | |
self._blockUI = blockUI; | |
} | |
var promise = this.handle(); | |
promise.then(reset, reset); | |
return promise; | |
}, | |
onClick: function(event) { | |
var self = this; | |
var prompt = this._getPrompt(); | |
if (prompt) { | |
var deferred = this.ws.defer(), | |
promise = deferred.promise; | |
axelor.dialogs.confirm(prompt, function(confirmed){ | |
if (confirmed) { | |
self._fireBeforeSave().then(function() { | |
self.handle().then(deferred.resolve, deferred.reject); | |
}); | |
} else { | |
self.scope.$timeout(deferred.reject); | |
} | |
}, { | |
yesNo: false | |
}); | |
return promise; | |
} | |
return this._fireBeforeSave().then(function() { | |
return self.handle(); | |
}); | |
}, | |
onChange: function(event) { | |
return this.handle({ wait: 100 }); | |
}, | |
_getPrompt: function () { | |
var prompt = this.prompt; | |
var itemScope = this.element.scope(); | |
if (_.isFunction(itemScope.attr) && !this.element.is('[ui-slick-grid]')) { | |
prompt = itemScope.attr('prompt') || prompt; | |
} | |
return _.isString(prompt) ? prompt : null; | |
}, | |
_getContext: function() { | |
var scope = this.scope, | |
context = scope.getContext ? scope.getContext() : scope.record, | |
viewParams = scope._viewParams || {}; | |
context = _.extend({}, viewParams.context, context); | |
if (context._model === undefined) { | |
context._model = scope._model; | |
} | |
// include button name as _signal (used by workflow engine) | |
if (this.element.is("button,a.button-item,li.action-item")) { | |
context._signal = this.element.attr('name') || this.element.attr('x-name'); | |
} | |
return context; | |
}, | |
_getRootFormElement: function () { | |
var formElement = $(this.element).parents('form[ui-form]:last'); | |
if (formElement.length === 0) { | |
formElement = this._getFormElement(); | |
} | |
return formElement; | |
}, | |
_getFormElement: function () { | |
var elem = $(this.element); | |
var formElement = elem; | |
if (formElement.is('form')) { | |
return formElement; | |
} | |
formElement = elem.data('$editorForm') || elem.parents('form:first'); | |
if (!formElement || !formElement.get(0)) { // toolbar button | |
formElement = this.element.parents('.form-view:first').find('form:first'); | |
} | |
if (formElement.length === 0) { | |
formElement = this.element; | |
} | |
return formElement; | |
}, | |
handle: function(options) { | |
var that = this; | |
var action = this.action.trim(); | |
var deferred = this.ws.defer(); | |
var all = this.scope.$actionPromises || []; | |
var pending = all.slice(); | |
var opts = _.extend({}, options); | |
all.push(deferred.promise); | |
this.scope.waitForActions(function () { | |
var promise = that._handleAction(action); | |
function done() { | |
setTimeout(function () { | |
var i = all.indexOf(deferred.promise); | |
if (i > -1) { | |
all.splice(i, 1); | |
} | |
}, 10); | |
} | |
promise.then(done, done); | |
promise.then(deferred.resolve, deferred.reject); | |
}, opts.wait || 10, pending); | |
return deferred.promise; | |
}, | |
_blockUI: function() { | |
// block the entire ui (auto unblocks when actions are complete) | |
_.delay(axelor.blockUI, 10); | |
}, | |
_fireBeforeSave: function() { | |
var scope = this._getRootFormElement().scope(); | |
var event = scope.$broadcast('on:before-save', scope.record); | |
var deferred = this.ws.defer(); | |
if (event.defaultPrevented) { | |
if (event.error) { | |
axelor.dialogs.error(event.error); | |
} | |
setTimeout(function() { | |
deferred.reject(event.error); | |
}); | |
} else { | |
scope.$timeout(function() { | |
scope.ajaxStop(function() { | |
deferred.resolve(); | |
}, 100); | |
}, 50); | |
} | |
return deferred.promise; | |
}, | |
_checkVersion: function() { | |
var self = this; | |
var scope = this.scope; | |
var deferred = this.ws.defer(); | |
if (scope.checkVersion) { | |
scope.checkVersion(function (verified) { | |
if (verified) { | |
return deferred.resolve(); | |
} | |
axelor.dialogs.error( | |
_t("The record has been updated or delete by another action.")); | |
deferred.reject(); | |
}); | |
} else { | |
deferred.resolve(); | |
} | |
return deferred.promise; | |
}, | |
_handleNew: function() { | |
var self = this; | |
var scope = this.scope; | |
var deferred = this.ws.defer(); | |
if (scope.onNew) { | |
return scope.onNew(); | |
} | |
if (scope.editRecord) { | |
scope.editRecord(null); | |
deferred.resolve(); | |
} else { | |
deferred.reject(); | |
} | |
return deferred.promise; | |
}, | |
_handleSave: function(validateOnly) { | |
if (validateOnly) { | |
return this.__handleSave(validateOnly); | |
} | |
var self = this; | |
var deferred = this.ws.defer(); | |
this._checkVersion().then(function () { | |
self.__handleSave().then(deferred.resolve, deferred.reject); | |
}, deferred.reject); | |
return deferred.promise; | |
}, | |
__handleSave: function(validateOnly) { | |
var self = this; | |
var scope = this.scope; | |
var id = (scope.record||{}).id; | |
var o2mPopup = scope._isPopup && (scope.$parent.field||{}).serverType === "one-to-many"; | |
if (o2mPopup && !validateOnly && this.name == 'onLoad' && (!id || id < 0)) { | |
var deferred = this.ws.defer(); | |
var msg = _t("The {0}={1} event can't call 'save' action on unsaved o2m item.", this.name, this.action); | |
deferred.reject(msg); | |
console.error(msg); | |
return deferred.promise; | |
} | |
return this._fireBeforeSave().then(function() { | |
return self.__doHandleSave(validateOnly); | |
}); | |
}, | |
__doHandleSave: function(validateOnly) { | |
this._blockUI(); | |
// save should be done on root form scope only | |
var rootForm = this._getRootFormElement(); | |
var scope = rootForm.is('[ui-view-grid]') ? this.scope : rootForm.scope(); | |
var deferred = this.ws.defer(); | |
if (scope.isValid && !scope.isValid()) { | |
if (scope.showErrorNotice) { | |
scope.showErrorNotice(); | |
} else { | |
axelor.notify.error(_t('Please correct the invalid form values.'), { | |
title: _t('Validation error') | |
}); | |
} | |
deferred.reject(); | |
return deferred.promise; | |
} | |
if (validateOnly || (scope.isDirty && !scope.isDirty())) { | |
deferred.resolve(); | |
return deferred.promise; | |
} | |
function doEdit(rec) { | |
var params = scope._viewParams || {}; | |
scope.editRecord(rec); | |
if (params.$viewScope) { | |
params.$viewScope.updateRoute(); | |
} | |
deferred.resolve(); | |
} | |
function doSave(values) { | |
var ds = scope._dataSource; | |
ds.save(values).success(function(rec, page) { | |
if (scope.doRead) { | |
return scope.doRead(rec.id).success(doEdit); | |
} | |
return ds.read(rec.id).success(doEdit); | |
}); | |
} | |
var values = _.extend({ _original: scope.$$original }, scope.record); | |
if (scope.onSave) { | |
scope.onSave({ | |
values: values, | |
callOnSave: false, | |
wait: false | |
}).then(deferred.resolve, deferred.reject); | |
} else { | |
doSave(values); | |
} | |
this._invalidateContext = true; | |
return deferred.promise; | |
}, | |
_closeView: function (scope) { | |
if (scope.onOK) { | |
return scope.onOK(); | |
} | |
var tab = scope._viewParams || scope.selectedTab; | |
if (scope.closeTab) { | |
scope.closeTab(tab); | |
} else if (scope.$parent) { | |
this._closeView(scope.$parent); | |
} | |
}, | |
_isSameViewType: function () { | |
return this.viewType === this.scope.viewType; | |
}, | |
_handleAction: function(action) { | |
this._blockUI(); | |
var self = this, | |
scope = this.scope, | |
context = this._getContext(), | |
deferred = this.ws.defer(); | |
if (!this._isSameViewType()) { | |
deferred.reject(); | |
return deferred.promise; | |
} | |
function resolveLater() { | |
deferred.resolve(); | |
return deferred.promise; | |
} | |
function chain(items) { | |
var first = _.first(items); | |
if (first === undefined) { | |
return resolveLater(); | |
} | |
return self._handleSingle(first).then(function(pending) { | |
if (_.isString(pending) && pending.trim().length) { | |
return self._handleAction(pending); | |
} | |
var _deferred = self.ws.defer(); | |
scope.$timeout(function () { | |
scope.ajaxStop(function() { | |
_deferred.resolve(); | |
}); | |
}); | |
return _deferred.promise.then(function () { | |
return chain(_.rest(items)); | |
}); | |
}); | |
} | |
if (!action) { | |
return resolveLater(); | |
} | |
action = action.replace(/(^\s*,?\s*)|(\s*,?\s*$)/, ''); | |
var pattern = /,\s*(sync)\s*(,|$)/; | |
if (pattern.test(action)) { | |
var which = pattern.exec(action)[1]; | |
axelor.dialogs.error(_t('Invalid use of "{0}" action, must be the first action.', which)); | |
deferred.reject(); | |
return deferred.promise; | |
} | |
pattern = /(^sync\s*,\s*)|(^sync$)/; | |
if (pattern.test(action)) { | |
action = action.replace(pattern, ''); | |
return this._fireBeforeSave().then(function() { | |
return self._handleAction(action); | |
}); | |
} | |
pattern = /(^|,)\s*(new)\s*,/; | |
if (pattern.test(action)) { | |
var which = pattern.exec(action)[2]; | |
axelor.dialogs.error(_t('Invalid use of "{0}" action, must be the last action.', which)); | |
deferred.reject(); | |
return deferred.promise; | |
} | |
pattern = /(^|,)\s*(close)\s*,/; | |
if (pattern.test(action)) { | |
axelor.dialogs.error(_t('Invalid use of "{0}" action, must be the last action.', pattern.exec(action)[2])); | |
deferred.reject(); | |
return deferred.promise; | |
} | |
if (action === 'close') { | |
this._closeView(scope); | |
deferred.resolve(); | |
return deferred.promise; | |
} | |
if (action === 'new') { | |
return this._handleNew(); | |
} | |
if (action === 'validate') { | |
return this._handleSave(true); | |
} | |
if (action === 'save') { | |
return this._handleSave(); | |
} | |
if (this._invalidateContext) { | |
context = this._getContext(); | |
this._invalidateContext = false; | |
} | |
var model = context._model || scope._model; | |
var data = scope.getActionData ? scope.getActionData(context) : null; | |
if (data && context._signal) { | |
data._signal = context._signal; | |
} | |
var promise = this.ws.action(action, model, context, data).then(function(response){ | |
var resp = response.data, | |
data = resp.data || []; | |
if (resp.errors) { | |
data.splice(0, 0, { | |
errors: resp.errors | |
}); | |
} | |
return chain(data); | |
}); | |
promise.then(deferred.resolve, deferred.reject); | |
return deferred.promise; | |
}, | |
_handleSingle: function(data) { | |
var deferred = this.ws.defer(); | |
if (!data || data.length === 0) { | |
deferred.resolve(); | |
return deferred.promise; | |
} | |
if (!this._isSameViewType()) { | |
deferred.reject(); | |
return deferred.promise; | |
} | |
var self = this, | |
scope = this.scope, | |
formElement = this._getFormElement(), | |
formScope = formElement.data('$scope') || scope, | |
rootForm = this._getRootFormElement(), | |
rootScope = rootForm.is('[ui-view-grid]') ? scope : rootForm.scope(); | |
function doReload(pending) { | |
self._invalidateContext = true; | |
var promise = _.isFunction(rootScope.reload) ? rootScope.reload() : scope.reload(); | |
if (promise) { | |
promise.then(function(){ | |
deferred.resolve(pending); | |
}, deferred.reject); | |
} else { | |
deferred.resolve(pending); | |
} | |
return deferred.promise; | |
} | |
if (data.exportFile) { | |
(function () { | |
var link = "ws/files/data-export/" + data.exportFile; | |
var frame = $('<iframe>').appendTo('body').hide(); | |
frame.attr("src", link); | |
setTimeout(function(){ | |
frame.attr("src", ""); | |
frame.remove(); | |
frame = null; | |
}, 5000); | |
})(); | |
} | |
if (data.signal === 'refresh-app') { | |
if(data.flash || data.info) { | |
axelor.dialogs.box(data.flash || data.info, { | |
onClose: function () { | |
window.location.reload(); | |
} | |
}); | |
} else { | |
window.location.reload(); | |
} | |
return deferred.promise; | |
} | |
if(data.flash || data.info) { | |
axelor.dialogs.box(data.flash || data.info, { | |
onClose: function () { | |
if (data.pending) { | |
scope.$applyAsync(function(){ | |
if (data.reload) { | |
return doReload(data.pending); | |
} | |
deferred.resolve(data.pending); | |
}); | |
} | |
} | |
}); | |
if (data.pending) { | |
return deferred.promise; | |
} | |
} | |
if(data.notify) { | |
axelor.notify.info(data.notify); | |
} | |
if(data.error) { | |
axelor.dialogs.error(data.error, function(){ | |
scope.$applyAsync(function(){ | |
if (data.action) { | |
self._handleAction(data.action); | |
} | |
deferred.reject(); | |
}); | |
}); | |
return deferred.promise; | |
} | |
if (data.alert) { | |
axelor.dialogs.confirm(data.alert, function(confirmed){ | |
scope.$applyAsync(function(){ | |
if (confirmed) { | |
return deferred.resolve(data.pending); | |
} | |
if (data.action) { | |
self._handleAction(data.action); | |
} | |
deferred.reject(); | |
}); | |
}, { | |
title: _t('Warning'), | |
yesNo: false | |
}); | |
return deferred.promise; | |
} | |
if (!_.isEmpty(data.errors)) { | |
var hasError = false; | |
_.each(data.errors, function(v, k){ | |
var item = (findItems(k) || $()).first(); | |
handleError(scope, item, v); | |
if(v && v.length > 0) { | |
hasError = true; | |
} | |
}); | |
if(hasError) { | |
deferred.reject(); | |
return deferred.promise; | |
} | |
} | |
if (data.values) { | |
updateValues(data.values, scope.record, scope, formScope); | |
if (scope.onChangeNotify) { | |
scope.onChangeNotify(scope, data.values); | |
} | |
this._invalidateContext = true; | |
axelor.$adjustSize(); | |
} | |
if (data.reload) { | |
return (function () { | |
var promise = doReload(data.pending); | |
if (data.view) { | |
promise.then(function () { | |
doOpenView(data.view); | |
}); | |
} | |
return promise; | |
})(); | |
} | |
if (data.validate || data.save) { | |
scope.$timeout(function () { | |
self._handleSave(!!data.validate).then(function(){ | |
scope.ajaxStop(function () { | |
deferred.resolve(data.pending); | |
}, 100); | |
}, deferred.reject); | |
}); | |
return deferred.promise; | |
} | |
if (data['new']) { | |
scope.$timeout(function () { | |
self._handleNew().then(function(){ | |
scope.ajaxStop(function () { | |
deferred.resolve(data.pending); | |
}, 100); | |
}, deferred.reject); | |
}); | |
return deferred.promise; | |
} | |
if (data.signal) { | |
formScope.$broadcast(data.signal, data['signal-data']); | |
} | |
function findItems(name) { | |
var items; | |
var toolbar; | |
var containers; | |
if (formElement.is('[ui-slick-editors]')) { | |
containers = formElement.parent().add(formElement); | |
} else if (formElement.parent().is('[ui-slick-editors],.slick-cell')) { | |
containers = formElement.parent().parent().add(formElement); | |
} else if (formElement.parent().is('[ui-panel-editor]')) { | |
containers = formElement.parent().add(formElement).is('.m2o-editor-form,.o2m-editor-form') ? formElement : formElement.parents('[ui-form]:first').add(formElement); | |
} else { | |
containers = formElement; | |
toolbar = formElement.parents('.form-view:first,.search-view:first') | |
.find('.record-toolbar:first,.search-view-toolbar:first'); | |
} | |
var formPath = formScope.formPath; | |
if (formScope._model === 'com.axelor.meta.db.MetaJsonRecord') { | |
formPath = formPath || 'attrs'; | |
} | |
items = containers.find('[x-path="' + (formPath ? formPath + '.' + name : name) + '"]'); | |
if (items.length === 0 && formPath != 'attrs') { | |
items = containers.find('[x-path="attrs.' + name + '"]'); | |
} | |
if (toolbar) { | |
return toolbar.find('[name="' + name + '"],[x-name="' + name + '"]').add(items); | |
} | |
return items; | |
} | |
function setAttrs(item, itemAttrs, itemIndex) { | |
var label = item.data('label'), | |
itemScope = item.data('$scope'), | |
hasValues = false, | |
column; | |
if (item.is('[ui-menu-item]')) { | |
itemScope = item.isolateScope(); | |
} | |
// handle o2m/m2m columns | |
if (item.is('.slick-dummy-column')) { | |
column = item.data('column'); | |
itemScope = item.parents('[x-path]:first,.portlet-grid').data('$scope'); | |
forEach(itemAttrs, function(value, attr){ | |
if (attr == 'hidden') | |
itemScope.showColumn(column.id, !value); | |
if (attr == 'title') | |
setTimeout(function(){ | |
itemScope.setColumnTitle(column.id, value); | |
}); | |
}); | |
return; | |
} | |
//handle o2m/m2m title | |
if(item.is('.one2many-item') || item.is('.many2many-item')){ | |
forEach(itemAttrs, function(value, attr){ | |
if (attr == 'title') { | |
itemScope.title = value; | |
} | |
}); | |
} | |
// handle notebook | |
if (item.is('.tab-pane')) { | |
forEach(itemAttrs, function(value, attr){ | |
if (attr == 'hidden') { | |
itemScope.attr('hidden', value); | |
} | |
if (attr == 'title') { | |
itemScope.title = value; | |
} | |
}); | |
return; | |
} | |
function isDotted() { | |
var name = item.attr('x-field') || ''; | |
var dotted = name.indexOf('.') > -1; | |
if (dotted) { | |
itemAttrs.$hasDotted = true; | |
} | |
return dotted; | |
} | |
forEach(itemAttrs, function(value, attr){ | |
if ((attr === "value" || attr.indexOf('value:') === 0)) { | |
hasValues = true; | |
if (itemScope && itemScope.$setForceWatch) { | |
itemScope.$setForceWatch(true); | |
} | |
if (isDotted()) return; | |
if (itemAttrs.$hasDotted) { | |
itemAttrs.$hasDotted = false; | |
} else if (itemIndex > 0) { | |
return; | |
} | |
} | |
switch(attr) { | |
case 'hidden': | |
if (itemScope.field && itemScope.field.hideIf === "true") return; | |
case 'required': | |
case 'readonly': | |
case 'collapse': | |
case 'precision': | |
case 'scale': | |
case 'prompt': | |
case 'css': | |
case 'icon': | |
case 'selection-in': | |
itemScope.attr(attr, value); | |
break; | |
case 'title': | |
(function () { | |
var span = $(label).add(item).children('span[ui-help-popover]:first'); | |
if (span.length === 0) { | |
span = label; | |
} | |
if (span && span.length > 0) { | |
span.html(value); | |
} else if (item.is('label')) { | |
item.html(value); | |
} | |
})(); | |
itemScope.attr('title', value); | |
break; | |
case 'domain': | |
if (itemScope.setDomain) | |
itemScope.setDomain(value); | |
break; | |
case 'refresh': | |
itemScope.waitForActions(function () { | |
itemScope.$broadcast('on:attrs-change:refresh'); | |
}, 100); | |
break; | |
case 'url': | |
case 'url:set': | |
if (item.is('[ui-portlet]')) { | |
item.find('iframe:first').attr('src', value); | |
} | |
break; | |
case 'value': | |
case 'value:set': | |
if (itemScope.setValue) { | |
itemScope.setValue(value); | |
} | |
break; | |
case 'value:add': | |
if (itemScope.fetchData && itemScope.select) { | |
itemScope.fetchData(value, function(records){ | |
itemScope.select(records); | |
}); | |
} | |
break; | |
case 'value:del': | |
if (itemScope.removeItems) { | |
itemScope.removeItems(value); | |
} | |
break; | |
case 'selection': | |
//itemScope.attr(attr, value); | |
itemScope.field.selection = value; | |
itemScope.loadSelection(); | |
break; | |
} | |
}); | |
if (hasValues && formScope.onChangeNotify) { | |
formScope.onChangeNotify(formScope, formScope.record); | |
} | |
} | |
forEach(data.attrs, function(itemAttrs, itemName) { | |
var items = findItems(itemName); | |
if (!items || items.length === 0) { | |
// dashlet still not loaded ? | |
if (itemName.indexOf('.') > -1) { | |
var parentName = itemName.substring(0, itemName.indexOf('.')); | |
var parentElem = findItems(parentName); | |
if (parentElem.is('[ui-dashlet]')) { | |
parentElem.scope().$$pendingAttrs = parentElem.scope().$$pendingAttrs || {}; | |
parentElem.scope().$$pendingAttrs[itemName.substring(itemName.indexOf('.')+1)] = itemAttrs; | |
} | |
} | |
return; | |
} | |
items.each(function(i) { | |
setAttrs($(this), itemAttrs, i); | |
}); | |
}); | |
if (data.report) { | |
return openReport(data); | |
} | |
function openReport(data) { | |
var record = formScope.record || {}; | |
if (data.attached) { | |
record.$attachments = (record.$attachments || 0) + 1; | |
axelor.dialogs.confirm(_t('Report attached to current object. Would you like to download?'), | |
function(confirmed) { | |
scope.$applyAsync(function() { | |
if (confirmed) { | |
var url = "ws/rest/com.axelor.meta.db.MetaFile/" + data.attached.id + "/content/download"; | |
ui.download(url); | |
return deferred.resolve(); | |
} | |
deferred.reject(); | |
}); | |
}, { | |
title: _t('Download'), | |
yesNo: false | |
}); | |
return deferred.promise; | |
} | |
var url = "ws/files/report/" + data.reportLink + "?name=" + data.reportFile; | |
var tab = { | |
title: data.reportFile, | |
resource: url, | |
viewType: 'html' | |
}; | |
if (axelor.device.mobile && data.reportFormat !== "html") { | |
ui.download(url, data.reportFile); | |
} else if (['pdf', 'html'].indexOf(data.reportFormat) > -1) { | |
doOpenView(tab); | |
} else { | |
ui.download(url); | |
} | |
scope.$timeout(deferred.resolve); | |
return deferred.promise; | |
} | |
function openTab(scope, tab) { | |
if (scope.openTab) { | |
scope.openTab(tab); | |
} else if (scope.$parent) { | |
openTab(scope.$parent, tab); | |
} | |
} | |
function doOpenView(tab) { | |
tab.action = _.uniqueId('$act'); | |
if (!tab.viewType) | |
tab.viewType = 'grid'; | |
if (tab.viewType == 'grid' || tab.viewType == 'form') | |
tab.model = tab.model || tab.resource; | |
if (!tab.views) { | |
tab.views = [{ type: tab.viewType }]; | |
if (tab.viewType === 'html') { | |
angular.extend(tab.views[0], { | |
resource: tab.resource, | |
title: tab.title | |
}); | |
} | |
} | |
if (tab.viewType === "html" && (tab.params||{}).download) { | |
var view = _.findWhere(tab.views, { type: "html" }); | |
if (view) { | |
var url = view.name || view.resource; | |
var fileName = tab.params.fileName || "true"; | |
ui.download(url, fileName); | |
return scope.$applyAsync(); | |
} | |
} | |
if (tab.viewType === "html" && (tab.params||{}).target === "_blank") { | |
var view = _.findWhere(tab.views, { type: "html" }); | |
if (view) { | |
var url = view.name || view.resource; | |
setTimeout(function () { | |
window.open(url); | |
}); | |
return scope.$applyAsync(); | |
} | |
} | |
if ((tab.params && tab.params.popup) || axelor.device.mobile) { | |
tab.$popupParent = formScope; | |
} | |
openTab(scope, tab); | |
scope.$applyAsync(); | |
} | |
if (data.view) { | |
doOpenView(data.view); | |
} | |
if (data.close || data.canClose) { | |
this._closeView(scope); | |
} | |
deferred.resolve(); | |
return deferred.promise; | |
} | |
}; | |
ui.factory('ActionService', ['ViewService', function(ViewService) { | |
function handler(scope, element, options) { | |
var opts = _.extend({}, options, { element: element }); | |
return new ActionHandler(scope, ViewService, opts); | |
} | |
return { | |
handler: handler | |
}; | |
}]); | |
var EVENTS = ['onClick', 'onChange', 'onSelect', 'onTabSelect', 'onNew', 'onLoad', 'onSave']; | |
ui.directive('uiActions', ['ViewService', function(ViewService) { | |
function link(scope, element, attrs) { | |
var props = _.isEmpty(scope.field) ? scope.schema : scope.field; | |
if (!props) { | |
return; | |
} | |
_.each(EVENTS, function(name){ | |
var action = props[name]; | |
if (!action) { | |
return; | |
} | |
var handler = new ActionHandler(scope, ViewService, { | |
name: name, | |
element: element, | |
action: action, | |
canSave: props.canSave, | |
prompt: props.prompt | |
}); | |
scope.$events[name] = _.bind(handler[name], handler); | |
}); | |
} | |
return { | |
link: function(scope, element, attrs) { | |
scope.$evalAsync(function() { | |
link(scope, element, attrs); | |
}); | |
} | |
}; | |
}]); | |
ui.directive('uiActionClick', ['ViewService', function(ViewService) { | |
return { | |
link: function(scope, element, attrs) { | |
var action = attrs.uiActionClick; | |
scope.$evalAsync(function() { | |
var handler = new ActionHandler(scope, ViewService, { | |
element: element, | |
action: action | |
}); | |
element.on("click", function () { | |
handler.handle(); | |
scope.$applyAsync(); | |
}); | |
}); | |
} | |
}; | |
}]); | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Axelor Business Solutions | |
* | |
* Copyright (C) 2005-2020 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'); | |
function acceptNumber(value) { | |
if (value === null || value === undefined) { | |
return value; | |
} | |
if (_.isNumber(value)) { | |
return +value; | |
} | |
if (/^(-)?\d+(\.\d+)?$/.test(value)) { | |
return +value; | |
} | |
return value; | |
} | |
function parseNumber(field, value) { | |
if (value === null || value === undefined) { | |
return value; | |
} | |
if (!field || ['integer', 'long'].indexOf(field.serverType) === -1) { | |
return value; | |
} | |
var num = +value; | |
if (isNaN(num)) { | |
return value; | |
} | |
return num; | |
} | |
ui.formWidget('BaseSelect', { | |
findInput: function(element) { | |
return element.find('input:first:not([ui-panel-editor] input)'); | |
}, | |
init: function(scope) { | |
scope.loadSelection = function(request, response) { | |
}; | |
scope.parse = function(value) { | |
return value; | |
}; | |
scope.format = function(value) { | |
return this.formatItem(value); | |
}; | |
scope.formatItem = function(item) { | |
return item; | |
}; | |
}, | |
link_editable: function (scope, element, attrs, model) { | |
var input = this.findInput(element); | |
var showing = false; | |
var willShow = false; | |
scope.showSelection = function(delay) { | |
if (scope.isReadonly() || showing || willShow) { | |
return; | |
} | |
if (input.is('.x-focus')) { | |
input.removeClass('.x-focus'); | |
return; | |
} | |
willShow = true; | |
input.addClass('.x-focus'); | |
doSetup(input); | |
setTimeout(function () { | |
if (input.is(':focus')) { | |
input.autocomplete("search" , ''); | |
input.removeClass('.x-focus'); | |
} | |
willShow = false; | |
}, delay || 100); | |
}; | |
scope.handleClear = function(e) { | |
scope.setValue(null, true); | |
}; | |
scope.handleDelete = function(e) { | |
}; | |
scope.handleEnter = function(e) { | |
}; | |
scope.handleSelect = function(e, ui) { | |
}; | |
scope.handleClose = function(e, ui) { | |
}; | |
scope.handleOpen = function(e, ui) { | |
}; | |
function renderItem(ul, item) { | |
var el = $("<li>").append( $("<a>").html(item.label)).appendTo(ul); | |
if (item.click) { | |
el.addClass("tag-select-action"); | |
ul.addClass("tag-select-action-menu"); | |
} | |
return el; | |
} | |
var doSetup = _.once(function (input) { | |
var loading = false; | |
var pending = null; | |
function doLoad(request, response) { | |
if (loading) { | |
return pending = _.partial(doLoad, request, response); | |
} | |
loading = true; | |
scope.loadSelection(request, function() { | |
loading = false; | |
response.apply(null, arguments); | |
if (pending) { | |
pending(); | |
pending = null; | |
} | |
}); | |
} | |
input.autocomplete({ | |
minLength: 0, | |
position: { collision: "flip" }, | |
source: doLoad, | |
focus: function(event, ui) { | |
return false; | |
}, | |
select: function(event, ui) { | |
// do not select with tab key, to prevent unexpected result on editable grid | |
if (event.keyCode === 9) { | |
return false; | |
} | |
var ret = scope.handleSelect(event, ui); | |
if (ret !== undefined) { | |
return ret; | |
} | |
return false; | |
}, | |
open: function(event, ui) { | |
showing = true; | |
scope.handleOpen(event, ui); | |
}, | |
close: function(event, ui) { | |
showing = false; | |
scope.handleClose(event, ui); | |
} | |
}); | |
input.data('ui-autocomplete')._renderItem = scope.renderSelectItem || renderItem; | |
scope.$onAdjust('size scroll', function (e) { | |
if (e.type === 'adjust:size' && e.target !== document) { | |
return; | |
} | |
if (showing) { | |
input.autocomplete('close'); | |
} | |
}); | |
}); | |
input.focus(function(e) { | |
element.addClass('focus'); | |
doSetup(input); | |
}).blur(function() { | |
element.removeClass('focus'); | |
if (showing) { | |
input.autocomplete('close'); | |
} | |
}).keyup(function(e) { | |
// if TAB key | |
if (e.which === 9) { | |
scope.showSelection(300); | |
} | |
}).keydown(function(e) { | |
var KEY = $.ui.keyCode; | |
switch(e.keyCode) { | |
case KEY.DELETE: | |
case KEY.BACKSPACE: | |
scope.handleDelete(e); | |
break; | |
case KEY.ENTER: | |
scope.handleEnter(e); | |
break; | |
} | |
}).click(function() { | |
scope.showSelection(); | |
}); | |
if (axelor.browser.mozilla) { | |
input.mousedown(function () { | |
if (!input.is(':focus')) { | |
scope.showSelection(300); | |
} | |
}); | |
} | |
}, | |
template_editable: | |
'<span class="picker-input">'+ | |
'<input type="text" autocomplete="off">'+ | |
'<span class="picker-icons picker-icons-2">'+ | |
'<i class="fa fa-times" ng-show="text" ng-click="handleClear()"></i>'+ | |
'<i class="fa fa-caret-down" ng-click="showSelection()"></i>'+ | |
'</span>'+ | |
'</span>' | |
}); | |
function filterSelection(scope, field, selection, current) { | |
var selectionIn = scope.attr('selection-in') || field.selectionIn; | |
if (_.isEmpty(selection)) return selection; | |
if (_.isEmpty(selectionIn)) return selection; | |
var context = (scope.getContext || angular.noop)() || {}; | |
var list = selectionIn; | |
if (_.isString(selectionIn)) { | |
var expr = selectionIn.trim(); | |
if (expr.indexOf('[') !== 0) { | |
expr = '[' + expr + ']'; | |
} | |
list = axelor.$eval(scope, expr, context); | |
} | |
var value = acceptNumber(current); | |
if (_.isEmpty(list)) { | |
return selection; | |
} | |
list = _.map(list, acceptNumber); | |
return _.filter(selection, function (item) { | |
var val = acceptNumber(item.value); | |
return val === value || list.indexOf(val) > -1; | |
}); | |
} | |
ui.formInput('Select', 'BaseSelect', { | |
css: 'select-item', | |
cellCss: 'form-item select-item', | |
init: function(scope) { | |
this._super(scope); | |
var field = scope.field, | |
selectionList = field.selectionList || [], | |
selectionMap = {}; | |
var data = _.map(selectionList, function(item) { | |
var value = "" + item.value; | |
selectionMap[value] = item.title; | |
return { | |
value: value, | |
label: item.title || " " | |
}; | |
}); | |
var dataSource = null; | |
function getDataSource() { | |
if (dataSource || !field.selection || !field.domain) { | |
return dataSource; | |
} | |
return dataSource = scope._dataSource._new('com.axelor.meta.db.MetaSelectItem', { | |
domain: "(self.select.name = :_select) AND (" + field.domain + ")", | |
context: { | |
_select: field.selection | |
} | |
}); | |
} | |
scope.loadSelection = function(request, response) { | |
var ds = getDataSource(); | |
function select(records) { | |
var items = _.filter(records, function(item) { | |
var label = item.label || "", | |
term = request.term || ""; | |
return label.toLowerCase().indexOf(term.toLowerCase()) > -1; | |
}); | |
items = filterSelection(scope, field, items); | |
return response(items); | |
} | |
if (ds) { | |
return ds.search({ | |
fields: ['value', 'title'], | |
context: scope.getContext ? scope.getContext() : undefined | |
}).success(function (records) { | |
_.each(records, function (item) { | |
item.label = selectionMap[item.value] || item.title; | |
}); | |
return select(records); | |
}); | |
} | |
return select(data); | |
}; | |
scope.formatItem = function(item) { | |
var key = _.isNumber(item) ? "" + item : item; | |
if (!key) { | |
return item; | |
} | |
if (_.isString(key)) { | |
return selectionMap[key] || ""; | |
} | |
return item.label; | |
}; | |
if (field.enumType) { | |
var __enumValues = {}; | |
var __hasValue = false; | |
_.each(selectionList, function (item) { | |
__enumValues[item.value] = (item.data || {}).value; | |
__hasValue = __hasValue || __enumValues[item.value] !== undefined; | |
}); | |
if (__hasValue) { | |
scope.$watch('record.' + field.name, function selectFieldNameWatch(value, old) { | |
if (value && value !== old) { | |
var enumValue = __enumValues[value]; | |
if (scope.record && enumValue !== value) { | |
scope.record[field.name + '$value'] = enumValue; | |
} | |
} | |
}); | |
} | |
} | |
}, | |
link_editable: function(scope, element, attrs, model) { | |
this._super.apply(this, arguments); | |
var input = this.findInput(element); | |
function update(value) { | |
var val = parseNumber(scope.field, value); | |
scope.setValue(val, true); | |
scope.$applyAsync(); | |
} | |
scope.handleDelete = function(e) { | |
if (e.keyCode === 46) { // DELETE | |
update(null); | |
} | |
if (e.keyCode === 8) { // BACKSPACE | |
var value = scope.getValue(); | |
if (value || (e.target.value||'').length < 2) { | |
update(null); | |
} | |
} | |
}; | |
scope.handleSelect = function(e, ui) { | |
update(ui.item.value); | |
}; | |
scope.$render_editable = function() { | |
input.val(this.getText()); | |
}; | |
scope.$on('on:edit', function () { | |
// force update input text, fixes #5965 | |
scope.$render_editable(); | |
}); | |
} | |
}); | |
ui.formInput('Enum', 'Select'); | |
ui.formInput('ImageSelect', 'Select', { | |
metaWidget: true, | |
BLANK: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7", | |
link: function(scope, element, attrs) { | |
this._super(scope, element, attrs); | |
var field = scope.field; | |
var formatItem = scope.formatItem; | |
var selectIcons = {}; | |
_.each(field.selectionList, function (item) { | |
selectIcons[item.value] = item.icon || item.value; | |
}); | |
scope.canShowText = function () { | |
return field.labels === undefined || field.labels; | |
}; | |
scope.formatItem = function (item) { | |
if (scope.canShowText()) { | |
return formatItem(item); | |
} | |
return ""; | |
}; | |
scope.findImage = function (value) { | |
return selectIcons[value] || this.BLANK; | |
}; | |
scope.$watch('getValue()', function selectFieldValueWatch(value, old) { | |
scope.image = scope.findImage(value); | |
scope.isIcon = scope.image && scope.image.indexOf('fa-') === 0; | |
element.toggleClass('empty', !value); | |
}.bind(this)); | |
}, | |
link_editable: function(scope, element, attrs) { | |
this._super(scope, element, attrs); | |
var input = this.findInput(element); | |
var selects = {}; | |
_.each(scope.field.selectionList, function (item) { | |
selects[item.value] = (item.data||{}).icon || item.value; | |
}); | |
scope.renderSelectItem = function(ul, item) { | |
var a = $("<a>"); | |
var el = $("<li>").addClass("image-select-item").append(a).appendTo(ul); | |
var image = scope.findImage(item.value); | |
if (image && image.indexOf('fa-') === 0) { | |
a.append($("<i>").addClass("fa").addClass(image)); | |
} else { | |
a.append($("<img>").attr("src", image)); | |
} | |
if (scope.canShowText()) { | |
a.append($("<span></span>").html(item.label)); | |
} | |
return el; | |
}; | |
var $render_editable = scope.$render_editable; | |
scope.$render_editable = function () { | |
$render_editable.apply(scope, arguments); | |
setTimeout(function () { | |
element.find('input').css('padding-left', element.find('i.image,img').width()); | |
}); | |
}; | |
}, | |
template_readonly: | |
'<span class="image-select readonly">'+ | |
'<i ng-if="isIcon" class="fa" ng-class="image"></i>'+ | |
'<img ng-if="!isIcon" ng-src="{{image}}"></img> <span ng-show="canShowText()">{{text}}</span>' + | |
'</span>', | |
template_editable: | |
'<span class="picker-input image-select">'+ | |
'<i ng-if="isIcon" class="fa" ng-class="image"></i>'+ | |
'<img ng-if="!isIcon" ng-src="{{image}}"></img>' + | |
'<input type="text" autocomplete="off">'+ | |
'<span class="picker-icons">'+ | |
'<i class="fa fa-caret-down" ng-click="showSelection()"></i>'+ | |
'</span>'+ | |
'</span>' | |
}); | |
ui.formInput('MultiSelect', 'Select', { | |
css: 'multi-select-item', | |
cellCss: 'form-item multi-select-item', | |
metaWidget: true, | |
init: function(scope) { | |
this._super(scope); | |
var __parse = scope.parse; | |
scope.parse = function(value) { | |
if (_.isArray(value)) { | |
return value.join(', '); | |
} | |
return __parse(value); | |
}; | |
scope.format = function(value) { | |
var items = value, | |
values = []; | |
if (!value) { | |
scope.items = []; | |
return value; | |
} | |
if (!_.isArray(items)) items = items.split(/,\s*/); | |
values = _.map(items, function(item) { | |
return { | |
value: item, | |
title: scope.formatItem(item) | |
}; | |
}); | |
scope.items = values; | |
return _.pluck(values, 'title').join(', '); | |
}; | |
scope.matchValues = function(a, b) { | |
if (a === b) return true; | |
if (!a) return false; | |
if (!b) return false; | |
if (_.isString(a)) return a === b; | |
return a.value === b.value; | |
}; | |
scope.getSelection = function() { | |
return this.items; | |
}; | |
var max = +(scope.field.max); | |
scope.limited = function(items) { | |
if (max && items && items.length > max) { | |
scope.more = _t("and {0} more", items.length - max); | |
return _.first(items, max); | |
} | |
scope.more = null; | |
return items; | |
}; | |
}, | |
link_editable: function(scope, element, attrs, model) { | |
this._super.apply(this, arguments); | |
var input = this.findInput(element); | |
input.on('input focus', function() { | |
scaleInput(); | |
}).on('blur', function() { | |
scaleInput(50); | |
input.val(''); | |
}); | |
var placeholder = null; | |
if (scope.field.placeholder) { | |
placeholder = $('<span class="tag-select-placeholder hidden"></span>') | |
.text(scope.field.placeholder) | |
.appendTo(element) | |
.click(function (e) { | |
scope.showSelection(); | |
}); | |
} | |
function scaleInput(width) { | |
var elem = element.find('.tag-selector'), | |
pos = elem.position(); | |
if (width) { | |
return input.width(width); | |
} | |
input.css('width', element.innerWidth() - pos.left - 24); | |
} | |
function update(value) { | |
var val = parseNumber(scope.field, value); | |
scope.setValue(val, true); | |
scope.$applyAsync(); | |
setTimeout(function () { | |
scaleInput(50); | |
}); | |
} | |
scope.removeItem = function(item) { | |
var items = this.getSelection(), | |
value = _.isString(item) ? item : (item||{}).value; | |
items = _.chain(items) | |
.pluck('value') | |
.filter(function(v){ | |
return !scope.matchValues(v, value); | |
}) | |
.value(); | |
update(items); | |
}; | |
scope.onShowSelection = function(e) { | |
if (e && $(e.target || e.srcElement).is('input,li,i,span.tag-text')) { | |
return; | |
} | |
input.focus(); | |
setTimeout(function() { | |
scope.showSelection(); | |
}); | |
}; | |
scope.handleDelete = function(e) { | |
if (input.val()) { | |
return; | |
} | |
var items = this.getSelection(); | |
this.removeItem(_.last(items)); | |
}; | |
scope.handleSelect = function(e, ui) { | |
var items = this.getSelection(), | |
values = _.pluck(items, 'value'); | |
var found = _.find(values, function(v){ | |
return scope.matchValues(v, ui.item.value); | |
}); | |
if (found) { | |
return false; | |
} | |
values.push(ui.item.value); | |
update(values); | |
scaleInput(50); | |
}; | |
scope.handleOpen = function(e, ui) { | |
input.data('autocomplete') | |
.menu | |
.element | |
.position({ | |
my: "left top", | |
at: "left bottom", | |
of: element | |
}) | |
.width(element.width() - 4); | |
}; | |
scope.$render_editable = function() { | |
if (placeholder) { | |
placeholder.toggleClass('hidden', !!scope.getValue()); | |
} | |
return input.val(''); | |
}; | |
input.on("input blur", function () { | |
if (placeholder) { | |
placeholder.toggleClass('hidden', !!(input.val() || scope.getValue())); | |
} | |
}); | |
scope.$watch('items.length', function selectItemsLengthWatch(value, old) { | |
setTimeout(function () { | |
scaleInput(50); | |
}); | |
}); | |
}, | |
template_editable: | |
'<div class="tag-select picker-input" ng-click="onShowSelection($event)">'+ | |
'<ul>'+ | |
'<li class="tag-item label label-primary" ng-repeat="item in items">'+ | |
'<span ng-class="{\'tag-link\': handleClick}" class="tag-text" ng-click="handleClick($event, item.value)">{{item.title}}</span> '+ | |
'<i class="fa fa-times fa-small" ng-click="removeItem(item)"></i>'+ | |
'</li>'+ | |
'<li class="tag-selector">'+ | |
'<input type="text" autocomplete="off">'+ | |
'</li>'+ | |
'</ul>'+ | |
'<span class="picker-icons">'+ | |
'<i class="fa fa-caret-down" ng-click="onShowSelection()"></i>'+ | |
'</span>'+ | |
'</div>', | |
template_readonly: | |
'<div class="tag-select">'+ | |
'<span class="label label-primary" ng-repeat="item in limited(items)">'+ | |
'<span ng-class="{\'tag-link\': handleClick}" class="tag-text" ng-click="handleClick($event, item.value)">{{item.title}}</span>'+ | |
'</span>'+ | |
'<span ng-show="more"> {{more}}</span>'+ | |
'</div>' | |
}); | |
ui.formInput('SelectQuery', 'Select', { | |
link_editable: function(scope, element, attrs, model) { | |
this._super.apply(this, arguments); | |
var current = {}; | |
function update(value) { | |
scope.setValue(value); | |
scope.$applyAsync(); | |
} | |
scope.format = function(value) { | |
if (!value) return ""; | |
if (_.isString(value)) { | |
return current.label || value; | |
} | |
current = value; | |
return value.label; | |
}; | |
scope.parse = function(value) { | |
if (!value || _.isString(value)) return value; | |
return value.value; | |
}; | |
scope.handleSelect = function(e, ui) { | |
update(ui.item); | |
}; | |
var query = scope.$eval(attrs.query); | |
scope.loadSelection = function(request, response) { | |
return query(request, response); | |
}; | |
} | |
}); | |
ui.formInput('RadioSelect', { | |
css: "radio-select", | |
metaWidget: true, | |
link: function(scope, element, attrs, model) { | |
var field = scope.field; | |
var selection = field.selectionList || []; | |
var dataSource = null, selectionMap = {}; | |
function getDataSource() { | |
if (dataSource ) { | |
return dataSource; | |
} | |
return dataSource = scope._dataSource._new('com.axelor.meta.db.MetaSelectItem', { | |
domain: "(self.select.name = :_select)", | |
context: { | |
_select: field.selection | |
} | |
}); | |
} | |
scope.loadSelection = function() { | |
var ds = getDataSource(); | |
if (ds) { | |
return ds.search({ | |
fields: ['value', 'title'], | |
context: scope.getContext ? scope.getContext() : undefined | |
}).success(function (records) { | |
_.each(records, function (item) { | |
item.label = selectionMap[item.value] || item.title; | |
}); | |
selection = records; | |
scope.field.selectionList = selection; | |
return (records); | |
}); | |
} | |
}; | |
scope.formatItem = function(item) { | |
var key = _.isNumber(item) ? "" + item : item; | |
if (!key) { | |
return item; | |
} | |
if (_.isString(key)) { | |
return selectionMap[key] || ""; | |
} | |
return item.label; | |
}; | |
scope.getSelection = function () { | |
return filterSelection(scope, field, selection, scope.getValue()); | |
}; | |
element.on("change", ":input", function(e) { | |
var val = parseNumber(scope.field, $(e.target).val()); | |
scope.setValue(val, true); | |
scope.$applyAsync(); | |
}); | |
if (field.direction === "vertical" || field.dir === "vert") { | |
setTimeout(function(){ | |
element.addClass("radio-select-vertical"); | |
}); | |
} | |
}, | |
template_editable: null, | |
template_readonly: null, | |
template: | |
'<ul ng-class="{ readonly: isReadonly() }">'+ | |
'<li ng-repeat="select in getSelection()">'+ | |
'<label class="ibox round">'+ | |
'<input type="radio" name="radio_{{$parent.$id}}" value="{{select.value}}"'+ | |
' ng-disabled="isReadonly()"'+ | |
' ng-checked="getValue() == select.value">'+ | |
'<span class="box"></span>'+ | |
'<span class="title">{{select.title}}</span>'+ | |
'</label>'+ | |
'</li>'+ | |
'</ul>' | |
}); | |
ui.formInput('CheckboxSelect', { | |
css: "checkbox-select", | |
metaWidget: true, | |
link: function(scope, element, attrs, model) { | |
var field = scope.field; | |
var selection = field.selectionList || []; | |
scope.getSelection = function () { | |
return filterSelection(scope, field, selection, scope.getValue()); | |
}; | |
scope.isSelected = function (select) { | |
var value = scope.getValue(); | |
var current = ("" + value).split(",").map(function (val) { | |
return parseNumber(scope.field, val); | |
}); | |
return current.indexOf(select.value) > -1; | |
}; | |
element.on("change", ":input", function(e) { | |
var all = element.find("input:checked"); | |
var selected = []; | |
all.each(function () { | |
var val = parseNumber(scope.field, $(this).val()); | |
selected.push(val); | |
}); | |
var value = selected.length === 0 ? null : selected.join(","); | |
scope.setValue(value, true); | |
scope.$applyAsync(); | |
}); | |
if (field.direction === "vertical" || field.dir === "vert") { | |
setTimeout(function(){ | |
element.addClass("checkbox-select-vertical"); | |
}); | |
} | |
}, | |
template_editable: null, | |
template_readonly: null, | |
template: | |
'<ul ng-class="{ readonly: isReadonly() }">'+ | |
'<li ng-repeat="select in getSelection()">'+ | |
'<label class="ibox">'+ | |
'<input type="checkbox" value="{{select.value}}"'+ | |
' ng-disabled="isReadonly()"'+ | |
' ng-checked="isSelected(select)">'+ | |
'<span class="box"></span>'+ | |
'<span class="title">{{select.title}}</span>'+ | |
'</label>'+ | |
'</li>'+ | |
'</ul>' | |
}); | |
ui.formInput('NavSelect', { | |
css: "nav-select", | |
metaWidget: true, | |
link: function(scope, element, attrs, model) { | |
var field = scope.field; | |
var selection = field.selectionList || []; | |
scope.getSelection = function () { | |
return filterSelection(scope, field, selection, scope.getValue()) || []; | |
}; | |
scope.$watch('text', function navSelectTextWatch(text, old) { | |
adjust(); | |
}); | |
scope.onSelect = function(select) { | |
if (scope.attr('readonly')) { | |
return; | |
} | |
var val = parseNumber(scope.field, select.value); | |
this.setValue(val, true); | |
elemNavs.removeClass('open'); | |
elemMenu.removeClass('open'); | |
// if selection change is used to show/hide some elements | |
// the layout should be adjusted | |
axelor.$adjustSize(); | |
}; | |
scope.isSelected = function (select) { | |
return select && scope.getValue() == select.value; | |
}; | |
var lastWidth = 0; | |
var lastValue = null; | |
var elemNavs = null; | |
var elemMenu = null; | |
var elemMenuTitle = null; | |
var elemMenuItems = null; | |
function setup() { | |
elemNavs = element.children('.nav-steps').children('li:not(.dropdown,.ignore)'); | |
elemMenu = element.children('.nav-steps').children('li.dropdown'); | |
elemMenuTitle = elemMenu.find('a.nav-label > span'); | |
elemMenuItems = elemMenu.find('li'); | |
adjust(); | |
} | |
var setMenuTitle = (function() { | |
var setActive = _.debounce(function(selected) { | |
elemMenu.toggleClass('active', !!selected); | |
}); | |
return function setMenuTitle(selected) { | |
elemMenu.show(); | |
elemMenuTitle.html(selected && selected.title); | |
setActive(selected); | |
}; | |
}()); | |
function adjust() { | |
if (elemNavs === null || element.is(":hidden")) { | |
return; | |
} | |
var currentValue = scope.getValue(); | |
var parentWidth = element.width() - 16; | |
if (parentWidth === lastWidth && currentValue === lastValue) { | |
return; | |
} | |
lastWidth = parentWidth; | |
lastValue = currentValue; | |
elemNavs.parent().css('visibility', 'hidden'); | |
elemNavs.show(); | |
elemMenu.hide(); | |
if (elemNavs.parent().width() <= parentWidth) { | |
elemNavs.parent().css('visibility', ''); | |
return; | |
} | |
var navs = scope.getSelection(); | |
var selected = _.find(navs, scope.isSelected.bind(scope)); | |
var selectedIndex = navs.indexOf(selected); | |
var elem = null; | |
var index = navs.length; | |
setMenuTitle(null); | |
while (elemNavs.parent().width() > parentWidth) { | |
elem = $(elemNavs[--index]); | |
elem.hide(); | |
if (index === selectedIndex) { | |
setMenuTitle(selected); | |
} | |
} | |
elemMenuItems.hide(); | |
while(index < navs.length) { | |
$(elemMenuItems[index++]).show(); | |
} | |
elemNavs.parent().css('visibility', ''); | |
} | |
scope.$onAdjust(adjust); | |
scope.$callWhen(setup, function () { | |
return element.is(':visible'); | |
}); | |
}, | |
template_editable: null, | |
template_readonly: null, | |
template: | |
"<div class='nav-select'>" + | |
"<ul class='nav-steps' style='display: inline-flex; visibility: hidden;'>" + | |
"<li class='nav-step' ng-repeat='select in getSelection()' ng-class='{ active: isSelected(select), last: $last }'>" + | |
"<a href='' class='nav-label' ng-click='onSelect(select)' ng-bind-html='select.title'></a>" + | |
"</li>" + | |
"<li class='nav-step dropdown'>" + | |
"<a href='' class='nav-label dropdown-toggle' data-toggle='dropdown'><span></span></a>" + | |
"<ul class='dropdown-menu pull-right'>" + | |
"<li ng-repeat='select in getSelection()' ng-class='{active: getValue() == select.value}'>" + | |
"<a tabindex='-1' href='' ng-click='onSelect(select)' ng-bind-html='select.title'></a>" + | |
"</li>" + | |
"</ul>" + | |
"</li>" + | |
"</ul>"+ | |
"</div>" | |
}); | |
ui.formInput('ThemeSelect', 'Select', { | |
init: function (scope) { | |
scope.field.selectionList = _.map(axelor.config['application.themes'], function (name) { | |
return { value: name, title: _.titleize(name) }; | |
}); | |
scope.field.selectionList.unshift({ | |
value: "default", | |
title: "Default" | |
}); | |
this._super(scope); | |
} | |
}); | |
ui.formInput('WidgetSelect', 'Select', { | |
init: function (scope) { | |
scope.field.selectionList = _.map(ui.getMetaWidgets(), function (name) { | |
return { value: name, title: name }; | |
}); | |
this._super(scope); | |
} | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment