Source: tpfs.js

const host = `http://${getFromLocalStorage('smartcube')}`||"";
const ws = `ws://${getFromLocalStorage('smartcube')}`||"";

const setting = new Object();
setting.text_color = '#ffffff';

const cw = document.getElementsByClassName("ska")[0].offsetWidth;
saveToLocalStorage('sk','');

const httpsend = new XMLHttpRequest();

const host_2 = location.origin;
const ws_2 = host_2.replace('http','ws');
const apps= "sitzknochenabstand";
const Matte = document.getElementById('Matte')
const SmartCube = document.getElementById('SmartCube')

const TitleTpfs = getFromLocalStorage('tpfs');

if(TitleTpfs!='sattel'){
  let title = document.getElementsByClassName('uk-card-title')[0];
  title.setAttribute('key',`tpfs_head_${TitleTpfs}`);
  translatejs();
}


//const baseURL ="https://www.dasberatungsportal.de/"
const baseURL ="https://www.the-perfect-fit-system.com/"

if(getFromLocalStorage('client_mail')){
  let inp = document.getElementById('TPFSMail');
  inp.value = getFromLocalStorage('client_mail');
  let c = document.getElementById('BackToClient');
  c.classList.remove('hidden');
  let cc = document.getElementById('btn_BackToClient');
  cc.setAttribute('onclick',`LinkTo('client.tcls')`)
  cc.innerHTML =`${getFromLocalStorage('client_name')}`
  var interv = setInterval( () => {
    loadTPFS();
    clearInterval(interv);
  }, 1500);
}


var Stationsinfo = new Object();
var Fragen = new Object();
var user_data = new Object();

let userdata = new Object();
let isrequired = new Object();
let produkte = new Object();
let produktdetails = new Object();

var dom = document.getElementById('chart-container');
var myChartX = echarts.init(dom, 'dark', {
  renderer: 'canvas',
  useDirtyRect: false
});

document.getElementById('product_count').value = getFromLocalStorage('product_count') || 3
//var sessionID;
//if(getFromLocalStorage('session').split(" ")[0]=='restart'){
//  sessionID = getFromLocalStorage('session').split(" ")[1]
//} else{
//  sessionID = getFromLocalStorage('new_session')
//}

const canvas_ska = new fabric.Canvas("ska_img");
createCanvas(canvas_ska);
setImage(canvas_ska,`/production/images/ska.png`);

let cm;
let inch;
var vsum = 0;

translatejs();
//_name();

/**
 * The function show3d toggles the 'hidden' class on the element with the id 'chart-container'.
 */
function show3d() {
    document.getElementById('chart-container').classList.toggle('hidden')
}

try {
    var ws_ska = new WebSocket(`${ws}/apps/${apps}`, ["soap", "wamp"]);
    } catch (error) {
      console.error('ws_ska',error);
    }

try {
    var sitzknochen = new WebSocket(`${ws_2}/apps/${apps}`, ["soap", "wamp"]);
} catch (error) {
      console.error('ws_ska',error);
}

var sitzknochen_activ = false
var initChart = false;
var analysis = new Object();
analysis.sitbones = new Object();
analysis.anamnesis = new Object();
analysis.x1 = new Object();
//{"sitbones":{"sitzknochenabstand":"","asymmetrie":""}}


sitzknochen.onopen = (e)=>{
//  sitzknochen.send(`session restart ${sessionID}`)
//  sitzknochen.send(`analysis ["sitbones","anamnesis","x1"]`)
sendMatte('switch create_norm on')
}

sitzknochen.onmessage = (e) => {

  let data = JSON.parse(e.data)
  console.log('sitzknochen',data); //TODO DEF

  if(data.change === 'unplugged'){
    sitzknochen_activ=false;
    Matte.classList.remove('nor')
    Matte.classList.add('err')
    OpenModDialog({type:'warn',title:["sk_hocker"],msg:["sk_hocker_dis"],master_btn:['button_ok','location.reload()']})
    
  }
  if(data.change === "plugged"){
    sitzknochen_activ=true;
      Matte.classList.remove('nor')
      Matte.classList.add('err')
    //OpenModDialog({type:'neutral',title:["sk_hocker"],msg:["sk_hocker_con"],master_btn:['button_ok']})
  }
  
  if(data.wsevent ==="ttystate"){
        if(data.state === "inactive"){
            sitzknochen_activ=false;
              Matte.classList.remove('nor')
              Matte.classList.add('err')
        } else if(data.state === "active"){
            Matte.classList.add('nor')
            Matte.classList.remove('err')
            sitzknochen_activ=true;
            //OpenModDialog({type:'neutral',title:["sk_hocker"],msg:["sk_hocker_con"],master_btn:['button_ok']})
        }
  }
  if(data.wsevent === 'hockerbild'&&sitzknochen_activ===true){
    setImage(canvas_ska, `${host_2}/hockerbild?rnd=${(new Date).getTime()}`);
  }

  if(data.wsevent === 'sitzknochenabstand'&&sitzknochen_activ===true){
    vsum = 1
        //store(data.sitzknochenabstand,"sitzknochenabstand","sitbones")
        document.getElementById('val').value = data.sitzknochenabstand
        var inter = setInterval( () => {
          draw_cross(canvas_ska,[(data.schwerpunkt1_x+0.5) * (cw / 28) , (cw/1.75) - (data.schwerpunkt1_y+0.5) * (cw / 28)]);
          draw_cross(canvas_ska,[(data.schwerpunkt2_x+0.5)* (cw / 28) , (cw/1.75) - (data.schwerpunkt2_y+0.5) * (cw / 28)]);
          console.log(lineDistance([data.schwerpunkt1_x * (cw / 28) , (cw/1.75) - data.schwerpunkt1_y * (cw / 28)],[data.schwerpunkt2_x * (cw / 28) , (cw/1.75) - data.schwerpunkt2_y * (cw / 28)])/(cw/28));
          cm_inch(data.sitzknochenabstand)
          document.getElementById('val').value = `${cm}` //<br> ${inch}`
          document.getElementById('sitzknochenabstand').value = data.sitzknochenabstand
            userdata['sitzknochenabstand'] = data.sitzknochenabstand
            //SendSkImg();
        //  changeEventHandler(document.getElementById('sitzknochenabstand').value)
            progress(1)
          saveToLocalStorage('sk',`${cm}`)
          vsum++
          clearInterval(inter);
        }, 1000);
  }
  


  if(data.wsevent === "hockernorm"&&sitzknochen_activ===true){
    let d = document.getElementById('button3d')
    d.classList.remove('hidden')

    var numRows = data.n_rows;
    var numCols = data.n_cols;
    var max = 0
        // Create an empty 2D array with the specified dimensions
    var data2D = new Array(numRows);
    for (var i = 0; i < numRows; i++) {
        data2D[i] = new Array(numCols);
      }
      // Fill the 2D array with your data
      for (var i = 0; i < data.values.length; i++) {
        var row = Math.floor(i / numCols);
        var col = i % numCols;
          if(Number(data.values[i]) > max){
            max = data.values[i]
          }
          data2D[row][col] = [col, row, data.values[i]];
      }
    //  console.log(data2D);
    var option;

    if(initChart===false){

    //var ColorRange = ['#000000','#0000ff','#00ff00','#ffff00','#ff0000']
    var ColorRange = ['#9f9f9f', '#666666', '#333333', '#0000ff', '#0066cc', '#00cccc', '#00ffcc', '#00ff66', '#00ff00', '#66ff00', '#ccff00', '#ffff00', '#ffcc00', '#ff9933', '#ff6600', '#ff3300', '#ff0000', '#cc0000', '#990000', '#660000'];

    
    $.getScript(
      '/production/js/chart/simplex-noise.js'
    ).done(function () {
      myChartX.setOption(
        (option = {
            backgroundColor: '#00000000',
        visualMap: {
            show: false, //true
            calculable: true,
            min: 0,
            max: 255,
            inRange: {
                color: ColorRange,
            },
          },
          viewControl: {
            distance:300,
            alpha:45,
            beta:0
          },
          xAxis3D: {
            type: 'category', //category
            show:false,
            max:numCols,
            min:0
          },
          yAxis3D: {
            type: 'category', //category
            show:true,
            max: numRows,
            min:0
          },
          zAxis3D: {
            type: 'value',
            max: 255*2,
            min: 0
          },
          toolbox: { 
            show: true, 
            orient: 'vertical', 
            itemsize: 16,
            iconStyle: {
                borderColor: '#5cbc40',
                borderWidth: 1 ,
            },
            left:10,
            feature: {
                //magicType: {
                //    type: ["line3D", "bar3D","scatter3D","surface"]
                //}, 
                //dataZoom: { show: true }, 
                //dataView: { show: true },
                saveAsImage: { 
                    show: true, 
                    name: `Sitzknochen`, 
                    type: 'png',
                    pixelRatio:2 
                }
            } 
          },
          grid3D: {
            boxWidth: numCols*10,
            boxDepth: numRows*10,
            axisLine: {
              //show:false,
              lineStyle: { color: '#99999900' }
            },
            splitLine: {
              show:false
            },
            splitArea: {
              show:false
            }, 
            axisPointer: {
              lineStyle: { color: '#99999900' }
            },
            viewControl: {
                distance:300,
                alpha:45,
                beta:0 //270
            },
            light: {
                main: {
                  color: '#fff' ,
                  intensity: 1 ,
                  shadow: false ,
                  shadowQuality: 'medium' ,
                  alpha: 30 ,
                  beta: 30 ,
                },
                ambient: {                 
                  color: '#fff' ,
                  intensity: 0.2 ,
                },
            },            
          },
          series: [
            {
              type: 'bar3D',//line3D, bar3D,scatter3D,surface
              data: data2D.flat(),
              shading: 'lambert',//color,lambert,realistic
              label: {
                fontSize: 16,
                borderWidth: 1,
                show:false
              },
              
              emphasis: {
                label: {
                  fontSize: 20,
                  color: '#00000000',
                  show: true,
                },
                itemStyle: {
                    color: '#000000'
                }
              }
            }
          ]
        })
      );
    });
    
    if (option && typeof option === 'object') {
      myChartX.setOption(option);
    }
    initChart = true;
    window.addEventListener('resize', myChartX.resize);
} else {
    myChartX.setOption((option = {
        series: [
            {
                data:data2D.flat(),
            }
          ]
    }))
}

}
//  if (data.wsevent === "analyses"){
//    let d = data.analyses;
//    for (const key in d) {
//            const element = d[key];
//            for (const key2 in element) {
//                    const element2 = element[key2];
//                    let ele = document.getElementById(key2)
//                    ele.value = element2
//                    // console.log(key,key2,element2);
//                    analysis[key][key2]=element2
//            }
//    }
//  }
}

ws_ska.onerror = () =>{
  SmartCube.classList.add('err')
  SmartCube.classList.remove('nor')
}

ws_ska.onmessage = (e) =>{
    //translatejs()
    let data = JSON.parse(e.data)

    console.log('ws_ska',data);

    //if(data.v_sum == 0.0){
    //  vsum = 0;
    //}
    
    if(data.wsevent == 'ttystate' && data.state == 'active'){
    //OpenModDialog({type:'neutral',title:["con_connected"],msg:['con_connected_msg'],master_btn:['button_ok']})
    SmartCube.classList.add('nor');
    SmartCube.classList.remove('err');
    //document.getElementById('con_ws').innerHTML = aLangKeys[lang][]
        //OpenMod('connected')
        //document.getElementById('glow').classList.add('glow')
    }
    if(data.wsevent === 'hockerbild' && sitzknochen_activ===false){
      let img = `${host}/hockerbild?rnd=${(new Date).getTime()}`;
      console.log(`%c draw image ${img}`,'background: #222;color:#5cbc40;padding:1em;');
      setImage(canvas_ska, img);

    }
    if(data.wsevent === 'sitzknochenabstand' && sitzknochen_activ===false){
      //if(vsum == 0){
        //vsum = 1
        //store(data.sitzknochenabstand,"sitzknochenabstand","sitbones")
        document.getElementById('val').value = data.sitzknochenabstand
        var inter = setInterval( () => {
          draw_cross(canvas_ska,[(data.schwerpunkt1_x+0.5) * (cw / 28) , (cw/1.75) - (data.schwerpunkt1_y+0.5) * (cw / 28)]);
          draw_cross(canvas_ska,[(data.schwerpunkt2_x+0.5)* (cw / 28) , (cw/1.75) - (data.schwerpunkt2_y+0.5) * (cw / 28)]);
          console.log(lineDistance([data.schwerpunkt1_x * (cw / 28) , (cw/1.75) - data.schwerpunkt1_y * (cw / 28)],[data.schwerpunkt2_x * (cw / 28) , (cw/1.75) - data.schwerpunkt2_y * (cw / 28)])/(cw/28));
          cm_inch(data.sitzknochenabstand)
          document.getElementById('val').value = `${cm}` //<br> ${inch}`
          document.getElementById('sitzknochenabstand').value = data.sitzknochenabstand
            userdata['sitzknochenabstand'] = data.sitzknochenabstand
            //SendSkImg();
        //  changeEventHandler(document.getElementById('sitzknochenabstand').value)
            progress(1)
          saveToLocalStorage('sk',`${cm}`)
          vsum++
          clearInterval(inter);
        }, 1000);

      //}
    }
}

/**
 * The lineDistance function calculates the distance between two points in a two-dimensional space.
 * @param p1 - The parameter `p1` represents the coordinates of the first point on the line. It is an
 * array with two elements, where the first element is the x-coordinate and the second element is the
 * y-coordinate.
 * @param p2 - The parameter `p2` represents the coordinates of the second point in the line. It is an
 * array with two elements, where the first element is the x-coordinate and the second element is the
 * y-coordinate.
 * @returns the distance between two points, `p1` and `p2`.
 */
function lineDistance(p1, p2) {
  return Math.hypot(p2[0] - p1[0], p2[1] - p1[1])
}

/**
 * The function `draw_cross` draws a cross shape on a canvas element at a specified position.
 * @param ele - The parameter "ele" represents the HTML canvas element on which the cross will be
 * drawn. It is of type "CanvasRenderingContext2D".
 * @param pos - The `pos` parameter represents the position of the cross on the canvas. It is an array
 * containing the x and y coordinates of the cross's center point.
 */
function draw_cross(ele,pos) {
  console.log(pos);
  let c = ele.getContext('2d')
  c.lineWidth = 5;
  c.strokeStyle = setting.text_color;

  c.beginPath()
  c.moveTo(pos[0],pos[1])
  c.lineTo(pos[0],pos[1]+10)
  c.stroke();

  c.beginPath()
  c.moveTo(pos[0],pos[1])
  c.lineTo(pos[0],pos[1]-10)
  c.stroke();

  c.beginPath()
  c.moveTo(pos[0],pos[1])
  c.lineTo(pos[0]+10,pos[1])
  c.stroke();
  
  c.beginPath()
  c.moveTo(pos[0],pos[1])
  c.lineTo(pos[0]-10,pos[1])
  c.stroke();
}

/**
 * The function converts a measurement in centimeters to inches.
 * @param params - The parameter "params" in the given function represents the length in centimeters
 * that needs to be converted to inches.
 */
function cm_inch(params) {
    cm = `${params.toFixed(2)} cm`;
    inch = params / 2.54;
    inch = `${inch.toFixed(2)} inch`;
    ska = true;
}

/**
 * It takes a canvas, an image, and a boolean, and then it adds the image to the canvas, and then it
 * draws the text and the grid if the boolean is true
 * @param params - the canvas object
 * @param img - the image to be loaded
 * @param grid - boolean, whether to draw the grid or not
 */
function setImage(params, img) {
    fabric.Image.fromURL(img, function (oImg) {
      oImg.scaleToWidth(cw);
      oImg.hasControls = false;
      oImg.set("selectable", false);
      params.add(oImg);
    });
  }
  
  /**
   * Create a canvas object with the given parameters, and set the height and width to the values of the
   * global variables cw and ch.
   * @param params - The object that contains the parameters for the canvas.
   */
  function createCanvas(params) {
    params.selection = false; // disable group selection
    params.hasControls = false;
    params.lockMovement= false;
    params.setHeight(cw/1.75);
    params.setWidth(cw);
  }

  /**
 * The function opens a UIkit modal with the ID specified in the parameter.
 * @param params - The parameter `params` is a string that represents the ID of the modal that needs to
 * be opened. The function `OpenMod` takes this parameter and uses it to show the corresponding modal
 * using the UIkit framework.
 */
function OpenMod(params) {
    UIkit.modal(`#modal-${params}`).show();
  }

/**
 * It gets the name from local storage and puts it in the right places
 */
function _name() {
  let name = getFromLocalStorage('client_name').split(' ')
  //document.getElementById('auswertung_h3').innerHTML += ' '+name[1]+" "+name[0]
  document.getElementById('name_vorname').innerHTML = name[0]+', '+name[1] 
}

/**
 * The function "store" stores a value in an object called "analysis" and sends the updated analysis
 * object as a JSON string.
 * @param val - The `val` parameter represents the value that you want to store in the analysis object.
 * @param key - The `key` parameter is a string that represents the key or property name in the
 * `analysis` object where the `val` value will be stored.
 * @param a - The parameter "a" is used as a key to access a specific object within the "analysis"
 * object. It is used to store the value "val" with the key "key" in the specified object.
 */
function store(val,key,a) {
  analysis[a][key] = val

  for (const key in analysis) {
          const element = analysis[key];
          sattelanalyse.send(`analysis {"${key}":${JSON.stringify(element)}}`)
          if(log)
          console.log('store',`analysis {"${key}":${JSON.stringify(element)}}`);
  }
  
}


var user = document.getElementById('stnr').value;
sessionStorage.setItem('user',user)

//AXIOS_Login();

AXIOS_Ping();
AXIOS_Load_Fragen();

/**
 * The function AXIOS_Login sends a GET request to a login endpoint with user credentials and saves the
 * response data to session storage.
 */
function AXIOS_Login() {
    axios.get(`${ baseURL }vmkservice/login`, {
            params: {
                portaluser: user,
                passwd: 'velopasswort'
            },
            method: 'GET',
            withCredentials: true
        })
        .then(response => {
            if (response.data === '12 forbidden' || response.data === '13 login failed') {
                //TODO ERROR HANDLING
                OpenModDialog({type:'error',title:["error"],msg:`${response.data}`,master_btn:['button_ok']})
                return;
            }
            AXIOS_Ping();
            AXIOS_Load_Fragen();
            //AXIOS_logout();
        })
        .catch(err => {
            OpenModDialog({type:'error',title:["error"],msg:`${err}`,master_btn:['button_ok']})
        });
}

/**
 * The AXIOS_Ping function sends a GET request to a specific endpoint and handles the response data
 * accordingly, including error handling.
 */
function AXIOS_Ping() {
    axios.get(`/vlbservice/ping`, {
            method: 'GET',
            withCredentials: true
        })
        .then(response => {
          console.log('response',response);
            if (response.data === '12 forbidden') {
                //TODO ERROR HANDLING
                OpenModDialog({type:'error',title:["error"],msg:`${response.data}`,master_btn:['button_ok']})
                
                console.log('error');
                return;
            }
            console.log('ping', response.data);
            Stationsinfo = response.data;
            time_to_use(Stationsinfo.optionen['sattel.beratung'])
            translatejs();
        })
        .catch(err => {
            //TODO ERROR HANDLING
            OpenModDialog({type:'error',title:["error"],msg:`${err}`,master_btn:['button_ok']})
        });
}

/**
 * The AXIOS_logout function sends a GET request to the specified URL to log out the user, and handles
 * the response and error accordingly.
 */
function AXIOS_logout() {
    axios.get(`${ baseURL }vmkservice/logout`)
        .then(response => {
            console.log('logout',response);
        })
        .catch(err => {
            OpenModDialog({type:'error',title:["error"],msg:`${err}`,master_btn:['button_ok']})
        });
}

/**
 * The function AXIOS_Load_Fragen makes an HTTP GET request using the Axios library to load questions
 * in a specific language and product type, and then processes the response data.
 */
function AXIOS_Load_Fragen() {
    if(getFromLocalStorage('tpfs')!='sattel'){
      let im = document.getElementById('glow')
      let inp= document.getElementById('val_ska')
      let conSmart = document.getElementById('conn_smartcube')
      conSmart.classList.add('hidden')
      inp.classList.add('hidden')
      im.classList.add('hidden')
    }
    const lang = 'deu'
    const temp = '&produktart='+getFromLocalStorage('tpfs') //sattel, ergodreieck
    axios.get(`/vlbservice/start?lang=${ lang }${ temp }`, {
            method: 'GET',
            withCredentials: true
        })
        .then(response => {
            if (response.data === '12 forbidden') {
                //TODO ERROR HANDLING
                OpenModDialog({type:'error',title:["error"],msg:`${response.data}`,master_btn:['button_ok']})
                return;
            }
            Fragen = response.data

            for (const key in Fragen) {
                const element = Fragen[key];
                //TODO Create Block
                create_block(element)                   
            }
        })
        .catch(err => {
            //TODO ERROR HANDLING
            OpenModDialog({type:'error',title:["error"],msg:`${err}`,master_btn:['button_ok']})
        });
}
/**
 * The function AXIOS_AutoLogin sends a GET request to a login endpoint using the Axios library in
 * JavaScript, with the provided user and password parameters, and handles the response accordingly.
 */
function AXIOS_AutoLogin() {
        axios.get(`${ baseURL }vmkservice/login`, {
                params: {
                    portaluser: user,
                    passwd: 'velopasswort'
                },
                method: 'GET',
                withCredentials: true
            })
            .then(response => {
                if (response.data === '12 forbidden') {
                    sessionStorage.setItem('error', JSON.stringify(response));
                    OpenModDialog({type:'error',title:["error"],msg:`${response.data}`,master_btn:['button_ok']})
                    return;
                }
                    AXIOS_Load_Produckte();
            })
            .catch(err => {
                console.error(err);
                sessionStorage.setItem('error', err);
                OpenModDialog({type:'error',title:["error"],msg:`${err}`,master_btn:['button_ok']})
            });
}

/**
 * The function AXIOS_Load_Produckte makes a GET request to a specific URL with some parameters,
 * retrieves the response data, stores it in sessionStorage, and then redirects to another page.
 */
var prod_anzahl = getFromLocalStorage('tpfs') === "ergodreieck" ? 1 : getFromLocalStorage('product_count') || 3

/**
 * The function `AXIOS_Load_Produckte` sends a GET request using Axios to load product data based on
 * various parameters and user input.
 */
function AXIOS_Load_Produckte() {
    let inp = document.getElementById('TPFSMail')
    let options = {};
    let params = {};
    if(!inp.value ==""){
      params['#email'] = inp.value
    }
    params.lang = 'deu';
    params['#artcode'] = getFromLocalStorage('tpfs'); //sattel,ergodreieck
    params['#max_produkte'] = prod_anzahl; // prod_anzahl
    if(document.getElementById('sitzknochenabstand'))
    params['sitzknochenabstand'] = document.getElementById('sitzknochenabstand').value
    //console.log(userdata);
    for (const key in userdata) {
        params[key] = userdata[key.replaceAll(' ','_')];
        //console.log(params);
    }
    options.params = params;
    options.method = 'GET';
    options.withCredentials = 'true';

    console.log('load prod:', options);
    axios.get(`/vlbservice/bewerten`, options)
        .then(response => {
            if (response.data === '12 forbidden') {
                return;
            }
            produkte = response.data;
            console.log(produkte);
            sessionStorage.setItem('produkte',JSON.stringify(produkte))
            console.log(getFromLocalStorage('tpfs'));
            //if(getFromLocalStorage('tpfs')==='sattel'){
              LinkTo('products.tcls')
            //}
            //AXIOS_logout();
        })
        .catch(err => {
            console.error(err);
        });
}


/**
 * The function calculates the number of days between a given date and the current date, and displays a
 * warning message if the number of days is less than 300.
 * @param params - The parameter `params` is expected to be a date string in a format that can be
 * parsed by the `Date` constructor. It represents the date for which the time to use is being
 * calculated.
 */
function time_to_use(params) {
    let date = new Date(params)
    let today = new Date();
    let days = `${Math.floor((date - today) / (1000*60*60*24))} ${aLangKeys[lang]['tpfs_days']||""}`
    //document.getElementById('time_beratung').innerHTML = `${aLangKeys[lang]['tpfs_use']} ${days}`
    if(Math.floor((date - today) / (1000*60*60*24)) < 182){
        if(Math.floor((date - today) / (1000*60*60*24)) < 0){
          
          OpenModDialog({type:'warn',title:["tpfs_use_end"],msg:`${days}`,master_btn:['button_ok','LinkTo("dashboard.tcls")']});
        } else {
          OpenModDialog({type:'warn',title:["tpfs_use"],msg:`${days}`,master_btn:['button_ok']});
        }
    }
    
}

/**
 * The function `create_block` creates a block of HTML elements based on a JSON object and appends it
 * to the 'questions' element in the document.
 * @param jsn - The `jsn` parameter is an object that contains the following properties:
 */
function create_block(jsn) {
    let ins = document.getElementById('questions');
    //console.log(jsn);
    let content;
    let headT;
    let block   = newElement({element:"div",id:`${jsn.code}`,cls:['uk-card'],attr:[['style','border:none;']]},ins)
    let head    = newElement({element:"div",cls:['uk-card-header'],attr:[['style','padding:0']]},block)
    let headD   = newElement({element:"div",cls:["uk-grid-small", "uk-flex-middle"],attr:[['uk-grid','']]},head)
    if(jsn.code == 'sattelextras'||jsn.code == 'probleme'){
        headT   = newElement({element:"h6",cls:['isLink'],attr:[['key',`tpfs_${jsn.code}`],['onclick',`toggleHidden('${jsn.code}')`]]},headD).innerHTML = `${aLangKeys[lang]['tpfs_${jsn.code}']}`
        newElement({element:'span',cls:['isLink','mdi','mdi-24px','mdi-chevron-down'],attr:[['style','color:var(--primary)'],['onclick',`toggleHidden('${jsn.code}')`]]},headD) //mdi-chevron-right
        newElement({element:'hr'},block)
        content = newElement({element:"div",id:`hidden_${jsn.code}`,cls:['uk-card-body','uk-hidden']},block)

    } else {
        headT   = newElement({element:"h6",attr:[['key',`tpfs_${jsn.code}`]]},headD).innerHTML = `${aLangKeys[lang]['tpfs_${jsn.code}']}`
        newElement({element:'hr'},block)
        content = newElement({element:"div",cls:['uk-card-body']},block)

    }
    var koerpermaße = ['ua','oa','ok','rhh','os','usf','skv']
    for (const key in jsn.fragen) {
        const element = jsn.fragen[key];
        //console.log(element.code);
        let bl  = newElement({element:"div",cls:['block']},content)
        if(koerpermaße.includes(element.code)){
          let img = newElement({element:'img',attr:[['style','width:25em'],['src',`/production/images/svg/koerperdaten-${element.code}.png`]]},bl)
        } else {
          let h   = newElement({element:"div",cls:["block_head"],attr:[['key',`tpfs_${element.code}`]]},bl).innerHTML = `<!--${element.code}-->`
        }
        let con = newElement({element:"div",cls:["block_content"]},bl)
        if(element.vorgaben == null){
            let inp = newElement({element:"input",cls:["uk-input"],id:`${element.code}`,attr:[['oninput','changeEventHandler(event)'],['name',element.code],['required','true'],['data-progress',"1"]]},con)
        } else {
            genSelect(element.vorgaben,con,element.code,element.pflicht)
        }
    }
    translatejs();
}



/**
 * The function `genSelect` generates a select element with options based on a given JSON object.
 * @param jsn - The `jsn` parameter is a JSON object that contains the data for generating the select
 * options.
 * @param to - The "to" parameter is the element where the generated select options will be appended
 * to. It can be a DOM element or a selector string.
 * @param name - The `name` parameter is a string that represents the name of the select element.
 * @param pflicht - The parameter "pflicht" is a boolean value that determines whether the select
 * options are required or not. If "pflicht" is true, the select options will be marked as required and
 * will have a data-progress attribute set to 1. If "pflicht" is false, the select options
 */
function genSelect(jsn,to,name,pflicht) {
    //console.log(jsn);
    for (const key in jsn) {
            const e = jsn[key];

            let card    = newElement({element:"div"},to)
            //let head    = newElement({element:"div",cls:["flex-column-center"]},card)

            if(pflicht===true){
                //card.setAttribute('data-pflicht','true')
                //card.setAttribute('data-progres','1')
                //card.setAttribute('required','')
                isrequired[name] = true
                progressMax(Object.keys(isrequired).length+1)
                if(e.code != null){
                    //console.log(name);
                    
                    //if(name === 'fahrposition'&& getFromLocalStorage('tpfs')!='sattel'){
                    //  name = 'sitzposition'
                    //}
                    newElement({element:'input',id:`${name}_${e.code}`,cls:['question_input'],attr:[['type','radio'],['name',`${name}`],['value',`${e.code}`],['required','true'],['data-progress','1'],['onclick','changeEventHandler(event)']]},card)
                    newElement({element:'label',cls:['question_label'],attr:[['key-title',`title_${name}${e.code}`],['for',`${name}_${e.code}`],['style',`background-image: url(/production/images/svg/${name}_${e.code.replace(' ','_')}.png)`]]},card)
                    //newElement({element:"input",attr:[["style",`background: url(/production/images/svg/${name}_${e.code}.png)`],['data-value',`${e.code}`],['data-name',`${name}`],['type','radio']]},head)
                }
            } else {
                //card.setAttribute('data-pflicht','false')
                if(e.code != null){
                    newElement({element:'input',id:`${name}_${e.code}`,cls:['question_input'],attr:[['type','radio'],['name',`${name}`],['value',`${e.code}`],['onclick','changeEventHandler(event)']]},card)
                    if(e.code == 'ja'||e.code == 'nein'||e.code == 'egal'){
                        newElement({element:'label',cls:['question_label'],attr:[['key-title',`title_${name}${e.code}`],['for',`${name}_${e.code}`],['style',`background-image: url(/production/images/svg/${e.code}.png)`]]},card)
                    } else {
                        newElement({element:'label',cls:['question_label'],attr:[['key-title',`title_${name}${e.code}`],['for',`${name}_${e.code}`],['style',`background-image: url(/production/images/svg/${name}_${e.code}.png)`]]},card)
                    }
                    //newElement({element:"input",attr:[["style",`background: url(/production/images/svg/${name}_${e.code}.png)`],['data-value',`${e.code}`],['data-name',`${name}`],['type','radio']]},head)
                }
            }

           
    }
}


/**
 * The function `changeEventHandler` handles changes in form input fields, logs the changed value and
 * parent element, checks if the field is required and if it already exists in `userdata`, updates the
 * progress, validates the input, and updates the `userdata` object accordingly.
 * @param event - The `event` parameter is an object that represents the event that triggered the
 * change event handler. It contains information about the event, such as the target element that
 * triggered the event, the type of event, and any additional data associated with the event. In this
 * case, the event is a change event
 */
function changeEventHandler(event) {
    console.log(`${event.target.name} -> ${event.target.value.replace('_',' ')}`);
    console.log(event.target.parentElement);
    if (event.target.required)
        if (event.target.name in userdata) {
            console.log('schon vorhanden');
        } else
          progress(event.currentTarget.getAttribute('data-progress'));
    let obj = event.target;
    let name = event.target.name;
    let check;
    if (obj.checkValidity()) {
        if (event.target.type === 'checkbox') {
            check = event.target.checked;
            console.log('checkbox',check);
            userdata[name] = check;
        } else {
            check = event.target.value.replace(' ', '_');
            console.log('else',check);
            userdata[name] = check;
        }
        if (obj.classList.item('is-invalid'))
            obj.classList.remove('is-invalid');
        obj.classList.add('is-valid');
    } else {
        if (obj.classList.item('is-valid'))
            obj.classList.remove('is-valid');
        obj.classList.add('is-invalid');
    }
    ergebnisse();
}


/**
 * The function "progress" increases the value of a progress bar by a specified amount.
 * @param {number}add - The `add` parameter is the value that will be added to the current value of the
 * progress bar.
 */
function progress(add) {
    let p = $('#mybar')[0];
    let value = p.getAttribute('value');
    value = parseInt(value) + parseInt(add);
    p.setAttribute('value', value);
}

/**
 * The function `progressMax` sets the maximum value of a progress bar element.
 * @param add - The `add` parameter is the value that you want to set as the maximum value for the
 * progress bar.
 */
function progressMax(add) {
   let p = $('#mybar')[0];
   p.setAttribute('max',add) 
}

/**
 * The function "ergebnisse" checks if all required input fields have been filled out and if so, it
 * displays the "products" element.
 */
function ergebnisse() {
    let name;
    let x = false;
    for (let index = 0; index < $('input').length; index++) {
        const element = $('input')[index];
        if (name !== element.name && element.required) {
            if (element.name in userdata && Object.keys(userdata).length-1 >= Object.keys(isrequired).length) {
                x = true;
            } else {
                //TODO Was wenn nicht
            }
        }
        name = element.name;
    }
    if (x) {
        //console.log('ergebniss');
        document.getElementById('products').classList.remove('uk-hidden')
        //if ($('.velo_ergebniss'))
        //    $('.velo_ergebniss').remove();
        //$('#fragen').append(CreateLinkA(language.Navigation.ergebnis, 'btnAuswertungStart()', ['nav-link', 'height_a', 'btn', 'velo_ergebniss'], ''));
        //$('.velo_ergebniss')[0].setAttribute('style', 'animation: fertig 2s ease 0s 1 normal forwards;background-color: var(--primary);color: var(--primary-color-font);');
    }
}

/**
 * The function creates a modal window with a list of products based on the provided JSON data.
 * @param jsn - The `jsn` parameter is an array of objects that contains information about the
 * products. Each object in the array represents a product and has properties such as name, price,
 * description, etc.
 */
function createModalProducts(jsn) {
    
    //console.log(jsn);//TODO DEF

    let to = document.getElementById('ModalHolder');

    let Modal   = newElement({element:'div',attr:[['uk-modal','']]},to)
    let Dialog  = newElement({element:'div',cls:['uk-modal-dialog']},Modal)
    let close   = newElement({element:'button',cls:['uk-modal-close-default'],attr:[['type','button'],['uk-close','']]},Dialog)
    let Header  = newElement({element:'div',cls:['uk-modal-header']},Dialog)
    let HeaderT = newElement({element:'h2',cls:['uk-modal-title']},Header).innerHTML = `${aLangKeys[lang]['tpfs_products_head']}`
    let Body    = newElement({element:'div',cls:['uk-modal-body']},Dialog)
    let Footer  = newElement({element:'div',cls:['uk-modal-footer','uk-text-right']},Dialog)
    let cancel   = newElement({element:'button',cls:['uk-button','uk-button-default','uk-modal-close'],attr:[['type','button'],['onclick','location.reload()']]},Footer).innerHTML = `${aLangKeys[lang]['button_cancel']}`


    for (const i of jsn) {
        createProduct(i,Body)
        newElement({element:'hr'},Body)
    }
    UIkit.modal(Modal).show();

}

/**
 * The function creates a product card with an image, manufacturer, and type, and hides additional
 * details.
 * @param jsn - The `jsn` parameter is a JSON object that contains information about a product. It
 * likely includes properties such as `vmkid`, `herst`, and `typ`, which are used to populate the
 * elements in the function.
 * @param to - The `to` parameter is the element where the created product will be appended to. It
 * should be a valid DOM element or a selector string that can be used to select the element.
 */
function createProduct(jsn,to) {
    console.log(jsn);
    let Card    = newElement({element:'div',cls:['uk-card','uk-card-default']},to);
    let imgDiv  = newElement({element:'div',cls:['uk-card-media-top']},Card);
    let img     = newElement({element:'img',attr:[['src',`https://produkte.velometrik.de/${jsn.vmkid}.jpg`],['width','1800'],['height','1200']]},imgDiv)
    let CardBody= newElement({element:'div',cls:['uk-card-body']},Card);
    let herst   = newElement({element:'h3',attr:[['onclick',`showProductDetails(${jsn.vmkid})`]]},CardBody).innerHTML = jsn.herst;
    let typ     = newElement({element:'h2',attr:[['onclick',`showProductDetails(${jsn.vmkid})`]]},CardBody).innerHTML = jsn.typ;
    let hiddenD = newElement({element:'div',id:`hidden_${jsn.vmkid}`,cls:['uk-hidden'],attr:[['uk-grid','']]},CardBody)
}

/**
 * The function `showProductDetails` retrieves product information using an AXIOS request and displays
 * it in a table on the webpage.
 * @param num - The `num` parameter is the product number or identifier that is used to fetch the
 * product details from the server.
 */
function showProductDetails(num) {
    AXIOS_Produkt_info(num);
    let to = document.getElementById(`hidden_${num}`)
    let table = newElement({element:'table',cls:['uk-table','uk-table-striped']},to);
    let tbody = newElement({element:'tbody'},table);
    let inter = setInterval(()=> {
        for (const i of produktdetails) {
            let tr = newElement({element:'tr'},tbody)
            for (const key in i) {
                    const element = i[key];
                    newElement({element:'td'},tr).innerHTML = element || ""
           }
        }
        to.classList.remove('uk-hidden')
        clearInterval(inter);
        }, 1000);
}

/**
 * The function `toggleHidden` toggles the visibility of an element with the ID `hidden_` by
 * adding or removing the class `uk-hidden`.
 * @param params - The `params` parameter is a string that represents the ID of the element you want to
 * toggle the visibility of.
 */
function toggleHidden(params) {
    let tog = document.getElementById(`hidden_${params}`)
    console.log(tog,params);
    tog.classList.toggle('uk-hidden')
}

/**
 * The function `SendSkImg()` sends an HTTP GET request to a specified URL with an encoded email
 * parameter.
 */
function SendSkImg() {
  let mail = `ska-${user}@velometrik.de`//document.getElementById("UserMail").value;
  let url = `${host}/storedbld`;
  url += `?email=${encodeURI(mail)}`;
  httpsend.open("GET", url, true);
  httpsend.send();
}

/**
 * The function "sendMatte" sends parameters to the "sitzknochen" object.
 * @param parameters - I'm sorry, but the code snippet you provided doesn't include any information
 * about the parameters that are expected by the `sendMatte` function. Can you please provide more
 * context or code so I can better understand what the function does and what parameters it expects?
 */
function sendMatte(parameters) {
    sitzknochen.send(parameters)
}

function setProductCount(param) {
  saveToLocalStorage('product_count',param)
  location.reload(true)
}

function loadTPFS() {
  let inp = document.getElementById('TPFSMail')

  if(!inp.value ==""){
    ///vlbservice/antworten?email=

    axios.get(`/vlbservice/antworten?email=${inp.value}`, {
      method: 'GET',
      withCredentials: true
  })
  .then(response => {
      if (response.data === '12 forbidden') {
          //TODO ERROR HANDLING
          OpenModDialog({type:'error',title:["error"],msg:`${response.data}`,master_btn:['button_ok']})
          return;
      }
      user_data = response.data

      for (const key in user_data) {
          const element = user_data[key];
          //TODO Create Block
          console.log(`userData: ${key} -> `,element);
          if(key === 'sitzknochenabstand'||key === 'oa'||key === 'ok'||key === 'os'||key === 'ua'||key === 'rhh'||key === 'skv'||key === 'usf'){
            if(document.getElementById(key)){
            document.getElementById(key).value = element
            userdata[key] = element
            document.getElementById('val').value = `${element} cm`
          }
          } else {
            if(document.getElementById(`${key}_${element}`))
            document.getElementById(`${key}_${element}`).click()
          }
      }
  })
  .catch(err => {
      //TODO ERROR HANDLING
      OpenModDialog({type:'error',title:["error"],msg:`${err}`,master_btn:['button_ok']})
  });

  }
}

//config min_rawvalue <value>
//Set lowest accepted raw value from pressure sensor mat. Values lower than that value will be regarded to be noise.
//(This method is from the early days of Velometriks to adapt different sensor charges. It might be replaced in the near future.)
//switch create_jpeg on|off
//Switch the creation of JPEG pressure images on or off rsp.
//switch create_norm on|off
//Switch the creation of normalized pressure value on or off rsp.
//set jpeg colorcontrast 0...7
//Set color contrast.
//Changing the color contrast will affect the other Apps too.
//set jpeg bgcolor #rrggbb
//Set background color.
//rr, gg, bb stands for a hex value for red, green an blue respectively. Changing the background color will affect the other Apps too.
//set jpeg grid no|dotted|solid
//Shows or doesn't show the grid that separates the measuring points
//set jpeg resolution 1...
//Sets the number of pixels generated per measuring point
//Although there is no upper limit, a large number will probabely knock out the Velobox.
//set jpeg frame 0...
//Draws a black frame around the generated image of the width (pixels) specified.
//set jpeg quality 0...100
//Sets quality for JPEG compression.
//version
//Query Version
//The versions of vmkstationd (key vmkstationd) and of the application (key app) will be returned.