- Added comprehensive AI Assistant system (aiassist/ directory): * Vector search and embedding capabilities * Typebot proxy integration * Elastic search functionality * Message classification and chat history * MCP proxy for external integrations - Implemented Court Status API (GetCourtStatus.php): * Real-time court document status checking * Integration with external court systems * Comprehensive error handling and logging - Enhanced S3 integration: * Improved file backup system with metadata * Batch processing capabilities * Enhanced error logging and recovery * Copy operations with URL fixing - Added Telegram contact creation API - Improved error logging across all modules - Enhanced callback system for AI responses - Extensive backup file storage with timestamps - Updated documentation and README files - File storage improvements: * Thousands of backup files with proper metadata * Fix operations for broken file references * Project-specific backup and recovery systems * Comprehensive file integrity checking Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
1147 lines
32 KiB
JavaScript
1147 lines
32 KiB
JavaScript
/*+***********************************************************************************
|
|
* 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.
|
|
*************************************************************************************/
|
|
|
|
var app = {
|
|
|
|
/**
|
|
* variable stores client side language strings
|
|
*/
|
|
languageString : [],
|
|
|
|
|
|
weekDaysArray : {Sunday : 0,Monday : 1, Tuesday : 2, Wednesday : 3,Thursday : 4, Friday : 5, Saturday : 6},
|
|
|
|
/**
|
|
* 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 jQuery('#module').val();
|
|
},
|
|
|
|
/**
|
|
* 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 jQuery('#parent').val();
|
|
},
|
|
|
|
/**
|
|
* Function returns the current view name
|
|
*/
|
|
getViewName : function() {
|
|
return jQuery('#view').val();
|
|
},
|
|
|
|
/**
|
|
* Function returns the record id
|
|
*/
|
|
getRecordId : function(){
|
|
var view = jQuery('[name="view"]').val();
|
|
var recordId;
|
|
if(view == "Edit"){
|
|
recordId = jQuery('[name="record"]').val();
|
|
}else if(view == "Detail"){
|
|
recordId = jQuery('#recordId').val();
|
|
}
|
|
return recordId;
|
|
},
|
|
|
|
/**
|
|
* Function to get the contents container
|
|
* @returns jQuery object
|
|
*/
|
|
getContentsContainer : function() {
|
|
return jQuery('.bodyContents');
|
|
},
|
|
|
|
getDecimalSeparator : function() {
|
|
return jQuery('body').data('user-decimalseparator');
|
|
},
|
|
|
|
getGroupingSeparator : function() {
|
|
return jQuery('body').data('user-groupingseparator');
|
|
},
|
|
|
|
getNumberOfDecimals : function() {
|
|
return jQuery('body').data('user-numberofdecimals');
|
|
},
|
|
|
|
getUserCurrencySymbol : function() {
|
|
return jQuery('body').data('user-currency-symbol');
|
|
},
|
|
|
|
getUserCurrencySymbolPlacement : function() {
|
|
return jQuery('body').data('user-currency-symbol-placement');
|
|
},
|
|
|
|
appendUserCurrencySymbol : function(value) {
|
|
var userCurrencySymbol = app.getUserCurrencySymbol();
|
|
var userCurrencySymbolPlacement = app.getUserCurrencySymbolPlacement();
|
|
|
|
var appendedValue = value;
|
|
if (userCurrencySymbolPlacement === '1.0$') {
|
|
appendedValue = value + userCurrencySymbol;
|
|
} else {
|
|
appendedValue = userCurrencySymbol + value;
|
|
}
|
|
|
|
return appendedValue;
|
|
},
|
|
|
|
convertCurrencyToUserFormat : function(value, appendCurrencySymbol) {
|
|
var displayValue;
|
|
var isNegative = false;
|
|
value = value.toString();
|
|
if(parseFloat(value) < 0) {
|
|
isNegative = true;
|
|
value = value.replace('-', '');
|
|
}
|
|
var groupingPattern = jQuery('body').data('user-currencygroupingpattern');
|
|
var numberOfDecimals = app.getNumberOfDecimals();
|
|
var decimalSeparator = app.getDecimalSeparator();
|
|
var groupingSeparator = app.getGroupingSeparator();
|
|
value = parseFloat(value).toFixed(parseInt(numberOfDecimals));
|
|
value = value.toString();
|
|
var valueParts = value.split('.');
|
|
var wholePart = valueParts[0];
|
|
var decimalPart = valueParts[1];
|
|
var truncateTrailingZeros = jQuery('body').data('user-truncatetrailingzeros');
|
|
var finalWholePart;
|
|
var ignoreDecimal = false;
|
|
if(truncateTrailingZeros == '1' && parseInt(decimalPart) === 0) {
|
|
ignoreDecimal = true;
|
|
}
|
|
|
|
if(groupingPattern == '123456789') {
|
|
finalWholePart = wholePart;
|
|
} else if(groupingPattern == '123456,789') {
|
|
if(wholePart.length > 3) {
|
|
var wholeFirstPart = wholePart.substr(0, (wholePart.length - 3));
|
|
}
|
|
var wholeLastPart = wholePart.substr(wholePart.length - 3);
|
|
if(wholeFirstPart) {
|
|
wholePart = wholeFirstPart+groupingSeparator+wholeLastPart;
|
|
}
|
|
finalWholePart = wholePart;
|
|
} else if(groupingPattern == '123,456,789') {
|
|
var wholeParts = wholePart.toString().split("").reverse().join("").match(/.{1,3}/g).reverse();
|
|
for(var i = 0; i<wholeParts.length; i++) {
|
|
wholeParts[i] = wholeParts[i].toString().split("").reverse().join("");
|
|
}
|
|
finalWholePart = wholeParts.join(groupingSeparator);
|
|
} else if(groupingPattern == '12,34,56,789') {
|
|
if(wholePart.length > 3) {
|
|
var wholeFirstPart = wholePart.substr(0, (wholePart.length - 3));
|
|
}
|
|
var wholeLastPart = wholePart.substr(wholePart.length - 3);
|
|
if(wholeFirstPart) {
|
|
wholeLastPart = groupingSeparator+wholeLastPart;
|
|
var wholeFirstParts = wholeFirstPart.toString().split("").reverse().join("").match(/.{1,2}/g).reverse();
|
|
for(var i = 0; i<wholeFirstParts.length; i++) {
|
|
wholeFirstParts[i] = wholeFirstParts[i].toString().split("").reverse().join("");
|
|
}
|
|
wholeFirstPart = wholeFirstParts.join(groupingSeparator);
|
|
finalWholePart = wholeFirstPart+wholeLastPart;
|
|
} else {
|
|
finalWholePart = wholeLastPart;
|
|
}
|
|
}
|
|
|
|
if(ignoreDecimal) {
|
|
displayValue = finalWholePart;
|
|
} else {
|
|
displayValue = finalWholePart+decimalSeparator+decimalPart;
|
|
}
|
|
|
|
if(isNegative) {
|
|
displayValue = '-'+displayValue;
|
|
}
|
|
|
|
if(appendCurrencySymbol) {
|
|
displayValue = app.appendUserCurrencySymbol(displayValue);
|
|
}
|
|
|
|
return displayValue;
|
|
},
|
|
|
|
/**
|
|
* Function which will convert ui of select boxes.
|
|
* @params parent - select element
|
|
* @params view - select2
|
|
* @params viewParams - select2 params
|
|
* @returns jquery object list which represents changed select elements
|
|
*/
|
|
changeSelectElementView : function(parent, view, viewParams){
|
|
|
|
var selectElement = jQuery();
|
|
if(typeof parent == 'undefined') {
|
|
parent = jQuery('body');
|
|
}
|
|
|
|
//If view is select2, This will convert the ui of select boxes to select2 elements.
|
|
if(view == 'select2') {
|
|
app.showSelect2ElementView(parent, viewParams);
|
|
return;
|
|
}
|
|
selectElement = jQuery('.chzn-select', parent);
|
|
//parent itself is the element
|
|
if(parent.is('select.chzn-select')) {
|
|
selectElement = parent;
|
|
}
|
|
|
|
//fix for multiselect error prompt hide when validation is success
|
|
selectElement.filter('[multiple]').filter('[data-validation-engine*="validate"]').on('change',function(e){
|
|
jQuery(e.currentTarget).trigger('focusout');
|
|
});
|
|
|
|
var chosenElement = selectElement.chosen({search_contains: true});
|
|
var chosenSelectConainer = jQuery('.chzn-container');
|
|
//Fix for z-index issue in IE 7
|
|
if (jQuery.browser.msie && jQuery.browser.version === "7.0") {
|
|
var zidx = 1000;
|
|
chosenSelectConainer.each(function(){
|
|
$(this).css('z-index', zidx);
|
|
zidx-=10;
|
|
});
|
|
}
|
|
return chosenSelectConainer;
|
|
},
|
|
|
|
/**
|
|
* Function to destroy the chosen element and get back the basic select Element
|
|
*/
|
|
destroyChosenElement : function(parent) {
|
|
var selectElement = jQuery();
|
|
if(typeof parent == 'undefined') {
|
|
parent = jQuery('body');
|
|
}
|
|
|
|
selectElement = jQuery('.chzn-select', parent);
|
|
//parent itself is the element
|
|
if(parent.is('select.chzn-select')) {
|
|
selectElement = parent;
|
|
}
|
|
|
|
selectElement.css('display','block').removeClass("chzn-done").data("chosen", null).next().remove();
|
|
|
|
return selectElement;
|
|
|
|
},
|
|
/**
|
|
* Function which will show the select2 element for select boxes . This will use select2 library
|
|
*/
|
|
showSelect2ElementView : function(selectElement, params) {
|
|
if(typeof params == 'undefined') {
|
|
params = {};
|
|
}
|
|
|
|
var data = selectElement.data();
|
|
if(data != null) {
|
|
params = jQuery.extend(data,params);
|
|
}
|
|
|
|
// Sort DOM nodes alphabetically in select box.
|
|
if (typeof params['customSortOptGroup'] != 'undefined' && params['customSortOptGroup']) {
|
|
jQuery('optgroup', selectElement).each(function(){
|
|
var optgroup = jQuery(this);
|
|
var options = optgroup.children().toArray().sort(function(a, b){
|
|
var aText = jQuery(a).text();
|
|
var bText = jQuery(b).text();
|
|
return aText < bText ? 1 : -1;
|
|
});
|
|
jQuery.each(options, function(i, v){
|
|
optgroup.prepend(v);
|
|
});
|
|
});
|
|
delete params['customSortOptGroup'];
|
|
}
|
|
|
|
//formatSelectionTooBig param is not defined even it has the maximumSelectionSize,
|
|
//then we should send our custom function for formatSelectionTooBig
|
|
if(typeof params.maximumSelectionSize != "undefined" && typeof params.formatSelectionTooBig == "undefined") {
|
|
var limit = params.maximumSelectionSize;
|
|
//custom function which will return the maximum selection size exceeds message.
|
|
var formatSelectionExceeds = function(limit) {
|
|
return app.vtranslate('JS_YOU_CAN_SELECT_ONLY')+' '+limit+' '+app.vtranslate('JS_ITEMS');
|
|
}
|
|
params.formatSelectionTooBig = formatSelectionExceeds;
|
|
}
|
|
if(selectElement.attr('multiple') != 'undefined' && typeof params.closeOnSelect == 'undefined') {
|
|
params.closeOnSelect = false;
|
|
}
|
|
|
|
selectElement.select2(params)
|
|
.on("open", function(e) {
|
|
var element = jQuery(e.currentTarget);
|
|
var instance = element.data('select2');
|
|
instance.dropdown.css('z-index',1000002);
|
|
});
|
|
if(typeof params.maximumSelectionSize != "undefined") {
|
|
app.registerChangeEventForMultiSelect(selectElement,params);
|
|
}
|
|
return selectElement;
|
|
},
|
|
|
|
/**
|
|
* 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.maximumSelectionSize;
|
|
selectElement.on('change',function(e){
|
|
var data = instance.data()
|
|
if (jQuery.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 = jQuery(parentElement);
|
|
|
|
var encodedString = parentElement.children().serialize();
|
|
if(returnFormat == 'string'){
|
|
return encodedString;
|
|
}
|
|
var keyValueMap = {};
|
|
var valueList = encodedString.split('&')
|
|
|
|
for(var index in valueList){
|
|
var keyValueString = valueList[index];
|
|
var keyValueArr = keyValueString.split('=');
|
|
var nameOfElement = keyValueArr[0];
|
|
var valueOfElement = keyValueArr[1];
|
|
keyValueMap[nameOfElement] = decodeURIComponent(valueOfElement);
|
|
}
|
|
return keyValueMap;
|
|
},
|
|
|
|
showModalWindow: function(data, url, cb, css) {
|
|
|
|
var unBlockCb = function(){};
|
|
var overlayCss = {};
|
|
//This is indicate whether to improve the ui by convert select element to select 2 plugin
|
|
var enhanceUi = true;
|
|
|
|
//null is also an object
|
|
var backDrop = false;
|
|
if(typeof data == 'object' && data != null && !(data instanceof jQuery)){
|
|
css = data.css;
|
|
cb = data.cb;
|
|
url = data.url;
|
|
unBlockCb = data.unblockcb;
|
|
overlayCss = data.overlayCss;
|
|
backDrop = data.backDrop;
|
|
if(typeof data.enhanceUi != "undefined")
|
|
enhanceUi = data.enhanceUi;
|
|
data = data.data
|
|
|
|
}
|
|
if (typeof url == 'function') {
|
|
if(typeof cb == 'object') {
|
|
css = cb;
|
|
}
|
|
cb = url;
|
|
url = false;
|
|
}
|
|
else if (typeof url == 'object') {
|
|
cb = function() { };
|
|
css = url;
|
|
url = false;
|
|
}
|
|
|
|
if (typeof cb != 'function') {
|
|
cb = function() { }
|
|
}
|
|
|
|
var id = 'globalmodal';
|
|
var container = jQuery('#'+id);
|
|
if (container.length) {
|
|
container.remove();
|
|
}
|
|
container = jQuery('<div></div>');
|
|
container.attr('id', id);
|
|
|
|
var showModalData = function (data) {
|
|
|
|
var defaultCss = {
|
|
'top' : '0px',
|
|
'width' : 'auto',
|
|
'cursor' : 'default',
|
|
'left' : '35px',
|
|
'text-align' : 'left',
|
|
'border-radius':'6px'
|
|
};
|
|
var effectiveCss = defaultCss;
|
|
if(typeof css == 'object') {
|
|
effectiveCss = jQuery.extend(defaultCss, css)
|
|
}
|
|
|
|
var defaultOverlayCss = {
|
|
'cursor' : 'default'
|
|
};
|
|
var effectiveOverlayCss = defaultOverlayCss;
|
|
if(typeof overlayCss == 'object' ) {
|
|
effectiveOverlayCss = jQuery.extend(defaultOverlayCss,overlayCss);
|
|
}
|
|
container.html(data);
|
|
|
|
// Mimic bootstrap modal action body state change
|
|
jQuery('body').addClass('modal-open');
|
|
|
|
//container.modal();
|
|
jQuery.blockUI({
|
|
'message' : container,
|
|
'overlayCSS' : effectiveOverlayCss,
|
|
'css' : effectiveCss,
|
|
|
|
// disable if you want key and mouse events to be enable for content that is blocked (fix for select2 search box)
|
|
bindEvents: false,
|
|
|
|
//Fix for overlay opacity issue in FF/Linux
|
|
applyPlatformOpacityRules : false
|
|
});
|
|
var unblockUi = function() {
|
|
app.hideModalWindow(unBlockCb);
|
|
jQuery(document).unbind("keyup",escapeKeyHandler);
|
|
}
|
|
var escapeKeyHandler = function(e){
|
|
if (e.keyCode == 27) {
|
|
unblockUi();
|
|
}
|
|
}
|
|
|
|
if (backDrop != 'static') {
|
|
jQuery('.blockOverlay').click(unblockUi);
|
|
}
|
|
jQuery(document).on('keyup',escapeKeyHandler);
|
|
jQuery('[data-dismiss="modal"]', container).click(unblockUi);
|
|
|
|
container.closest('.blockMsg').position({
|
|
'of' : jQuery(window),
|
|
'my' : 'center top',
|
|
'at' : 'center top',
|
|
'collision' : 'flip none',
|
|
//TODO : By default the position of the container is taking as -ve so we are giving offset
|
|
// Check why it is happening
|
|
'offset' : '0 50'
|
|
});
|
|
//container.css({'height' : container.innerHeight()+15+'px'});
|
|
if(enhanceUi) {
|
|
// TODO Make it better with jQuery.on
|
|
app.changeSelectElementView(container);
|
|
//register all select2 Elements
|
|
app.showSelect2ElementView(container.find('select.select2'));
|
|
//register date fields event to show mini calendar on click of element
|
|
app.registerEventForDatePickerFields(container);
|
|
}
|
|
cb(container);
|
|
}
|
|
|
|
if (data) {
|
|
showModalData(data)
|
|
|
|
} else {
|
|
jQuery.get(url).then(function(response){
|
|
showModalData(response);
|
|
});
|
|
}
|
|
|
|
return container;
|
|
},
|
|
|
|
/**
|
|
* 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) {
|
|
// Mimic bootstrap modal action body state change - helps to avoid body scroll
|
|
// when modal is shown using css: http://stackoverflow.com/a/11013994
|
|
jQuery('body').removeClass('modal-open');
|
|
|
|
var id = 'globalmodal';
|
|
var container = jQuery('#'+id);
|
|
if (container.length <= 0) {
|
|
return;
|
|
}
|
|
|
|
if(typeof callback != 'function') {
|
|
callback = function() {};
|
|
}
|
|
jQuery.unblockUI({
|
|
'onUnblock' : callback
|
|
});
|
|
},
|
|
|
|
isHidden : function(element) {
|
|
if(element.css('display')== 'none') {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* 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 chosen select box
|
|
prettySelect : true,
|
|
useSuffix: "_chzn",
|
|
usePrefix : "s2id_"
|
|
},
|
|
|
|
/**
|
|
* 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 formOffset = form.find(".formError:not('.greenPopup'):first").offset();
|
|
if(formOffset !== null && typeof(formOffset) === 'object' && formOffset.hasOwnProperty('top')) {
|
|
var resizedDestnation = formOffset.top - 105;
|
|
$('html, body').animate({
|
|
scrollTop:resizedDestnation
|
|
}, 'slow');
|
|
}
|
|
},
|
|
|
|
convertToDatePickerFormat: function(dateFormat){
|
|
if(dateFormat == 'yyyy-mm-dd'){
|
|
return 'Y-m-d';
|
|
} else if(dateFormat == 'mm-dd-yyyy') {
|
|
return 'm-d-Y';
|
|
} else if (dateFormat == 'dd-mm-yyyy') {
|
|
return 'd-m-Y';
|
|
}
|
|
},
|
|
|
|
convertTojQueryDatePickerFormat: function(dateFormat){
|
|
var i = 0;
|
|
var splitDateFormat = dateFormat.split('-');
|
|
for(var i in splitDateFormat){
|
|
var sectionDate = splitDateFormat[i];
|
|
var sectionCount = sectionDate.length;
|
|
if(sectionCount == 4){
|
|
var strippedString = sectionDate.substring(0,2);
|
|
splitDateFormat[i] = strippedString;
|
|
}
|
|
}
|
|
var joinedDateFormat = splitDateFormat.join('-');
|
|
return joinedDateFormat;
|
|
},
|
|
getDateInVtigerFormat: function(dateFormat,dateObject){
|
|
var finalFormat = app.convertTojQueryDatePickerFormat(dateFormat);
|
|
var date = jQuery.datepicker.formatDate(finalFormat,dateObject);
|
|
return date;
|
|
},
|
|
|
|
registerEventForTextAreaFields : function(parentElement) {
|
|
if(typeof parentElement == 'undefined') {
|
|
parentElement = jQuery('body');
|
|
}
|
|
|
|
parentElement = jQuery(parentElement);
|
|
|
|
if(parentElement.is('textarea')){
|
|
var element = parentElement;
|
|
}else{
|
|
var element = jQuery('textarea', parentElement);
|
|
}
|
|
if(element.length == 0){
|
|
return;
|
|
}
|
|
element.autosize();
|
|
},
|
|
|
|
registerEventForDatePickerFields : function(parentElement,registerForAddon,customParams){
|
|
if(typeof parentElement == 'undefined') {
|
|
parentElement = jQuery('body');
|
|
}
|
|
if(typeof registerForAddon == 'undefined'){
|
|
registerForAddon = true;
|
|
}
|
|
|
|
parentElement = jQuery(parentElement);
|
|
|
|
if(parentElement.hasClass('dateField')){
|
|
var element = parentElement;
|
|
}else{
|
|
var element = jQuery('.dateField', parentElement);
|
|
}
|
|
if(element.length == 0){
|
|
return;
|
|
}
|
|
if(registerForAddon == true){
|
|
var parentDateElem = element.closest('.date');
|
|
jQuery('.add-on',parentDateElem).on('click',function(e){
|
|
var elem = jQuery(e.currentTarget);
|
|
//Using focus api of DOM instead of jQuery because show api of datePicker is calling e.preventDefault
|
|
//which is stopping from getting focus to input element
|
|
elem.closest('.date').find('input.dateField').get(0).focus();
|
|
});
|
|
}
|
|
var dateFormat = element.data('dateFormat');
|
|
var vtigerDateFormat = app.convertToDatePickerFormat(dateFormat);
|
|
var language = jQuery('body').data('language');
|
|
var lang = language.split('_');
|
|
|
|
//Default first day of the week
|
|
var defaultFirstDay = jQuery('#start_day').val();
|
|
if(defaultFirstDay == '' || typeof(defaultFirstDay) == 'undefined'){
|
|
var convertedFirstDay = 1
|
|
} else {
|
|
convertedFirstDay = this.weekDaysArray[defaultFirstDay];
|
|
}
|
|
var params = {
|
|
format : vtigerDateFormat,
|
|
calendars: 1,
|
|
locale: $.fn.datepicker.dates[lang[0]],
|
|
starts: convertedFirstDay,
|
|
eventName : 'focus',
|
|
onChange: function(formated){
|
|
var element = jQuery(this).data('datepicker').el;
|
|
element = jQuery(element);
|
|
var datePicker = jQuery('#'+ jQuery(this).data('datepicker').id);
|
|
var viewDaysElement = datePicker.find('table.datepickerViewDays');
|
|
//If it is in day mode and the prev value is not eqaul to current value
|
|
//Second condition is manily useful in places where user navigates to other month
|
|
if(viewDaysElement.length > 0 && element.val() != formated) {
|
|
element.DatePickerHide();
|
|
element.blur();
|
|
}
|
|
element.val(formated).trigger('change').focusout();
|
|
}
|
|
}
|
|
if(typeof customParams != 'undefined'){
|
|
var params = jQuery.extend(params,customParams);
|
|
}
|
|
element.each(function(index,domElement){
|
|
var jQelement = jQuery(domElement);
|
|
var dateObj = new Date();
|
|
var selectedDate = app.getDateInVtigerFormat(dateFormat, dateObj);
|
|
//Take the element value as current date or current date
|
|
if(jQelement.val() != '') {
|
|
selectedDate = jQelement.val();
|
|
}
|
|
params.date = selectedDate;
|
|
params.current = selectedDate;
|
|
jQelement.DatePicker(params)
|
|
});
|
|
|
|
},
|
|
registerEventForDateFields : function(parentElement) {
|
|
if(typeof parentElement == 'undefined') {
|
|
parentElement = jQuery('body');
|
|
}
|
|
|
|
parentElement = jQuery(parentElement);
|
|
|
|
if(parentElement.hasClass('dateField')){
|
|
var element = parentElement;
|
|
}else{
|
|
var element = jQuery('.dateField', parentElement);
|
|
}
|
|
element.datepicker({'autoclose':true}).on('changeDate', function(ev){
|
|
var currentElement = jQuery(ev.currentTarget);
|
|
var dateFormat = currentElement.data('dateFormat');
|
|
var finalFormat = app.getDateInVtigerFormat(dateFormat,ev.date);
|
|
var date = jQuery.datepicker.formatDate(finalFormat,ev.date);
|
|
currentElement.val(date);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Function which will register time fields
|
|
*
|
|
* @params : container - jquery object which contains time fields with class timepicker-default or itself can be time field
|
|
* registerForAddon - boolean value to register the event for Addon or not
|
|
* params - params for the plugin
|
|
*
|
|
* @return : container to support chaining
|
|
*/
|
|
registerEventForTimeFields : function(container, registerForAddon, params) {
|
|
|
|
if(typeof container == 'undefined') {
|
|
container = jQuery('body');
|
|
}
|
|
if(typeof registerForAddon == 'undefined'){
|
|
registerForAddon = true;
|
|
}
|
|
|
|
container = jQuery(container);
|
|
|
|
if(container.hasClass('timepicker-default')) {
|
|
var element = container;
|
|
}else{
|
|
var element = container.find('.timepicker-default');
|
|
}
|
|
|
|
if(registerForAddon == true){
|
|
var parentTimeElem = element.closest('.time');
|
|
jQuery('.add-on',parentTimeElem).on('click',function(e){
|
|
var elem = jQuery(e.currentTarget);
|
|
elem.closest('.time').find('.timepicker-default').focus();
|
|
});
|
|
}
|
|
|
|
if(typeof params == 'undefined') {
|
|
params = {};
|
|
}
|
|
|
|
var timeFormat = element.data('format');
|
|
if(timeFormat == '24') {
|
|
timeFormat = 'H:i';
|
|
} else {
|
|
timeFormat = 'h:i A';
|
|
}
|
|
var defaultsTimePickerParams = {
|
|
'timeFormat' : timeFormat,
|
|
'className' : 'timePicker'
|
|
};
|
|
var params = jQuery.extend(defaultsTimePickerParams, params);
|
|
|
|
element.timepicker(params);
|
|
|
|
return container;
|
|
},
|
|
|
|
/**
|
|
* Function to destroy time fields
|
|
*/
|
|
destroyTimeFields : function(container) {
|
|
|
|
if(typeof container == 'undefined') {
|
|
container = jQuery('body');
|
|
}
|
|
|
|
if(container.hasClass('timepicker-default')) {
|
|
var element = container;
|
|
}else{
|
|
var element = container.find('.timepicker-default');
|
|
}
|
|
element.data('timepicker-list',null);
|
|
return container;
|
|
},
|
|
|
|
/**
|
|
* Function to get the chosen element from the raw select element
|
|
* @params: select element
|
|
* @return : chosenElement - corresponding chosen element
|
|
*/
|
|
getChosenElementFromSelect : function(selectElement) {
|
|
var selectId = selectElement.attr('id');
|
|
var chosenEleId = selectId+"_chzn";
|
|
return jQuery('#'+chosenEleId);
|
|
},
|
|
|
|
/**
|
|
* 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 = "s2id_"+selectId;
|
|
return jQuery('#'+select2EleId);
|
|
},
|
|
|
|
/**
|
|
* Function to get the select element from the chosen element
|
|
* @params: chosen element
|
|
* @return : selectElement - corresponding select element
|
|
*/
|
|
getSelectElementFromChosen : function(chosenElement) {
|
|
var chosenId = chosenElement.attr('id');
|
|
var selectEleIdArr = chosenId.split('_chzn');
|
|
var selectEleId = selectEleIdArr['0'];
|
|
return jQuery('#'+selectEleId);
|
|
},
|
|
|
|
/**
|
|
* Function to set with of the element to parent width
|
|
* @params : jQuery element for which the action to take place
|
|
*/
|
|
setInheritWidth : function(elements) {
|
|
jQuery(elements).each(function(index,element){
|
|
var parentWidth = jQuery(element).parent().width();
|
|
jQuery(element).width(parentWidth);
|
|
});
|
|
},
|
|
|
|
|
|
initGuiders: function (list) {
|
|
if (list) {
|
|
for (var index = 0, len = list.length; index < len; ++index) {
|
|
var guiderData = list[index];
|
|
guiderData['id'] = "" + index;
|
|
guiderData['overlay'] = true;
|
|
guiderData['highlight'] = true;
|
|
guiderData['xButton'] = true;
|
|
if (index < len - 1) {
|
|
guiderData['buttons'] = [{name: 'Next'}];
|
|
guiderData['next'] = "" + (index + 1);
|
|
|
|
}
|
|
guiders.createGuider(guiderData);
|
|
}
|
|
// TODO auto-trigger the guider.
|
|
guiders.show('0');
|
|
}
|
|
},
|
|
|
|
showScrollBar : function(element, options) {
|
|
if(typeof options == 'undefined') {
|
|
options = {};
|
|
}
|
|
if(typeof options.height == 'undefined') {
|
|
options.height = element.css('height');
|
|
}
|
|
|
|
var givenHeight = parseInt(options.height.toString().replace('px', ''));
|
|
var modalBodyHeight = element.find('.modal-body').height();
|
|
if (element.hasClass('modal-body') && modalBodyHeight == null) {
|
|
modalBodyHeight = element.height();
|
|
}
|
|
if (modalBodyHeight > givenHeight) {
|
|
var windowHeight = window.innerHeight * 0.7;
|
|
options.height = windowHeight + 'px';
|
|
}
|
|
return element.slimScroll(options);
|
|
},
|
|
|
|
showHorizontalScrollBar : function(element, options) {
|
|
if(typeof options == 'undefined') {
|
|
options = {};
|
|
}
|
|
var params = {
|
|
horizontalScroll: true,
|
|
theme: "dark-thick",
|
|
advanced: {
|
|
autoExpandHorizontalScroll:true
|
|
}
|
|
}
|
|
if(typeof options != 'undefined'){
|
|
var params = jQuery.extend(params,options);
|
|
}
|
|
return element.mCustomScrollbar(params);
|
|
},
|
|
|
|
/**
|
|
* Function returns translated string
|
|
*/
|
|
vtranslate : function(key) {
|
|
//convert arguments in to proper array
|
|
var params = [].slice.apply(arguments);
|
|
params.shift();
|
|
|
|
if(app.languageString[key] != undefined) {
|
|
var translatedString = app.languageString[key];
|
|
if(params.length > 0) {
|
|
var replaceRegex = new RegExp("(%s)", "g");
|
|
var paramsPointer = 0;
|
|
translatedString = translatedString.replace(replaceRegex,function(){
|
|
var string = params[paramsPointer];
|
|
paramsPointer++;
|
|
return string;
|
|
})
|
|
}
|
|
return translatedString;
|
|
} else {
|
|
var strings = jQuery('#js_strings').text();
|
|
if(strings != '') {
|
|
app.languageString = JSON.parse(strings);
|
|
if(key in app.languageString){
|
|
var translatedString = app.languageString[key];
|
|
if(params.length > 0) {
|
|
var replaceRegex = new RegExp("(%s)", "g");
|
|
var paramsPointer = 0;
|
|
translatedString = translatedString.replace(replaceRegex,function(){
|
|
var string = params[paramsPointer];
|
|
paramsPointer++;
|
|
return string;
|
|
})
|
|
}
|
|
return translatedString;
|
|
}
|
|
}
|
|
}
|
|
return key;
|
|
},
|
|
|
|
/**
|
|
* Function which will set the contents height to window height
|
|
*/
|
|
setContentsHeight : function() {
|
|
var borderTopWidth = parseInt(jQuery(".mainContainer").css('margin-top'))+21; // (footer height 21px)
|
|
jQuery('#leftPanel, .contentsDiv, .details').css('min-height',(jQuery(document).innerHeight()-borderTopWidth));
|
|
},
|
|
|
|
/**
|
|
* Function will return the current users layout + skin path
|
|
* @param <string> img - image name
|
|
* @return <string>
|
|
*/
|
|
vimage_path : function(img) {
|
|
return jQuery('body').data('skinpath')+ '/images/' + img ;
|
|
},
|
|
|
|
/*
|
|
* Cache API on client-side
|
|
*/
|
|
cacheNSKey: function(key) { // Namespace in client-storage
|
|
return 'vtiger6.' + key;
|
|
},
|
|
cacheGet: function(key, defvalue) {
|
|
key = this.cacheNSKey(key);
|
|
return jQuery.jStorage.get(key, defvalue);
|
|
},
|
|
cacheSet: function(key, value) {
|
|
key = this.cacheNSKey(key);
|
|
jQuery.jStorage.set(key, value);
|
|
},
|
|
cacheClear : function(key) {
|
|
key = this.cacheNSKey(key);
|
|
return jQuery.jStorage.deleteKey(key);
|
|
},
|
|
|
|
htmlEncode : function(value){
|
|
if (value) {
|
|
return jQuery('<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", ((jQuery(window).height() - element.outerHeight()) / 2) + jQuery(window).scrollTop() + "px");
|
|
element.css("left", ((jQuery(window).width() - element.outerWidth()) / 2) + jQuery(window).scrollLeft() + "px");
|
|
},
|
|
|
|
getvalidationEngineOptions : function() {
|
|
var options = app.validationEngineOptions;
|
|
return jQuery.extend({}, options);
|
|
},
|
|
|
|
/**
|
|
* 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() {
|
|
jQuery(document).trigger('postajaxready');
|
|
},
|
|
|
|
/**
|
|
* Listen to xready notiications.
|
|
*/
|
|
listenPostAjaxReady: function(callback) {
|
|
jQuery(document).on('postajaxready', callback);
|
|
},
|
|
|
|
/**
|
|
* Form function handlers
|
|
*/
|
|
setFormValues: function(kv) {
|
|
for (var k in kv) {
|
|
jQuery(k).val(kv[k]);
|
|
}
|
|
},
|
|
|
|
setRTEValues: function(kv) {
|
|
for (var k in kv) {
|
|
var rte = CKEDITOR.instances[k];
|
|
if (rte) rte.setData(kv[k]);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Function returns the javascript controller based on the current view
|
|
*/
|
|
getPageController : function() {
|
|
var moduleName = app.getModuleName();
|
|
var view = app.getViewName()
|
|
var parentModule = app.getParentModuleName();
|
|
|
|
var moduleClassName = parentModule+"_"+moduleName+"_"+view+"_Js";
|
|
if(typeof window[moduleClassName] == 'undefined'){
|
|
moduleClassName = parentModule+"_Vtiger_"+view+"_Js";
|
|
}
|
|
if(typeof window[moduleClassName] == 'undefined') {
|
|
moduleClassName = moduleName+"_"+view+"_Js";
|
|
}
|
|
if(typeof window[moduleClassName] == 'undefined') {
|
|
moduleClassName = "Vtiger_"+view+"_Js";
|
|
}
|
|
if(typeof window[moduleClassName] != 'undefined') {
|
|
return new window[moduleClassName]();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Function to decode the encoded htmlentities values
|
|
*/
|
|
getDecodedValue : function(value) {
|
|
return jQuery('<div></div>').html(value).text();
|
|
},
|
|
|
|
/**
|
|
* Function to check whether the color is dark or light
|
|
*/
|
|
getColorContrast: function(hexcolor){
|
|
var r = parseInt(hexcolor.substr(0,2),16);
|
|
var g = parseInt(hexcolor.substr(2,2),16);
|
|
var b = parseInt(hexcolor.substr(4,2),16);
|
|
var yiq = ((r*299)+(g*587)+(b*114))/1000;
|
|
return (yiq >= 128) ? 'light' : 'dark';
|
|
},
|
|
|
|
updateRowHeight : function() {
|
|
var rowType = jQuery('#row_type').val();
|
|
if(rowType.length <=0 ){
|
|
//Need to update the row height
|
|
var widthType = app.cacheGet('widthType', 'mediumWidthType');
|
|
var serverWidth = widthType;
|
|
switch(serverWidth) {
|
|
case 'narrowWidthType' : serverWidth = 'narrow'; break;
|
|
case 'wideWidthType' : serverWidth = 'wide'; break;
|
|
default : serverWidth = 'medium';
|
|
}
|
|
var userid = jQuery('#current_user_id').val();
|
|
var params = {
|
|
'module' : 'Users',
|
|
'action' : 'SaveAjax',
|
|
'record' : userid,
|
|
'value' : serverWidth,
|
|
'field' : 'rowheight'
|
|
};
|
|
AppConnector.request(params).then(function(){
|
|
jQuery(rowType).val(serverWidth);
|
|
});
|
|
}
|
|
},
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
jQuery(document).ready(function(){
|
|
app.changeSelectElementView();
|
|
|
|
//register all select2 Elements
|
|
app.showSelect2ElementView(jQuery('body').find('select.select2'));
|
|
|
|
app.setContentsHeight();
|
|
|
|
//Updating row height
|
|
app.updateRowHeight();
|
|
|
|
jQuery(window).resize(function(){
|
|
app.setContentsHeight();
|
|
})
|
|
|
|
String.prototype.toCamelCase = function(){
|
|
var 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(jQuery.browser.msie || (/Trident/).test(navigator.userAgent)) {
|
|
var makeResizable = function(e) {
|
|
if(e.resizable("option","disabled")) {
|
|
e.resizable().css('height','').css('width','');
|
|
// jQuery ui resizable is adding a parent div which contains fixed height and width which need to be removed
|
|
e.parent('.ui-wrapper').css('height','').css('width','');
|
|
}
|
|
};
|
|
jQuery(document).on('focus', 'textarea', function(e){
|
|
var element = jQuery(e.currentTarget);
|
|
makeResizable(element);
|
|
});
|
|
makeResizable(jQuery('textarea'));
|
|
}
|
|
|
|
// Instantiate Page Controller
|
|
var pageController = app.getPageController();
|
|
if(pageController) pageController.registerEvents();
|
|
});
|
|
|
|
/* Global function for UI5 embed page to callback */
|
|
function resizeUI5IframeReset() {
|
|
jQuery('#ui5frame').height(650);
|
|
}
|
|
function resizeUI5Iframe(newHeight) {
|
|
jQuery('#ui5frame').height(parseInt(newHeight,10)+15); // +15px - resize on IE without scrollbars
|
|
}
|