var TABLESECTIONCFTABLE="tableSectionCFTable_";
var CFTABLERESULT= "cfTableResult_";
var READ_ONLY_MODE = "1";
var relatedIndex = {};
var cfTableAttr_observer;
var cfTableAttr_formulaMap = {}; 
var cfTableAttr_formulaTriggers = {};
var cfTableAttr_formuleCache = {};

function CustomFieldsRowTable(configColumnsJSON){
	this.cfId = configColumnsJSON.cfId;
	this.hasTBody = configColumnsJSON.hasTBodyTable;
	this.dataColumns = configColumnsJSON.dataColumns;
	this.tableResults = configColumnsJSON.tableResults;
	this.rowId = configColumnsJSON.rowId;
    this.rowIndexTemp = configColumnsJSON.rowIndexTemp;
	
	var thisO = this;

	var isColumnValid = function(oColJSON){
		return oColJSON.active && oColJSON.configured;
	};
	
	var isReadOnly=function(oColJSON){
		return oColJSON.data.readOnlyMode == READ_ONLY_MODE;
	};
	
	var createObjCFText = function(oColJSON){
		var constraintByRegExp = ValueIsBlank(oColJSON.data.regExpAceptedValue) ? 0 : 2;
		var cfConstraint = parseInt(oColJSON.data.obligatory) + constraintByRegExp;
		var cfClass = oColJSON.data.acceptedValue == 4 
			? "textCFDate1"
			: oColJSON.data.acceptedValue == 5
				? "textCFDate2" 
				: "";

		const format = oColJSON.data.format;
		const nDecimal = oColJSON.data.numberDecimal;
		let cfformat = ( format != undefined && format) || ( nDecimal != undefined && nDecimal)
			? oColJSON.data.format+"|"+oColJSON.data.numberDecimal
			: undefined;
		
		let isReadOnlyCFText = isReadOnly(oColJSON);
		
		if(!isReadOnlyCFText){
			//if(!ValueIsBlank(oColJSON.data.roleLineDefense) && typeof userColumnReadOnly === 'function'){			
			if(typeof userColumnReadOnly === 'function'){				
				isReadOnlyCFText = userColumnReadOnly(oColJSON.data.roleLineDefense);
			}
		}
		
        let aditionalClassReadOnly = isReadOnlyCFText ? 'cf-text--read-only' : '';
        return $jq("<div>",{
            class: `cf-text ${aditionalClassReadOnly}`
        }).append(
            $jq("<input>", {
                id: "cfieldtext_"+thisO.cfId,
                name: "cfieldtext_"+thisO.cfId,
                type: "text",
                labeltitleap:oColJSON.data.label,
                cfconstraint: cfConstraint,
                includetotal: oColJSON.includetotal,
                format: cfformat,
                colDB: oColJSON.colDB,
                value: oColJSON.data.valueDefault,
                regexp: oColJSON.data.regExpAceptedValue,
                cfType: oColJSON.data.type,
                maxlength: $jq(thisO.tableResults).attr("maxlengthtext"),
                class : cfClass,
                placeholder: oColJSON.data.labelsValue.acceptedValue,
                "data-row-db": thisO.rowId,
            })
        );
	};
	
	var createObjCFTextArea = function(oColJSON){
		let isReadOnlyCFTextArea = isReadOnly(oColJSON);
		if(!isReadOnlyCFTextArea){
			//if(!ValueIsBlank(oColJSON.data.roleLineDefense) && typeof userColumnReadOnly === 'function'){			
			if(typeof userColumnReadOnly === 'function'){				
				isReadOnlyCFTextArea = userColumnReadOnly(oColJSON.data.roleLineDefense);
			}
		}
		
		if(isReadOnlyCFTextArea){
			return $jq("<span>", {
				id: "cfieldarea_"+thisO.cfId,
				name: "cfieldarea_"+thisO.cfId,
				rows: "3",
				isreadonly:isReadOnlyCFTextArea,
				labeltitleap:oColJSON.data.label,
				cfconstraint: oColJSON.data.obligatory,
				colDB: oColJSON.colDB,
				cfType: oColJSON.data.type,
			});
		}
		return $jq("<textarea>", {
			id: "cfieldarea_"+thisO.cfId,
			name: "cfieldarea_"+thisO.cfId,
			labeltitleap:oColJSON.data.label,
			rows: "3",
			cfconstraint: oColJSON.data.obligatory,
			colDB: oColJSON.colDB,
			cfType: oColJSON.data.type
		});
	};
	
	var createObjCFAttachFile = (fieldId, obligatory, cftype, label) =>{
		$element = `
			<input type="hidden" labeltitleap="${label}" id="cfAttFilesIdsAdd${fieldId}" name="cfAttFilesIdsAdd${fieldId}" cfconstraint="${obligatory}" value="" cftype="${cftype}"/>
			<div id="attachFile${fieldId}" class="att-cf att-cf--files"> <div>
		`;
		return $element;
	};

	var createObjCFAttachConcept = function(fieldId, obligatory, cftype, label){
		$element = `
			<input type="hidden" labeltitleap="${label}" id="cfAttConceptsIdsAdd${fieldId}" name="cfAttConceptsIdsAdd${fieldId}" cfconstraint="${obligatory}" value="" cftype="${cftype}"/>
			<div id="attachConcept${fieldId}" class="att-cf att-cf--concepts">
				<div class="att-cf__search sve-icon--search"></div>
				<div class="att-cf__search-text">${jsonAttachCF.labels.addConcept}</div>
				<div class="att-cf__previews"></div>
				<div class="att-cf__counter"></div>
			</div>`;
		return $element;
	};
	
	var createObjCFAttach = function(oColJSON){
		const ALL_ATTACH_SUPPORT = 0;
		const ONLY_ATTACH_SUPPORT = 1;
		const ONLY_CONCEPT_SUPPORT = 2;

		const typeSelected = Number(oColJSON.data.typeAttachAcepted);
		const obligatory = Number(oColJSON.data.obligatory);
		const printAttFile = typeSelected == ALL_ATTACH_SUPPORT || typeSelected == ONLY_ATTACH_SUPPORT ? true : false;
		const printAttConcept = typeSelected == ALL_ATTACH_SUPPORT || typeSelected == ONLY_CONCEPT_SUPPORT ? true : false;

		const fieldId = `${thisO.cfId}_${thisO.rowId}_${oColJSON.colDB}`;
		window[`jsonAttachmentsCF${fieldId}`] = {};

		$box_cf = document.createElement('div');
		$box_cf.classList.add('attach-cf-box');
		$box_cf.setAttribute('colDB', oColJSON.colDB);
		$box_cf.setAttribute('cfType', oColJSON.data.type);
		$box_cf.setAttribute('fieldid', fieldId);
		$box_cf.setAttribute('labeltitleap', oColJSON.data.label);   
		$box_cf.setAttribute('tabindex', 0);


		if(printAttFile){
			$box_cf.innerHTML += createObjCFAttachFile(fieldId,obligatory,oColJSON.data.type,oColJSON.data.label);
		}

		if(printAttConcept){
			$box_cf.innerHTML += createObjCFAttachConcept(fieldId,obligatory,oColJSON.data.type,oColJSON.data.label);
		}

		return $box_cf;
	};
	
	var createObjCFList = function(oColJSON , $lastRow){
		let isReadOnlyCFList = isReadOnly(oColJSON);
		
		if(!isReadOnlyCFList){
			//if(!ValueIsBlank(oColJSON.data.roleLineDefense) && typeof userColumnReadOnly === 'function'){			
			if(typeof userColumnReadOnly === 'function'){				
				isReadOnlyCFList = userColumnReadOnly(oColJSON.data.roleLineDefense);
			}
		}
		
		if(isReadOnlyCFList){
			return $jq("<span>", {
				id: "cfieldlist_"+thisO.cfId,
				colDB: oColJSON.colDB,
				cfconstraint: oColJSON.data.obligatory,
				cfType: oColJSON.data.type,
				isreadonly:isReadOnlyCFList,
				dependOf: oColJSON.data.dependOf,
				labeltitleap:oColJSON.data.label,
				defaultValues: oColJSON.data.defaultValue,
				options: oColJSON.data.labelsValue.classAcepted
			});
		}
		
		var $select = $jq("<select>", {
			id: "cfieldlist_"+thisO.cfId,
			colDB: oColJSON.colDB,
			cfconstraint: oColJSON.data.obligatory,
			cfType: oColJSON.data.type,
			dependOf: oColJSON.data.dependOf,
			labeltitleap:oColJSON.data.label,
			defaultValues: oColJSON.data.defaultValue,
			options: oColJSON.data.labelsValue.classAcepted
		});
		var isMultiple = oColJSON.data.selection === "multipleList";
		if(isMultiple){
			$select.attr("multiple","multiple");
		}else{
			$select.append($jq("<option>"));
		}
		initializeOnChangeCFList();

		var optionList = oColJSON.data.labelsValue.classAcepted.split("|");
		if(oColJSON.data.dependOf === "-1" || oColJSON.data.dependOf === "" || oColJSON.data.dependOf === undefined ) {
			createObjCFListOption(optionList, $select);
			$select.val(unescape(oColJSON.data.defaultValue).split("|"));
		} else {
			let partentDefaultValue = ($lastRow.find(`td > select[coldb='${oColJSON.data.dependOf }']`).val());
			if (partentDefaultValue && partentDefaultValue !== "") {
				createObjCFListOption(optionList, $select, partentDefaultValue);
				$select.val(getDefaultValueCFListOption($select.attr('defaultValues') ,partentDefaultValue ));
			} 
		}
		
		
		$select.prop('disabled', isReadOnlyCFList);
		
		return $select;
	};
	
	var createObjCFListOption = function(optionList, $select , parentValue) {
		if (parentValue) {
			$jq.each(optionList, function(i, val){
				if(ValueIsBlank(val)){
					return true;
				}
				let addOption = parentValue !== "" && unescape(val).indexOf(parentValue) === 0 ? true : false;
				if (addOption) {
					$select.append($jq("<option>",{
						text: removePathNameCFListOption(unescape(val)),
						value: unescape(val),
						title: unescape(val),
					}));
				}
			});
		} else {
			$jq.each(optionList, function(i, val){
				if(ValueIsBlank(val)){
					return true;
				}
				$select.append($jq("<option>",{
					text: removePathNameCFListOption(unescape(val)),
					value: unescape(val),
					title: unescape(val),
					}));
			});
		}
	
	}

	var createObjCFConcept = function(oColJSON){
		let isReadOnlyCFConcept = isReadOnly(oColJSON);
		
		if(!isReadOnlyCFConcept){
			//if(!ValueIsBlank(oColJSON.data.roleLineDefense) && typeof userColumnReadOnly === 'function'){			
			if(typeof userColumnReadOnly === 'function'){				
				isReadOnlyCFConcept = userColumnReadOnly(oColJSON.data.roleLineDefense);
			}
		}
		
		var $contentConcept = $jq("<div>",{
			class: "content-cf-concept"
		});
		var cfConceptId = "cfield_concept_" + thisO.cfId + "_" + thisO.rowId + "_" + oColJSON.colDB;
		
		var $contentConceptInput = $jq("<div>",{
			class: isReadOnlyCFConcept == true ? "" : "finput",
			style: "flex-grow: 1"
		});

		$contentConceptInput.append($jq("<input>",{
			type: "hidden",
			id: cfConceptId,
			name: cfConceptId,
			value: oColJSON.data.defaultValue,
			colDB: oColJSON.colDB,
			cfType: oColJSON.data.type
		}));

		$contentConceptInput.append($jq("<input>",{
			type: "hidden",
			id: cfConceptId + "_labAux",
			name: cfConceptId + "_labAux"
		}));
		if(oColJSON.data.searcherDef == undefined){
			oColJSON.data.searcherDef = "base:ConceptSearcher";
		}
		
		if(isReadOnlyCFConcept){
			$contentConceptInput.append($jq("<span>", {
				type: "text",
				id: cfConceptId + "_lab",
				name: cfConceptId + "_lab",			
				value: oColJSON.data.labelsValue.defaultValue,
				colDB: oColJSON.colDB,
				cfconstraint: oColJSON.data.obligatory,
				def: oColJSON.data.searcherDef,
				cfType: oColJSON.data.type,
				riskControls: oColJSON.data.riskControls,
				isreadonly:isReadOnlyCFConcept,
				labeltitleap:oColJSON.data.label,
				onlyclass: oColJSON.data.classAcepted
			}));
		} else{
			$contentConceptInput.append($jq("<input>", {
				type: "text",
				id: cfConceptId + "_lab",
				name: cfConceptId + "_lab",			
				value: oColJSON.data.labelsValue.defaultValue,
				colDB: oColJSON.colDB,
				cfconstraint: oColJSON.data.obligatory,
				def: oColJSON.data.searcherDef,
				cfType: oColJSON.data.type,
				riskControls: oColJSON.data.riskControls,
				labeltitleap:oColJSON.data.label,
				onlyclass: oColJSON.data.classAcepted
			}));
		}
		
		
		
		let classAcepted=oColJSON.data.classAcepted;
		
		if(classAcepted==RISK_CONTROL_CLASS && oColJSON.data.riskControls>0){
			$contentConceptInput.append(`<span id='${cfConceptId}_link' class='span'></span>`);			
			$contentConceptInput.append($jq("<input>", {
				type: "hidden",
				id: cfConceptId + "_type",
				name: cfConceptId + "_type",			
				value: oColJSON.data.classAcepted,
			}));	
			$contentConcept.append($contentConceptInput);			
		}else{				
			let iconClearActive = ValueIsBlank(oColJSON.data.defaultValue) ? '' : 'x-icon-clear-active';
			$contentConceptInput.append($jq("<div>", {
				class: `x-icon-clear icon_clear ${iconClearActive}`,
				click: eventClickIconClear
			}));
			
			if(classAcepted==RISK_CONTROL_CLASS){
				$contentConceptInput.append($jq("<input>", {
					type: "hidden",
					id: cfConceptId + "_type",
					name: cfConceptId + "_type",			
					value: oColJSON.data.classAcepted,
				}));	
				$contentConcept.append($contentConceptInput);	
			}

			$contentConcept.append($contentConceptInput);	
			if(!isReadOnlyCFConcept){
				$contentConcept.append($jq("<input>", {
					type: "button",
					id: cfConceptId + "_btn",
					name: cfConceptId + "_btn",
					class: "fontNormal XCommandButton searchLittleBottom",
					value: "...",
					click: eventClickButtonConcept
				}));				
			}
			
			$contentConcept.append($jq("<input>", {
				type: "button",
				id: cfConceptId + "_verconcep",
				name: cfConceptId + "_verconcep",
				class: "searchLittleBottomWithText searchLittleBottom button_action_floating button_action_primary_floating viewConceptCss XCommandButton",
				style: ValueIsBlank(oColJSON.data.defaultValue) ? '' : 'display: inline !important;',
				value: "Ver",
				click: eventClickButtonViewConcept
			}));
		}
		
	 	$contentConcept.append(printJSConcept(cfConceptId));
		return $contentConcept;
	};

	var createCFCol = function(oColJSON){
		const vAling = oColJSON.data.type == customFieldTypeEnum.ATTACHMENTS ? 'td-cf-attachment' : '';
		return $jq("<td>", {
			align: "left",
			nowrap: "nowrap",
			class: vAling
		});	
	};

	var createCheckboxHeader = function(){
		return $jq("<input>", {
			id: "chkHeader_"+thisO.cfId,
			type: "checkbox",
			click: eventCheckHeader
		});
	};

	var createCheckboxRow = function(){
		var $tdChk = $jq("<td>", {
			align: "center"
		});	
		$tdChk.append(
			$jq("<input>", {
			id: "chkRow_"+thisO.cfId,
			type: "checkbox",
			click: eventCheckRow
		}));
		return $tdChk;
	};

    var createRowIndexTemp = function(){
		var $td = $jq("<td>", {
			align: "center",
            class: "cf-attr-table__i-temp-row"
		});	
		$td.text(thisO.rowIndexTemp)
		return $td;
	};

	var createCFRow = function(){
		return $jq("<tr>", {
			id: thisO.rowId
		});	
	};
	
	var createCFTBody = function(){
		return $jq("<tbody>", {
			id: "cftbody_"+thisO.cfId
		});
	};
	
	var hasCheckboxHeader = function(){
		return $jq(thisO.tableResults).find("thead input[type=checkbox]").length > 0;
	};

	var createContainerRow = function(){
		if(!hasCheckboxHeader()){
			$jq(thisO.tableResults).find("tr>td:nth-child(2)").append(createCheckboxHeader());
		}
		if(!thisO.hasTBody){
			$jq(thisO.tableResults).append(createCFTBody());
		}
		var $tbody = $jq(thisO.tableResults).find("tbody");
		$tbody.append(createCFRow());
		$tbody.find("tr:last").append(createRowIndexTemp()).append(createCheckboxRow());
	};
	
	var createColByCFType = function(oColJSON){
		var $lastRow = $jq(thisO.tableResults).find("tbody > tr:last");
		$lastRow.append(createCFCol(oColJSON));	
		var $lastCol = $lastRow.find("td:last");
		$lastCol.attr("includeTotal",oColJSON.data.includeTotal);

		const format = oColJSON.data.format;
		const nDecimal = oColJSON.data.numberDecimal;
		let cfformat = ( format != undefined && format) || ( nDecimal != undefined && nDecimal)
			? oColJSON.data.format+"|"+oColJSON.data.numberDecimal
			: undefined;
		$lastCol.attr("format", cfformat);
		var type = oColJSON.data.type;
		switch(type){
			case customFieldTypeEnum.TEXT_CUSTOM_FIELD:
				$lastCol.append(createObjCFText(oColJSON));
			break;	
			case customFieldTypeEnum.CONCEPT_CUSTOM_FIELD:
				$lastCol.append(createObjCFConcept(oColJSON));		
				var cfConceptId = "cfield_concept_" + thisO.cfId + "_" + thisO.rowId + "_" + oColJSON.colDB;
				configureAutocomplete(cfConceptId);
			break;	
			case customFieldTypeEnum.COMMENT_CUSTOM_FIELD:
				$lastCol.append(createObjCFTextArea(oColJSON));
			break;	
			case customFieldTypeEnum.LIST_CUSTOM_FIELD:
				$lastCol.append(createObjCFList(oColJSON , $lastRow));
			break;
			case customFieldTypeEnum.ATTACHMENTS:
				$lastCol.append(createObjCFAttach(oColJSON , $lastRow));
			break;	
			default: return;
		}	
	};
	
	var configureAutocomplete=function(cfConceptId){
	 	var autocompleteOpts = {};	   	
	 	var onlyclass=$jq("#"+cfConceptId+"_lab").attr("onlyclass");
		let searcher = $jq("#"+cfConceptId+"_lab").attr("def");
		let jIndex;
		if (searcher === 'base:CReportsCustomConceptWorkFlowSearcher') { 
			jIndex = getIndexAutocompleteByModule(onlyclass , true);
		} else {
			jIndex = getIndexAutocompleteByModule(onlyclass);
		}
		autocompleteOpts[cfConceptId] = {'qsid' : 'name', 'keyIndex': jIndex.keyIndex, 'valueIndex': jIndex.valueIndex, 'qstring':{'onlyclass' : onlyclass , 'clazz' : onlyclass },'callbackPart' : "callBack" + cfConceptId + "()",'emptycallback':"callBack" + cfConceptId + "()" };
		$jq.extend(autocompleteJSON,autocompleteOpts);
		configureBlurEvent(cfConceptId);
	};

	var initializeCreationRow = function(){
		createContainerRow();
		$jq.each(thisO.dataColumns, function(i,o){
			if(!isColumnValid(o)){
				return true;
			}
			createColByCFType(o);
		});
		initAutocompleteSearchers(autocompleteJSON);
		initializeTextCFDate();
		initializeAttachFileCFByRow(thisO);
		initializeAttachConcepCFByRow(thisO);
        initializeTextCFCalculateCellByRow(thisO);
	};

	initializeCreationRow();
};

//hidde the btn "ver" when the the cfConcept havent a valid value.
var configureBlurEvent = function( cfConceptId ){ 
	$jq("#"+cfConceptId+"_lab").blur(function() {
		if ($jq("#"+cfConceptId).val() === '') {
			$jq("#"+cfConceptId + "_verconcep").hide();	
		}
	});
}

var textJSConcept = function(cfConceptId){
	let functionJs = " window.setValueAfterCreate" + cfConceptId + "= function(entityResponse){"+
	" $jq('#" + cfConceptId + "_lab').val(unescape(entityResponse['name']));"+
	" $jq('#" + cfConceptId + "').val(entityResponse['id']);"+
	" $jq('#" + cfConceptId + "_lab').next('.icon_clear').animate({opacity: 1},300).css('display','inline-block');"+
	" $jq('#" + cfConceptId + "_verconcep').show();"+
	" hideMessageWaiting(); }; ";
			
	functionJs += "window.refresh"+cfConceptId+"View = function(id, row, c){ "+
		"var cellIndex = getCellIndexByModule($jq('#"+cfConceptId+"_lab').attr('onlyclass')); " +
		"$(forms_getId('"+cfConceptId+"', c)).value = id; " +
		"$(forms_getId('"+cfConceptId+"_lab', c)).value = row.cells[cellIndex].getAttribute('label'); " +
		"showIconClear($jq('#"+cfConceptId+"_lab').siblings('div.x-icon-clear'));" +
		"showViewConcept($jq('#"+cfConceptId+"_verconcep'));"+	
		"searcher_key_press($(forms_getId('"+cfConceptId+"_lab', c)), forms_getId('"+cfConceptId+"',c), 4); }; " +
				"window.callBack"+cfConceptId+"=function(){showIconClear($jq('#"+cfConceptId+"_lab').siblings('div.x-icon-clear'));" +
						"showViewConcept($jq('#"+cfConceptId+"_verconcep'));};" ;
	
	return functionJs;
}

var printJSConcept = function(cfConceptId){
	return $jq("<script>",{
	 	type:"text/javascript",
	 	text: textJSConcept(cfConceptId)
	});
};

var eventCheckRow = function(){
    if( cfTableAttr_validateRow(this) ){
        return false;
    }
	$jq(this).closest("tr").toggleClass("deleted");
	let cfId=$jq(this).closest("table.dataGrid").attr("id");
	$jq(`#${cfId} tr td[includeTotal=1]`).each(function(index) {
		getTotal(cfId.replace(CFTABLERESULT,""),index);
	});
};

var eventCheckHeader = function(){
	if($jq(this).is(":checked")){
        let isCorrectAllCheck = true;
		$checksBoxes = $jq(this).closest("table.dataGrid").find("tbody input[type=checkbox][id^=chkRow_]:not(:checked)");
        $checksBoxes.each(function(){
            $jq(this).trigger("click");
            if( document.querySelectorAll("#id_Boxy").length > 0 ){
                isCorrectAllCheck = false;
                return false;
            }
        });
        if( isCorrectAllCheck == false ){
            return false;
        }
	}else{
		$jq(this).closest("table.dataGrid").find("tbody input[type=checkbox][id^=chkRow_]:checked").trigger("click");
	}
};

var eventClickButtonConcept = function(){
	var $objText = $jq(this).siblings(".finput").children("input[type=text]");
	var $objValue = $jq(this).siblings(".finput").children("input[type=hidden]");
	var searcherDefOptions = $objText.attr("def").split(":");
	var cfConceptId = $objValue.attr("id");
	var onlyclass = parseInt($objText.attr("onlyclass")) > 0 ? "&onlyclass="+$objText.attr("onlyclass") : "";
	var clazz = parseInt($objText.attr("onlyclass")) > 0 ? "&clazz="+$objText.attr("onlyclass") : "";
	var qstr = '&__xtpl=1'+onlyclass+clazz+'&_security=a586d828ce9abe12e59c75ec105e3fa0a8b20f33&apTA=1';
	
	if(clazz.indexOf(RISK_CONTROL_CLASS) >= 0){
		qstr+='&risk='+$jq("#id").val();
	}	
	showSingleSearcher(searcherDefOptions[0], searcherDefOptions[1], 'null', 'null', forms_getId(cfConceptId, undefined), searcherDefOptions[0], qstr ,570,450);
};

var eventClickButtonViewConcept = function(){
	var $objText = $jq(this).siblings(".finput").children("input[type=text]");
	var $objValue = $jq(this).siblings(".finput").children("input[type=hidden]");	
	var cfConceptId = $objValue.val();
	
	let reviewId = "";
	
	let tableId = $jq(this).closest("table").attr("id")
	if(tableId != undefined){
		let indexReview = tableId.indexOf("cfPrint");
		if (indexReview !== -1) {
		    reviewId = tableId.substring(indexReview + "cfPrint".length);
		}
		
	}
	
	let typeId=$objValue.attr("id");
	let qstring='';
	let conceptName = '';
	if($jq(`#${typeId}_type`).length>0){
		typeId=$jq(`#${typeId}_type`).val();
		let riskId=$jq("#id").val();
		if(riskId==undefined){
			riskId=parent.$jq("#id").val();
		}
		qstring="&qString="+escape('&riskId='+riskId+'&valuationId=' + $jq("#valuationId").val() + '&review=' + reviewId);
	}else{
		typeId=-1;
		if($objText.val() && $objText.val().length>0){
			conceptName = $objText.val();
		}
	}
	
	let onlyclass=$objText.attr("onlyclass");
	
	if(onlyclass!=undefined && onlyclass==modulesForCellIndexEnum.TASK){
		typeId=modulesForCellIndexEnum.TASK;
	}
	xComment_ViewModalConcept(parseInt(cfConceptId),typeId, qstring,conceptName,onlyclass);
};

var showViewConcept = function(element){	
	$jq(element).hide();
 	var siblingText = $jq(element).siblings(".finput").children("input[type=hidden]").val();
	if(!ValueIsBlank(siblingText) && $jq.isNumeric(siblingText)){		
		$jq(element).show();
	}
};

var eventClickIconClear = function(){
	$jq(this).removeClass("x-icon-clear-active");
	$jq(this).siblings("input[type=text],input[type=hidden]").val("");
	$jq(this).parent().siblings(".viewConceptCss").hide();
	
};

var showIconClear = function(oIcon){
 	var siblingText = $jq(oIcon).siblings("input[type=text]").val();
	if(!ValueIsBlank(siblingText)){
		$jq(oIcon).addClass("x-icon-clear-active");
	}
};

var initializeIconClear = function(){
	var $xIconClear = $jq(".content-cf-concept div.finput .x-icon-clear");
	$xIconClear.each(function(){
		showIconClear(this);
	});
	$xIconClear.unbind("click");
	$xIconClear.click(eventClickIconClear);
};

var initializeViewConcept = function(){
	var $concept =  $jq(".content-cf-concept .viewConceptCss"); 
	$concept.each(function(){
		showViewConcept(this);
	});
	$concept.unbind("click");
	$concept.click(eventClickButtonViewConcept);
};

var initializeOnChangeCFList = function() {
	$jq(`select[cftype='${customFieldTypeEnum.LIST_CUSTOM_FIELD}']` ).unbind("change").bind("change", function() {  
		changeChildOptionsCFList($jq(this));
	});
}

var changeChildOptionsCFList = function( $selectList ) {
	let $childsSelectList = $selectList.parent().parent().find(`td > select[dependof='${$selectList.attr('coldb')}']`);
	if (!$childsSelectList || $childsSelectList.length === 0) {
		return;
	}
	
	let parentValue = $selectList.val();
	$jq($childsSelectList).each(function(indice, $childSelectListElement) {
		let $childSelect =  $jq($childSelectListElement);
		$childSelect.find('option').remove();
		let arrayOptions = $childSelect.attr('options').split('|');
		$childSelect.append($jq("<option>"));
		arrayOptions.forEach(function( opt , index ){
			let addOption = (parentValue !== "" && opt.indexOf(parentValue) === 0) ? true : false;
			if (addOption) {
				$childSelect.append($jq("<option>",{
					text: removePathNameCFListOption(unescape(opt)),
					value: unescape(opt),
					title: unescape(opt),
				})
				);
			}
		});
		
		$childSelect.val(getDefaultValueCFListOption($childSelect.attr('defaultValues') ,parentValue ));
		changeChildOptionsCFList($childSelect);
	});
}

var getDefaultValueCFListOption = function( defaultValues, parentValue) {
	var defaultValue = "";
	if ( defaultValues && parentValue ) {
		let match = unescape(defaultValues).split("|").find( value => value.indexOf(parentValue) === 0);
		if (match){
			defaultValue = match
		}
	}
	return defaultValue;
}

var initializePropertiesCFList = function() {
	$jq( `select[cftype='${customFieldTypeEnum.LIST_CUSTOM_FIELD}']` ).parent().parent().each(function( index, tr){
		let $tBody = $jq(tr).parent();
		
		const $jsonHiddenElement = $jq("#configColumnsJSON_tableSectionCFTable_" + $tBody.attr('id').replace('cftbody_',''))
		const $jsonHiddenElementTBody = $jq("#configColumnsJSON_tableSectionCFTable_" + $tBody.attr('id').replace(/\D/g,''));
		
		let configColumJSON =  ($jsonHiddenElementTBody.length > 0 ? $jsonHiddenElementTBody : $jsonHiddenElement).val();
		if (configColumJSON) {
			configColumJSON = JSON.parse(unescape(configColumJSON));
		}
		let $lastElement = "";
		$jq(tr).find(`select[cftype='${customFieldTypeEnum.LIST_CUSTOM_FIELD}']:not(select[dependof])`).each(function( i , selectTag){
			let $selectTag = $jq(selectTag);
			let $selectTagOptions = $selectTag.find("option");

			if ( $selectTag.attr('dependof') && $selectTag.attr('dependof') !== "") {
				return;
			}
			
			let defaultValues = "";
			let dependOf = "";
			if (configColumJSON && configColumJSON.dataColumns) {
				
				Object.keys(configColumJSON.dataColumns).forEach( (item) => {
					if (configColumJSON.dataColumns[item] && configColumJSON.dataColumns[item].colDB == $selectTag.attr('coldb') ) {
						defaultValues = configColumJSON.dataColumns[item].data.defaultValue;
						dependOf = configColumJSON.dataColumns[item].data.dependOf;
					}
				});
				$selectTag.attr({'defaultvalues': defaultValues,'dependof': dependOf});
				if ($selectTag.attr('dependof') && $selectTag.attr('dependof') !== "" && $selectTag.attr('dependof') !== undefined) {
					
				let $lastElement = $selectTag.parent().parent().find(`select[coldb='${dependOf}'`);
				let stringOptions = "";
				$selectTagOptions.each(function( optIndex, opt ){
					$option = $jq(opt);
					$option.text(removePathNameCFListOption(unescape($option.text())));
					stringOptions = stringOptions + $option.val();
					if (optIndex !== 0 && optIndex !== $selectTagOptions.length-1) {
						stringOptions = stringOptions + "|";
					}
					if (dependOf !== "-1" && dependOf !== "" && $lastElement) {
						if (($option.val() && $lastElement.val() && ($option.val().indexOf($lastElement.val()) === 0)) || $option.val() === "") {
							$option.show();
						} else {
							$option.remove();
						}
					}
				});

				$selectTag.attr('options',stringOptions);
				}
			}
		});
	});
}

var removePathNameCFListOption = function( str ) {
	if (!str || str === "") {
		return "";
	}
	let nameItems = str.split(">");
	return nameItems[nameItems.length-1]
}

var initializeActionsObjectsTable = function(){
	initializeIconClear();
	$jq("table[id^=cfTableResult]>tbody input[type=checkbox][id^=chkRow_]").unbind('click');
	$jq("table[id^=cfTableResult]>tbody input[type=checkbox][id^=chkRow_]").click(eventCheckRow);
	$jq("table[id^=cfTableResult]>thead input[id^=chkHeader_]").unbind('click');
	$jq("table[id^=cfTableResult]>thead input[id^=chkHeader_]").click(eventCheckHeader);
	$jq("div.content-cf-concept input[type=button][id^=cfield_concept_]").unbind('click');
	$jq("div.content-cf-concept input[type=button][id^=cfield_concept_]").click(eventClickButtonConcept);
	$jq("div.content-cf-concept").each(function(){
		var cfConceptId = $jq(this).find("input[type=hidden][id^=cfield_concept_]").attr("id");
		$jq(this).append(printJSConcept(cfConceptId));
		configureBlurEvent(cfConceptId);
	});
	initializeViewConcept();
	initializeTextCFDate();
	initializeOnChangeCFList();
	initializePropertiesCFList();
};

var initializeCFTextFromTable = function (cfId) {
	let cfTableId = CFTABLERESULT + cfId;
	initializeCFTextEvents(`#${cfTableId} input:text[id^='cfieldtext_'][format]`);
	addRowTotal(cfTableId);
};

var focusAutoWidthTdFromTable = function() {
	this.scrollIntoView({behavior: "smooth", inline: "center", block: "nearest"});
	this.classList.remove('with-focus');
};

var focusAutoWidthTdFromTable2 = function() {
	this.classList.add('with-focus');
	this.scrollIntoView({behavior: "auto", inline: "center", block: "nearest"});
};

var initializeAutoWidtTdFromTable = (cfId) => {
	$jq('.content-cf-table-result select')
		.unbind('mousedown', focusAutoWidthTdFromTable2)
		.mousedown( focusAutoWidthTdFromTable2)

	$jq('.content-cf-table-result input[type=text], .content-cf-table-result textarea, .content-cf-table-result select')
		.unbind('focus', focusAutoWidthTdFromTable)
		.focus( focusAutoWidthTdFromTable );
};

function removeCheckboxColumn(tableId) {
      var table = document.getElementById(tableId); // Obtén la tabla por ID
        if (table) {
        table.classList.add('hidden-chk');
    }
}

var getTotal= function getTotal(cfId,index) {
    let total = 0;
    $jq(`#${cfId} tr:not(.totalizer`).each(function() {
		if($jq(this).hasClass("deleted")){
			return;
		}
		let value ="";
		if($jq('td[includeTotal=1] input', this).length>0){
			value = removeFormatToNumber($jq('td[includeTotal=1] input', this).eq(index));
		}else{
			value = $jq('td[includeTotal=1] span', this).eq(index).attr("withoutformat");
		}
        if (ValueIsNumber(value)) {
            total += parseFloat(value);
        }
    });
    let cell=$jq(`#${cfId} tr:last td[includeTotal=1]:eq(${index})`);

   if(cell.length==0){
	  return;
   }

	let format=cell.attr("format").split("|");
    $jq(`#${cfId} tr:last td[includeTotal=1]:eq(${index})`).text(formatNumberWithDecimals(format[0],total,format[1]));
};

var calculateColumns=function(cfId){
	$jq(`#${cfId} tr td[includeTotal=1]`).each(function(index) {
		getTotal(cfId,index);
	});
};

var activateTotalizerInputFields=function(cfId){
	$jq(`#${cfId} tr`).find("td[includeTotal=1] input:text").blur(function() {
		calculateColumns(cfId); 
	});
};

var addRowTotal=function(cfId){
	if($jq(`#${cfId} tr.totalizer`).length>0){
		return;
	}
	if($jq(`#${cfId}`).find('tbody').find('td[includeTotal=1]"').length==0){
		return;
	}	
	var totalRow=$jq(`#${cfId} tr:last`).clone(true);
	totalRow.children('td').empty();
	totalRow.addClass("totalizer");
	$jq(`#${cfId}`).append(totalRow);
	calculateColumns(cfId); 
	activateTotalizerInputFields(cfId);
};

function loadDefaultRecords(relatedObjectArray,tableId){
	let relatedObjectData=eval("("+unescape(relatedObjectArray)+")");
	
	let customfieldId=tableId.replace(TABLESECTIONCFTABLE,CFTABLERESULT);
	let trElements = document.querySelectorAll(`#${customfieldId} > tbody > tr`);
	//Si no hay controles por defecto, marca los posibles controles guardado para eliminar
	if(relatedObjectData.length==0){
		return;
	}

	idToTrMap = getMapIdControlTr(trElements);
	//Recorre un mapa de los controles guardado en la tabla
	idToTrMap.forEach((trElement, idControl ) => {
		const relatedObject = relatedObjectData.find(obj => obj.id === idControl);
		//Si el control esta en la lista de los controles por defecto, actualiza el nombre del control
		if (relatedObject) {
			let elements = trElement.querySelectorAll(`[riskControls="1"]`);
			elements.forEach(element => {
				let spanElement = element.querySelector('span');
                if (spanElement) {
                    spanElement.innerHTML = relatedObject.name;
                }
			});
		}
	});
	// Recorre los controles por defecto que no estan en la lista de controles guardados y los agrega a la tabla
	relatedObjectData.forEach(item => {
		if (!idToTrMap.has(item.id)) {
			addRowNewControl(item,tableId);
		}
	});

	let customfieldTableId=tableId.replace(TABLESECTIONCFTABLE,CFTABLERESULT);
	if($jq(`#${customfieldTableId}>thead>tr`).find("input:checkbox[id^=chkHeader_]").length>0){
		$jq(`#${customfieldTableId}>thead>tr`).find('td:eq(0)').hide();
		$jq(`#${customfieldTableId}>tbody>tr`).find('td:eq(0)').hide();
	}
};

function getMapIdControlTr(trElements){
	let idToTrMap = new Map();
	trElements.forEach(trElement => {
		const elements = trElement.querySelectorAll(`[riskControls="1"]`);
		
		elements.forEach(element => {
			let parentElement = element.parentElement;
			if (parentElement) {
				const inputElements = parentElement.querySelectorAll('input:not([id*="_type"]):not([id*="_labAux"]):not([id*="_lab"])');
				let idControl;
	
				inputElements.forEach(input => {
					let inputValue = input.value;
					if (!isNaN(inputValue) && inputValue.trim() !== "") {
						idControl = Number(inputValue);
					}
				});
				if (idControl !== undefined) {
					idToTrMap.set(idControl, trElement);
				}
			}
		});
	});
	return idToTrMap;
}

function addRowNewControl(item,tableId,i){
	addRowTOCFTable(`${tableId}`);
	let customfieldId=tableId.replace(TABLESECTIONCFTABLE,CFTABLERESULT);
	$jq(`#${customfieldId} > tbody > tr:last`).find("input:text[onlyclass=${RISK_CONTROL_CLASS}][riskControls=1]").each(function(){
		$jq(this).val(item.name);
		$jq(this).hide();
		$jq(this).parent().removeClass("finput");
		if($jq(this).attr("id")==undefined){
			return;
		}
		let textId=$jq(this).attr("id").replace("_lab","");
		$jq(`#${textId}`).val(item.id);
		$jq(`#${textId}_link`).html(`<a href="javascript:windowModal('${item.url}${item.qstring}');" style="white-space:initial">${item.name}</a>`);
	});	
}


var cfTableCalculateCellReconfHtml = ($input) => {
    const $wrapper = $input.closest('.cf-text');
    $input.setAttribute('placeholder', jsonAttrTableCF.labels.formulaBarPlaceHolder )

    // Agregar boton de calcular
    const $btnConfigFormula = document.createElement('div');
    $btnConfigFormula.classList.add('cf-text__btn-calculate');
    $wrapper.appendChild($btnConfigFormula);
}

var cfTableCalculateCellMarkCell = ($input, configColumn) => {
    $input.setAttribute("data-is-calculate-cell", configColumn.data.isCalculatedCell);

    const acceptedValueIncluedInCalculation = ["1","2"];
    $input.setAttribute('data-valid-calculation',acceptedValueIncluedInCalculation.includes( configColumn.data.acceptedValue ) ? 1 : 0);
}

var initializeCFTextCalculateCell = (cfId, $wrapper) => {
    let cfTableId = TABLESECTIONCFTABLE + cfId;
    const configTable = JSON.parse(unescape($jq("#configColumnsJSON_"+cfTableId).val()));

    // Configurar las celdas calculadas
    const columnCalculated = Object.values( configTable.dataColumns ).filter( col => col.data.isCalculatedCell == 1);

    if( columnCalculated.length == 0 ){
        console.warn("No hay columna calculadas");
        return false;
    }

    const colsWithCalculateCell = columnCalculated.map( cc => cc.colDB);
    const $inputs = [...$wrapper.querySelectorAll(`.cf-text > input[cftype="1"]`)];
    $inputs.forEach($inp => {
        const colDb = $inp.getAttribute('coldb');
        const configColumn = Object.values( configTable.dataColumns ).find( col => col.colDB == colDb);

        if( colsWithCalculateCell.includes(Number(colDb))){
            cfTableCalculateCellReconfHtml( $inp);
        }

        cfTableCalculateCellMarkCell($inp, configColumn);
    });

    [...$wrapper.querySelectorAll(`[data-is-calculate-cell="1"]`)].forEach( $input => {
        initializeCfTableTdWithCalculateCell($input, cfId);
    });

    [...$wrapper.querySelectorAll(`[data-valid-calculation="1"]`)].forEach( $input => {
        initializeCfTableTdValidForFormula($input );  
    });
}


var initializeCFTextCalculateCellFromTable = cfId => {
    let cfTableResult = CFTABLERESULT + cfId;
    if( document.querySelector(`#${cfTableResult}`).dataset.readOnlyTable == 'true'){
        return false;
    }
    let cfTableId = TABLESECTIONCFTABLE + cfId;
    const isInit = initializeCFTextCalculateCell(cfId, document.querySelector(`#${cfTableId}`));
    if( isInit == false ){
        return;
    }
    relatedIndex[cfId] = {x: {}, y:{}, xInv: {}, yInv: {}};

    // Crear relacion de indices temporales y indices reales
    const configTable = JSON.parse(unescape($jq("#configColumnsJSON_"+cfTableId).val()));

    let countIndX = 0;
    $indexCols = [...document.querySelectorAll(`#${cfTableId} .cf-attr-table__i-tem-col`)];
    $indexCols.forEach( col =>{
        relatedIndex[cfId].x[col.innerText.trim()] = configTable.dataColumns[++countIndX].colDB;       
    });

    $indexRows = [...document.querySelectorAll(`#${cfTableId} .cf-attr-table__i-temp-row`)];
    $indexRows.forEach( row_td =>{
        const $row = row_td.closest('tr');
        const ix = row_td.innerText.trim();
        if( ValueIsBlank(ix) ){
            return;
        }
        relatedIndex[cfId].y[ix] = $row.id;       
    });

    relatedIndex[cfId].xInv = investmentIndex(relatedIndex[cfId].x);
    relatedIndex[cfId].yInv = investmentIndex(relatedIndex[cfId].y);
    
    cfTableAttr_observer_init(cfId);
    cfTableAttr_initformulaMap(document.querySelector(`#${cfTableId}`));
}

var investmentIndex = obj => {
    return Object.keys(obj).reduce(function(investment, key) {
        investment[obj[key]] = key;
        return investment;
    }, {});
}


var initializeCfTableTdWithCalculateCell = ($input, cfId) => {
    $input.readOnly = true;
    const $btnShowBar = $input.nextElementSibling;

    $td = $input.closest('td');
    $td.classList.add('cfTable__td__text--calculate-cell');

    $btnShowBar.addEventListener("click", function(){
        showConfigCalculateCellBar(this, cfId)
    });
}

var  initializeCfTableTdValidForFormula = ($input) => {
    $td = $input.closest('td');
    $td.classList.add('cfTable__td__text--valid-calculation');
}

var initializeTextCFCalculateCellByRow = rowObj => {
    const $lastRow = rowObj.tableResults[0].querySelector('tbody > tr:last-child');
    initializeCFTextCalculateCell(rowObj.cfId, $lastRow);
}

var cfTableAttr_getRealIndex = $input => {
    return `${$input.getAttribute('coldb')}:${$input.dataset.rowDb}`;
}

var initFormulaBar = ($input, $formulaBar) => {
    const cfId = getCfId($input);
    const realIndex = cfTableAttr_getRealIndex($input);
    $formulaBar.querySelector('.cf-f-bar__current-index').innerText = indexRealToTemp(realIndex, cfId);

    $inputFormula = $formulaBar.querySelector('.cf-f-bar__input');
    const currentFomula = $input.getAttribute('cf-formule');
    if( !ValueIsBlank(currentFomula)){
        $inputFormula.value =  cfTableAttr_getTempFormula(decodeURIComponent(currentFomula), cfId);
    }
    $inputFormula.focus();

    $input.classList.add('cf-text--current-config');

    includeInCurrentFormula($inputFormula);
}

var indexRealToTemp = (ix, cfId) => {
    ix = ix.replace(/[()\[\]]/g, '');
    var ixArray = ix.split(':');
    var ixX = parseInt(ixArray[0]);
    var ixY = parseInt(ixArray[1]);
    var newIxX = relatedIndex[cfId].xInv[ixX];
    var newIyY = relatedIndex[cfId].yInv[ixY];
    return `[${newIxX}${newIyY}]`;
}

// ix always is only one letter
var indexTempToReal = (ix, cfId) => {
    var ixX = ix.charAt(1);
    var ixY = parseInt(ix.substring(2));
    var newIxX = relatedIndex[cfId].x[ixX];
    var newIyY = relatedIndex[cfId].y[ixY];
    if( !newIxX || !newIyY ){
        return false;
    }
    return `[${newIxX}:${newIyY}]`;
}

var clearIndludeElementsInCurrentFx = $globalWrapper =>{
    [...$globalWrapper.querySelectorAll('.cf-attr-table__include-in-current-fx')].forEach( $inp =>{
        $inp.classList.remove('cf-attr-table__include-in-current-fx');
    } );
}

var clearCurrentCellFx = $globalWrapper =>{
    [...$globalWrapper.querySelectorAll('.cf_attr-table__current-cell')].forEach( $td =>{
        $td.classList.remove('cf_attr-table__current-cell');
    } );

    [...$globalWrapper.querySelectorAll('.cf-text--current-config')].forEach( $td =>{
        $td.classList.remove('cf-text--current-config');
    } );

	[...$globalWrapper.querySelectorAll('.cf-text--current-config-copy')].forEach( $td =>{
        $td.classList.remove('cf-text--current-config-copy');
    } );
}

var includeInCurrentFormula = ($input) => {
    const cfId = getCfId($input);
    const ixFound = cfTableAttr_getIxTextFormula($input.value, cfId);

    $globalWrapper = $input.closest('.cf-attr-table');
    clearIndludeElementsInCurrentFx($globalWrapper);
    
    ixFound.forEach( (ix) => {
        // Extraer los valores del índice
        const [ixCol, ixRow] = ix.substring(1, ix.length - 1).split(':');

        $component = $globalWrapper.querySelector(`input[data-valid-calculation="1"][coldb="${ixCol}"][data-row-db="${ixRow}"]`);
        if($component){
            $component.classList.add('cf-attr-table__include-in-current-fx');
        }
        else{
            console.warn(`Indice invalido: ${ix}`);
        }
    });
}

var showConfigCalculateCellBar = function($elem, cfId){
    const $cfWrapper = $elem.closest('.cf-attr-table');
    if( $cfWrapper.classList.contains('cf-attr-table--formula-bar-mode') ){
        return;
    }

    const $myTd = $elem.closest('td');
    const $myInput = $elem.parentNode.querySelector('input[type="text"]');

    $formulaBar = showFormulaBar($cfWrapper, cfId);
    initFormulaBar($myInput, $formulaBar);
    
    $cfWrapper.classList.add('cf-attr-table--formula-bar-mode');
    
    const $bodyTable = $elem.closest('tbody');
    [...$bodyTable.querySelectorAll(`tr:not(.totalizer) > td`)].forEach( $td => {
        if( $td == $myTd 
            || $td.classList.contains('cf-attr-table__i-temp-row') ){
            if( $td == $myTd ){
                $myTd.classList.add('cf_attr-table__current-cell')
            }
            
            return;
        }

        toggleBlockCell($td, true);
    });
}

var hideFormulaBar = $cfWrapper => {
    const $formulaBar = $cfWrapper.querySelector('.cf-attr-table__formula-bar');
    if( !$formulaBar ){
        return;  
    }

    $formulaBar.classList.add('hide-sve');
    $formulaBar.querySelector('.cf-f-bar__input').value = "";
}

var hideConfigCalculateCellBar = $table => {
    $cfWrapper = $table.closest('.cf-attr-table');
    $cfWrapper.classList.remove('cf-attr-table--formula-bar-mode');

    hideFormulaBar($cfWrapper);
    clearIndludeElementsInCurrentFx($cfWrapper);
    clearCurrentCellFx($cfWrapper);

    [...$table.querySelectorAll(`td`)].forEach( $td => {
        toggleBlockCell($td, false);
    });
}

var removeSelectedText = (text, start, end) => {
    return text.substring(0, start) + text.substring(end);
}

var insertTextAtCursorPosition = (textToInsert, inputField) => {
    let currentStartPos = inputField.selectionStart;
    let currentEndPos = inputField.selectionEnd;

    // Si hay un rango seleccionado, eliminarlo antes de insertar el nuevo texto
    let text = inputField.value;
    if (currentStartPos !== currentEndPos) {
        text = removeSelectedText(text, currentStartPos, currentEndPos);
        currentEndPos = currentStartPos
    }

    let insertionPosition = findInsertionPosition(text, currentStartPos, currentEndPos);

    // Insertar el texto en la posición adecuada
    inputField.value = text.substring(0, insertionPosition) + textToInsert + text.substring(insertionPosition);

    // Mover el cursor a la posición correcta después de la inserción
    const newPosition = insertionPosition + textToInsert.length;
    inputField.selectionStart = inputField.selectionEnd = newPosition;

    return insertionPosition;
}

var findInsertionPosition = (text, startPos, endPos) => {
	let searchStart = startPos;
	// forward
	const fClose = text.indexOf(']', searchStart);
	if( fClose == -1 ){
		return endPos;
	}
	const fStart = text.indexOf('[', searchStart);
	if( fStart != -1 && fStart < fClose ){
		return endPos;
	}

	// backward
	const bStart = text.lastIndexOf('[', searchStart-1);
	if( bStart == -1 ){
		return endPos;
	}
	const bClose = text.lastIndexOf(']', searchStart-1);
	if( bStart < bClose ){
		return endPos;
	}

	return fClose+1;
}

var cfTableAttr_addIx = function() {
    const $input = this.parentNode.querySelector('.cf-text > input');
   
    if( !$input || !$input.dataset.validCalculation || $input.dataset.validCalculation != 1){
        return;
    }
    if( $input.closest('tr').classList.contains('deleted')){
        return;
    }
    const $globalWrapper = this.closest('.cf-attr-table');
    const $formuleBarInput = $globalWrapper.querySelector('.cf-f-bar__input');

    const cfId = getCfId($input);
    const realIndex = cfTableAttr_getRealIndex($input);
    const tempIndex = indexRealToTemp(realIndex, cfId);
    insertTextAtCursorPosition(tempIndex, $formuleBarInput);
    includeInCurrentFormula($formuleBarInput);
    $formuleBarInput.focus();
};

var toggleBlockCell = ($td, block) => {
    if( block ){
        $td.classList.add("cfTable__td__text--blocking-cell");

        const $blockingLayer = document.createElement('div');
        $blockingLayer.classList.add("cf-text__block-cell");
        $blockingLayer.addEventListener('click', cfTableAttr_addIx);
        if ($td.classList.contains("cfTable__td__text--calculate-cell") && !$td.querySelector('.cf-text').classList.contains("cf-text--read-only")) {
        $blockingLayer.addEventListener('contextmenu', function(event) {
		    cfTableAtrr_contextMenuOpcions(event);
		});
		}
      
        $td.appendChild($blockingLayer);
        return;
    }
    
    $td.classList.remove("cfTable__td__text--blocking-cell");
    let $currentCell = $td.querySelector('.cf-text__block-cell')
    if( $currentCell ){
        $currentCell.remove();
    }
}

var getHtmlFomulaBar = () => {
    let selectTemplate = getCloneTemplateHtml('cf-attr-table__select-template');

    let html = `
        <div class="cf-f-bar__current-cell"> 
            ${jsonAttrTableCF.labels.formulaBarCurrentCell} <span class="cf-f-bar__current-index"></span> 
        </div>
        <div class="cf-f-bar__helper-fn">

            <div class="cf-f-bar__helper-label label-sve">${jsonAttrTableCF.labels.formulaBarOperators} </div>
            <div class="cf-f-bar__helper-operators">
                <div class="cf-f-bar__helper-operator" title="${jsonAttrTableCF.labels.operators.addition}">+</div>
                <div class="cf-f-bar__helper-operator" title="${jsonAttrTableCF.labels.operators.subtraction}">-</div>
                <div class="cf-f-bar__helper-operator" title="${jsonAttrTableCF.labels.operators.multiplication}">*</div>
                <div class="cf-f-bar__helper-operator" title="${jsonAttrTableCF.labels.operators.division}">/</div>
                <div class="cf-f-bar__helper-operator" title="${jsonAttrTableCF.labels.operators.leftParenthesis}">(</div>
                <div class="cf-f-bar__helper-operator" title="${jsonAttrTableCF.labels.operators.rightParenthesis}">)</div>
            </div>
            <div class="cf-f-bar__helper-label label-sve">${jsonAttrTableCF.labels.formulaBarFx} </div>
            <div class="cf-f-bar__helper-fx">
                ${selectTemplate.querySelector('select').outerHTML}
            </div>
            <input type="button" class="cf-f-bar__helper-operator" value=" > ">

        </div>
        <div class="cf-f-bar__text">
            <input type="text" class="cf-f-bar__input"/>
        </div>
        <div class="cf-f-bar_btns">
            <button class="button-sve button-sve--size-small cf-f-bar_btn-ok" type="button"> 
                ${jsonAttrTableCF.labels.formulaBtnOk}  
            </button>
            <button class="button-sve button-sve--red button-sve--size-small cf-f-bar_btn-cancel" type="button"> 
                ${jsonAttrTableCF.labels.formulaBtnCancel}  
            </button>
        </div>
    `;
    return html;
}

var showFormulaBar = ($cf, cfId) => {
    const $formulaBar = $cf.querySelector('.cf-attr-table__formula-bar');
    if( $formulaBar ){
        $formulaBar.classList.remove('hide-sve');
        return $formulaBar;
    }

    // Create formula bar
    const $initformulaBar = document.createElement("div");
    $initformulaBar.classList.add('cf-attr-table__formula-bar','cf-f-bar');
    $initformulaBar.innerHTML = getHtmlFomulaBar();
    
    let $parentFormulaBar = $cf.querySelector('.xsection_content .box_global_dataGrid').parentNode;
    $parentFormulaBar.prepend($initformulaBar);

    $fBarBtnCancel = $initformulaBar.querySelector('.cf-f-bar_btn-cancel');
    $fBarBtnCancel.addEventListener('click', ()=>{
        hideConfigCalculateCellBar( $cf, false)
    });

    $inputConfigFormula = $cf.querySelector('.cf-f-bar__input');

    $fBarBtnOk = $initformulaBar.querySelector('.cf-f-bar_btn-ok');
    $fBarBtnOk.addEventListener('click', ()=>{
        cfTableAttr_textFormuleFN($inputConfigFormula, $cf);
    });

    $inputConfigFormula.addEventListener('input', function(){
        includeInCurrentFormula(this);
    } ); 

    $operators = [...$initformulaBar.querySelectorAll(".cf-f-bar__helper-operators .cf-f-bar__helper-operator")];
    $operators.forEach($op => {
        $op.addEventListener('click', function() {
            insertTextAtCursorPosition(this.innerText.trim(), $inputConfigFormula);
            $inputConfigFormula.focus();
        });
    });

    $initformulaBar.querySelector(`input[type="button"].cf-f-bar__helper-operator`).addEventListener("click", function(){
        $select = this.parentNode.querySelector('.cf-f-bar__helper-fx select');
        const newPosition = insertTextAtCursorPosition($select.value.trim(), $inputConfigFormula);

        //Seleccionar rango
        const rangeIni = $inputConfigFormula.value.indexOf('(', newPosition);
        const rangeEnd = $inputConfigFormula.value.indexOf(')', rangeIni);
        
        // Si se encuentra un paréntesis de apertura y cierre
        if (rangeIni !== -1 && rangeEnd !== -1 && rangeEnd > rangeIni) {
            // Seleccionar el texto entre paréntesis
            $inputConfigFormula.setSelectionRange(rangeIni + 1, rangeEnd);
        }

        $inputConfigFormula.focus();
        
    })

    return $initformulaBar;
}

var cfTableAttr_validateFormulaWords = $input => {
    const input = $input.value;
    // Regular expression to validate the format
    const regex = /^(sum|suma|promedio|average|\[[a-zA-Z]\d+\])+$/;

    // Array to store invalid words
    const invalidWordsArray = [];

    // Find invalid words in the input
    const words = input.match(/\[.*?\]|(\b\w+\b)/g) || [];

    words.forEach(word => {
        if (isNaN(word) && !regex.test(word)) {
            invalidWordsArray.push(word);
        }
    });

    return invalidWordsArray;
}

var cfTableAttr_formuleBarMsgValidation = (subtitle, elements, formula) => {
    return `-- ${formula} <br>
        <div style="padding-top:10px"><b>${subtitle}</b></div>
        <ul>
            <li>${elements.join("</li><li>")}</li>
        </ul>
    `;
}

var cfTableAttr_validateFormule = $input => {
    const invalidIndex = cfTableAttr_validateIndex($input);
    if( invalidIndex.length > 0 ){
        showUploadError(local_msgInformation, 
            cfTableAttr_formuleBarMsgValidation(jsonAttrTableCF.msg.invalidCells, invalidIndex, $input.value),
            ()=> $input.focus()
        );
        return false;
    }

    const invalidWords = cfTableAttr_validateFormulaWords($input);
    if( invalidWords.length > 0 ){
        showUploadError(local_msgInformation,
            cfTableAttr_formuleBarMsgValidation(jsonAttrTableCF.msg.invalidComponents, invalidWords, $input.value),
            ()=> $input.focus() 
        );
        return false;
    }

    return true;
} 

var cfTableAttr_textFormuleFN = function($input, $cf){
    if( ValueIsBlank($input.value) ){
        const cfId = getCfId($input);
                
        //---------------------------------------------------------------
        let newFormule = "";
        const $currentInputConfigs = [...$cf.querySelectorAll('.cf-text--current-config')];
        $currentInputConfigs.forEach($currentInputConfig => {
            const oldFormule = $currentInputConfig.getAttribute('cf-formule');
            newFormule = "";
            $currentInputConfig.setAttribute('cf-formule', "");
            cfTableAttr_delformulaMap(oldFormule, $input);
            $currentInputConfig.value = "";
            $currentInputConfig.dispatchEvent(new Event('blur'));
        });
        //---------------------------------------------------------------
        hideConfigCalculateCellBar( $cf, false);
        return;
    }

    if( ! cfTableAttr_validateFormule($input)){
        return;
    }

	showMessageWaiting();
	let formule = escape( cfTableAttr_convertIx( cfTableAttr_cleanFormule($input), $input ));
	$jq.ajax({
		type: "POST",
		url:  jsonAttrTableCF.urls.urlTextFormule,
		data: {"formula" : formule, "isComponentsAP" : "1", "clazz": $jq("#clazz").val()},
		success: function(msg){
			hideMessageWaiting();
			var msgJson = SVEJson.toJson(msg);
			if( msgJson["status"] === "200" ){
                const cfId = getCfId($input);
                
                //---------------------------------------------------------------
                let newFormule = "";
                const $currentInputConfigs = [...$cf.querySelectorAll('.cf-text--current-config')];
                $currentInputConfigs.forEach($currentInputConfig => {
                    const oldFormule = $currentInputConfig.getAttribute('cf-formule');
                    newFormule = encodeURIComponent( cfTableAttr_getRealFormula( $input.value, cfId ));
                    $currentInputConfig.setAttribute('cf-formule', newFormule);
                    cfTableAttr_initformulaMap($currentInputConfig.parentNode);
                    cfTableAttr_delformulaMap(oldFormule, $input);
                });
                //---------------------------------------------------------------

                cfTableAttr_sendFormulaToServer(newFormule, $currentInputConfigs[0]);
                hideConfigCalculateCellBar( $cf, false);
			} else{
                const regex = /^(.*?)<br>/;
                const msg = msgJson["msg"].replace(regex, `${$input.value} <br>`);
				showUploadError(local_msgInformation, msg, ()=> $input.focus());
			}
		}
	});
};

var getCfId = $element => {
    return $element.closest('.cssRowContainerCF').id.replace("row_", "");
}

var cfTableAttr_validateIndex = ($input) => {
    const cfId = getCfId($input);
    const $globalWrapper = $input.closest('.cf-attr-table');

    const $currentCells = [...$globalWrapper.querySelectorAll('.cf-text--current-config')];

    const ixCurrentCells = $currentCells.map($currentCell => {
            return `[${$currentCell.getAttribute('coldb')}:${$currentCell.dataset.rowDb}]`;
        });

    const invalidIndex = $input.value.replace(/\[[A-Z]\d+\]/g, function (match) {
        const ixReal = indexTempToReal(match , cfId);
        if( ixCurrentCells.includes(ixReal) || ixReal == false){
            return match;
        }

        // Extraer los valores del índice
        const [ixCol, ixRow] = ixReal.substring(1, ixReal.length - 1).split(':');
        $component = $globalWrapper.querySelector(`input[data-valid-calculation="1"][coldb="${ixCol}"][data-row-db="${ixRow}"]`);
        if(!$component){
            return match;
        }

        return ixReal;
    });

    // Expresión regular para encontrar todos los índices dentro de corchetes
    const patron = /\[(?!\d+:\d+\])[^[\]]+\]/g;

    // Usar el método match para encontrar todos los índices en la cadena
    const ixFound = invalidIndex.match(patron) || [];
    return ixFound;
}

var cfTableAttr_convertIx = (text, $input) => {
    const cfId = getCfId($input);
    let realFormula = text.replace(/\[[A-Z]\d+\]/g, function(match) {
        return indexTempToReal(match, cfId);
    });

    // Expresión regular para encontrar todos los índices dentro de corchetes
    const patron = /\[\d+:\d+\]/g;

    // Usar el método match para encontrar todos los índices en la cadena
    const ixFound = realFormula.match(patron) || [];

    $globalWrapper = $input.closest('.cf-attr-table');
    
    ixFound.forEach( (ix) => {
        // Extraer los valores del índice
        const [ixCol, ixRow] = ix.substring(1, ix.length - 1).split(':');

        $component = $globalWrapper.querySelector(`input[data-valid-calculation="1"][coldb="${ixCol}"][data-row-db="${ixRow}"]`);
        if($component){
            realFormula = realFormula.replace(ix, " 1 ");
        }
        else{
            console.warn(`Indice invalido: ${ix}`);
        }
    });

    return realFormula;
}

var cfTableAttr_cleanFormule = ($input) =>{
	let value = $input.value;
	value = OBJLIB_RemoveEnter(value);
	value = value.replace(/[;]/,";\r");
	return value;
}

var cfTableAttr_getIxTextFormula = (text, cfId) => {
    const realFormula = text.replace(/\[[A-Z]\d+\]/g, function (match) {
        return indexTempToReal(match , cfId);
    });

    // Expresión regular para encontrar todos los índices dentro de corchetes
    const patron = /\[\d+:\d+\]/g;

    // Usar el método match para encontrar todos los índices en la cadena
    const ixFound = realFormula.match(patron) || [];
    return ixFound;
}

var cfTableAttr_getRealFormula = (text, cfId) => {
    const realFormula = text.replace(/\[[A-Z]\d+\]/g, function (match) {
        return indexTempToReal(match , cfId);
    });

    return realFormula;
}

var cfTableAttr_getTempFormula = (text, cfId) => {
    const tempFormula = text.replace(/\[\d+:\d+\]/g, function (match) {
        return indexRealToTemp(match , cfId);
    });

    return tempFormula;
}

var cfTableAttr_getSendFormula =  (realFormula, $input) => {
    realFormula = decodeURIComponent(realFormula);
    // Expresión regular para encontrar todos los índices dentro de corchetes
    const patron = /\[\d+:\d+\]/g;

    // Usar el método match para encontrar todos los índices en la cadena
    const ixFound = realFormula.match(patron) || [];

    $globalWrapper = $input.closest('.cf-attr-table');
    
    ixFound.forEach( (ix) => {
        // Extraer los valores del índice
        const [ixCol, ixRow] = ix.substring(1, ix.length - 1).split(':');

        $component = $globalWrapper.querySelector(`input[data-valid-calculation="1"][coldb="${ixCol}"][data-row-db="${ixRow}"]`);
        
        let value = !$component || ValueIsBlank($component.getAttribute("withoutformat")) 
            ? null
            : $component.getAttribute("withoutformat");
        realFormula = realFormula.replace(ix, value);
        if(!$component){
            console.warn(`Indice invalido: ${ix}`);
        }
    });

    return realFormula;
}
//-------------------------------------------------------------

var cfTableAttr_observer_init = (cfId)=>{
    $globalSelector = document.querySelector(`#${TABLESECTIONCFTABLE}${cfId}`);

    cfTableAttr_observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
            //console.info(mutation.target.getAttribute('withoutformat'));
            cfTableAttr_updateDependencies(mutation.target);
        });
    });

    const config = { subtree: true, attributes: true, attributeFilter: ['withoutformat']}
    cfTableAttr_observer.observe( $globalSelector, config);

    cfTableAttr_formulaMap[cfId] = {}; 
    cfTableAttr_formulaTriggers[cfId] = {};
    cfTableAttr_formuleCache[cfId] = [];
}

var cfTableAttr_delformulaMap = (formula, $input) => {
    const $globalWrapper = $input.closest('.cf-attr-table');
    const $inputs =  $globalWrapper.querySelectorAll(`input[type="text"][cf-formule="${formula}"]`);
    if( $inputs.length > 0){
        return;
    }

    const cfId = getCfId($input);

    if (cfTableAttr_formulaMap[cfId][formula]) {
        delete cfTableAttr_formulaMap[cfId][formula];
        delete cfTableAttr_formulaTriggers[cfId][formula];
    }
}

var cfTableAttr_initformulaMap = $wrapper => {
    const cfId = getCfId($wrapper);
    const inputs = $wrapper.querySelectorAll('input[type="text"][data-valid-calculation="1"]');
    // Construir el mapa de dependencias de fórmulas y el mapeo de fórmulas a inputs
    inputs.forEach(input => {
        if ( !ValueIsBlank(input.getAttribute('cf-formule'))) {
    
            const formula = input.getAttribute('cf-formule');
            if (!cfTableAttr_formulaMap[cfId][formula]) {
                cfTableAttr_formulaMap[cfId][formula] = [];
                cfTableAttr_formulaTriggers[cfId][formula] = new Set();
            }
            cfTableAttr_formulaMap[cfId][formula].push(input);

            const matches = decodeURIComponent(formula).match(/\[\d+:\d+\]/g) || [];
            matches.forEach(ix => {
                cfTableAttr_formulaTriggers[cfId][formula].add(ix);
            });
        }
    }); 
}

 var cfTableAttr_updateDependencies = (input) => {
    const cfId = getCfId(input);
    const ix = `[${cfTableAttr_getRealIndex(input)}]`;
    Object.keys(cfTableAttr_formulaTriggers[cfId]).forEach(formula => {
        if (cfTableAttr_formulaTriggers[cfId][formula].has(ix)) {
            cfTableAttr_sendFormulaToServer(formula, input);
        }
    });
}

// Función para enviar una fórmula específica al servidor y actualizar los inputs asociados
var cfTableAttr_sendFormulaToServer = (formula, $input) => {
    const cfId = getCfId($input);
    let formulaSend = cfTableAttr_getSendFormula(formula, $input);

    cfTableAttr_obtenerDatos(formulaSend, cfId)
        .then(data => {
            const newValue = ValueIsBlank(data.compute) ? "" : data.compute;
            cfTableAttr_formulaMap[cfId][formula].forEach(input => {
                input.value = newValue;
                input.dispatchEvent(new Event('blur'));
            });
        })
        .catch(error => console.error('Error:', error));
}

var cfTableAttr_obtenerDatos =  async (formule, cfId) => {
    try {
        let qsObject = {};
        qsObject['formuleToCalculate'] = formule;
        //activeCalculateProcess("btnSave");
        const cacheFormula = cfTableAttr_formuleCache[cfId].find( x => x.f == formule) ;
        if( cacheFormula ){
           return {compute: cacheFormula.r};
        }

        const data = await $jq.getJSON(jsonAttrTableCF.urls.urlCalculateFormule, qsObject);
        cfTableAttr_formuleCache[cfId].push({f:formule, r:data.compute});

        return data;
    } catch (error) {
        console.error('Error al obtener los datos:', error);
    }
}

function cfTableAttr_validateRow($element) {
    const cfId = getCfId($element);
    const triggers = cfTableAttr_formulaTriggers[cfId];
	if( !triggers ){
		return false;
	}
    const allTriggers = new Set([...Object.values(triggers)].flatMap(set => [...set]));

    const keysInRow = [...$element.closest('tr').querySelectorAll('input[type="text"][data-valid-calculation="1"]')].map(i => `[${i.getAttribute('coldb')}:${i.dataset.rowDb}]`);
    const stopDelete = keysInRow.some(key => allTriggers.has(key));
    if( stopDelete ){
        messageShow(jsonAttrTableCF.msg.invalidDeletedRow);
    }
    return stopDelete;
}


var eventListenerassignCalculatedCell;
var eventListenerUnassignCalculatedCell;

var cfTableAtrr_contextMenuOpcions = (e)=>{
	e.preventDefault();
	let $contextMenu = document.querySelector('#contextMenuCalculatedCell');
    $contextMenu.style.visibility = 'visible';
    $contextMenu.style.left = `${e.pageX-60}px`;
    $contextMenu.style.top = `${e.pageY-15}px`;
    validationOpcions__contextMenu(e);
}


var validationOpcions__contextMenu = (e)=>{
	
	let $assignCalculatedCellBtn = document.getElementById('assignCalculatedCell');
	let $UnassignCalculatedCellBtn = document.getElementById('UnassignCalculatedCell');
	
	if (!e.target.classList.contains('cf-text-assigned-now')) {
	    $assignCalculatedCellBtn.style.display = 'block';
	    $UnassignCalculatedCellBtn.style.display = 'none'; 
		eventListenerassignCalculatedCell = function() {
			contextMenuOpcionAssing(e);
		};
		$assignCalculatedCellBtn.addEventListener('click', eventListenerassignCalculatedCell)
	}
	else{
	    $assignCalculatedCellBtn.style.display = 'none'; 
	    $UnassignCalculatedCellBtn.style.display = 'block';
	    eventListenerUnassignCalculatedCell = function() {
		 	contextMenuOpcionUnassign(e);
		};
		$UnassignCalculatedCellBtn.addEventListener('click', eventListenerUnassignCalculatedCell)
	}
	hideContextMenuCalculatedCell($assignCalculatedCellBtn,$UnassignCalculatedCellBtn);
	e.stopPropagation();
}

var hideContextMenuCalculatedCell = ($copyFormuleBtn,$UnassignCalculatedCellBtn) => {
	let $contextMenuCalculatedCell = document.getElementById('contextMenuCalculatedCell');
	$contextMenuCalculatedCell.addEventListener("mouseout", function(event) {
		const targetElementEvent = event.relatedTarget;
		if (!targetElementEvent || targetElementEvent.id !== 'cmOrder' && targetElementEvent.id !== 'assignCalculatedCell' && targetElementEvent.id !== 'UnassignCalculatedCell' && targetElementEvent.id !="contextMenuCalculatedCell") {
			document.querySelector('#contextMenuCalculatedCell').style.visibility = 'hidden';
			$copyFormuleBtn.removeEventListener('click', eventListenerassignCalculatedCell);
			$UnassignCalculatedCellBtn.removeEventListener('click', eventListenerUnassignCalculatedCell);
		}
	});		
};

var contextMenuOpcionAssing = (e) => {
	
    let $containerCalculatedCell = e.target.parentElement; 
    $containerCalculatedCell.classList.add('cf_attr-table__current-cell');
	const $inputCalculatedCell = $containerCalculatedCell.querySelector('.cf-text input');
	$inputCalculatedCell.classList.add('cf-text--current-config', 'cf-text--current-config-copy');
	e.target.classList.add('cf-text-assigned-now');

	document.querySelector('#contextMenuCalculatedCell').style.visibility = 'hidden';
};

var contextMenuOpcionUnassign = (e) => {
	
    let $containerCalculatedCell = e.target.parentElement; 
    $containerCalculatedCell.classList.remove('cf_attr-table__current-cell');
	const $inputCalculatedCell = $containerCalculatedCell.querySelector('.cf-text input');
	$inputCalculatedCell.classList.remove('cf-text--current-config', 'cf-text--current-config-copy');
	e.target.classList.remove('cf-text-assigned-now');

	document.querySelector('#contextMenuCalculatedCell').style.visibility = 'hidden';
};
