public_html/layouts/resources/app.js
/*+***********************************************************************************
* The contents of this file are subject to the vtiger CRM Public License Version 1.0
* ("License"); You may not use this file except in compliance with the License
* The Original Code is: vtiger CRM Open Source
* The Initial Developer of the Original Code is vtiger.
* Portions created by vtiger are Copyright (C) vtiger.
* All Rights Reserved.
* Contributor(s): YetiForce S.A.
*************************************************************************************/
'use strict';
const App = (window.App = {
Components: {
Tree: {
Basic: class {
constructor(container = $('.js-tree-container')) {
this.treeInstance = false;
this.treeData = false;
this.generateTree(container);
}
generateTree(container) {
const self = this;
if (self.treeInstance === false) {
self.treeInstance = container;
self.treeInstance
.on('select_node.jstree', function (_e, data) {
if (data.event !== undefined && $(data.event.target).hasClass('jstree-checkbox')) {
return;
}
data.instance.select_node(data.node.children_d);
})
.on('deselect_node.jstree', function (_e, data) {
if (data.event !== undefined && $(data.event.target).hasClass('jstree-checkbox')) {
return;
}
data.instance.deselect_node(data.node.children_d);
})
.jstree({
core: {
data: self.getRecords(container),
themes: {
name: 'proton',
responsive: true
}
},
plugins: ['search', 'checkbox']
});
this.registerSearchEvent();
}
}
registerSearchEvent() {
const self = this;
let searchTimeout = false,
treeSearch = $('.js-tree-search');
treeSearch.on('keyup', () => {
if (searchTimeout) {
clearTimeout(searchTimeout);
}
searchTimeout = setTimeout(function () {
var searchValue = treeSearch.val();
self.treeInstance.jstree(true).search(searchValue);
}, 250);
});
}
getRecords(container) {
if (this.treeData === false && container !== 'undefined') {
this.treeData = JSON.parse(container.find('.js-tree-data').val());
}
return this.treeData;
}
}
},
/**
* Quick create object used by Header.js and yf plugins
*
*/
QuickCreate: {
/**
* module quick create data cache
*/
moduleCache: {},
/**
* Register function
* @param {jQuery} container
*/
register(container) {
if (typeof container === 'undefined') {
container = $('body');
} else {
container = $(container);
}
container.on('click', '.js-quick-create-modal', function (e) {
e.preventDefault();
let element = $(this);
if (element.data('module')) {
App.Components.QuickCreate.createRecord(element.data('module'));
}
if (element.data('url')) {
let url = element.data('url');
let urlObject = app.convertUrlToObject(url);
let params = { callbackFunction: function () {} };
const progress = $.progressIndicator({ blockInfo: { enabled: true } });
App.Components.QuickCreate.getForm(url, urlObject.module, params).done((data) => {
progress.progressIndicator({
mode: 'hide'
});
App.Components.QuickCreate.showModal(data, params, element);
app.registerEventForClockPicker();
});
}
});
},
/**
* createRecord
*
* @param {string} moduleName
* @param {object} params
*/
createRecord(moduleName, params = {}) {
if ('parentIframe' === CONFIG.modalTarget) {
window.parent.App.Components.QuickCreate.createRecord(moduleName, params);
return;
}
let url = 'index.php?module=' + moduleName + '&view=QuickCreateAjax';
if (undefined === params.callbackFunction) {
params.callbackFunction = function () {};
}
if (
(app.getViewName() === 'Detail' || (app.getViewName() === 'Edit' && app.getRecordId() !== undefined)) &&
app.getParentModuleName() != 'Settings' &&
(!params['data'] || !('sourceModule' in params['data']))
) {
url += '&sourceModule=' + app.getModuleName();
url += '&sourceRecord=' + app.getRecordId();
}
const progress = $.progressIndicator({ blockInfo: { enabled: true } });
this.getForm(url, moduleName, params).done((data) => {
progress.progressIndicator({
mode: 'hide'
});
this.showModal(data, params);
app.registerEventForClockPicker();
});
},
/**
* Get quick create form
*
* @param {string} url
* @param {string} moduleName
* @param {object} params
*
* @return {Promise} aDeferred
*/
getForm(url, moduleName, params = {}) {
const aDeferred = $.Deferred();
let requestParams;
let isCacheActive = !params.noCache || undefined === params.noCache;
if (isCacheActive) {
if (App.Components.QuickCreate.moduleCache[moduleName]) {
aDeferred.resolve(App.Components.QuickCreate.moduleCache[moduleName]);
return aDeferred.promise();
}
}
requestParams = url;
if (typeof params.data !== 'undefined') {
requestParams = {};
requestParams['data'] = params.data;
requestParams['url'] = url;
}
AppConnector.request(requestParams).done(function (data) {
if (isCacheActive) {
App.Components.QuickCreate.moduleCache[moduleName] = data;
}
aDeferred.resolve(data);
});
return aDeferred.promise();
},
/**
* Show modal
*
* @param {string} html
* @param {object} params
* @param {jQuery} element
*/
showModal(html, params = {}, element = null) {
app.showModalWindow(html, (container) => {
const quickCreateForm = container.find('form.js-form');
const moduleName = quickCreateForm.find('[name="module"]').val();
if (typeof params.callbackBeforeRegister !== 'undefined') {
params.callbackBeforeRegister(container);
}
const editViewInstance = Vtiger_Edit_Js.getInstanceByModuleName(moduleName);
editViewInstance.setForm(quickCreateForm);
editViewInstance.registerBasicEvents(quickCreateForm);
const moduleClassName = moduleName + '_QuickCreate_Js';
if (typeof window[moduleClassName] !== 'undefined') {
new window[moduleClassName]().registerEvents(container);
}
quickCreateForm.validationEngine(app.validationEngineOptionsForRecord);
if (typeof params.callbackPostShown !== 'undefined') {
params.callbackPostShown(quickCreateForm);
}
this.registerPostLoadEvents(quickCreateForm, params, element);
});
},
/**
* Register post load events
*
* @param {jQuery} form
* @param {object} params
* @param {jQuery} element
*
* @return {boolean}
*/
registerPostLoadEvents(form, params, element) {
const submitSuccessCallback = params.callbackFunction || function () {};
const goToFullFormCallBack = params.goToFullFormcallback || function () {};
form.on('submit', (e) => {
if (form.hasClass('not_validation')) {
return true;
}
const moduleName = form.find('[name="module"]').val();
//Form should submit only once for multiple clicks also
if (typeof form.data('submit') !== 'undefined') {
return false;
} else {
if (form.data('jqv').InvalidFields.length > 0) {
//If validation fails, form should submit again
form.removeData('submit');
$.progressIndicator({ mode: 'hide' });
e.preventDefault();
return;
} else {
//Once the form is submiting add data attribute to that form element
form.data('submit', 'true');
$.progressIndicator({ mode: 'hide' });
}
const recordPreSaveEvent = $.Event(Vtiger_Edit_Js.recordPreSave);
form.trigger(recordPreSaveEvent, {
value: 'edit',
module: moduleName
});
if (!recordPreSaveEvent.isDefaultPrevented()) {
const moduleInstance = Vtiger_Edit_Js.getInstanceByModuleName(moduleName);
const saveHandler = !!moduleInstance.quickCreateSave ? moduleInstance.quickCreateSave : this.save;
let progress = $.progressIndicator({
message: app.vtranslate('JS_SAVE_LOADER_INFO'),
position: 'html',
blockInfo: {
enabled: true
}
});
saveHandler(form)
.done((data) => {
let modalContainer = form.closest('.modalContainer');
if (modalContainer.length) {
app.hideModalWindow(false, modalContainer[0].id);
}
submitSuccessCallback(data);
app.event.trigger('QuickCreate.AfterSaveFinal', data, form);
progress.progressIndicator({ mode: 'hide' });
if (data.success) {
app.showNotify({
text: app.vtranslate('JS_SAVE_NOTIFY_SUCCESS'),
type: 'success'
});
}
app.reloadAfterSave(data, params, form, element);
})
.fail(function (_, errorThrown) {
app.showNotify({
textTrusted: false,
text: errorThrown,
title: app.vtranslate('JS_ERROR'),
type: 'error'
});
});
} else {
//If validation fails in recordPreSaveEvent, form should submit again
form.removeData('submit');
$.progressIndicator({ mode: 'hide' });
}
e.preventDefault();
}
});
form.find('.js-full-editlink').on('click', (e) => {
const form = $(e.currentTarget).closest('form');
const editViewUrl = $(e.currentTarget).data('url');
goToFullFormCallBack(form);
this.goToFullForm(form, editViewUrl);
});
this.registerTabEvents(form);
},
/**
* Function to navigate from quick create to edit iew full form
*
* @param {object} form jQuery
*/
goToFullForm(form) {
//As formData contains information about both view and action removed action and directed to view
form.find('input[name="action"]').remove();
form.append('<input type="hidden" name="view" value="Edit" />');
$.each(form.find('[data-validation-engine]'), function (key, data) {
$(data).removeAttr('data-validation-engine');
});
form.addClass('not_validation');
form.trigger('submit');
},
/**
* Register tab events
*
* @param {object} form jQuery
*/
registerTabEvents(form) {
const tabElements = form.find('.nav.nav-pills , .nav.nav-tabs').find('a');
//This will remove the name attributes and assign it to data-element-name . We are doing this to avoid
//Multiple element to send as in calendar
const quickCreateTabOnHide = function (target) {
$(target)
.find('[name]')
.each(function (index, element) {
element = $(element);
element.attr('data-element-name', element.attr('name')).removeAttr('name');
});
};
//This will add the name attributes and get value from data-element-name . We are doing this to avoid
//Multiple element to send as in calendar
const quickCreateTabOnShow = function (target) {
$(target)
.find('[data-element-name]')
.each(function (index, element) {
element = $(element);
element.attr('name', element.attr('data-element-name')).removeAttr('data-element-name');
});
};
tabElements.on('click', function (e) {
quickCreateTabOnHide(tabElements.not('[aria-expanded="false"]').attr('data-target'));
quickCreateTabOnShow($(this).attr('data-target'));
//while switching tabs we have to clear the invalid fields list
form.data('jqv').InvalidFields = [];
});
//To show aleady non active element , this we are doing so that on load we can remove name attributes for other fields
tabElements.filter('a:not(.active)').each(function (e) {
quickCreateTabOnHide($(this).attr('data-target'));
});
},
/**
* Save quick create form
*
* @param {object} form jQuery
*
* @return {Promise} aDeferred
*/
save(form) {
let aDeferred = $.Deferred();
AppConnector.request(form.serializeFormData())
.done((data) => {
aDeferred.resolve(data);
})
.fail(function (textStatus, errorThrown) {
aDeferred.reject(textStatus, errorThrown);
});
return aDeferred.promise();
}
},
QuickEdit: {
/**
* Show modal
*
* @param {string} html
* @param {object} params
*/
showModal(params = {}, element) {
const self = this;
params['view'] = 'QuickEditModal';
AppConnector.request(params).done(function (html) {
app.showModalWindow(html, (container) => {
let form = container.find('form[name="QuickEdit"]');
let moduleName = form.find('[name="module"]').val();
let editViewInstance = Vtiger_Edit_Js.getInstanceByModuleName(moduleName);
let moduleClassName = moduleName + '_QuickEdit_Js';
editViewInstance.setForm(form);
editViewInstance.registerBasicEvents(form);
if (typeof window[moduleClassName] !== 'undefined') {
new window[moduleClassName]().registerEvents(container);
}
form.validationEngine(app.validationEngineOptionsForRecord);
if (typeof params.callbackPostShown !== 'undefined') {
params.callbackPostShown(form, params);
}
self.registerPostLoadEvents(form, params, element);
});
});
},
/**
* Register post load events
*
* @param {jQuery} form jQuery
* @param {object} params
* @param {jQuery} element
*/
registerPostLoadEvents(form, params, element) {
const submitSuccessCallback = params.callbackFunction || function () {};
const goToFullFormCallBack = params.goToFullFormcallback || function () {};
form.on('submit', (e) => {
if (form.hasClass('not_validation')) {
return true;
}
const moduleName = form.find('[name="module"]').val();
//Form should submit only once for multiple clicks also
if (typeof form.data('submit') !== 'undefined') {
return false;
} else {
if (form.data('jqv').InvalidFields.length > 0) {
//If validation fails, form should submit again
form.removeData('submit');
$.progressIndicator({ mode: 'hide' });
e.preventDefault();
return;
} else {
//Once the form is submiting add data attribute to that form element
form.data('submit', 'true');
$.progressIndicator({ mode: 'hide' });
}
const recordPreSaveEvent = $.Event(Vtiger_Edit_Js.recordPreSave);
form.trigger(recordPreSaveEvent, {
value: 'edit',
module: moduleName
});
if (!recordPreSaveEvent.isDefaultPrevented()) {
const moduleInstance = Vtiger_Edit_Js.getInstanceByModuleName(moduleName);
const saveHandler = !!moduleInstance.quickEditSave ? moduleInstance.quickEditSave : this.save;
let progress = $.progressIndicator({
message: app.vtranslate('JS_SAVE_LOADER_INFO'),
position: 'html',
blockInfo: {
enabled: true
}
});
saveHandler(form).done((data) => {
const modalContainer = form.closest('.modalContainer');
if (modalContainer.length) {
app.hideModalWindow(false, modalContainer[0].id);
}
submitSuccessCallback(data);
app.event.trigger('QuickEdit.AfterSaveFinal', data, form, element);
delete window.popoverCache[data.result._recordId];
progress.progressIndicator({ mode: 'hide' });
if (data.success) {
app.showNotify({
text: app.vtranslate('JS_SAVE_NOTIFY_SUCCESS'),
type: 'success'
});
}
app.reloadAfterSave(data, params, form, element);
});
} else {
//If validation fails in recordPreSaveEvent, form should submit again
form.removeData('submit');
$.progressIndicator({ mode: 'hide' });
}
e.preventDefault();
}
});
form.find('.js-full-editlink').on('click', (e) => {
const form = $(e.currentTarget).closest('form');
const editViewUrl = $(e.currentTarget).data('url');
goToFullFormCallBack(form);
this.goToFullForm(form, editViewUrl);
});
},
/**
* Function to navigate from quick create to edit iew full form
*
* @param {object} form jQuery
*/
goToFullForm(form) {
form.find('input[name="action"]').remove();
form.append('<input type="hidden" name="view" value="Edit" />');
$.each(form.find('[data-validation-engine]'), function (key, data) {
$(data).removeAttr('data-validation-engine');
});
form.addClass('not_validation');
form.trigger('submit');
},
/**
* Save quick create form
*
* @param {object} form jQuery
*
* @return {Promise} aDeferred
*/
save(form) {
const aDeferred = $.Deferred();
form.serializeFormData();
let formData = new FormData(form[0]);
AppConnector.request({
url: 'index.php',
type: 'POST',
data: formData,
processData: false,
contentType: false
})
.done(function (data) {
aDeferred.resolve(data);
})
.fail(function (textStatus, errorThrown) {
aDeferred.reject(textStatus, errorThrown);
});
return aDeferred.promise();
}
},
Scrollbar: {
active: true,
defaults: {
scrollbars: {
autoHide: 'leave'
}
},
page: {
instance: {},
element: null
},
initPage() {
let scrollbarContainer = $('.mainBody');
if (!scrollbarContainer.length) {
scrollbarContainer = $('#page');
}
if (!scrollbarContainer.length) {
scrollbarContainer = $('body');
}
if (this.active) {
this.page.instance = this.y(scrollbarContainer);
this.page.element = $(this.page.instance.getElements().viewport);
}
},
xy(element, options = {}) {
return element.overlayScrollbars(options).overlayScrollbars();
},
y(element, options) {
const yOptions = {
overflowBehavior: {
x: 'h'
}
};
const mergedOptions = Object.assign(this.defaults, options, yOptions);
return element.overlayScrollbars(mergedOptions).overlayScrollbars();
}
},
DropFile: class {
constructor(container, params) {
this.container = container;
this.init(params);
}
/**
* Register function
* @param {jQuery} container
* @param {Object} params
*/
static register(container, params = {}) {
if (typeof container === 'undefined') {
container = $('body');
}
if (container.hasClass('js-drop-container') && !container.prop('disabled')) {
return new App.Components.DropFile(container, params);
}
const instances = [];
container.find('.js-drop-container').each((_, e) => {
instances.push(new App.Components.DropFile($(e), params));
});
return instances;
}
/**
* Initiation
* @param {Object} params
*/
init(params) {
let css = {
border: this.container.css('border'),
opacity: 'unset'
};
this.container.bind('dragenter dragover', (e) => {
$(e.currentTarget).css({
border: '2px dashed #4aa1f3',
opacity: 0.4
});
e.preventDefault();
});
this.container.bind('dragleave', (e) => {
$(e.currentTarget).css(css);
e.preventDefault();
});
this.container.bind('drop', (e) => {
let element = $(e.currentTarget).css(css);
e.preventDefault();
const files = e.originalEvent.dataTransfer.files;
if (files.length < 1) {
return false;
}
params.callback =
params.callback ||
function () {
let progressIndicatorElement = $.progressIndicator({
blockInfo: { enabled: true }
});
let formData = new FormData();
for (let file of files) {
formData.append(element.data('field-name'), file, file.name);
}
formData.append('action', 'SaveAjax');
formData.append('record', element.data('id'));
formData.append('module', element.data('module'));
AppConnector.request({
method: 'POST',
data: formData,
processData: false,
contentType: false
})
.done(function (data) {
if (data.success) {
progressIndicatorElement.progressIndicator({ mode: 'hide' });
app.showNotify({ text: app.vtranslate('JS_SAVE_NOTIFY_SUCCESS'), type: 'success' });
if (element.closest('.js-detail-widget').length) {
Vtiger_Detail_Js.getInstance().getFiltersDataAndLoad(e);
}
} else {
app.showNotify({ text: app.vtranslate('JS_UNEXPECTED_ERROR'), type: 'error' });
progressIndicatorElement.progressIndicator({ mode: 'hide' });
}
})
.fail(function (error, err) {
app.showNotify({ text: app.vtranslate('JS_ERROR'), type: 'error' });
progressIndicatorElement.progressIndicator({ mode: 'hide' });
app.errorLog(error, err);
});
};
app.showConfirmModal({
text: app.vtranslate('JS_CHANGE_CONFIRMATION'),
confirmedCallback: () => {
params.callback(e, this);
}
});
});
}
},
ActivityNotifier: class ActivityNotifier {
notice = {
type: 'error',
icon: false,
hide: true,
delay: 8000,
stack: new PNotify.Stack({
dir1: 'up',
dir2: 'left',
firstpos1: 25,
firstpos2: 25,
modal: false,
maxOpen: 2,
maxStrategy: 'close',
maxClosureCausesWait: true
})
};
intervalId = null;
state = null;
static identifier = '#recordActivityNotifier';
constructor(element, params, interval, notice = {}) {
this.nodeElement = element.get(0);
this.url = params;
this.interval = interval || 10;
if (notice.length) {
this.notice = { ...this.notice, ...notice };
}
}
/**
* Register
* @param {jQuery} container
*/
static register(container) {
let element = container.find(ActivityNotifier.identifier);
if (element.length) {
let notifierData = element.data();
new ActivityNotifier(
element,
{ module: notifierData.module, view: 'RecordActivity', record: notifierData.record },
notifierData.interval
).init();
}
}
/**
* Initiation
*/
init() {
this.setFormat();
this.setTime();
document.addEventListener('visibilitychange', (_) => {
if (document.hidden) {
this.destroyInterval();
} else {
this.setInterval();
this.requestNotifier();
}
});
if (!document.hidden) {
this.setInterval();
}
}
/**
* Set date format
* @param string
*/
setFormat(format = '') {
if (!format) {
let timeFormat = '';
if (CONFIG.hourFormat.toUpperCase() == 24) {
timeFormat = 'HH:mm:ss';
} else {
timeFormat = 'hh:mm:ss A';
}
format = CONFIG.dateFormat.toUpperCase() + ' ' + timeFormat;
}
this.format = format;
}
/**
* Set date time
* @param string
*/
setTime(time = '') {
if (!time) {
time = moment().format(this.format);
}
this.startTime = time;
}
/**
* Set Interval
*/
setInterval() {
if (this.nodeElement.isConnected) {
this.intervalId = setInterval(() => {
if (!this.state) {
this.requestNotifier();
}
}, this.interval * 1000);
}
}
/**
* Destroy Interval
*/
destroyInterval() {
clearInterval(this.intervalId);
}
/**
* Function request for notifier popups
*/
requestNotifier() {
if (!this.nodeElement.isConnected) {
this.destroyInterval();
return false;
}
this.url.dateTime = this.startTime;
this.state = 1;
AppConnector.request(this.url)
.done((data) => {
this.state = 0;
if (app.isJsonString(data)) {
data = JSON.parse(data);
}
let response = data.result;
if (response.text) {
this.notice.text = response.text.trim();
this.notice.title = response.title.trim();
app.showNotify(this.notice);
}
this.setTime(response.dateTime);
})
.fail((data, err) => {
app.errorLog(data, err);
this.destroyInterval();
});
}
},
/**
* Icons class
*/
Icons: class Icons {
/**
* Show modal window with icons to select
* @param {Object} params
*/
static modalView(params = {}) {
var aDeferred = $.Deferred();
let url = 'index.php?module=AppComponents&view=MediaModal';
if (params && Object.keys(params).length) {
url = app.convertObjectToUrl(params, url);
}
let progressElement = $.progressIndicator({ position: 'html', blockInfo: { enabled: true } });
app.showModalWindow({
id: 'MediaModal',
url,
cb: (container) => {
progressElement.progressIndicator({ mode: 'hide' });
container.on('click', '.js-icon-item', (e) => {
let data = {
type: e.currentTarget.dataset.type,
name: e.currentTarget.dataset.name
};
if (data.type === 'image') {
data.src = $(e.currentTarget).find('img').attr('src');
data.key = e.currentTarget.dataset.key;
}
aDeferred.resolve(data);
app.hideModalWindow(null, 'MediaModal');
});
}
});
return aDeferred.promise();
}
}
},
Notify: {
/**
* Check if notifications are allowed
*/
isDesktopPermitted: function () {
return typeof Notification !== 'undefined' && Notification.permission === 'granted';
},
/**
* Show desktop notification
* @param {Object} params
*/
desktop: function (params) {
let type = 'error';
params.modules = new Map([
...PNotify.defaultModules,
[
PNotifyDesktop,
{
fallback: false,
icon: params.icon
}
]
]);
if (typeof params.type !== 'undefined') {
type = params.type;
if (params.type != 'error') {
params.hide = true;
}
}
return PNotify[type](params);
}
},
Clipboard: class Clipboard {
constructor(container) {
this.text = null;
this.oClipboard = null;
this.container = container;
}
/**
* Register
* @param {jQuery} params
*/
static register(container) {
if (typeof container === 'undefined') {
container = $('body');
}
container.on('dblclick', '.js-copy-clipboard', (e) => {
e.preventDefault();
new Clipboard($(e.currentTarget)).load().then((instance) => {
instance.createClipboard();
instance.copy();
instance.destroy();
});
});
}
/**
* Initiation
*/
load() {
const aDeferred = $.Deferred();
let url = this.container.data('url');
if (url) {
this.getTextFromUrl(url).then((response) => {
this.text = response.result.text;
aDeferred.resolve(this);
});
} else {
aDeferred.resolve(this);
}
return aDeferred.promise();
}
/**
* Create ClipboardJS
*/
createClipboard() {
let id = this.container.attr('id');
this.oClipboard = new ClipboardJS(`#${id}`, {
text: (_) => {
return this.text;
}
});
}
/**
* Get text to Clipboard from URL
*/
getTextFromUrl(url) {
const aDeferred = $.Deferred();
let progressIndicatorElement = $.progressIndicator({ blockInfo: { enabled: true } });
AppConnector.request(url)
.done((response) => {
progressIndicatorElement.progressIndicator({ mode: 'hide' });
if (response.success) {
aDeferred.resolve(response);
} else {
aDeferred.reject(response);
}
})
.fail((_) => {
app.showNotify({
text: app.vtranslate('JS_ERROR'),
type: 'error'
});
progressIndicatorElement.progressIndicator({ mode: 'hide' });
aDeferred.reject(_);
});
return aDeferred.promise();
}
/**
* Set text to Clipboard
*/
copy() {
this.container.trigger('click');
app.showNotify({
text: app.vtranslate('JS_NOTIFY_COPY_TEXT'),
type: 'success'
});
}
/**
* Destroy ClipboardJS object
*/
destroy() {
this.oClipboard.destroy();
}
},
/**
* File
*/
File: class File {
/**
* Defalut configuration for fileupload
*/
fileupload = {
dataType: 'json',
replaceFileInput: false,
autoUpload: false,
fail: this.uploadError.bind(this),
add: this.add.bind(this),
change: this.change.bind(this)
};
/**
* Defalut options
*/
options = {
formats: [],
limit: 1,
maxFileSize: CONFIG.maxUploadLimit || 0,
maxFileSizeDisplay: ''
};
files = [];
/**
* Constructor
* @param {jQuery} element
* @param {Object} options
*/
constructor(element, options = {}) {
this.fileInput = element;
if (typeof options.fileupload !== 'undefined') {
this.fileupload = { ...this.fileupload, ...options.fileupload };
delete options.fileupload;
}
this.options = { ...this.options, ...options };
}
/**
* Register file element
* @param {jQuery} element
* @param {Object} options
* @returns
*/
static register(element, options = {}) {
let file = new File(element, options);
file.init();
return file;
}
/**
* Initiation
*/
init() {
this.fileInput.detach();
this.fileupload.fileInput = this.fileInput;
this.fileInput.fileupload(this.fileupload);
this.filesActive = 0;
}
/**
* Add event handler from jQuery-file-upload
*
* @param {Event} e
* @param {Object} data
*/
add(_e, data) {
if (data.files.length > 0) {
data.submit();
}
}
/**
* File change event handler from jQuery-file-upload
*
* @param {Event} e
* @param {object} data
*/
change(_e, data) {
let { valid, error } = this.filterFiles(data.files);
data.files = valid;
if (!valid.length) {
this.fileInput.val('');
}
if (error.length) {
this.showErrors(error);
}
}
/**
* Get only valid files from list
* @param {Array} files
*
* @returns {Object}
*/
filterFiles(files) {
let valid = [],
error = [];
if (files.length + this.files.length > this.options.limit) {
error.push({ error: { text: `${app.vtranslate('JS_FILE_LIMIT')} [${this.options.limit}]` } });
} else {
for (let file of files) {
this.validateFileType(file) && this.validateFileSize(file) ? valid.push(file) : error.push(file);
}
}
return { valid, error };
}
/**
* Validate maximum file size
* @param {Object} file
* @returns {Boolean}
*/
validateFileSize(file) {
let result = typeof file.size === 'number' && file.size < this.options.maxFileSize;
if (!result) {
file.error = {
title: `${app.vtranslate('JS_UPLOADED_FILE_SIZE_EXCEEDS')} <br> [${this.options.maxFileSizeDisplay}]`,
text: file.name
};
}
return result;
}
/**
* Validate file type
*
* @param {Object} file
* @returns {boolean}
*/
validateFileType(file) {
let result =
!this.options.formats.length ||
this.options.formats.filter((format) => {
return file.type === format || (format.slice(-2) === '/*' && file.type.indexOf(format.slice(0, -1)) === 0);
}).length > 0;
if (!result) {
file.error = { title: app.vtranslate('JS_INVALID_FILE_TYPE'), text: file.name };
}
return result;
}
/**
* Show errors
*/
showErrors(errors = []) {
for (let info of errors) {
this.showError(info.error);
}
}
/**
* Show error
*/
showError(error) {
if (typeof error.type === 'undefined') {
error.type = 'error';
}
error.textTrusted = false;
app.showNotify(error);
}
/**
* Error event handler from file upload request
*
* @param {Event} e
* @param {Object} data
*/
uploadError(_e, data) {
this.filesActive--;
app.errorLog('File upload error.');
const { jqXHR, files } = data;
if (typeof jqXHR.responseJSON === 'undefined' || jqXHR.responseJSON === null) {
return this.showError({
title: app.vtranslate('JS_FILE_UPLOAD_ERROR'),
type: 'error'
});
}
files.forEach((file) => {
this.showError({
title: app.vtranslate('JS_FILE_UPLOAD_ERROR'),
text: file.name,
type: 'error'
});
});
}
}
});
const app = (window.app = {
/**
* variable stores client side language strings
*/
languageString: [],
breakpoints: {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200,
xxl: 1300,
xxxl: 1700
},
cacheParams: [],
modalEvents: [],
mousePosition: { x: 0, y: 0 },
childFrame: false,
touchDevice: false,
event: new (function () {
this.el = $({});
this.trigger = function () {
this.el.trigger(arguments[0], Array.prototype.slice.call(arguments, 1));
};
this.on = function () {
this.el.on.apply(this.el, arguments);
};
this.one = function () {
this.el.one.apply(this.el, arguments);
};
this.off = function () {
this.el.off.apply(this.el, arguments);
};
})(),
/**
* Function to get the module name. This function will get the value from element which has id module
* @return : string - module name
*/
getModuleName: function () {
return this.getMainParams('module');
},
/**
* Function to get the module name. This function will get the value from element which has id module
* @return : string - module name
*/
getParentModuleName: function () {
return this.getMainParams('parent');
},
/**
* Function returns the current view name
*/
getViewName: function () {
return this.getMainParams('view');
},
/**
* Function returns the record id
*/
getRecordId: function () {
let recordId;
if (
$.inArray(this.getViewName(), ['Edit', 'PreferenceEdit', 'Detail', 'PreferenceDetail', 'DetailPreview']) !== -1
) {
recordId = this.getMainParams('recordId');
}
return recordId;
},
/**
* Function which will give you all details of the selected record
* @params {object} params - an object of values like {'record' : recordId, 'module' : searchModule, 'fieldType' : 'email'}
*/
getRecordDetails: function (params) {
let aDeferred = $.Deferred();
if (app.getParentModuleName() === 'Settings') {
params.parent = 'Settings';
}
AppConnector.request(Object.assign(params, { action: 'GetData' }))
.done(function (data) {
if (data.success) {
aDeferred.resolve(data);
} else {
aDeferred.reject(data.message);
}
})
.fail(function (error) {
aDeferred.reject();
});
return aDeferred.promise();
},
/**
* Function to get language
*/
getLanguage: function () {
return $('body').data('language');
},
/**
* Function to get page title
*/
getPageTitle: function () {
return document.title;
},
/**
* Function gets current window parent
* @returns {object}
*/
getWindowParent() {
if (
typeof window.frames[0] !== 'undefined' &&
typeof window.frames[0].app !== 'undefined' &&
window.frames[0].app.childFrame
) {
return window.frames[0];
} else {
return window;
}
},
/**
* Check if current window is window top
*/
isWindowTop() {
return window.top === window.self;
},
/**
* Function gets current window parent
* @returns {boolean}
*/
isTouchDevice() {
let supportsTouch = false;
if ('ontouchstart' in window) {
// iOS & android
supportsTouch = true;
} else if (window.navigator.msPointerEnabled) {
// Win8
supportsTouch = true;
} else if ('ontouchstart' in document.documentElement) {
//additional check
supportsTouch = true;
}
if (supportsTouch) {
//remove browser scrollbar visibility (doesn't work in firefox, edge and ie)
$("<style type='text/css'> ::-webkit-scrollbar { display: none;} </style>").appendTo('head');
}
return supportsTouch;
},
/**
* Check if string is json
* @param {string} str
* @returns {boolean}
*/
isJsonString(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
},
/**
* Function to set page title
*/
setPageTitle: function (title) {
document.title = title;
},
/**
* Function to get the contents container
* @returns jQuery object
*/
getContentsContainer: function () {
return $('.bodyContents');
},
hidePopover: function (element) {
if (typeof element === 'undefined') {
element = $('body .js-popover-tooltip');
}
element.popover('hide');
},
hidePopoversAfterClick(popoverParent) {
popoverParent.on('mousedown', (e) => {
setTimeout(() => {
popoverParent.popover('hide');
}, 100);
});
},
registerPopoverManualTrigger(element, manualTriggerDelay) {
const hideDelay = 500;
element.on('mouseleave', (e) => {
setTimeout(() => {
let currentPopover = this.getBindedPopover(element);
if (
!$(':hover').filter(currentPopover).length &&
!currentPopover.find('.js-popover-tooltip--record[aria-describedby]').length
) {
currentPopover.popover('hide');
}
}, hideDelay);
});
element.on('mouseenter', () => {
setTimeout(() => {
if (element.is(':hover')) {
element.popover('show');
let currentPopover = this.getBindedPopover(element);
currentPopover.on('mouseleave', () => {
setTimeout(() => {
if (
!$(':hover').filter(currentPopover).length &&
!currentPopover.find('.js-popover-tooltip--record[aria-describedby]').length
) {
currentPopover.popover('hide'); //close current popover
}
if (!$(':hover').filter($('.popover')).length) {
$('.popover').popover('hide'); //close all popovers
}
}, hideDelay);
});
}
}, manualTriggerDelay);
});
app.hidePopoversAfterClick(element);
},
isEllipsisActive(element) {
let clone = element
.clone()
.addClass('u-text-ellipsis--not-active')
.css(element.css(['font-size', 'font-weight', 'font-family']))
.appendTo('body');
clone.find('.u-text-ellipsis').removeClass('u-text-ellipsis').addClass('u-text-ellipsis--not-active');
if (clone.width() - 1 > element.width()) {
clone.remove();
return true;
}
clone.remove();
return false;
},
showPopoverElementView: function (selectElement = $('.js-popover-tooltip'), params = {}) {
let defaultParams = {
trigger: 'manual',
manualTriggerDelay: 500,
placement: 'auto',
html: true,
template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3></div>',
container: 'body',
boundary: 'viewport',
delay: { show: 300, hide: 100 }
};
selectElement.each(function (_index, domElement) {
let element = $(domElement);
let elementParams = $.extend(true, defaultParams, params, element.data());
let tmp = elementParams.template;
if (elementParams.class) {
tmp = tmp.replace('class="popover"', `class="popover ${elementParams.class}"`);
}
if (elementParams.content) {
tmp = tmp.replace('</h3></div>', `</h3><div class="popover-body">${elementParams.content}</div></div>`);
}
elementParams.template = tmp;
if (element.hasClass('delay0')) {
elementParams.delay = { show: 0, hide: 0 };
}
element.popover(elementParams);
if (elementParams.trigger === 'manual' || typeof elementParams.trigger === 'undefined') {
app.registerPopoverManualTrigger(element, elementParams.manualTriggerDelay);
}
if (elementParams.callbackShown) {
element.on('shown.bs.popover', function (e) {
elementParams.callbackShown(e);
});
}
element.addClass('popover-triggered');
});
return selectElement;
},
registerPopoverEllipsis({
element = $('.js-popover-tooltip--ellipsis'),
params = { trigger: 'hover focus' },
container = $(window)
} = {}) {
const self = this;
params = {
callbackShown: () => {
self.setPopoverPosition(element, container);
},
trigger: 'manual',
placement: 'right',
class: 'js-popover--before-positioned'
};
let popoverText = element.find('.js-popover-text').length ? element.find('.js-popover-text') : element;
if (!app.isEllipsisActive(popoverText)) {
element.addClass('popover-triggered');
return;
}
app.showPopoverElementView(element, params);
},
registerPopoverEllipsisIcon(
selectElement = $('.js-popover-tooltip--ellipsis-icon'),
params = { trigger: 'hover focus' }
) {
selectElement.each(function (index, domElement) {
let element = $(domElement);
let popoverText = element.find('.js-popover-text').length ? element.find('.js-popover-text') : element;
if (!app.isEllipsisActive(popoverText)) {
return;
}
let iconElement = element.find('.js-popover-icon');
if (iconElement.length) {
element.find('.js-popover-icon').removeClass('d-none');
params.selector = '.js-popover-icon';
}
app.showPopoverElementView(element, params);
});
},
/**
* Register popover record
* @param {jQuery} selectElement
* @param {object} customParams
*/
registerPopoverRecord: function (
selectElement = $('.js-popover-tooltip--record'),
customParams = {},
container = $(document)
) {
const self = this;
app.showPopoverElementView(selectElement, {
template:
'<div class="popover c-popover--link js-popover--before-positioned" role="tooltip"><div class="popover-body"></div></div>',
content: '<div class="d-none"></div>',
manualTriggerDelay: app.getMainParams('recordPopoverDelay'),
placement: 'right',
callbackShown: () => {
let href;
if (!selectElement.attr('href')) {
href = selectElement.find('a').attr('href');
}
if (
!href &&
(!selectElement.attr('href') || selectElement.closest('.ui-sortable-handle').hasClass('ui-sortable-helper'))
) {
return false;
}
if (!href) {
href = selectElement.eq(0).attr('href');
}
let link = new URL(href, window.location.origin);
if (!link.searchParams.get('record') || !link.searchParams.get('view')) {
return false;
}
let url = link.href;
url = url.replace('view=', 'xview=') + '&view=RecordPopover';
let currentPopover = self.getBindedPopover(selectElement);
let popoverBody = currentPopover.find('.popover-body');
popoverBody.progressIndicator({});
let appendPopoverData = (data) => {
popoverBody.progressIndicator({ mode: 'hide' }).html(data);
if (typeof customParams.callback === 'function') {
customParams.callback(popoverBody);
}
self.setPopoverPosition(selectElement, container);
};
let urlObject = app.convertUrlToObject(url);
let cacheData = window.popoverCache[urlObject['record']];
if (typeof cacheData !== 'undefined') {
appendPopoverData(cacheData);
} else {
AppConnector.request(url).done((data) => {
window.popoverCache[urlObject['record']] = data;
appendPopoverData(data);
});
}
}
});
},
/**
* Update popover record position (overwrite bootstrap positioning, failing on huge elements)
* @param {jQuery} popover
* @param {number} offsetLeft
*/
setPopoverPosition(popoverElement, container = $(window)) {
let popover = this.getBindedPopover(popoverElement);
if (!popover.length) {
return;
}
const iframeOffset = this.computePopoverIframeOffset(popoverElement);
let windowHeight = $(window).height(),
windowWidth = $(window).width(),
popoverPadding = 10,
popoverBody = popover.find('.popover-body'),
popoverHeight = popoverBody.height(),
popoverWidth = popoverBody.width(),
offsetTop = app.mousePosition.y + iframeOffset.top,
offsetLeft = app.mousePosition.x + iframeOffset.left;
if (popoverHeight + offsetTop + popoverPadding > windowHeight) {
offsetTop = offsetTop - popoverHeight - popoverPadding;
}
if (popoverWidth + offsetLeft + popoverPadding > windowWidth) {
offsetLeft = windowWidth - popoverWidth;
}
popover.css({
transform: `translate3d(${offsetLeft}px, ${offsetTop}px, 0)`
});
popover.removeClass('js-popover--before-positioned');
popoverElement.one('hide.bs.popover', () => {
popover.addClass('js-popover--before-positioned');
});
},
/**
* Compute popover iframe offset
*
* @param {Object} popoverElement jquery
*
* @return {Object} offset top and left
*/
computePopoverIframeOffset(popoverElement) {
let iframeOffsetTop = 0;
let iframeOffsetLeft = 0;
if (!$(document).find(popoverElement).length) {
let iframe = $(document).find('iframe');
const iframeOffset = iframe.offset();
iframeOffsetTop += iframeOffset.top;
iframeOffsetLeft += iframeOffset.left;
if (!iframe.contents().find(popoverElement).length) {
let iframe2 = iframe.contents().find('iframe');
const iframeOffset2 = iframe2.offset();
iframeOffsetTop += iframeOffset2.top;
iframeOffsetLeft += iframeOffset2.left;
}
}
return { top: iframeOffsetTop, left: iframeOffsetLeft };
},
/**
* Get binded popover
* @param {jQuery} element
* @returns {Mixed|jQuery|HTMLElement}
*/
getBindedPopover(element) {
return $(`#${element.attr('aria-describedby')}`);
},
/**
* Function to check the maximum selection size of multiselect and update the results
* @params <object> multiSelectElement
* @params <object> select2 params
*/
registerChangeEventForMultiSelect: function (selectElement, params) {
if (typeof selectElement === 'undefined') {
return;
}
var instance = selectElement.data('select2');
var limit = params.maximumSelectionLength;
selectElement.on('change', function (e) {
var data = instance.data();
if ($.isArray(data) && data.length >= limit) {
instance.updateResults();
}
});
},
/**
* Function to get data of the child elements in serialized format
* @params <object> parentElement - element in which the data should be serialized. Can be selector , domelement or jquery object
* @params <String> returnFormat - optional which will indicate which format return value should be valid values "object" and "string"
* @return <object> - encoded string or value map
*/
getSerializedData: function (parentElement, returnFormat) {
if (typeof returnFormat === 'undefined') {
returnFormat = 'string';
}
parentElement = $(parentElement);
let encodedString = parentElement.children().serialize();
if (returnFormat == 'string') {
return encodedString;
}
let keyValueMap = {};
let valueList = encodedString.split('&');
for (let index in valueList) {
let keyValueString = valueList[index];
let keyValueArr = keyValueString.split('=');
let nameOfElement = keyValueArr[0];
let valueOfElement = keyValueArr[1];
keyValueMap[nameOfElement] = decodeURIComponent(valueOfElement);
}
return keyValueMap;
},
showModalData(data, container, paramsObject, cb, url, sendByAjaxCb) {
const thisInstance = this;
let params = {
show: true
};
if (!app.getMainParams('backgroundClosingModal')) {
params.backdrop = 'static';
params.keyboard = false;
}
if (typeof paramsObject === 'object') {
container.css(paramsObject);
params = $.extend(params, paramsObject);
}
container.html(data);
if (container.find('.modal').hasClass('static')) {
params.backdrop = 'static';
}
// In a modal dialog elements can be specified which can receive focus even though they are not descendants of the modal dialog.
$.fn.modal.Constructor.prototype._enforceFocus = function (e) {
$(document)
.off('focusin.bs.modal') // guard against infinite focus loop
.on(
'focusin.bs.modal',
$.proxy(function (e) {
if ($(e.target).hasClass('select2-search__field')) {
return true;
}
}, this)
);
};
const modalContainer = container.find('.modal:first');
modalContainer.one('shown.bs.modal', function () {
thisInstance.registerDataTables(modalContainer.find('.js-modal-data-table'));
cb(modalContainer);
App.Fields.Picklist.changeSelectElementView(modalContainer);
App.Fields.Date.register(modalContainer);
App.Fields.DateTime.register(modalContainer);
App.Fields.Text.Editor.register(modalContainer.find('.js-editor'), {
height: '5em',
toolbar: 'Min'
});
App.Fields.MultiAttachment.register(modalContainer);
App.Fields.Icon.register(modalContainer);
app.registesterScrollbar(modalContainer);
app.registerIframeEvents(modalContainer);
modalContainer.find('.modal-dialog:not(.js-no-drag)').draggable({
handle: '.modal-title'
});
modalContainer.find('.modal-title').css('cursor', 'move');
});
$('body').append(container);
modalContainer.modal(params);
app.registerFormsEvents(modalContainer);
thisInstance.registerModalEvents(modalContainer, sendByAjaxCb);
},
showModalWindow: function (data, url, cb, paramsObject = {}) {
if (!app.isCurrentWindowTarget('app.showModalWindow', arguments)) {
return false;
}
const thisInstance = this;
let sendByAjaxCb, modalId;
modalId = 'modal_' + Math.random().toString(36).substr(2, 9);
//null is also an object
if (typeof data === 'object' && data != null && !(data instanceof $)) {
if (data.id != undefined) {
modalId = data.id;
}
paramsObject = data.css;
cb = data.cb;
url = data.url;
if (data.sendByAjaxCb !== 'undefined') {
sendByAjaxCb = data.sendByAjaxCb;
}
data = data.data;
} else if (typeof data === 'string') {
let modalData = $(data).last();
if (modalData.data('modalid')) {
modalId = modalData.data('modalid');
}
}
if (typeof url === 'function') {
if (typeof cb === 'object') {
paramsObject = cb;
}
cb = url;
url = false;
} else if (typeof url === 'object') {
cb = function () {};
paramsObject = url;
url = false;
}
if (typeof cb !== 'function') {
cb = function () {};
}
if (typeof sendByAjaxCb !== 'function') {
sendByAjaxCb = function () {};
}
if (paramsObject !== undefined && paramsObject.modalId !== undefined) {
modalId = paramsObject.modalId;
}
// prevent duplicate hash generation
let container = $('#' + modalId);
if (container.length) {
container.remove();
}
container = $('<div></div>');
container.attr('id', modalId).addClass('modalContainer js-modal-container');
container.one('hidden.bs.modal', function () {
container.remove();
let backdrop = $('.modal-backdrop');
if (!$('.modal.show').length) {
backdrop.remove();
}
if (backdrop.length > 0) {
$('body').addClass('modal-open');
}
});
Window.lastModalId = modalId;
if (data) {
thisInstance.showModalData(data, container, paramsObject, cb, url, sendByAjaxCb);
} else {
$.get(url).done(function (response) {
thisInstance.showModalData(response, container, paramsObject, cb, url, sendByAjaxCb);
});
}
return container;
},
showModalHtml: function (params) {
let data = '',
icon = '';
let footer = params['footer'] ?? '';
if (params['header']) {
params['header'] = `<span class="${params['headerIcon']} mr-2"></span>${params['header']}`;
}
if (params['footerButtons']) {
$.each(params['footerButtons'], (i, button) => {
icon = data = '';
$.each(button['data'], (key, val) => {
data += ` data-${key}="${val}"`;
});
if (button['icon']) {
icon += `<span class="${button['icon']} mr-2"></span>`;
}
footer += `<button type="button" class="btn ${button['class']}" ${data}>${icon}${button['text']}</button>`;
});
}
if (footer) {
footer = `<div class="modal-footer">${footer}</div>`;
}
let html = `<div class="modal" role="dialog"><div class="modal-dialog ${params['class']}" role="document"><div class="modal-content">
<div class="modal-header"><h5 class="modal-title js-modal-title" data-js="container">${params['header']}</h5><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button></div>
<div class="modal-body js-modal-content text-break ${params['bodyClass']}" data-js="container">${params['body']}</div>${footer}</div></div></div>`;
params.data = html;
return app.showModalWindow(params);
},
/**
* Check if current window is target for a modal and trigger in correct window if not
*
* @param {String} sourceFunction source function name in dot prop notation object
* @param {Array} args source function arguments
*
* @return {Boolean} isCurrentWindowTarget
*/
isCurrentWindowTarget(sourceFunction, args) {
let isCurrentWindowTarget = true;
if (CONFIG.modalTarget === 'parentIframe') {
this.childFrame = true;
sourceFunction = sourceFunction.split('.');
sourceFunction.unshift('parent');
sourceFunction = sourceFunction.reduce((o, i) => o[i], window);
sourceFunction.apply(window.parent.app, args);
isCurrentWindowTarget = false;
}
return isCurrentWindowTarget;
},
/**
* Function which you can use to hide the modal
* This api assumes that we are using block ui plugin and uses unblock api to unblock it
*/
hideModalWindow: function (callback, id) {
if (!app.isCurrentWindowTarget('app.hideModalWindow', arguments)) {
return false;
}
let container;
if (callback && typeof callback === 'object') {
container = callback;
} else if (id == undefined) {
container = $('.modalContainer');
} else {
container = $('#' + id);
}
if (container.length <= 0) {
return;
}
if (typeof callback !== 'function') {
callback = function () {};
}
let modalContainer = container.find('.modal');
modalContainer.modal('hide');
let backdrop = $('.modal-backdrop:last');
if ($('.modalContainer').length == 0 && backdrop.length) {
backdrop.remove();
}
modalContainer.one('hidden.bs.modal', callback);
},
registerModalController: function (modalId, modalContainer, cb) {
let windowParent = this.childFrame ? window.parent : window;
if (!modalId) {
modalId = Window.lastModalId;
}
if (!modalContainer) {
modalContainer = $('#' + modalId + ' .js-modal-data');
}
let moduleName = modalContainer.data('module') || 'Base';
let modalClass = moduleName.replace(':', '_') + '_' + modalContainer.data('view') + '_JS';
if (typeof windowParent[modalClass] === 'undefined') {
modalClass = [...modalClass.split('_').slice(0, -1), 'Js'].join('_');
}
if (typeof windowParent[modalClass] === 'undefined') {
modalClass = 'Base_' + modalContainer.data('view') + '_JS';
}
if (typeof windowParent[modalClass] !== 'undefined') {
let instance = new windowParent[modalClass]();
if (typeof cb === 'function') {
cb(modalContainer, instance);
}
instance.registerEvents(modalContainer);
if (modalId && app.modalEvents[modalId]) {
app.modalEvents[modalId](modalContainer, instance);
}
}
},
registerModalEvents: function (container, sendByAjaxCb) {
let form = container.find('form');
let validationForm = false;
if (form.hasClass('validateForm') || form.hasClass('js-validate-form')) {
form.validationEngine(app.validationEngineOptions);
validationForm = true;
}
if (container.data('view') === 'QuickDetailModal') {
this.registerBlockAnimationEvent(container);
}
if (form.hasClass('sendByAjax') || form.hasClass('js-send-by-ajax')) {
form.on('submit', function (e) {
let save = true;
e.preventDefault();
if (validationForm && form.data('jqv').InvalidFields.length > 0) {
app.formAlignmentAfterValidation(form);
save = false;
}
if (save) {
let progressIndicatorElement = $.progressIndicator({
blockInfo: { enabled: true }
});
let formData = form.serializeFormData();
AppConnector.request(formData)
.done(function (responseData) {
sendByAjaxCb(formData, responseData);
if (responseData.success && responseData.result) {
if (responseData.result.notify) {
Vtiger_Helper_Js.showMessage(responseData.result.notify);
}
if (responseData.result.processStop) {
progressIndicatorElement.progressIndicator({ mode: 'hide' });
return false;
}
}
app.hideModalWindow();
progressIndicatorElement.progressIndicator({ mode: 'hide' });
})
.fail(function (error) {
app.showNotify({
type: 'error',
title: app.vtranslate('JS_UNEXPECTED_ERROR'),
text: error
});
progressIndicatorElement.progressIndicator({ mode: 'hide' });
});
}
});
}
},
registerFormsEvents: function (container) {
let forms = container.find('form.js-form-ajax-submit,form.js-form-single-save');
forms.each((i, form) => {
form = $(form);
let validationForm = false;
if (form.hasClass('js-validate-form')) {
form.validationEngine(app.validationEngineOptions);
validationForm = true;
}
if (form.hasClass('js-form-single-save')) {
form.find('select,input').on('change', function () {
let element = $(this);
if (validationForm && element.validationEngine('validate')) {
return;
}
let progressIndicatorElement = $.progressIndicator({
blockInfo: { enabled: true }
});
let formData = form.serializeFormData();
let name = element.attr('name').replace('[]', '');
formData['updateField'] = name;
formData['updateValue'] = formData[name];
AppConnector.request(formData)
.done(function (responseData) {
if (responseData.success && responseData.result) {
if (responseData.result.notify) {
app.showNotify(responseData.result.notify);
}
}
progressIndicatorElement.progressIndicator({ mode: 'hide' });
})
.fail(function (error) {
app.showNotify({
title: app.vtranslate('JS_UNEXPECTED_ERROR'),
text: error,
type: 'error'
});
progressIndicatorElement.progressIndicator({ mode: 'hide' });
});
});
}
if (form.hasClass('js-form-ajax-submit')) {
form.on('submit', function (e) {
let save = true;
e.preventDefault();
if (validationForm && form.data('jqv').InvalidFields.length > 0) {
app.formAlignmentAfterValidation(form);
save = false;
}
if (save) {
let progressIndicatorElement = $.progressIndicator({
blockInfo: { enabled: true }
});
AppConnector.request(form.serializeFormData())
.done(function (responseData) {
if (responseData.success && responseData.result) {
if (responseData.result.notify) {
Vtiger_Helper_Js.showMessage(responseData.result.notify);
}
if (responseData.result.closeModal) {
app.hideModalWindow(null, container.closest('.js-modal-container').attr('id'));
}
}
progressIndicatorElement.progressIndicator({ mode: 'hide' });
})
.fail(function () {
app.showNotify({
text: app.vtranslate('JS_UNEXPECTED_ERROR'),
type: 'error'
});
progressIndicatorElement.progressIndicator({ mode: 'hide' });
});
}
});
}
});
},
isHidden: function (element) {
return element.css('display') == 'none';
},
isInvisible: function (element) {
return element.css('visibility') == 'hidden';
},
/**
* Default validation eninge options
*/
validationEngineOptions: {
// Avoid scroll decision and let it scroll up page when form is too big
// Reference: http://www.position-absolute.com/articles/jquery-form-validator-because-form-validation-is-a-mess/
scroll: false,
promptPosition: 'topLeft',
//to support validation for select2 select box
prettySelect: true,
usePrefix: 's2id_'
},
validationEngineOptionsForRecord: {
scroll: false,
promptPosition: 'topLeft',
//to support validation for select2 select box
prettySelect: true,
usePrefix: 's2id_',
onBeforePromptType: function (field) {
var block = field.closest('.js-toggle-panel');
if (block.find('.blockContent').is(':hidden')) {
block.find('.blockHeader').click();
}
}
},
/**
* Default scroll options
*/
scrollOptions: {
wheelSpeed: 0.5
},
/**
* Function to push down the error message size when validation is invoked
* @params : form Element
*/
formAlignmentAfterValidation: function (form) {
// to avoid hiding of error message under the fixed nav bar
var formError = form.find(".formError:not('.greenPopup'):first");
if (formError.length > 0) {
var destination = formError.offset().top;
var resizedDestnation = destination - 105;
$('html').animate(
{
scrollTop: resizedDestnation
},
'slow'
);
}
},
/**
* Register block toggle event
* @param {jQuery} container
*/
registerBlockToggleEvent(container) {
container.on('click', '.js-block-header', function (e) {
const target = $(e.target);
if (
target.is('input') ||
target.is('button') ||
target.parents().is('button') ||
target.hasClass('js-stop-propagation') ||
target.parents().hasClass('js-stop-propagation')
) {
return false;
}
const blockHeader = $(e.currentTarget);
const blockContents = blockHeader.next();
const iconToggle = blockHeader.find('.iconToggle');
if (blockContents.hasClass('d-none')) {
blockContents.removeClass('d-none');
iconToggle.removeClass(iconToggle.data('hide')).addClass(iconToggle.data('show'));
} else {
blockContents.addClass('d-none');
iconToggle.removeClass(iconToggle.data('show')).addClass(iconToggle.data('hide'));
}
});
},
registerBlockAnimationEvent: function (container = false) {
let detailViewContentHolder = $('div.details div.contents');
let blockHeader = detailViewContentHolder.find('.blockHeader');
if (container !== false) {
blockHeader = container.find('.blockHeader');
}
blockHeader.on('click', function (e) {
const target = $(e.target);
if (
target.is('input') ||
target.is('button') ||
target.parents().is('button') ||
target.hasClass('js-stop-propagation') ||
target.parents().hasClass('js-stop-propagation')
) {
return false;
}
let currentTarget = $(this).find('.js-block-toggle').not('.d-none');
let blockId = currentTarget.data('id');
let closestBlock = currentTarget.closest('.js-toggle-panel');
let bodyContents = closestBlock.find('.blockContent');
let data = currentTarget.data();
let module = app.getModuleName();
if (data.mode === 'show') {
bodyContents.addClass('d-none');
app.cacheSet(module + '.' + blockId, 0);
currentTarget.addClass('d-none');
closestBlock.find('[data-mode="hide"]').removeClass('d-none');
} else {
bodyContents.removeClass('d-none');
app.cacheSet(module + '.' + blockId, 1);
currentTarget.addClass('d-none');
closestBlock.find('[data-mode="show"]').removeClass('d-none');
}
});
},
registerEventForDateFields: function (parentElement) {
if (typeof parentElement === 'undefined') {
parentElement = $('body');
}
parentElement = $(parentElement);
let element;
if (parentElement.hasClass('dateField')) {
element = parentElement;
} else {
element = $('.dateField', parentElement);
}
element.datepicker({ autoclose: true }).on('changeDate', function (ev) {
let currentElement = $(ev.currentTarget),
dateFormat = currentElement.data('dateFormat').toUpperCase(),
date = $.datepicker.formatDate(moment(ev.date).format(dateFormat), ev.date);
currentElement.val(date);
});
App.Fields.Utils.hideMobileKeyboard(element);
},
registerEventForClockPicker: function (timeInputs = $('.clockPicker')) {
if (!timeInputs.hasClass('clockPicker')) {
timeInputs = timeInputs.find('.clockPicker');
}
if (!timeInputs.length) {
return;
}
let params = {
placement: 'bottom',
autoclose: true,
minutestep: 5
};
$('.js-clock__btn').on('click', (e) => {
e.stopPropagation();
let tempElement = $(e.currentTarget).closest('.time').find('input.clockPicker');
if (tempElement.attr('disabled') !== 'disabled' && tempElement.attr('readonly') !== 'readonly') {
tempElement.clockpicker('show');
}
});
let formatTimeString = (timeInput) => {
if (params.twelvehour) {
let meridiemTime = '';
params.afterDone = () => {
//format time string after picking a value
let timeString = timeInput.val(),
timeStringFormatted = timeString.slice(0, timeString.length - 2) + ' ' + meridiemTime;
timeInput.val(timeStringFormatted);
app.event.trigger('Clockpicker.changed', timeInput);
};
params.beforeHide = () => {
meridiemTime = $('.clockpicker-buttons-am-pm:visible').find('a:not(.text-white-50)').text();
};
} else {
params.afterDone = () => {
app.event.trigger('Clockpicker.changed', timeInput);
};
}
};
timeInputs.each((i, e) => {
let timeInput = $(e);
let formatTime = timeInputs.data('format') || CONFIG.hourFormat;
params.twelvehour = parseInt(formatTime) === 12 ? true : false;
formatTimeString(timeInput);
timeInput.clockpicker(params);
});
App.Fields.Utils.hideMobileKeyboard(timeInputs);
},
registerDataTables: function (table, options = {}) {
if ($.fn.dataTable == undefined) {
return false;
}
if (table.length == 0) {
return false;
}
$.extend($.fn.dataTable.defaults, {
language: {
sLengthMenu: app.vtranslate('JS_S_LENGTH_MENU'),
sZeroRecords: app.vtranslate('JS_NO_RESULTS_FOUND'),
sInfo: app.vtranslate('JS_S_INFO'),
sInfoEmpty: app.vtranslate('JS_S_INFO_EMPTY'),
sSearch: app.vtranslate('JS_SEARCH'),
sEmptyTable: app.vtranslate('JS_NO_RESULTS_FOUND'),
sInfoFiltered: app.vtranslate('JS_S_INFO_FILTERED'),
sLoadingRecords: app.vtranslate('JS_LOADING_OF_RECORDS'),
sProcessing: app.vtranslate('JS_LOADING_OF_RECORDS'),
oPaginate: {
sFirst: app.vtranslate('JS_S_FIRST'),
sPrevious: app.vtranslate('JS_S_PREVIOUS'),
sNext: app.vtranslate('JS_S_NEXT'),
sLast: app.vtranslate('JS_S_LAST')
},
oAria: {
sSortAscending: app.vtranslate('JS_S_SORT_ASCENDING'),
sSortDescending: app.vtranslate('JS_S_SORT_DESCENDING')
}
}
});
if (!Object.keys(options).length) {
options = Object.assign({ searching: true, ordering: true, paging: true, info: true }, table.data());
}
return table.DataTable(options);
},
/**
* Function to get the select2 element from the raw select element
* @params: select element
* @return : select2Element - corresponding select2 element
*/
getSelect2ElementFromSelect: function (selectElement) {
var selectId = selectElement.attr('id');
//since select2 will add s2id_ to the id of select element
var select2EleId = 'select2-' + selectId + '-container';
return $('#' + select2EleId).closest('.select2-container');
},
/**
* Function to set with of the element to parent width
* @params : jQuery element for which the action to take place
*/
setInheritWidth: function (elements) {
$(elements).each(function (index, element) {
var parentWidth = $(element).parent().width();
$(element).width(parentWidth);
});
},
showNewScrollbar: function (element, options = { wheelPropagation: true }) {
if (typeof element === 'undefined' || !element.length) return;
return new PerfectScrollbar(element[0], Object.assign(this.scrollOptions, options));
},
showNewScrollbarTopBottomRight: function (element, options = {}) {
if (typeof element === 'undefined' || !element.length) return;
options = Object.assign(options, this.scrollOptions);
let scrollbarTopLeftInit = new PerfectScrollbar(element[0], options);
let scrollbarTopElement = element.find('.ps__rail-x').first();
scrollbarTopElement.css({
top: 0,
bottom: 'auto'
});
scrollbarTopElement.find('.ps__thumb-x').css({
top: 2,
bottom: 'auto'
});
let scrollbarBottomRightInit = new PerfectScrollbar(element[0], options);
return [scrollbarTopLeftInit, scrollbarBottomRightInit];
},
showNewScrollbarTopBottom: function (element, options = { wheelPropagation: true, suppressScrollY: true }) {
if (typeof element === 'undefined' || !element.length) return;
options = Object.assign(options, this.scrollOptions);
new PerfectScrollbar(element[0], options);
new PerfectScrollbar(element[0], options);
var scrollbarTopElement = element.find('.ps__rail-x').first();
scrollbarTopElement.css({
top: 0,
bottom: 'auto'
});
scrollbarTopElement.find('.ps__thumb-x').css({
top: 2,
bottom: 'auto'
});
},
showNewScrollbarTop: function (element, options = { wheelPropagation: true, suppressScrollY: true }) {
if (typeof element === 'undefined' || !element.length) return;
options = Object.assign(this.scrollOptions, options);
new PerfectScrollbar(element[0], options);
var scrollbarTopElement = element.find('.ps__rail-x').first();
scrollbarTopElement.css({
top: 0,
bottom: 'auto'
});
scrollbarTopElement.find('.ps__thumb-x').css({
top: 2,
bottom: 'auto'
});
},
showNewScrollbarLeft: function (element, options = { wheelPropagation: true }) {
if (typeof element === 'undefined' || !element.length) return;
options = Object.assign(this.scrollOptions, options);
new PerfectScrollbar(element[0], options);
var scrollbarLeftElement = element.children('.ps__rail-y').first();
scrollbarLeftElement.css({
left: 0,
right: 'auto'
});
scrollbarLeftElement.find('.ps__thumb-y').css({
left: 2,
right: 'auto'
});
},
showScrollBar: function (element, options = {}) {
if (typeof options.height === 'undefined') options.height = element.css('height');
return element.slimScroll(options);
},
/**
* Register middle scroll hack for scrollbar libraries
* @param {jQuery} container
*/
registerMiddleClickScroll(container) {
let middleScroll = false;
container.on('mousedown', (e) => {
let clickedMouseButton = e.which; // get clicked button id
if (clickedMouseButton == 2 && middleScroll == false) {
middleScroll = true;
let mouseY = e.pageY,
mouseX = e.pageX;
$(document).on('mousemove', (e) => {
if (middleScroll == true) {
$('body').addClass('u-cursor-scroll-all');
let mouseMoveY = mouseY - e.pageY,
scrollSlowerRate = 100, // higher number = slower scroll
contentScrollY = container.scrollTop(),
scrollerY = contentScrollY - mouseMoveY - scrollSlowerRate,
mouseMoveX = mouseX - e.pageX,
contentScrollX = container.scrollLeft(),
scrollerX = contentScrollX - mouseMoveX - scrollSlowerRate;
container.scrollTop(scrollerY);
container.scrollLeft(scrollerX);
}
});
}
});
container.on('mouseup', () => {
$('body').removeClass('u-cursor-scroll-all');
middleScroll = false;
});
},
/**
* Function returns translated string
*/
vtranslate: function (key) {
if (key in LANG) {
return LANG[key];
}
return key;
},
/*
* Cache API on client-side
*/
cacheNSKey: function (key) {
// Namespace in client-storage
return 'yf.' + key;
},
cacheGet: function (key) {
key = this.cacheNSKey(key);
return store.get(key);
},
cacheSet: function (key, value) {
key = this.cacheNSKey(key);
store.set(key, value);
},
cacheClear: function (key) {
key = this.cacheNSKey(key);
return store.remove(key);
},
moduleCacheSet: function (key, value) {
const orgKey = key;
key = this.getModuleName() + '_' + key;
this.cacheSet(key, value);
const cacheKey = 'mCache' + this.getModuleName();
let moduleCache = this.cacheGet(cacheKey);
if (moduleCache == null) {
moduleCache = [];
} else {
moduleCache = moduleCache.split(',');
}
moduleCache.push(orgKey);
this.cacheSet(cacheKey, Vtiger_Helper_Js.unique(moduleCache).join(','));
},
moduleCacheGet: function (key) {
return this.cacheGet(this.getModuleName() + '_' + key);
},
moduleCacheKeys: function () {
const modules = this.cacheGet('mCache' + this.getModuleName());
if (modules) {
return modules.split(',');
}
return [];
},
moduleCacheClear: function (key) {
var thisInstance = this;
var moduleName = this.getModuleName();
var cacheKey = 'mCache' + moduleName;
var moduleCache = this.cacheGet(cacheKey);
if (moduleCache == null) {
moduleCache = [];
} else {
moduleCache = moduleCache.split(',');
}
$.each(moduleCache, function (index, value) {
thisInstance.cacheClear(moduleName + '_' + value);
});
thisInstance.cacheClear(cacheKey);
},
htmlEncode: function (value) {
if (value) {
return $('<div />').text(value).html();
} else {
return '';
}
},
htmlDecode: function (value) {
if (value) {
return $('<div />').html(value).text();
} else {
return '';
}
},
/**
* Function places an element at the center of the page
* @param <jQuery Element> element
*/
placeAtCenter: function (element) {
element.css('position', 'absolute');
element.css('top', ($(window).height() - element.outerHeight()) / 2 + $(window).scrollTop() + 'px');
element.css('left', ($(window).width() - element.outerWidth()) / 2 + $(window).scrollLeft() + 'px');
},
getvalidationEngineOptions: function (select2Status) {
return Object.assign({}, app.validationEngineOptions);
},
/**
* Function to notify UI page ready after AJAX changes.
* This can help in re-registering the event handlers (which was done during ready event).
*/
notifyPostAjaxReady: function () {
$(document).trigger('postajaxready');
},
/**
* Listen to xready notiications.
*/
listenPostAjaxReady: function (callback) {
$(document).on('postajaxready', callback);
},
/**
* Form function handlers
*/
setFormValues: function (kv) {
for (var k in kv) {
$(k).val(kv[k]);
}
},
/**
* Function returns the javascript controller based on the current view
*/
getPageController: function () {
if (window.pageController) {
return window.pageController;
}
const moduleName = app.getModuleName();
const view = app.getViewName();
const parentModule = app.getParentModuleName();
let moduleClassName = parentModule + '_' + moduleName + '_' + view + '_Js';
if (typeof window[moduleClassName] === 'undefined') {
moduleClassName = parentModule + '_Vtiger_' + view + '_Js';
}
if (typeof window[moduleClassName] === 'undefined') {
moduleClassName = moduleName + '_' + view + '_Js';
}
var extendModules = $('#extendModules').val();
if (typeof window[moduleClassName] === 'undefined' && extendModules != undefined) {
moduleClassName = extendModules + '_' + view + '_Js';
}
if (typeof window[moduleClassName] === 'undefined') {
moduleClassName = 'Vtiger_' + view + '_Js';
}
if (typeof window[moduleClassName] !== 'undefined') {
if (typeof window[moduleClassName] === 'function') {
return (window.pageController = new window[moduleClassName]());
}
if (typeof window[moduleClassName] === 'object') {
return (window.pageController = window[moduleClassName]);
}
}
let moduleBaseClassName = parentModule + '_' + moduleName + '_' + 'Index_Js';
if (typeof window[moduleBaseClassName] !== 'undefined') {
if (typeof window[moduleBaseClassName] === 'function') {
return (window.pageController = new window[moduleBaseClassName]());
}
if (typeof window[moduleBaseClassName] === 'object') {
return (window.pageController = window[moduleBaseClassName]);
}
}
},
/**
* Function to decode the encoded htmlentities values
*/
getDecodedValue: function (value) {
return $('<div></div>').html(value).text();
},
getCookie: function (c_name) {
var c_value = document.cookie;
var c_start = c_value.indexOf(' ' + c_name + '=');
if (c_start === -1) {
c_start = c_value.indexOf(c_name + '=');
}
if (c_start === -1) {
c_value = null;
} else {
c_start = c_value.indexOf('=', c_start) + 1;
var c_end = c_value.indexOf(';', c_start);
if (c_end === -1) {
c_end = c_value.length;
}
c_value = unescape(c_value.substring(c_start, c_end));
}
return c_value;
},
setCookie: function (c_name, value, exdays) {
var exdate = new Date();
exdate.setDate(exdate.getDate() + exdays);
var c_value = escape(value) + (exdays == null ? '' : '; expires=' + exdate.toUTCString());
document.cookie = c_name + '=' + c_value;
},
getUrlVar: function (varName) {
var getVar = function () {
var vars = {};
window.location.search.replace(/[?&]+([^=&]+)=([^&]*)/gi, function (m, key, value) {
vars[key] = value;
});
return vars;
};
return getVar()[varName];
},
saveAjax: function (mode, param, addToParams) {
var aDeferred = $.Deferred();
var params = {};
params['module'] = app.getModuleName();
params['parent'] = app.getParentModuleName();
params['action'] = 'SaveAjax';
if (mode) {
params['mode'] = mode;
}
params['param'] = param;
if (addToParams != undefined) {
for (var i in addToParams) {
params[i] = addToParams[i];
}
}
AppConnector.request(params)
.done(function (data) {
aDeferred.resolve(data);
})
.fail(function (textStatus, errorThrown) {
aDeferred.reject(textStatus, errorThrown);
});
return aDeferred.promise();
},
/**
* Hack for Safari breaking down, when sending empty file input
* @param html
*/
removeEmptyFilesInput(form) {
for (let i = 0; i < form.elements.length; i++) {
if (form.elements[i].type === 'file') {
if (form.elements[i].value === '') {
form.elements[i].parentNode.removeChild(form.elements[i]);
}
}
}
},
getMainParams: function (param, json) {
if (param in CONFIG) {
return CONFIG[param];
}
if (app.cacheParams[param] === undefined) {
app.cacheParams[param] = $('#' + param).val();
}
let value = app.cacheParams[param];
if (json) {
if (value) {
value = JSON.parse(value);
} else {
value = [];
}
}
return value;
},
setMainParams: function (param, value) {
app.cacheParams[param] = value;
$('#' + param).val(value);
},
errorLog: function (error, err, errorThrown) {
if (!CONFIG.debug) {
return;
}
console.warn(
'%cYetiForce debug mode!!!',
'color: red; font-family: sans-serif; font-size: 1.5em; font-weight: bolder; text-shadow: #000 1px 1px;'
);
if (typeof error === 'object' && error.responseText) {
error = error.responseText;
}
if (typeof error === 'object' && error.statusText) {
error = error.statusText;
}
if (error) {
console.error(error);
}
if (err && err !== 'error') {
console.error(err);
}
if (errorThrown) {
console.error(errorThrown);
}
},
registerQuickEditModal: function (container) {
if (typeof container === 'undefined') {
container = $('body');
}
container.on('click', '.js-quick-edit-modal', function (e) {
e.preventDefault();
let element = $(this);
let data = {
module: element.data('module'),
record: element.data('record'),
removeFromUrl: 'step'
};
if (element.data('values')) {
$.extend(data, element.data('values'));
}
$.each(['mandatoryFields', 'modalTitle', 'showLayout', 'editFields', 'picklistValues'], function (index, value) {
if (element.data(value)) {
data[value] = element.data(value);
}
});
App.Components.QuickEdit.showModal(data, element);
});
},
registerModal: function (container) {
if (typeof container === 'undefined') {
container = $('body');
}
container
.off('click', 'button.showModal, a.showModal, .js-show-modal')
.on('click', 'button.showModal, a.showModal, .js-show-modal', function (e) {
e.preventDefault();
let currentElement = $(e.currentTarget);
let url = currentElement.data('url');
if (typeof url !== 'undefined') {
if (currentElement.hasClass('js-popover-tooltip')) {
currentElement.popover('hide');
}
if (currentElement.hasClass('disabledOnClick')) {
currentElement.attr('disabled', true);
}
let modalWindowParams = {
url: url,
cb: function (container) {
let call = currentElement.data('cb');
if (typeof call !== 'undefined') {
if (call.indexOf('.') !== -1) {
let callerArray = call.split('.');
if (typeof window[callerArray[0]] === 'object' || typeof window[callerArray[0]] === 'function') {
window[callerArray[0]][callerArray[1]](container, e);
}
} else {
if (typeof window[call] === 'function') {
window[call](container, e);
}
}
}
currentElement.removeAttr('disabled');
}
};
if (currentElement.data('modalid')) {
modalWindowParams['id'] = currentElement.data('modalid');
}
app.showModalWindow(modalWindowParams);
}
e.stopPropagation();
});
container.off('click', '.js-show-modal-content').on('click', '.js-show-modal-content', function (e) {
e.preventDefault();
let currentElement = $(e.currentTarget);
let content = currentElement.data('content');
let title = '',
modalClass = '';
if (currentElement.data('title')) {
title = currentElement.data('title');
}
if (currentElement.data('class')) {
modalClass = currentElement.data('class');
}
app.showModalHtml({
class: modalClass,
header: title,
body: content
});
e.stopPropagation();
});
},
playSound: function (action) {
const soundsConfig = app.getMainParams('sounds');
if (soundsConfig['IS_ENABLED']) {
const audio = new Audio(app.getMainParams('soundFilesPath') + soundsConfig[action]);
audio.volume = 0.3;
audio.play();
}
},
registerIframeAndMoreContent(container = $(document)) {
container.on('click', '.js-more', (e) => {
e.preventDefault();
e.stopPropagation();
const btn = $(e.currentTarget);
app.showModalHtml({
class: btn.data('modalSize') ? btn.data('modalSize') : 'modal-fullscreen',
header: app.vtranslate('JS_FULL_TEXT'),
headerIcon: 'mdi mdi-overscan',
bodyClass: 'u-word-break pb-0 pt-1',
footerButtons: [
{ text: app.vtranslate('JS_CANCEL'), icon: 'fas fa-times', class: 'btn-danger', data: { dismiss: 'modal' } }
],
cb: (modal) => {
if (btn.data('iframe')) {
let iframe = btn.siblings('iframe');
let message = iframe.clone();
if (message[0].hasAttribute('srcdoctemp')) {
message.attr('srcdoc', message.attr('srcdoctemp'));
}
let isHidden = iframe.is(':hidden');
let height = 0;
if (iframe.data('height')) {
if (iframe.data('height') === 'full') {
height = $(window).height() - 185;
} else {
height = iframe.data('height');
}
} else {
if (isHidden) {
message.css('display', '');
iframe.css('display', '');
}
height = iframe.contents().height() ?? iframe.contents().find('body').height();
}
if (height) {
message.height(height);
}
if (isHidden) {
iframe.css('display', 'none');
}
modal.find('.js-modal-content').html(message);
} else {
modal.find('.js-modal-content').html(btn.closest('.js-more-content').find('.fullContent').html());
}
}
});
});
},
registerIframeEvents(content) {
content.find('.js-iframe-full-height').each(function () {
let iframe = $(this);
iframe.on('load', (e) => {
iframe.height(iframe.contents().find('body').height() + 50);
});
});
content.find('.js-modal-iframe').each(function () {
let iframe = $(this);
iframe.on('load', (e) => {
let height = iframe.contents().find('body').height();
if (height && height < iframe.height()) {
iframe.height(height + 50);
}
});
});
},
registerMenu: function () {
const self = this;
self.keyboard = { DOWN: 40, ESCAPE: 27, LEFT: 37, RIGHT: 39, SPACE: 32, UP: 38 };
self.sidebarBtn = $('.js-sidebar-btn').first();
self.sidebar = $('.js-sidebar').first();
self.sidebarBtn.on('click', self.toggleSidebar.bind(self));
$('a.nav-link,[tabindex],input,select,textarea,button').on('focus', (e) => {
if (self.sidebarBtn[0] == e.target || self.sidebar.find(e.target).length) return;
if (self.sidebar.find(':focus').length) {
self.openSidebar();
} else if (self.sidebar.hasClass('js-expand')) {
self.closeSidebar();
}
});
self.sidebar.on('mouseenter', self.openSidebar.bind(self)).on('mouseleave', self.closeSidebar.bind(self));
self.sidebar.find('.js-menu__content').on('keydown', self.sidebarKeyboard.bind(self));
self.sidebar.on('keydown', (e) => {
if (e.which == self.keyboard.ESCAPE) {
self.closeSidebar();
if (self.sidebarBtn.is(':tabbable')) self.sidebarBtn.focus();
else
$(':tabbable')
.eq(parseInt($(':tabbable').index(self.sidebar.find(':tabbable').last())) + 1)
.focus();
}
});
$('.js-submenu-toggler').on('click', (e) => {
if (!$(e.currentTarget).hasClass('collapsed') && !$(e.target).closest('.toggler').length) {
window.location = $(e.currentTarget).attr('href');
}
});
self.registerPinEvent();
},
openSidebar: function () {
this.sidebar.addClass('js-expand');
this.sidebarBtn.attr('aria-expanded', true);
},
closeSidebar: function () {
this.sidebar.removeClass('js-expand');
this.sidebarBtn.attr('aria-expanded', false);
},
toggleSidebar: function () {
if (this.sidebar.hasClass('js-expand')) {
this.closeSidebar();
} else {
this.openSidebar();
this.sidebar.find('.js-menu__content :tabbable').first().focus();
}
},
registerPinEvent: function () {
const self = this;
let pinButton = self.sidebar.find('.js-menu--pin');
let baseContainer = self.sidebar.closest('.js-base-container');
pinButton.on('click', () => {
let hideMenu = 0;
baseContainer.removeClass('c-menu--animation');
if (pinButton.attr('data-show') === '0') {
hideMenu = 'on';
pinButton.removeClass('u-opacity-muted');
baseContainer.addClass('c-menu--open');
self.sidebar.off('mouseleave mouseenter');
} else {
pinButton.addClass('u-opacity-muted');
baseContainer.removeClass('c-menu--open');
self.sidebar.on('mouseenter', self.openSidebar.bind(self)).on('mouseleave', self.closeSidebar.bind(self));
self.closeSidebar.bind(self);
}
AppConnector.request({
module: 'Users',
action: 'SaveAjax',
field: 'leftpanelhide',
record: CONFIG.userId,
value: hideMenu
}).done(function (responseData) {
if (responseData.success && responseData.result) {
pinButton.attr('data-show', hideMenu);
}
});
setTimeout(() => {
baseContainer.addClass('c-menu--animation');
}, 300);
});
},
sidebarKeyboard: function (e) {
let target = $(e.target);
if (e.which == this.keyboard.LEFT) {
if (target.hasClass('js-submenu-toggler') && !target.hasClass('collapsed')) {
target.click();
return false;
} else {
let toggler = $(e.target).closest('.js-submenu').prev('.js-submenu-toggler');
if (toggler.length && !toggler.hasClass('collapsed')) {
toggler.click().focus();
return false;
}
}
} else if (
(target.hasClass('js-submenu-toggler') && e.which == this.keyboard.RIGHT && target.hasClass('collapsed')) ||
(target.hasClass('js-submenu-toggler') && e.which == this.keyboard.SPACE)
) {
target.click();
return false;
} else if (e.which == this.keyboard.UP) {
this.sidebar
.find('.js-menu__content :tabbable')
.eq(parseInt(this.sidebar.find('.js-menu__content :tabbable').index(target)) - 1)
.focus();
return false;
} else if (e.which == this.keyboard.DOWN) {
this.sidebar
.find('.js-menu__content :tabbable')
.eq(parseInt(this.sidebar.find('.js-menu__content :tabbable').index(target)) + 1)
.focus();
return false;
}
},
registerTabdrop: function () {
let tabs = $('.js-tabdrop');
if (!tabs.length) return;
let tab = tabs.find('> li');
tab.each(function () {
$(this).removeClass('d-none');
});
tabs.tabdrop({
text: app.vtranslate('JS_MORE')
});
//change position to the last element (wcag keyboard navigation)
let dropdown = tabs.find('> li.dropdown');
dropdown.appendTo(tabs);
//fix for toggle button text not changing
tab.on('click', function (e) {
setTimeout(function () {
$(window).trigger('resize');
}, 500);
});
$(window).trigger('resize');
},
getScreenHeight: function (percantage) {
if (typeof percantage === 'undefined') {
percantage = 100;
}
return ($(window).height() * percantage) / 100;
},
clearBrowsingHistory: function () {
AppConnector.request({
module: 'Home',
action: 'BrowsingHistory'
}).done(function (response) {
$('.historyList').html(
`<a class="item dropdown-item" href="#" role="listitem">${app.vtranslate('JS_NO_RECORDS')}</a>`
);
});
},
/**
* Open url in top window
* @param string url
*/
openUrl(url) {
if (CONFIG.openUrlTarget === 'parentIframe') {
window.parent.location.href = url;
} else {
window.location.href = url;
}
},
/**
* Convert url string to object
*
* @param {string} url example: index.php?module=LayoutEditor&parent=Settings
*/
changeUrl(params) {
let fullUrl = '';
if (params.data && typeof params.data.historyUrl !== 'undefined') {
fullUrl = params.data.historyUrl;
}
if (fullUrl === '') {
if (params.data) {
if (typeof params.data == 'string') {
fullUrl = 'index.php?' + params.data;
} else {
fullUrl = 'index.php?' + $.param(params.data);
}
} else if (typeof params === 'object') {
fullUrl = 'index.php?' + $.param(params);
}
} else if (fullUrl.indexOf('index.php?') === -1) {
fullUrl = 'index.php?' + fullUrl;
}
if (app.isWindowTop() && history && history.pushState && fullUrl !== '') {
if (!history.state) {
let currentHref = window.location.href;
history.replaceState(currentHref, 'title 1', currentHref);
}
history.pushState(fullUrl, 'title 2', fullUrl);
}
},
/**
* Convert url string to object
*
* @param {string} url example: index.php?module=LayoutEditor&parent=Settings
*
* @return {object} urlObject
*/
convertUrlToObject(url) {
let urlObject = {};
if (url.indexOf('index.php?') !== -1) {
url = url.split('index.php?')[1];
}
url.split('&').forEach((el) => {
if (el.includes('=')) {
let values = el.split('=');
urlObject[values[0]] = values[1];
}
});
return urlObject;
},
/**
* Convert object to url string
*
* @param {object} urlData
* @param {string} entryFile
*
* @return {string} url
*/
convertObjectToUrl(urlData = {}, entryFile = 'index.php?') {
let url = entryFile;
Object.keys(urlData).forEach((key) => {
let value = urlData[key];
if (typeof value === 'object' || (typeof value === 'string' && value.startsWith('<'))) {
return;
}
if (!url.endsWith('&') && !url.endsWith('?')) {
url += '&';
}
url += key + '=' + encodeURIComponent(value);
});
return url;
},
formatToHourText: function (decTime, type = 'short', withSeconds = false, withMinutes = true) {
const short = type === 'short';
const hour = Math.floor(decTime);
const min = Math.floor((decTime - hour) * 60);
const sec = Math.round(((decTime - hour) * 60 - min) * 60);
let result = '';
if (hour) {
result += short ? hour + app.vtranslate('JS_H') : `${hour} ` + app.vtranslate('JS_H_LONG');
}
if ((hour || min) && withMinutes) {
result += short ? ` ${min}` + app.vtranslate('JS_M') : ` ${min} ` + app.vtranslate('JS_M_LONG');
}
if (withSeconds !== false) {
result += short ? ` ${sec}` + app.vtranslate('JS_S') : ` ${sec} ` + app.vtranslate('JS_S_LONG');
}
if (!hour && !min && withSeconds === false && withMinutes) {
result += short ? '0' + app.vtranslate('JS_M') : '0 ' + app.vtranslate('JS_M_LONG');
}
if (!hour && !min && withSeconds === false && !withMinutes) {
result += short ? '0' + app.vtranslate('JS_H') : '0 ' + app.vtranslate('JS_H_LONG');
}
return result.trim();
},
showRecordsList: function (params, cb, afterShowModal) {
if (typeof params === 'object' && !params.view) {
params.view = 'RecordsList';
}
this.showRecordsListModal(params).done(function (modal) {
if (typeof afterShowModal === 'function') {
afterShowModal(modal);
}
app.registerModalController(false, modal, cb);
});
},
/**
* Show records list modal
* @param {object} params
* @returns {Promise}
*/
showRecordsListModal: function (params) {
const aDeferred = $.Deferred();
AppConnector.request(params)
.done(function (requestData) {
app.showModalWindow(requestData, function (modal) {
aDeferred.resolve(modal);
});
})
.fail(function (textStatus, errorThrown) {
aDeferred.reject(textStatus, errorThrown);
});
return aDeferred.promise();
},
/**
* Convert html content to base64 image
* This function can be used in promise chain or with callback if specified
*
* @param {HTMLElement} element
* @param {function} callback with imageString argument which contains an image in base64 string format
* @param {object} options see: https://html2canvas.hertzen.com/configuration , imageType is our custom option
* @return {Promise} with base64 string image as argument
*/
htmlToImage(element, callback, options = { imageType: 'image/png', logging: false }) {
element = $(element).get(0); // make sure we have HTMLElement not jQuery because it will not work
const imageType = options.imageType;
delete options.imageType;
return html2canvas(element, options).then((canvas) => {
const base64Image = canvas.toDataURL(imageType);
if (typeof callback === 'function') {
callback(base64Image);
}
return base64Image;
});
},
registerHtmlToImageDownloader: function (container) {
const self = this;
container.on('click', '.js-download-html', function () {
let element = $(this);
let fileName = element.data('fileName');
self.htmlToImage($(element.data('html'))).then((img) => {
$(`<a href="${img}" download="${fileName}.png"></a>`).get(0).click();
});
});
},
decodeHTML(html) {
let txt = document.createElement('textarea');
txt.innerHTML = html;
return txt.value;
},
showAlert: function (text) {
return this.showNotify({
title: text,
type: 'error',
closer: false,
sticker: false,
destroy: false,
modules: new Map([
...PNotify.defaultModules,
[
PNotifyConfirm,
{
confirm: true,
buttons: [
{
text: app.vtranslate('JS_OK'),
primary: true,
click: (notice) => notice.close()
}
]
}
]
]),
stack: new PNotify.Stack({
dir1: 'down',
modal: true,
firstpos1: 25,
overlayClose: false
})
});
},
/**
* Show notify
* @param {object} customParams
* @returns {PNotify}
*/
showNotify: function (customParams) {
let params = {
hide: false
};
let userParams = customParams;
let type = 'info';
if (typeof customParams === 'string') {
userParams = {
title: customParams
};
}
if (typeof customParams.type !== 'undefined') {
type = customParams.type;
}
if (type !== 'error') {
params.hide = true;
}
return PNotify[type]($.extend(params, userParams));
},
/**
* Set Pnotify defaults options
*/
setPnotifyDefaultOptions() {
PNotify.defaults.textTrusted = true; // *Trusted option enables html as parameter's value
PNotify.defaults.titleTrusted = true;
PNotify.defaults.sticker = false;
PNotify.defaults.styling = 'bootstrap4';
PNotify.defaults.icons = 'fontawesome5';
PNotify.defaults.delay = 3000;
PNotify.defaults.stack.maxOpen = 10;
PNotify.defaults.stack.spacing1 = 5;
PNotify.defaults.stack.spacing2 = 5;
PNotify.defaults.labels.close = app.vtranslate('JS_CLOSE');
PNotify.defaultModules.set(PNotifyBootstrap4, {});
PNotify.defaultModules.set(PNotifyFontAwesome5, {});
PNotify.defaultModules.set(PNotifyMobile, {});
},
/**
* Show confirm modal
* @param {object} params
* @returns {PNotify}
* @returns
*/
showConfirmModal: function (params) {
let confirmButtonLabel = 'JS_OK';
let rejectedButtonLabel = 'JS_CANCEL';
if (typeof params.confirmButtonLabel !== 'undefined') {
confirmButtonLabel = params.confirmButtonLabel;
}
if (typeof params.rejectedButtonLabel !== 'undefined') {
rejectedButtonLabel = params.rejectedButtonLabel;
}
return this.showNotify(
$.extend(
{
icon: 'fas fa-question-circle',
closer: false,
sticker: false,
destroy: false,
hide: false,
width: 'auto',
animateSpeed: 'fast',
addModalClass: 'c-confirm-modal',
modules: new Map([
...PNotify.defaultModules,
[
PNotifyConfirm,
{
confirm: true,
prompt: 'showDialog' in params ? params['showDialog'] : false,
promptMultiLine: 'multiLineDialog' in params ? params['multiLineDialog'] : false,
buttons: [
{
text: '<span class="fas fa-check mr-2"></span>' + app.vtranslate(confirmButtonLabel),
textTrusted: true,
primary: true,
promptTrigger: true,
click: function (notice, value, e) {
if (params['showDialog'] && !value) {
return;
}
if (typeof params.confirmedCallback !== 'undefined') {
params.confirmedCallback(notice, value, e);
}
notice.close();
}
},
{
text: '<span class="fas fa-times mr-2"></span>' + app.vtranslate(rejectedButtonLabel),
textTrusted: true,
click: function (notice) {
if (typeof params.rejectedCallback !== 'undefined') {
params.rejectedCallback(notice);
}
notice.close();
}
}
]
}
]
]),
stack: new PNotify.Stack({
dir1: 'down',
firstpos1: 50,
spacing1: 0,
push: 'top',
modal: true,
overlayClose: false
})
},
params
)
);
},
registesterScrollbar(container) {
container.find('.js-scrollbar').each(function () {
let element = $(this),
scrollbarFnName = element.data('scrollbarFnName');
if (typeof app[scrollbarFnName] === 'function') {
app[scrollbarFnName](element);
} else {
app.showNewScrollbar(element);
}
});
},
registerPopover(container = $(document)) {
window.popoverCache = {};
container.on('mousemove', (e) => {
app.mousePosition = { x: e.pageX, y: e.pageY };
});
container.on(
'mouseenter',
'.js-popover-tooltip, .js-popover-tooltip--record, .js-popover-tooltip--ellipsis, [data-field-type="reference"], [data-field-type="multireference"]',
(e) => {
let currentTarget = $(e.currentTarget);
if (!currentTarget.hasClass('popover-triggered')) {
if (currentTarget.hasClass('js-popover-tooltip--record')) {
app.registerPopoverRecord(currentTarget, {}, container);
currentTarget.trigger('mouseenter');
} else if (!currentTarget.hasClass('js-popover-tooltip--record') && currentTarget.data('field-type')) {
app.registerPopoverRecord(currentTarget.children('a'), {}, container); //popoverRecord on children doesn't need triggering
} else if (
!currentTarget.hasClass('js-popover-tooltip--record') &&
!currentTarget.find('.js-popover-tooltip--record').length &&
!currentTarget.data('field-type')
) {
if (currentTarget.hasClass('js-popover-tooltip--ellipsis')) {
app.registerPopoverEllipsis({ element: currentTarget, container });
} else {
app.showPopoverElementView(currentTarget);
}
currentTarget.trigger('mouseenter');
}
}
}
);
},
/**
* Register auto format number value
*/
registerFormatNumber() {
$(document).on('focusout', '.js-format-numer', (e) => {
$(e.currentTarget).formatNumber();
});
},
/**
* Register toggle icon click event
* @param container
*/
registerToggleIconClick(container) {
container.on('click', '.js-toggle-icon, .js-toggle-icon__container', (e) => {
let icon = $(e.target);
if (icon.hasClass('js-toggle-icon__container')) {
icon = icon.find('.js-toggle-icon');
}
let iconData = icon.data();
icon.toggleClass(`${iconData.active} ${iconData.inactive}`);
e.stopPropagation();
});
},
stripHtml(html) {
const temporalDiv = document.createElement('div');
temporalDiv.innerHTML = html;
return temporalDiv.textContent || temporalDiv.innerText || '';
},
registerShowHideBlock(container) {
container.on('click', '.js-hb__btn', (e) => {
$(e.currentTarget).closest('.js-hb__container').toggleClass('u-hidden-block__opened');
});
container.find('.js-fab__container').on('clickoutside', (e) => {
$(e.currentTarget).removeClass('u-hidden-block__opened');
});
},
processEvents: false,
registerAfterLoginEvents: function () {
if (this.processEvents === false) {
let processEvents = $('#processEvents');
if (processEvents.length === 0) {
return;
}
this.processEvents = JSON.parse(processEvents.val());
}
if (this.processEvents.length === 0) {
return;
}
let event = this.processEvents.shift();
switch (event.type) {
case 'modal':
AppConnector.request(event.url)
.done(function (requestData) {
app.showModalWindow(requestData).one('hidden.bs.modal', function () {
app.registerAfterLoginEvents();
});
})
.fail(function (_textStatus, errorThrown) {
app.showNotify({
title: app.vtranslate('JS_ERROR'),
textTrusted: false,
text: errorThrown,
type: 'error'
});
});
break;
case 'notify':
app.showNotify(event.notify);
app.registerAfterLoginEvents();
break;
default:
return;
}
},
/**
* Function to reload view after save event
*
* @param {object} responseData - Save responses data.
* @param {object} params - Save params.
* @param {jQuery} form - Jquery form container.
* @param {jQuery} element - Jquery trigger element.
*/
reloadAfterSave: function (responseData, params, form, element) {
if (responseData.skipReload) {
return;
}
const moduleName = params['module'];
const parentModuleName = app.getModuleName();
const viewName = app.getViewName();
if ('List' === viewName || 'Tiles' === viewName) {
if (moduleName === parentModuleName) {
app.pageController.getListViewRecords();
}
} else if ('Kanban' === viewName) {
app.pageController.loadKanban(false);
} else if ('Detail' === viewName) {
if (form && app.getRecordId() === form.find('[name="record"]').val()) {
if (responseData.result._isViewable == false) {
if (window !== window.parent) {
window.parent.location.href = 'index.php?module=' + moduleName + '&view=ListPreview';
} else {
window.location.href = 'index.php?module=' + moduleName + '&view=List';
}
} else if (params && params.removeFromUrl) {
let searchParams = new URLSearchParams(window.location.search);
searchParams.delete('step');
window.location.href = 'index.php?' + searchParams.toString();
} else {
window.location.reload();
}
} else {
let widget, block;
if (responseData.result && responseData.result._reload) {
window.location.reload();
} else if (app.getUrlVar('mode') === 'showRelatedList') {
app.pageController.loadRelatedList();
} else if (element && (widget = element.closest('.widgetContentBlock')) && widget.length !== 0) {
app.pageController.loadWidget(widget);
} else if (element && (block = element.closest('.detailViewBlockLink')) && block.length !== 0) {
app.pageController.reloadDetailViewBlock(block);
} else if (params && params.data) {
window.location.reload();
} else {
app.pageController.reloadTabContent();
}
}
}
},
/**
* Function to register the records events
* @param {jQuery} container - Jquery container.
*/
registerRecordActionsEvents: function (container) {
container.on('click', '.js-action-confirm', function (event) {
event.stopPropagation();
let target = $(this),
sourceView = target.data('sourceView'),
addBtnIcon = target.data('addBtnIcon');
let params = {
icon: false,
title: target.data('content'),
confirmedCallback: () => {
let progressIndicatorElement = $.progressIndicator({
position: 'html',
blockInfo: {
enabled: true
}
});
let url = target.data('url') + '&sourceView=' + sourceView;
AppConnector.request(url).done(function (data) {
progressIndicatorElement.progressIndicator({
mode: 'hide'
});
if (data && data.success) {
if (data.result.notify) {
app.showNotify(data.result.notify);
}
if (sourceView === 'Href') {
app.openUrl(data.result);
} else {
app.reloadAfterSave(data, app.convertUrlToObject(url), null, target);
}
} else {
app.showNotify({
text: app.vtranslate(data.error.message),
title: app.vtranslate('JS_LBL_PERMISSION'),
type: 'error'
});
}
});
}
};
if (target.data('confirm')) {
params.text = target.data('confirm');
addBtnIcon = 1;
}
if (addBtnIcon == 1) {
params.title = target.html() + (params.title ? ' ' + params.title : '');
}
app.showConfirmModal(params);
});
},
/**
* Register keyboard shortcuts events
* @param {jQuery} container
*/
registerKeyboardShortcutsEvent: function (container) {
if (app.getUrlVar('parent') !== 'Settings') {
document.addEventListener('keydown', (event) => {
if (CONFIG['isEntityModule'] && event.shiftKey && event.ctrlKey && event.code === 'KeyL') {
window.location.href = 'index.php?module=' + app.getModuleName() + '&view=List';
}
if (CONFIG['isQuickCreateSupported'] && event.shiftKey && event.ctrlKey && event.code === 'KeyQ') {
App.Components.QuickCreate.createRecord(app.getModuleName());
}
});
}
},
/**
* Register POST action
* @param {jQuery} container
*/
registerPostActionEvent: function (container) {
container.on('click', '.js-post-action', function (e) {
e.preventDefault();
let element = $(this);
if (element.attr('href')) {
AppConnector.requestForm(element.attr('href'));
}
});
},
/**
* Print data modal
* @param {jQuery} container
*/
printModal: function (container) {
const html = container.html().replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, ' '),
head = $('head')
.html()
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, ' ');
const modal = window.open();
modal.document.write(`<head>${head}</head>`);
modal.document.write(`<body>${html}</body>`);
modal.onafterprint = (_e) => {
modal.close();
};
setTimeout(function () {
modal.print();
}, 500);
},
/**
* Register print event
* @param {jQuery} container
*/
registerPrintEvent: function (container) {
container.on('click', '.js-print-container', function (_) {
app.printModal($($(this).data('container')).children());
});
}
});
$(function () {
Quasar.iconSet.set(Quasar.iconSet.mdiV3);
let document = $(this);
app.registerToggleIconClick(document);
app.touchDevice = app.isTouchDevice();
app.setPnotifyDefaultOptions();
App.Fields.Picklist.changeSelectElementView();
app.registerPopoverEllipsisIcon();
app.registerPopover();
app.registerFormatNumber();
app.registerIframeAndMoreContent();
app.registerModal();
app.registerQuickEditModal(document);
app.registerMenu();
app.registerTabdrop();
app.registerIframeEvents(document);
app.registesterScrollbar(document);
app.registerHtmlToImageDownloader(document);
app.registerShowHideBlock(document);
app.registerAfterLoginEvents(document);
app.registerFormsEvents(document);
app.registerRecordActionsEvents(document);
app.registerPrintEvent(document);
app.registerKeyboardShortcutsEvent(document);
app.registerPostActionEvent(document);
App.Components.QuickCreate.register(document);
App.Components.Scrollbar.initPage();
App.Clipboard.register(document);
String.prototype.toCamelCase = function () {
let value = this.valueOf();
return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
};
// in IE resize option for textarea is not there, so we have to use .resizable() api
if (/MSIE/.test(navigator.userAgent) || /Trident/.test(navigator.userAgent)) {
$('textarea').resizable();
}
// Instantiate Page Controller
app.pageController = app.getPageController();
if (app.pageController) {
app.pageController.registerEvents();
}
});
(function ($) {
$.fn.getNumberFromValue = function () {
return App.Fields.Double.formatToDb($(this).val());
};
$.fn.getNumberFromText = function () {
return App.Fields.Double.formatToDb($(this).text());
};
$.fn.setValue = function (value, params) {
return App.Fields.Utils.setValue($(this), value, params);
};
$.fn.formatNumber = function () {
let element = $(this);
element.val(App.Fields.Double.formatToDisplay(App.Fields.Double.formatToDb(element.val()), false));
};
$.fn.disable = function () {
this.attr('disabled', 'disabled');
};
$.fn.enable = function () {
this.removeAttr('disabled');
};
$.fn.serializeFormData = function () {
for (let instance in CKEDITOR.instances) {
CKEDITOR.instances[instance].updateElement();
}
const form = this,
values = form.serializeArray();
let data = {};
if (values) {
$(values).each(function (k, v) {
let element = form.find('[name="' + v.name + '"]');
if (element.is('select') && element.attr('multiple') != undefined) {
if (data[v.name] == undefined) {
data[v.name] = [];
}
data[v.name].push(v.value);
} else {
data[v.name] = v.value;
}
});
}
// If data-type="autocomplete", pickup data-value="..." set
let autocompletes = $('[data-type="autocomplete"]', $(this));
$(autocompletes).each(function (i) {
let ac = $(autocompletes[i]);
data[ac.attr('name')] = ac.data('value');
});
delete data['_csrf'];
return data;
};
// Case-insensitive :icontains expression
$.expr[':'].icontains = function (obj, index, meta, stack) {
return (
(obj.textContent || obj.innerText || $(obj).text() || '').toLowerCase().indexOf(meta[3].toLowerCase()) !== -1
);
};
$.fn.removeTextNode = function () {
$(this)
.contents()
.filter(function () {
return this.nodeType == 3; //Node.TEXT_NODE
})
.remove();
};
})($);