/**
* @license Ulakbus-UI
* Copyright (C) 2015 ZetaOps Inc.
* This file is licensed under the GNU General Public License v3
* (GPLv3). See LICENSE.txt for details.
*/
/**
* @ngdoc module
* @name ulakbus.formService
* @module ulakbus.formService
* @description
* The `formService` module provides generic services for auto generated forms.
* @requires ui.bootstrap
* @type {ng.$compileProvider|*}
*/
angular.module('ulakbus.formService', ['ui.bootstrap'])
/**
* there must be no global object, so change it into a service here.
*/
.service('Moment', function(){
return window.moment;
})
/**
* @memberof ulakbus.formService
* @ngdoc factory
* @name Generator
* @description form service's Generator factory service handles all generic form operations
*/
.factory('Generator', function ($http, $q, $timeout, $sce, $location, $route, $compile, $log, RESTURL, $rootScope, Moment) {
var generator = {};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name makeUrl
* @description this function generates url combining backend url and the related object properties for http requests
* @param scope
* @returns {string}
* @param scope
*/
generator.makeUrl = function (scope) {
var getparams = scope.form_params.param ? "?" + scope.form_params.param + "=" + scope.form_params.id : "";
return RESTURL.url + scope.url + getparams;
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name generate
* @param scope
* @param data
* @description - generate function is inclusive for form generation
* defines given scope's client_cmd, model, schema, form, token, object_id objects
* @returns {*} scope
* @param scope
* @param data
*/
generator.generate = function (scope, data) {
// if no form in response (in case of list and single item request) return scope
if (!data.forms) {
return scope;
}
// prepare scope form, schema and model from response object
angular.forEach(data.forms, function (value, key) {
scope[key] = data.forms[key];
});
scope.client_cmd = data.client_cmd;
scope.token = data.token;
// initialModel will be used in formDiff when submiting the form to submit only
scope.initialModel = angular.copy(scope.model);
// if fieldset in form, make it collapsable with template
//scope.listnodeform = {};
//scope.isCollapsed = true;
generator.prepareFormItems(scope);
scope.object_id = scope.form_params.object_id;
$log.debug('scope at after generate', scope);
return scope;
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name group
* @param scope
* @description group function to group form layout by form meta data for layout
* grouping will use an object like example below when parsing its items.
* @example
* `grouping = [
* {
* "groups": [
* {
* "group_title": "title1",
* "items": ["item1", "item2", "item3", "item4"],
* }
* ],
* "layout": "4",
* "collapse": False
* },
* {
* "groups": [
* {
* "group_title": "title2",
* "items": ["item5", "item6"],
* }
* ],
* "layout": "2",
* "collapse": False
* }]`
*
* @returns {*}
* @param scope
*/
generator.group = function (scope) {
if (!scope.grouping) {
return scope;
}
var newForm = [];
var extractFormItem = function (itemList) {
var extractedList = [];
angular.forEach(itemList, function (value, key) {
var item = getFormItem(value);
if (item) {
extractedList.push(item);
}
});
$log.debug('extractedList: ', extractedList);
return extractedList;
};
var getFormItem = function (item) {
var formItem;
if (scope.form.indexOf(item) > -1) {
formItem = scope.form[scope.form.indexOf(item)];
scope.form.splice(scope.form.indexOf(item), 1);
return formItem;
} else {
angular.forEach(scope.form, function (value, key) {
if (value.key === item) {
formItem = value;
scope.form.splice(key, 1);
return;
}
});
return formItem;
}
};
var makeGroup = function (itemsToGroup) {
var subItems = [];
angular.forEach(itemsToGroup, function (value, key) {
subItems.push({
type: 'fieldset',
items: extractFormItem(value.items),
title: value.group_title
});
});
return subItems;
};
angular.forEach(scope.grouping, function (value, key) {
newForm.push(
{
type: 'fieldset',
items: makeGroup(value.groups),
htmlClass: 'col-md-' + value.layout,
title: value.group_title
}
)
});
$log.debug('grouped form: ', newForm);
$log.debug('rest of form: ', scope.form);
$log.debug('form united: ', newForm.concat(scope.form));
scope.form = newForm.concat(scope.form);
return scope;
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name prepareFormItems
* @param scope {Object} given scope on which form items prepared
* @description
* It looks up fields of schema objects and changes their types to proper type for schemaform.
* To prepare items for schemaform loop items of scope.schema.properties by checking index value's `type` key.
*
* If `type` is in `['file', 'select', 'submit', 'date', 'int', 'text_general', 'model', 'ListNode', 'Node']`
* then the item must be converted into the data format which schemaform works with.
*
*
* For listnode, node and model types it uses templates to generate modal. The modal is aa instance of
* ui.bootstraps modal directive.
*
* @returns scope {Object}
*/
generator.prepareFormItems = function (scope) {
angular.forEach(scope.form, function (value, key) {
if (value.type === 'select') {
scope.schema.properties[value.key].type = 'select';
scope.schema.properties[value.key].titleMap = value.titleMap;
scope.form[key] = value.key;
}
});
angular.forEach(scope.schema.properties, function (v, k) {
// generically change _id fields model value
if ('form_params' in scope) {
if (k == scope.form_params.param) {
scope.model[k] = scope.form_params.id;
scope.form.splice(scope.form.indexOf(k), 1);
return;
}
}
if (v.type === 'file') {
scope.form[scope.form.indexOf(k)] = {
type: "template",
title: v.title,
templateUrl: "shared/templates/filefield.html",
name: k,
key: k,
fileInsert: function () {
$scope.$broadcast('schemaForm.error.' + k, 'tv4-302', true);
},
imageSrc: scope.model[k] ? $rootScope.settings.static_url + scope.model[k] : '',
avatar: k === 'avatar' ? true : false
};
v.type = 'string';
}
if (v.type === 'select') {
scope.form[scope.form.indexOf(k)] = {
type: "template",
title: v.title,
templateUrl: "shared/templates/select.html",
name: k,
key: k,
titleMap: v.titleMap
};
}
if (v.type === 'submit' || v.type === 'button') {
var buttonPositions = scope.modalElements ? scope.modalElements.buttonPositions : {
bottom: 'move-to-bottom',
top: 'move-to-top',
none: ''
};
var workOnForm = scope.modalElements ? scope.modalElements.workOnForm : 'formgenerated';
var workOnDiv = scope.modalElements ? scope.modalElements.workOnDiv : '';
var buttonClass = (buttonPositions[v.position] || buttonPositions.bottom);
var redirectTo = scope.modalElements ? false : true;
scope.form[scope.form.indexOf(k)] = {
type: v.type,
title: v.title,
style: "btn-danger hide " + buttonClass,
onClick: function () {
delete scope.form_params.cmd;
delete scope.form_params.flow;
if (v.cmd) {
scope.form_params["cmd"] = v.cmd;
}
if (v.flow) {
scope.form_params["flow"] = v.flow;
}
if (v.wf) {
delete scope.form_params["cmd"];
scope.form_params["wf"] = v.wf;
}
scope.model[k] = 1;
// todo: test it
if (scope.modalElements) {
scope.submitModalForm();
} else {
if (v.validation === false) {
generator.submit(scope, redirectTo);
} else {
scope.$broadcast('schemaFormValidate');
if (scope[workOnForm].$valid) {
generator.submit(scope, redirectTo);
scope.$broadcast('disposeModal');
}
}
}
}
};
// replace buttons according to their position values
$timeout(function () {
var selectorBottom = '.buttons-on-bottom' + workOnDiv;
//var selectorTop = '.buttons-on-top'+workOnDiv;
var buttonsToBottom = angular.element(document.querySelector('.' + buttonClass));
angular.element(document.querySelector(selectorBottom)).append(buttonsToBottom);
//var buttonsToTop = angular.element(document.querySelector('.' + buttonClass));
//angular.element(document.querySelector(selectorTop)).append(buttonsToTop);
buttonsToBottom.removeClass('hide');
//buttonsToTop.removeClass('hide');
}, 500);
}
// check if type is date and if type date found change it to string
if (v.type === 'date') {
$log.debug('date:', scope.model[k]);
scope.model[k] = generator.dateformatter(scope.model[k]);
scope.form[scope.form.indexOf(k)] = {
key: k, name: k, title: v.title,
type: 'template',
templateUrl: 'shared/templates/datefield.html',
validationMessage: {
'dateNotValid': "Girdiğiniz tarih geçerli değildir. <i>orn: '01.01.2015'<i/>",
302: 'Bu alan zorunludur.'
},
$asyncValidators: {
'dateNotValid': function (value) {
var deferred = $q.defer();
$timeout(function () {
scope.model[k] = angular.copy(generator.dateformatter(value));
if (scope.schema.required.indexOf(k) > -1) {
deferred.resolve();
}
if (value.constructor === Date) {
deferred.resolve();
}
else {
var dateValue = d = value.split('.');
if (isNaN(Date.parse(value)) || dateValue.length !== 3) {
deferred.reject();
} else {
deferred.resolve();
}
}
});
return deferred.promise;
}
},
status: {opened: false},
open: function ($event) {
this.status.opened = true;
},
format: 'dd.MM.yyyy',
onSelect: function () {
scope.model[k] = angular.copy(generator.dateformatter(scope.model[k]));
}
};
}
if (v.type === 'int' || v.type === 'float') {
v.type = 'number';
scope.model[k] = parseInt(scope.model[k]);
}
if (v.type === 'text_general') {
v.type = 'string';
v["x-schema-form"] = {
"type": "textarea"
//"placeholder": ""
}
}
// if type is model use foreignKey.html template to show them
if (v.type === 'model') {
var formitem = scope.form[scope.form.indexOf(k)];
var modelScope = {"url": v.wf, "wf": v.wf, "form_params": {model: v.model_name, cmd: v.list_cmd}};
//scope.$on('refreshTitleMap', function (event, data) {
// todo: write a function to refresh titleMap after new item add to linkedModel
//});
scope.generateTitleMap = function (modelScope) {
return generator.get_list(modelScope).then(function (res) {
formitem.titleMap = [];
angular.forEach(res.data.objects, function (item) {
if (item !== -1) {
formitem.titleMap.push({
"value": item.key,
"name": item.value
});
} else {
formitem.focusToInput = true;
}
});
return formitem.titleMap;
});
};
// get selected item from titleMap using model value
if (scope.model[k]) {
generator.get_list({
url: 'crud',
form_params: {model: v.model_name, object_id: scope.model[k], cmd: 'object_name'}
})
.then(function (data) {
try {
scope.$watch(document.querySelector('input[name=' + v.model_name + ']'),
function () {
document.querySelector('input[name=' + k + ']').value = data.data.object_name;
}
);
}
catch (e) {
document.querySelector('input[name=' + k + ']').value = data.data.object_name;
$log.debug('exception', e);
}
});
}
formitem = {
type: "template",
templateUrl: "shared/templates/foreignKey.html",
// formName will be used in modal return to save item on form
formName: k,
title: v.title,
wf: v.wf,
add_cmd: v.add_cmd,
name: k,
key: k,
model_name: v.model_name,
selected_item: {},
titleMap: [],
onSelect: function (item, inputname) {
scope.model[k] = item.value;
$timeout(function () {
document.querySelector('input[name=' + inputname + ']').value = item.name;
});
},
onDropdownSelect: function (item, inputname) {
scope.model[k] = item.value;
$timeout(function () {
document.querySelector('input[name=' + inputname + ']').value = item.name;
});
},
getTitleMap: function (viewValue) {
modelScope.form_params.query = viewValue;
return scope.generateTitleMap(modelScope);
},
getDropdownTitleMap: function () {
delete modelScope.form_params.query;
formitem.gettingTitleMap = true;
scope.generateTitleMap(modelScope)
.then(function (data) {
formitem.titleMap = data;
formitem.gettingTitleMap = false;
});
}
};
scope.form[scope.form.indexOf(k)] = formitem;
}
if ((v.type === 'ListNode' || v.type === 'Node') && v.widget === 'filter_interface') {
var formitem = scope.form[scope.form.indexOf(k)];
var modelScope = {
"url": v.wf || scope.wf, "wf": v.wf || scope.wf,
"form_params": {model: v.model_name || v.schema[0].model_name, cmd: v.list_cmd || 'select_list', query: ''}
};
scope.generateTitleMap = function (modelScope) {
generator.get_list(modelScope).then(function (res) {
formitem.titleMap = [];
angular.forEach(res.data.objects, function (item) {
if (item !== "-1") {
formitem.titleMap.push({
"value": item.key,
"name": item.value
});
}
});
formitem.filteredItems = generator.get_diff_array(angular.copy(formitem.titleMap), angular.copy(formitem.selectedFilteredItems), 1);
})
};
var modelItems = [];
var modelKeys = [];
angular.forEach(scope.model[k], function (value, mkey) {
modelItems.push({
"value": value[v.schema[0].name].key,
"name": value[v.schema[0].name].unicode
});
var modelKey = {};
modelKey[v.schema[0].name] = value[v.schema[0].name].key;
modelKeys.push(modelKey);
});
scope.model[k] = angular.copy(modelKeys);
formitem = {
type: "template",
templateUrl: "shared/templates/multiselect.html",
title: v.title,
// formName will be used in modal return to save item on form
formName: k,
wf: v.wf,
add_cmd: v.add_cmd,
name: v.model_name,
model_name: v.model_name,
filterValue: '',
selected_item: {},
filteredItems: [],
selectedFilteredItems: modelItems,
titleMap: scope.generateTitleMap(modelScope),
appendFiltered: function (filterValue) {
if (filterValue.length > 2) {
formitem.filteredItems = [];
angular.forEach(formitem.titleMap, function (value, key) {
if (value.name.indexOf(filterValue) > -1) {
formitem.filteredItems.push(formitem.titleMap[key]);
}
});
}
if (filterValue <= 2) { formitem.filteredItems = formitem.titleMap}
formitem.filteredItems = generator.get_diff_array(formitem.filteredItems, formitem.selectedFilteredItems);
},
select: function (selectedItemsModel) {
if (!selectedItemsModel) {
return;
}
formitem.selectedFilteredItems = formitem.selectedFilteredItems.concat(selectedItemsModel);
formitem.appendFiltered(formitem.filterValue);
scope.model[k] = (scope.model[k] || []).concat(formitem.dataToModel(selectedItemsModel));
},
deselect: function (selectedFilteredItemsModel) {
if (!selectedFilteredItemsModel) {
return;
}
formitem.selectedFilteredItems = generator.get_diff_array(angular.copy(formitem.selectedFilteredItems), angular.copy(selectedFilteredItemsModel));
formitem.appendFiltered(formitem.filterValue);
formitem.filteredItems = formitem.filteredItems.concat(selectedFilteredItemsModel);
scope.model[k] = generator.get_diff_array(scope.model[k] || [], formitem.dataToModel(selectedFilteredItemsModel));
},
dataToModel: function (data) {
var dataValues = [];
angular.forEach(data, function (value, key) {
var dataKey = {};
dataKey[v.schema[0].name] = value.value;
dataValues.push(dataKey);
});
return dataValues;
}
};
scope.form[scope.form.indexOf(k)] = formitem;
}
if ((v.type === 'ListNode' || v.type === 'Node') && v.widget !== 'filter_interface') {
scope[v.type] = scope[v.type] || {};
// no pass by reference
scope[v.type][k] = angular.copy({
title: v.title,
form: [],
schema: {
properties: {},
required: [],
title: v.title,
type: "object",
formType: v.type,
model_name: k,
inline_edit: scope.inline_edit
},
url: scope.url,
wf: scope.wf,
nodeModelChange: function (item) {
//debugger;
}
});
angular.forEach(v.schema, function (item) {
scope[v.type][k].schema.properties[item.name] = angular.copy(item);
// prepare required fields
if (item.required === true && item.name !== 'idx') {
scope[v.type][k].schema.required.push(angular.copy(item.name));
}
// idx field must be hidden
if (item.name !== 'idx') {
scope[v.type][k].form.push(item.name);
}
try {
if (item.type === 'date') {
scope.model[k][item.name] = generator.dateformatter(scope.model[k][item.name]);
}
} catch (e) {
$log.debug('Error: ', e.message);
}
});
$timeout(function () {
if (v.type === 'ListNode') {
scope[v.type][k].items = angular.copy(scope.model[k] || []);
angular.forEach(scope[v.type][k].items, function (value, key) {
if (value.constructor === Object) {
angular.forEach(value, function (x, y) {
try {
if (scope[v.type][k].schema.properties[y].type === 'date') {
scope[v.type][k].items[key][y] = generator.dateformatter(x);
scope[v.type][k].model[key][y] = generator.dateformatter(x);
}
if (scope[v.type][k].schema.properties[y].type === 'select') {
scope[v.type][k].items[key][y] = generator.item_from_array(x.toString(), scope[v.type][k].schema.properties[y].titleMap)
}
} catch (e) {
$log.debug('Field is not date');
}
});
}
});
}
});
if (scope.model[k]) {
angular.forEach(scope.model[k], function (value, key) {
angular.forEach(value, function (y, x) {
if (y.constructor === Object) {
scope.model[k][key][x] = y.key;
}
});
});
}
scope.model[k] = scope.model[k] || [];
scope[v.type][k].model = scope.model[k];
// lengthModels is length of the listnode models. if greater than 0 show records on template
scope[v.type][k]['lengthModels'] = scope.model[k] ? 1 : 0;
}
});
$log.debug('scope at after prepareformitems', scope);
return generator.group(scope);
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name dateformatter
* @description dateformatter handles all date fields and returns humanized and jquery datepicker format dates
* @param {Object} formObject
* @returns {*}
*/
generator.dateformatter = function (formObject) {
var ndate = new Date(formObject);
if (isNaN(ndate)) {
return '';
} else {
var newdatearray = Moment(ndate).format('DD.MM.YYYY');
$log.debug('date formatted: ', newdatearray);
return newdatearray;
}
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name doItemAction
* @description `mode` could be in ['normal', 'modal', 'new'] . the default mode is 'normal' and it loads data
* on same
* tab without modal. 'modal' will use modal to manipulate data and do all actions in that modal. 'new'
* will be open new page with response data
* @param {Object} $scope
* @param {string} key
* @param {Object} todo
* @param {string} mode
* @returns {*}
*/
generator.doItemAction = function ($scope, key, todo, mode) {
var _do = {
normal: function () {
$log.debug('normal mode starts');
$scope.form_params.cmd = todo.cmd;
if (todo.wf) {
$scope.url = todo.wf;
$scope.form_params.wf = todo.wf;
delete $scope.token;
delete $scope.form_params.model;
delete $scope.form_params.cmd
}
if (todo.object_key) {
$scope.form_params[todo.object_key] = key;
} else {
$scope.form_params.object_id = key;
}
$scope.form_params.param = $scope.param;
$scope.form_params.id = $scope.param_id;
$scope.form_params.token = $scope.token;
return generator.get_wf($scope);
},
modal: function () {
$log.debug('modal mode is not not ready');
},
new: function () {
$log.debug('new mode is not not ready');
}
};
return _do[mode]();
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name get_form
* @description Communicates with api with given scope object.
* @param {Object} scope
* @returns {*}
*/
generator.get_form = function (scope) {
return $http
.post(generator.makeUrl(scope), scope.form_params)
.then(function (res) {
return generator.generate(scope, res.data);
});
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name get_list
* @description gets list of related wf/model
* @param scope
* @returns {*}
*/
generator.get_list = function (scope) {
return $http
.post(generator.makeUrl(scope), scope.form_params)
.then(function (res) {
return res;
});
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name get_wf
* @description get_wf is the main function for client_cmd based api calls
* based on response content it redirects to related path/controller with pathDecider function
* @param scope
* @returns {*}
*/
generator.get_wf = function (scope) {
return $http
.post(generator.makeUrl(scope), scope.form_params)
.then(function (res) {
if (res.data.client_cmd) {
return generator.pathDecider(res.data.client_cmd, scope, res.data);
}
if (res.data.msgbox) {
scope.msgbox = res.data.msgbox;
var newElement = $compile("<msgbox></msgbox>")(scope);
// this is the default action, which is removing page items and reload page with msgbox
angular.element(document.querySelector('.main.ng-scope')).children().remove();
angular.element(document.querySelector('.main.ng-scope')).append(newElement);
}
});
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name isValidEmail
* @description checks if given value is a valid email address.
* @param email
* @returns {boolean}
*/
generator.isValidEmail = function (email) {
var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
return re.test(email);
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name isValidTCNo
* @description checks if given value is a valid identity number for Turkey.
* @param tcno
* @returns {boolean}
*/
generator.isValidTCNo = function (tcno) {
var re = /^([1-9]{1}[0-9]{9}[0,2,4,6,8]{1})$/i;
return re.test(tcno);
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name isValidDate
* @description checks if given value can be parsed as Date object
* @param dateValue
* @returns {boolean}
*/
generator.isValidDate = function (dateValue) {
return !isNaN(Date.parse(dateValue));
};
/**
* @memberof ulakbus.formService
* @ngdoc property
* @name pageData
* @description pageData object is moving object from response to controller
* with this object controller will not need to call the api for response object to work on to
* @type {{}}
*/
generator.pageData = {};
generator.getPageData = function () {
return generator.pageData;
};
generator.setPageData = function (value) {
generator.pageData = value;
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name pathDecider
* @description pathDecider is used to redirect related path by looking up the data in response
* @param {string} client_cmd
* @param {Object} $scope
* @param {Object} data
*/
generator.pathDecider = function (client_cmd, $scope, data) {
if (client_cmd[0] === 'reload' || client_cmd[0] === 'reset') {
$rootScope.$broadcast('reload_cmd', $scope.reload_cmd);
return;
}
/**
* @memberof ulakbus.formService~pathDecider
* @ngdoc function
* @name redirectTo
* @description redirectTo function redirects to related controller and path with given data
* before redirect setPageData must be called and pageData need to be defined
* otherwise redirected path will call api for its data
* @param {Object} scope
* @param {string} page
* @return {*}
*/
function redirectTo(scope, page) {
var pathUrl = '/' + scope.form_params.wf;
if (scope.form_params.model) {
pathUrl += '/' + scope.form_params.model + '/do/' + page;
} else {
pathUrl += '/do/' + page;
}
// todo add object url to path
// pathUrl += '/'+scope.form_params.object_id || '';
// if generated path url and the current path is equal route has to be reload
if ($location.path() === pathUrl) {
return $route.reload();
}
else {
$location.path(pathUrl);
}
}
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name dispatchClientCmd
* @description Sets params for scope to the related page and redirect to the page in client_cmd param.
* client_cmd can be in ['list', 'form', 'show', 'reload', 'reset']
*/
function dispatchClientCmd() {
data[$scope.form_params.param] = $scope.form_params.id;
data['model'] = $scope.form_params.model;
data['wf'] = $scope.form_params.wf;
data['param'] = $scope.form_params.param;
data['param_id'] = $scope.form_params.id;
data['pageData'] = true;
data['second_client_cmd'] = client_cmd[1];
generator.setPageData(data);
redirectTo($scope, client_cmd[0]);
}
dispatchClientCmd();
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name get_diff
* @description returns diff of the second param to first param
* @param {Object} obj1
* @param {Object} obj2
* @returns {Object} diff object of two given objects
*/
generator.get_diff = function (obj1, obj2) {
var result = {};
angular.forEach(obj1, function (value, key) {
if (obj2[key] != obj1[key]) {
result[key] = angular.copy(obj1[key])
}
if (obj2[key].constructor === Array && obj1[key].constructor === Array) {
result[key] = arguments.callee(obj1[key], obj2[key]);
}
if (obj2[key].constructor === Object && obj1[key].constructor === Object) {
result[key] = arguments.callee(obj1[key], obj2[key]);
}
});
return result;
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name get_diff_array
* @description extracts items of second array from the first array
* @param {Array} array1
* @param {Array} array2
* @param {Number} way
* @returns {Array} diff of arrays
*/
generator.get_diff_array = function (array1, array2, way) {
var result = [];
angular.forEach(array1, function (value, key) {
if (way === 1) {
if (angular.toJson(array2).indexOf(value.value) < 0) {
result.push(value);
}
} else {
if (angular.toJson(array2).indexOf(angular.toJson(value)) < 0) {
result.push(value);
}
}
});
return result;
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name item_from_array
* @description gets item unicode name from titleMap
* @param {Object} item
* @param {Array} array
* @returns {*}
*/
generator.item_from_array = function (item, array) {
var result = item;
angular.forEach(array, function (value, key) {
if (value.value === item) {
result = value.name;
}
});
return result;
};
/**
* @memberof ulakbus.formService
* @ngdoc function
* @name submit
* @description Submit function is generic function for submiting forms.
* - redirectTo param is used for redirect if return value will be evaluated in a new page.
* - In case of unformatted date object in any key recursively, it must be converted by convertDate function.
* - ListNode and Node objects get seperated from model in
* {@link prepareFormItems module:ulakbus.formService.function:prepareFormItems} They must be concat to model
* key of scope first.
* - Backend API waits form as model value. So `data.form` key must be set to `$scope.model`
* - Other parameters we pass to backend API are shown in the example below
* ```
* var data = {
"form": $scope.model,
"token": $scope.token,
"model": $scope.form_params.model,
"cmd": $scope.form_params.cmd,
"flow": $scope.form_params.flow,
"object_id": $scope.object_id,
"filter": $scope.filter,
"query": $scope.form_params.query
};
* ```
* ### Special response object process
* - If response object is a downloadable pdf file, checking from headers `headers('content-type') ===
* "application/pdf"` download using Blob object.
*
* @param {Object} $scope
* @param {Object} redirectTo
* @returns {*}
* @todo diff for all submits to recognize form change. if no change returns to view with no submit
*/
generator.submit = function ($scope, redirectTo) {
/**
* In case of unformatted date object in any key recursively, it must be converted.
* @param model
*/
var convertDate = function (model) {
angular.forEach(model, function (value, key) {
if (value && value.constructor === Date) {
model[key] = generator.dateformatter(value);
}
if (value && value.constructor === Object) {convertDate(value);}
});
};
angular.forEach($scope.ListNode, function (value, key) {
$scope.model[key] = value.model;
});
angular.forEach($scope.Node, function (value, key) {
$scope.model[key] = value.model;
});
var data = {
"form": $scope.model,
"token": $scope.token,
"model": $scope.form_params.model,
"cmd": $scope.form_params.cmd,
"flow": $scope.form_params.flow,
"object_id": $scope.object_id,
"filter": $scope.filter,
"query": $scope.form_params.query
};
return $http.post(generator.makeUrl($scope), data)
.success(function (data, status, headers) {
if (headers('content-type') === "application/pdf") {
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
var file = new Blob([data], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
var fileName = $scope.schema.title;
a.href = fileURL;
a.download = fileName;
a.click();
}
if (redirectTo === true) {
if (data.client_cmd) {
generator.pathDecider(data.client_cmd, $scope, data);
}
if (data.msgbox) {
$scope.msgbox = data.msgbox;
var newElement = $compile("<msgbox></msgbox>")($scope);
// this is the default action, which is removing page items and reload page with msgbox
angular.element(document.querySelector('.main.ng-scope')).children().remove();
angular.element(document.querySelector('.main.ng-scope')).append(newElement);
}
}
});
};
return generator;
})
/**
* @memberof ulakbus.formService
* @ngdoc controller
* @name ModalCtrl
* @description controller for listnode, node and linkedmodel modal and save data of it
* @param {Object} items
* @param {Object} $scope
* @param {Object} $uibModalInstance
* @param {Object} $route
* @returns {Object} returns value for modal
*/
.controller('ModalCtrl', function ($scope, $uibModalInstance, Generator, items) {
angular.forEach(items, function (value, key) {
$scope[key] = items[key];
});
$scope.$on('disposeModal', function () {
$scope.cancel();
});
$scope.$on('modalFormLocator', function (event) {
$scope.linkedModelForm = event.targetScope.linkedModelForm;
});
$scope.$on('submitModalForm', function () {
$scope.onSubmit($scope.linkedModelForm);
});
$scope.$on('validateModalDate', function (event, field) {
$scope.$broadcast('schemaForm.error.' + field, 'tv4-302', true);
});
$scope.onSubmit = function (form) {
$scope.$broadcast('schemaFormValidate');
if (form.$valid) {
// send form to modalinstance result function
$uibModalInstance.close($scope);
}
};
$scope.onNodeSubmit = function () {
$scope.$broadcast('schemaFormValidate');
if ($scope.modalForm.$valid) {
$uibModalInstance.close($scope);
}
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
})
/**
* @memberof ulakbus.formService
* @ngdoc directive
* @name modalForNodes
* @description add modal directive for nodes
* @param {Module} $uibModal
* @param {Service} Generator
* @returns {Object} openmodal directive
*/
.directive('modalForNodes', function ($uibModal, Generator) {
return {
link: function (scope, element, attributes) {
element.on('click', function () {
var modalInstance = $uibModal.open({
animation: true,
backdrop: 'static',
keyboard: false,
templateUrl: 'shared/templates/listnodeModalContent.html',
controller: 'ModalCtrl',
size: 'lg',
resolve: {
items: function () {
var attribs = attributes.modalForNodes.split(',');
// get node from parent scope catch with attribute
var node = angular.copy(scope.$parent[attribs[1]][attribs[0]]);
if (attribs[2] === 'add') {
node.model = {};
}
if (attribs[3]) {
// if listnode catch edit object with index
node.model = node.model[attribs[3]];
}
// tell result.then function which item to edit
node.edit = attribs[3];
scope.node.schema.wf = scope.node.url;
angular.forEach(scope.node.schema.properties, function (value, key) {
scope.node.schema.properties[key].wf = scope.node.url;
scope.node.schema.properties[key].list_cmd = 'select_list';
});
var newscope = {
wf: scope.node.wf,
url: scope.node.url,
form_params: {model: scope.node.schema.model_name},
edit: attribs[3]
};
Generator.generate(newscope, {forms: scope.node});
// modal will add only one item to listNode, so just need one model (not array)
newscope.model = newscope.model[node.edit] || newscope.model[0] || {};
return newscope;
}
}
});
modalInstance.result.then(function (childmodel, key) {
var listNodeItem = scope.$parent[childmodel.schema.formType][childmodel.schema.model_name];
if (childmodel.schema.formType === 'Node') {
listNodeItem.model = angular.copy(childmodel.model);
listNodeItem.lengthModels += 1;
}
if (childmodel.schema.formType === 'ListNode') {
// reformat listnode model
var reformattedModel = {};
angular.forEach(childmodel.model, function (value, key) {
if (key.indexOf('_id') > -1) {
angular.forEach(childmodel.form, function (v, k) {
if (v.formName === key) {
//if (!childmodel.model[key].key) {
function indexInTitleMap(element, index, array) {
if (element['value'] === value) {
return element;
}
}
reformattedModel[key] = {
"key": value,
"unicode": v.titleMap.find(indexInTitleMap).name
};
//}
}
});
} else {
reformattedModel[key] = {
"key": key,
"unicode": Generator.item_from_array(value, childmodel.schema.properties[key].titleMap)
};
}
});
if (childmodel.edit) {
listNodeItem.model[childmodel.edit] = childmodel.model;
if (Object.keys(reformattedModel).length > 0) {
listNodeItem.items[childmodel.edit] = reformattedModel;
} else {
listNodeItem.items[childmodel.edit] = angular.copy(childmodel.model);
}
} else {
listNodeItem.model.push(angular.copy(childmodel.model));
if (Object.keys(reformattedModel).length > 0) {
listNodeItem.items.push(reformattedModel);
} else {
listNodeItem.items.push(angular.copy(childmodel.model));
}
}
listNodeItem.lengthModels += 1;
}
});
});
}
};
})
/**
* @memberof ulakbus.formService
* @ngdoc directive
* @name addModalForLinkedModel
* @description add modal directive for linked models
* @param {Module} $uibModal
* @param {Object} $rootScope
* @param {Module} $route
* @param {Service} Generator
* @returns {Object} openmodal directive
*/
.directive('addModalForLinkedModel', function ($uibModal, $rootScope, $route, Generator) {
return {
link: function (scope, element, attributes) {
element.on('click', function () {
var modalInstance = $uibModal.open({
animation: true,
backdrop: 'static',
keyboard: false,
templateUrl: 'shared/templates/linkedModelModalContent.html',
controller: 'ModalCtrl',
size: 'lg',
resolve: {
items: function () {
var formName = attributes.addModalForLinkedModel;
return Generator.get_form({
url: scope.form.wf,
wf: scope.form.wf,
form_params: {model: scope.form.model_name, cmd: scope.form.add_cmd},
modalElements: {
// define button position properties
buttonPositions: {
bottom: 'move-to-bottom-modal',
top: 'move-to-top-modal',
none: ''
},
workOnForm: 'linkedModelForm',
workOnDiv: '-modal' + formName
},
submitModalForm: function () {
$rootScope.$broadcast('submitModalForm');
},
validateModalDate: function (field) {
$rootScope.$broadcast('validateModalDate', field);
},
formName: formName
});
}
}
});
modalInstance.result.then(function (childscope, key) {
var formName = childscope.formName;
Generator.submit(childscope, false)
.success(function (data) {
// response data contains object_id and unicode
// scope.model can be reached via prototype chain
scope.model[formName] = data.forms.model.object_key;
// scope.form prototype chain returns this form item
scope.form.titleMap.push({
value: data.forms.model.object_key,
name: data.forms.model.unicode
});
scope.form.selected_item = {
value: data.forms.model.object_key,
name: data.forms.model.unicode
};
scope.$watch(document.querySelector('input[name=' + scope.form.model_name + ']'),
function () {
angular.element(document.querySelector('input[name=' + scope.form.model_name + ']')).val(scope.form.selected_item.name);
}
);
});
//$route.reload();
});
});
}
};
})
/**
* @memberof ulakbus.formService
* @ngdoc directive
* @name modalFormLocator
* @description This directive helps to locate form object in modal.
* @returns {Object} form object
*/
.directive('modalFormLocator', function () {
return {
link: function (scope) {
scope.$emit('modalFormLocator');
}
}
});