
function startLoading(msg = config.useTranslate('saving_imgs_product')) {
   return new Promise(function (resolve) {
      $(document).on('popin:closed:after', "#popin_choose_quality", function() {
         setTimeout(() => {
            resolve();
          }, 200);
       });
      closePopIn("#popin_choose_quality");
       loadStep(msg);
       $("#inside_overlay").show();
   });
}

   function endLoading(msg = config.useTranslate('save_finish'), delayed = 0) {
      if (msg != "")
         loadStep(msg);
      $("#inside_overlay").delay(delayed).hide();
   }

   function loadStep(msg) {
      $("#loadingStep").html(msg);
   }

   function replacePolice() {
      $("#EditObjectPanel .dropdown .dropdownmenu li, .fontswitch li, #popinAddText .font_text li").each(function() {
         $(this).css({"font-family" : $(this).attr('data-value'), "font-size": "16px"});
      });
   }

   function addDesContActionLockable(x, y) {
      $(".des_cont_action.lock").remove(); 
      var btnLeft = x-10;
      var btnTop = y-10;
      var descontactionbtn = '<i class="showed_on_objselect material-icons des_cont_action lock bl" style="top:'+btnTop+'px;left:'+btnLeft+'px;" >lock_open</i>';
      $(".des_containers .canvas-container").append(descontactionbtn);
  }

  
  function addFollowingPanel() {
   $(".des_cont_panel").remove(); 
   var descontactionbtn = `<div class="showed_on_objselect des_cont_panel" style="top:0px;left:0px;" >
                              <div class="dims"><input type="text" name="width" /> x <input type="text" name="height" />${config.unit}</div>
                              <div class="panelloader" id="fountainG">
                                 <div class="fountainG fountainG_1"></div>
                                 <div class="fountainG fountainG_2"></div>
                                 <div class="fountainG fountainG_3"></div>
                              </div>
                              
                           </div>`;
   $("#layers").append(descontactionbtn);
}

   function triggerWhenObjectSelected() {
      if (typeof forceSpSelectValue === "function")
         forceSpSelectValue(".fontswitch", "");

      activeobj = config.design_canvas[config.view_select].getActiveObject();

      if(activeobj){
         $("#EditObjectPanel").show();
         $(".des_switchable").hide();
         $(".des_switchable["+activeobj.des_type+"]").show();

         $("[activeon].active").removeClass("active");
         $("[activeon='"+activeobj.des_type+"']").addClass("active");

         $("[activeon]").each(function (index, element) {
            let activeproperty = $(this).attr("activeon").split(",");
             for (let iap = 0; iap < activeproperty.length; iap++) {
                if (activeproperty[iap] == activeobj.des_type)
                  $(this).addClass("active");
             }
          });

         $(".EmpDetailsLines[uniqidobj='"+activeobj.uniqid+"']").addClass("active");
            config.showEmplacements(true);
            $(".des_cont_action.lock").html((activeobj.des_locked) ? "lock" : "lock_open" );
            $("input#check_overflow").prop("checked", !!activeobj.des_overflow);

         if(window.innerWidth <= 1100) {
            config.trigEditPersoPopin();
         }
      }
      else
       config.showEmplacements(config.zone_is_visible);

      if(!activeobj){
         document.getElementById("valeur_texte").value = "";
         $('input[type="checkbox"]#textcircle').prop( "checked", false );
         $("#EditObjectPanel").hide();
         $(".showed_on_objselect").hide();
         stopDashTipsMe();
      }
      else if(activeobj.des_type == "user_img"){
         var listePersos = config.design_canvas[config.view_select].getObjects();
         $(".onePerso").removeClass("selected");

         for (var i = 0, len = listePersos.length; i < len; i++) {
            if (listePersos[i].selected == true) {
               $(".onePerso[uniqidobj='"+listePersos[i].uniqid+"']").addClass("selected");
            }
         }
         DashTipsMe("photoselected");
      }
      else if(activeobj.des_type == "texte"){
         if (activeobj.hasOwnProperty('styles')) {
            let allFonts = [];

            for (i_line in activeobj.styles) {
               for (i_item in activeobj.styles[i_line]) {
                  let item = activeobj.styles[i_line][i_item];

                  if (item.hasOwnProperty('fontFamily') && allFonts.indexOf(item.fontFamily) === -1) {
                     allFonts.push(item.fontFamily);
                  }
               }
            }

            if (allFonts.length == 1)
               forceSpSelectValue('.editrow select.fontswitch', allFonts.shift());
         }
         
         var listePersos = config.design_canvas[config.view_select].getObjects();

         $(".onePerso").removeClass("selected");
         for (var i = 0, len = listePersos.length; i < len; i++) {
            if (listePersos[i].selected) {
               $(".onePerso[uniqidobj='"+listePersos[i].uniqid+"']").addClass("selected");
            }
         }

         DashTipsMe("texteselected");
      }
   }

   function ajouteTexte(zone = null, styles = {}, manualtext = "") {
      closePopIn('#popinAddText');
      config.addText("valeur_texte", zone, styles, manualtext);
      $("#valeur_texte").val("");
      triggerWhenObjectSelected();
      replacePolice();
   }

   function changevue(vue) {
      if (typeof config.design_canvas[vue] !== 'undefined') {
         config.design_canvas[config.view_select].discardActiveObject();
         triggerWhenObjectSelected();
         config.view_select = vue;
         displayVue();
      }
   }

   /*
   ** GENERATION HTML
   **/
   function toDataURL(src, callback) {
      var xhttp = new XMLHttpRequest();

      xhttp.onload = function() {
         var fileReader = new FileReader();

         fileReader.onloadend = function() {
            callback(fileReader.result);
         }

         fileReader.readAsDataURL(xhttp.response);
      };

      xhttp.responseType = 'blob';
      xhttp.open('GET', src, true);
      xhttp.send();
   }

   function generateChoixPack(defautPack = 0) {
      if (defautPack)
         config.nb_pack = defautPack;
      for(var j=0; j < config.viewSets.length;j++){
         var monopackhtml =  '<div class="pack '+((j == config.nb_pack) ? "active" : "")+'" nb_pack="' + j + '">';

         if (config.lazy_image_loader)
            monopackhtml += `
               <div class="img_loader">
                  <div class="fountainG fountainG_1"></div>
                  <div class="fountainG fountainG_2"></div>
                  <div class="fountainG fountainG_3"></div>
               </div>
               <img data-src="${config.viewSets[j].vues[0].thumb}" lazy>`
            ;
         else
            monopackhtml += '<img src="' + config.viewSets[j].vues[0].thumb + '">';

         if (config.packs_option_showname) {
            monopackhtml += '<span>' + config.viewSets[j].model + '</span>';
         }
         monopackhtml += '</div>';

         $(".choixpack .packs").prepend(monopackhtml);
      }

      $('.pack-nav').on('click', function() {
         if ($('.packs_mask').length)
            var offset = $('.packs_mask').width();
         else
            var offset = $('.choixpack').width();

            var packs = $('.packs');
            var left = parseInt(packs.css('left'));

         if ($(this).hasClass('nav-left')) 
               newleft = left + offset;
         else 
               newleft = left - offset;

         if (newleft > 0)
            newleft = 0;
         if (newleft <  -packs.width() + offset)
            newleft = -packs.width() + offset;

         packs.animate({
            left: newleft
         }, 250);
      });

      $(".pack").on("click",function() {
         startLoading("");
         config.intention = 0;
         var zonestoeditagain = {};
         zonestoeditagain.zones =   $.extend(true, {},  config.zonesparams);
         config.nb_pack = parseInt($(this).attr("nb_pack"));
         config.view_select = 0;
         $(".pack.active").removeClass("active");
         $(".pack[nb_pack='"+config.nb_pack+"']").addClass("active");
         $("#ligne_vues, .product_views").css({"margin-left": 0+"px"}, 500);
         $(".bouton_nav_small.btn_vues[direction='left']").hide();

         var exp = new Array();
         for (var itCanvas = 0; itCanvas < config.design_canvas.length; itCanvas++) {
            var tcvs = new Object();
            tcvs.emplacements = JSON.stringify(config.design_canvas[itCanvas].toJSON(sp_attributes_tab));
            exp.push(tcvs);
         }

         generateChoixVues(); //question
         config.editagain(exp, zonestoeditagain);

      });
      $('img[lazy]').each(function(i, e) {
         var src = $(e).attr('data-src');

         toDataURL(src, function(data) {
			   $(e).prev('.img_loader').remove();
			   $(e).attr('src', data).removeAttr('data-src');
         });
      });
      generateChoixVues();
   }

   function generateChoixVues() {
      var nb_views = config.viewSets[config.nb_pack].vues.length;
      config.generateCanvas();
      loadPhotos(); //question
      loadZones();  //question
      displayVue();

      $("#ligne_vues, .product_views").html("");

      for(var i=0;i < nb_views;i++) { 
         $("#ligne_vues, .product_views").append(`
            <div class="vue box${(!i) ? ' selected' : ''}" iview=${i}>
               <img 
                  class="img_btn_bandeau" 
                  face="${config.viewSets[config.nb_pack].vues[i].reference}" 
                  src="${config.viewSets[config.nb_pack].vues[i].thumb}"
               >
               <span>${config.viewSets[config.nb_pack].vues[i].nom}</span>
            </div>
         `);
      }

      if($("#ligne_vues .vue, .product_views .vue").length > 1 )
         $(".bouton_nav_small.btn_vues[direction='right']").show();
   }

   function generateZonesVues() {
      var nb_views = config.viewSets[config.nb_pack].vues.length;
      $("#txt_nav_zones_vues").hide();
      $( ".vertical-menu" ).html("<a href='' class='hidden_view active' id='vue_globale'>Vue globale</a>");

      for(var i=0;i < nb_views;i++) {
         if(config.viewSets[config.nb_pack].vues[i].zonesVues) {
            $("#txt_nav_zones_vues").show();
            var nb_zones_vues = config.viewSets[config.nb_pack].vues[i].zonesVues.length;

            for(var j=0; j< nb_zones_vues;j++) {
               $( ".vertical-menu" ).append('<a href="" class="hidden_view" style="display :none;"  id="'+config.viewSets[config.nb_pack].vues[i].zonesVues[j].nom+'" zonevue='+j+'>'+config.viewSets[config.nb_pack].vues[i].zonesVues[j].nom+'</a>');
            }
         }
      }

      if (!j)
         $( ".vertical-menu" ).html("");

      $('.vertical-menu a').on('click', function(event) {
         if($(this).hasClass('active'))
            $('.hidden_view').fadeIn(1000);
         else {
            $(".vertical-menu a.active").removeClass('active');
            $(this).addClass('active');
            $('.hidden_view:not(.active)').hide();
         }
      });

      $(".vertical-menu a").on("click",function() {
         if($(this).attr('id')== 'vue_globale')
            config.setBestViewport();
         //else if($(this).attr('class') == ('.hidden_view:not(.active)')){ // Agir ici pour pas zoomer au second clic
         else {
            config.num_zonevue = $(this).attr('zonevue');
            config.ZoomVuesZone(
               config.viewSets[config.nb_pack].vues[config.view_select].zonesVues[config.num_zonevue].x,
               config.viewSets[config.nb_pack].vues[config.view_select].zonesVues[config.num_zonevue].y,
               config.viewSets[config.nb_pack].vues[config.view_select].zonesVues[config.num_zonevue].scale
            );
         }
      });
   }

   function loadPhotos(iCanvas = 0) {
      loadStep(config.useTranslate('loading_image') + ' ' + config.viewSets[config.nb_pack].vues[iCanvas].nom);
      fabric.Image.fromURL(config.viewSets[config.nb_pack].vues[iCanvas].url, function(oImg) {
         config.design_canvas[iCanvas].add(oImg.set({
            selectable: false,
            hoverCursor: 'default',
            des_type:'photostock',
            left: 0,
            top: 0,
            // globalCompositeOperation: 'source-in'
         }));

         var ext = config.viewSets[config.nb_pack].vues[iCanvas].url.split('.').pop();
         // console.log(config.viewSets[config.nb_pack].vues[iCanvas]);
         fabric.Image.fromURL(config.viewSets[config.nb_pack].vues[iCanvas].overlay, function(oImgOverlay) {
            config.design_canvas[iCanvas].add(oImgOverlay.set({
               selectable: false,
               hoverCursor: 'default',
               des_type:'overlay',
               selection: false,
               hasControls  : false,
               hasBorders : false,
               perPixelTargetFind : true,
               left: 0,
               top: 0,
               // globalCompositeOperation: 'source-in'
            }));

            oImgOverlay.sendToBack();
            oImg.sendToBack();
            config.des_render();
         });
        
         
        

         oImg.sendToBack();
         config.setBestViewport();
         if (iCanvas < config.design_canvas.length - 1) {
            loadPhotos(iCanvas + 1);
         }
         else {
            endLoading("", 1000);
            config.setBestViewport();
            if (config.c2f.generic === true && config.c2f.generic_initialised === undefined) {
               $("#VotreProduitContain .btncolorizeJS").first().click();
               config.c2f.generic_initialised = true;
            }
            calcBackground();
         }
      }, {crossOrigin: 'anonymous'});

      //	config.design_canvas[iCanvas].setOverlayImage(config.viewSets[config.nb_pack].vues[iCanvas].url.replace(".png", "_overlay.png"), 	config.design_canvas[iCanvas].renderAll.bind(	config.design_canvas[iCanvas]));
   }

   // Calcul des couleurs des emplacements demo 3
   function calcBackground() {
      for (var key in config.zonesparams) {
         if (config.zonesparams.hasOwnProperty(key)) (function(key) {
            
            for (var ic = 0; ic < config.design_canvas.length; ic++) {
               var listePersos = config.design_canvas[ic].getObjects();
               for (var i = 0, len = listePersos.length; i < len; i++) {
                  if ( listePersos[i].des_type != "zone" || listePersos[i].label != key)
                  continue;
                  
                  
                  let dataurl = config.design_canvas[ic].toDataURL({
                        left: listePersos[i].getBoundingRect().left,
                        top: listePersos[i].getBoundingRect().top,
                        width: listePersos[i].getBoundingRect().width,
                        height: listePersos[i].getBoundingRect().height,
                        multiplier: 2
                     })
                     let img = new Image;
                     img.src = dataurl;
                     img.addEventListener('load', function() {
                        let ct = new ColorThief();
                        let rgb = ct.getColor(img);
                        
                        config.zonesparams[key].background = rgbToHex(rgb[0],rgb[1],rgb[2]);
                      });
               }
            }

         }) (key);
      }
   }
   function loadZones() {
      for (var ic = 0; ic < config.design_canvas.length; ic++) {
         for (var i = 0; i < config.viewSets[config.nb_pack].vues[ic].zonesVues.length; i++) {
            var oemp = config.viewSets[config.nb_pack].vues[ic].zonesVues[i];

            if (oemp.parameters) {
               var empobj = new fabric.Emplacement();
               let description = "";

               if (parseInt(oemp.is_linked_to_technos)) {
                  let technotab = [];
                  for (techno_i in oemp.technos) {
                     let filteredName = oemp.technos[techno_i].name.replace(/\s[0-9] couleurs?/gm, "");
                     if (technotab.indexOf(filteredName) == -1)
                        technotab.push(filteredName);
                  }
                  // console.log(technotab);
                  
                  description = technotab.join(" / ");
               }

               config.zonesparams[oemp.name] = {
                  isgrouped: false,
                  technos: oemp.technos, // A revoir car pas de technos dans les master emp ...
                  vue: ic
               };
               empobj.set({
                  selectable: false,
                  hoverCursor: 'default',
                  objectCaching: false,
                  label: oemp.name,
                  displayName: oemp.display_name,
                  description: description,
                  fill: config.options['style'].zone.fill,
                  stroke: config.options['style'].zone.stroke,
                  strokeDashArray: config.options['style'].zone.strokeDashArray,
                  strokeWidth: config.options['style'].zone.strokeWidth,
                  ctx: config.options['style'].zone.ctx,
                  left: parseFloat(oemp.parameters.x),
                  top: parseFloat(oemp.parameters.y),
                  angle: parseFloat(oemp.parameters.rot),
                  width: parseFloat(oemp.parameters.w),
                  height: parseFloat(oemp.parameters.h),
                  rh: parseFloat(oemp.parameters.rh),
                  rw: parseFloat(oemp.parameters.rw),
                  scaleX: parseFloat(oemp.parameters.scaleX),
                  scaleY: parseFloat(oemp.parameters.scaleY),
                  originX: "center",
                  originY: "center",
               });

               config.design_canvas[ic].add(empobj);
            }
         }
      }

      for (var iz = 0; iz < config.viewSets[config.nb_pack].zoningvue.length; iz++) {
         masteremp = config.viewSets[config.nb_pack].zoningvue[iz];
            var empobj = new fabric.Emplacement();

                  config.zonesparams[masteremp.name].left       = parseFloat(masteremp.parameters.x);
                  config.zonesparams[masteremp.name].top        = parseFloat(masteremp.parameters.y);
                  config.zonesparams[masteremp.name].angle      = parseFloat(masteremp.parameters.rot);
                  config.zonesparams[masteremp.name].width      = parseFloat(masteremp.parameters.w);
                  config.zonesparams[masteremp.name].height     = parseFloat(masteremp.parameters.h);
                  config.zonesparams[masteremp.name].rh         = parseFloat(masteremp.parameters.rh);
                  config.zonesparams[masteremp.name].rw         = parseFloat(masteremp.parameters.rw);
                  config.zonesparams[masteremp.name].scaleX     = parseFloat(masteremp.parameters.scaleX);
                  config.zonesparams[masteremp.name].scaleY     = parseFloat(masteremp.parameters.scaleY);
                  config.zonesparams[masteremp.name].displayName     = masteremp.displayName;

            empobj.set({
               selectable: false,
               hoverCursor: 'default',
               objectCaching: false,
               label: masteremp.name,
               fill:   'rgba(73, 126, 171, 0)', // 'rgba(73, 126, 171, 0.2)',
               stroke: 'rgba(0, 76, 140, 0)', // 'rgba(0, 76, 140, 0.8)',
               strokeDashArray: [0, 0], // [5, 5],
               strokeWidth: 0, // 1,
               left: parseFloat(masteremp.parameters.x),
               top: parseFloat(masteremp.parameters.y),
               angle: parseFloat(masteremp.parameters.rot),
               width: parseFloat(masteremp.parameters.w),
               height: parseFloat(masteremp.parameters.h),
               rh: parseFloat(masteremp.parameters.rh),
               rw: parseFloat(masteremp.parameters.rw),
               disableLabel: true,
               scaleX: parseFloat(masteremp.parameters.scaleX),
               scaleY: parseFloat(masteremp.parameters.scaleY),
               originX: "center",
               originY: "center",
            });

            config.design_canvas_zoning.add(empobj);
      }
   }
   function rgbToHex(r, g, b) {
      return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
    }
   function displayVue() {
      config.setBestViewport();
      $(".des_containers").hide();
      $("#des_container_" + config.view_select).show();
      $(".vue.selected").removeClass("selected");
      $(".vue[iview="+config.view_select+"]").addClass("selected");

      nbre_vues_left = $("#ligne_vues, .product_views tr > td").length;

      if(config.view_select == 0)
         $(".bouton_nav_small.btn_vues[direction='left']").hide();
      if(config.view_select == nbre_vues_left -1 )
         $(".bouton_nav_small.btn_vues[direction='right']").hide();

      // $("h2.titre_content_panneau.vospersos").html("Vos personnalisations");
      config.listepersos();
      config.updateZoneList()
   }


   function uploadFile(files) {
      if (files.length > 0) {
         var file = files[0],
         image_preview = $('#img_preview');

         $(".wrap_img_thumbnail").show();
         $("#img_preview").show().next(".progress-wrp").show();

         image_preview.find('.thumbnail').removeClass('hidden');
         image_preview.find('img').attr('src', window.URL.createObjectURL(file));
         image_preview.find('h4').html(file.name);
         image_preview.find('.caption p:first').html(file.size +' bytes');

         $("#popup_img").animate({
            'height': '320px', //this.img_upload.clientHeight, tentative pour donner la taille de l'image
            'max-height' : '320px'
         }, 400);

         $(".browse").hide();
         $('.drop-field').hide();
         $('.logos-container').hide();
         $('.pagination').hide();
         $('.search-bloc').hide();

         $(".wrap_img_thumbnail").on('click', function() {
            $('#parcourir_img').click();
         });

         var form = $('#form_upload_image');
         var data = new FormData();
         data.append('data', file);
         data.append('useImagick', !!CONFIG_CLIENT['useImagick']);
         data.append('usr', $("input[type='hidden'][name='hd_usr']").val());

         if (config.upload_key !== undefined)
            data.append('key', config.upload_key);

         $("#popup_img_txt").html("Transfert en cours");

         $.ajax({
            url: form.attr('action'),
            type: form.attr('method'),
            contentType: false, // obligatoire pour de l'upload
            processData: false, // obligatoire pour de l'upload
            dataType: 'json', // selon le retour attendu
            data: data,
            xhr: function(){
               //upload Progress
               var xhr = $.ajaxSettings.xhr();

               if (xhr.upload) {
                  xhr.upload.addEventListener('progress', function(event) {
                     var percent = 0;
                     var position = event.loaded || event.position;
                     var total = event.total;
                     if (event.lengthComputable) {
                        percent = Math.ceil(position / total * 100);
                     }
                     //update progressbar
                     $(".progress-wrp .progress-bar").css("width", + percent + "%");
                     $(".progress-wrp .status").text(percent + "%");
                  }, true);
               }

               return xhr;
            },
            mimeType:"multipart/form-data"
         })
         .done(function (response) {
            if(response.success) {
               closeUploadPopin("Transfert réussi")
               // close popin
               closePopIn('#popinAddImg');

               config.upload_key = response.key;
               if (config.options['image'].edit.enabled)
                  trigEditImg(response.trimmed_path, response.original_name, {
                     des_trimmed_path: response.trimmed_path,
                     des_original_path: response.original_path
                  });
               else {
                  config.addImage(response.trimmed_path, response.nom, response.original_name, null, {
                     des_trimmed_path: response.trimmed_path,
                     des_original_path: response.original_path
                  });
                  config.listepersos();
                  config.selectLastObj();
                  triggerWhenObjectSelected();
               }
            } else if(response.error_type == "BAD_FORMAT") {
               closeUploadPopin("Transfert échoué")
               //traitement resultat
               $('#popinErrorUpload .popin-container').css('top', 'calc(50vh - 127.5px)');
               openPopIn('#popinErrorUpload');
            }
            else if(response.error_type == "EXPORTED_BY_COREL") {
               closeUploadPopin("Transfert échoué")
               //traitement resultat
               $('#popinErrorCorel .popin-container').css('top', 'calc(50vh - 127.5px)');
               openPopIn('#popinErrorCorel');
            }
         });
      }
   }
   function closeUploadPopin(error_message){
      $("#popup_img_txt").html(error_message);
      //reset form
      $("#form_upload_image")[0].reset();
      $(".wrap_img_thumbnail").hide();
      $("#img_preview").hide();
      $(".progress-wrp").hide();
      $(".browse").show();
      $('.drop-field').show();
      $('.logos-container').show();
      $('.pagination').show();
      $('.search-bloc').show();
      closePopIn('#popinAddImg');
   }

   function saveConfig(ioToMerge = {}, args = {}) {
         startLoading().then(function() {
         var exprod = {};
         var that = this;
   
         var exp = new Array();
         var views_infos = new Array();
         var user_objects = {};
         for (var itCanvas = 0; itCanvas < config.design_canvas.length; itCanvas++) {
            var tcvs = new Object();
            
            config.setStockResolution(itCanvas);
            // get Data url
            changevue(config.view_select = itCanvas);
            config.showEmplacements(false, itCanvas, true);
   
            let multiplierProduct = (config.product_resolution ) / ((config.width_photostock > config.height_photostock) ? config.width_photostock : config.height_photostock );
   
            tcvs.imagedata = config.design_canvas[itCanvas].toDataURL({multiplier: multiplierProduct});
   
            // Get json info
            tcvs.emplacements = JSON.stringify(config.design_canvas[itCanvas].toJSON(sp_attributes_tab));
   
            // Background texture
            if(typeof config.modele_bg_texture == 'string')
               tcvs.texture_url = config.modele_bg_texture;
            
            if (views_infos[itCanvas] === undefined)
               views_infos[itCanvas] = {
                  name: config.c2f.declinaisons[0].views[itCanvas].name,
                  reference: config.c2f.declinaisons[0].views[itCanvas].reference
               };
   
            exp.push(tcvs);
         }
   
         for (var key in config.zonesparams) 
            if (config.zonesparams.hasOwnProperty(key)) 
               config.genZoneThumbnail(key, 1);
   
         loadStep(config.useTranslate('saving_customizations'))
         for (o of config.design_canvas_zoning.getObjects()) {
            if (['user_img', 'texte'].indexOf(o.des_type) !== -1) {
               if (user_objects[o.activeZoningName] === undefined)
                  user_objects[o.activeZoningName] = [];
   
               let current_object = {};
               current_object.type = o.des_type;
   
               current_object.object                       = {};
               current_object.object.left                  = o.left.toFixed(2);
               current_object.object.top                   = o.top.toFixed(2);
               current_object.object.scaleX                = o.scaleX.toFixed(2);
               current_object.object.scaleY                = o.scaleY.toFixed(2);
               current_object.object.width                 = o.width;
               current_object.object.height                = o.height;
               current_object.object.des_dimensions_width  = o.des_dimensions_width;
               current_object.object.des_dimensions_height = o.des_dimensions_height;
               current_object.object.des_colors            = o.des_colors;
               current_object.object.des_params            = o.des_params;
               current_object.object.des_textequal         = o.des_textequal;
   
               if (o.des_type == 'user_img') {
                  current_object.object.path = o.des_trimmed_path;
                  current_object.object.original_path = o.des_original_path;
                  if(typeof o.des_superlogo != "undefined")
                     current_object.object.des_superlogo = o.des_superlogo;
               }
               else if (o.des_type == 'texte') {
                  current_object.object.text = o.text;
                  current_object.object.styles = o.styles;
                 
                  current_object.object.des_colors = []; 
                  if(o.styles[0] != undefined) {
                     for(const [key, value] of Object.entries(o.styles[0])) {
                        let from = {'r' : value.r, 'g' : value.g, 'b' : value.b};
                        let to = {'id' : value.id, 'r' : value.r, 'g' : value.g, 'b' : value.b};
                        let row = {'from' : from, 'to' : to};
   
                        current_object.object.des_colors.push(row);
                     }
                  }
               }
   
               user_objects[o.activeZoningName].push(current_object);
            }
         }
   
         if (config.c2f.model !== undefined) {
            exprod.parentId = config.c2f.id;
            exprod.nom      = config.c2f.name + ' ' + config.c2f.model;
            exprod.model    = config.c2f.model;
            if (config.c2f.decli_id !== undefined)
               exprod.decli_id    = config.c2f.decli_id;
         }
         else {
            exprod.parentId   = config.viewSets[config.nb_pack].parentId;
            exprod.nom        = config.viewSets[config.nb_pack].nom;
            exprod.model      = config.viewSets[config.nb_pack].model;  
         }
         exprod.ref           = config.c2f.identifier;
         exprod.id            = exprod.parentId;
         exprod.base_pdt      = (typeof config.c2f.base_pdt != undefined) ? config.c2f.base_pdt : null;
         exprod.product_resolution            = config.product_resolution;
   
   
         if (config.c2f.generic === true && config.c2f.decli_id !== undefined)
            exprod.declinaison_id = config.c2f.decli_id;
         else 
            exprod.declinaison_id = config.viewSets[config.nb_pack].id;
   
         var data = {
            exp        : exp,
            views_infos: views_infos,
            product    : JSON.stringify(exprod),
            zj         : JSON.stringify(config.zonesparams),
            uo         : JSON.stringify(user_objects)
         };
   
         if (CONFIG_CLIENT['outputMode'] != 'preview') {
            $.extend(data, {
   
            });
         }
         config.mergeIO(ioToMerge);
         data.io_container = config.io_container;
         data.cpid = (config.cpid !== undefined) ? config.cpid : null;
         data.key = (config.upload_key !== undefined) ? config.upload_key : null;
   
         $.ajax({
            xhr: function() {
               var xhr = new window.XMLHttpRequest();
   
               xhr.upload.addEventListener("progress", function(evt) {
                  if (evt.lengthComputable) {
                     var percentComplete = evt.loaded / evt.total;
   
                     if (percentComplete >= 1)
                        loadStep(config.useTranslate('saving_mockup') + "...");
                     else
                        loadStep(config.useTranslate('saving_mockup') + " " + Math.round(percentComplete * 100) + "%");
                  }
               }, false);
   
               xhr.addEventListener("progress", function(evt) {
                  if (evt.lengthComputable) {
                     var percentComplete = evt.loaded / evt.total;
   
                     if (percentComplete >= 1)
                        loadStep(config.useTranslate('saving_mockup') + "...");
                     else
                        loadStep(config.useTranslate('saving_mockup') + " " + Math.round(percentComplete * 100) + "%");
                  }
               }, false);
   
               return xhr;
            },
            type: "POST",
            url: CONFIG_CLIENT[CONFIG_CLIENT['outputMode'] + 'Action'],
            data: data,
            dataType: 'json'
         })
         .done(function(response) {
            if (CONFIG_CLIENT['outputMode'] != 'preview') {
               that.response = response;
               afterSaveFnType = typeof window[CONFIG_CLIENT['afterSaveFn']];
   
               if (args) that.afterSaveArgs = args;
   
               if (afterSaveFnType !== undefined && afterSaveFnType === 'function')
                  window[CONFIG_CLIENT['afterSaveFn']].call(that);
            }
            else {
               var preJson = $('#pre-json');
   
               if (!preJson.length)
                  preJson = $('<pre id="pre-json">');
   
               preJson.jsonViewer(response);
               doPopInConfirm({
                  id: 'jsonPopin',
                  visibles: {
                     body: true,
                     footer: false
                  },
                  bodyHtml: preJson
               }, undefined);
            }
         });
      });
      
   }

   function saveModel(ioToMerge = {}, args = {}) {
      $('.close_admin_panel').click();
      startLoading();
      var exprod = {};
      var that = this;

      var exp = new Array();
      var views_infos = new Array();
      var user_objects = {};
      for (var itCanvas = 0; itCanvas < config.design_canvas.length; itCanvas++) {
         var tcvs = new Object();
         
         config.setStockResolution(itCanvas);
         // get Data url
         changevue(config.view_select = itCanvas);
         config.showEmplacements(false, itCanvas, true);
         tcvs.imagedata = config.design_canvas[itCanvas].toDataURL("image/png");

         // Get json info
         tcvs.emplacements = JSON.stringify(config.design_canvas[itCanvas].toJSON(sp_attributes_tab));

         if (views_infos[itCanvas] === undefined)
            views_infos[itCanvas] = {
               name: config.c2f.declinaisons[0].views[itCanvas].name
            };

         exp.push(tcvs);
      }

      for (var key in config.zonesparams) 
         if (config.zonesparams.hasOwnProperty(key)) 
            config.genZoneThumbnail(key, 1);

      for (o of config.design_canvas_zoning.getObjects()) {
         if (['user_img', 'texte'].indexOf(o.des_type) !== -1) {
            if (user_objects[o.activeZoningName] === undefined)
               user_objects[o.activeZoningName] = [];

            let current_object = {};
            current_object.type = o.des_type;

            current_object.object                       = {};
            current_object.object.left                  = o.left.toFixed(2);
            current_object.object.top                   = o.top.toFixed(2);
            current_object.object.scaleX                = o.scaleX.toFixed(2);
            current_object.object.scaleY                = o.scaleY.toFixed(2);
            current_object.object.width                 = o.width;
            current_object.object.height                = o.height;
            current_object.object.des_dimensions_width  = o.des_dimensions_width;
            current_object.object.des_dimensions_height = o.des_dimensions_height;
            current_object.object.des_colors = o.des_colors;

            if (o.des_type == 'user_img') {
               current_object.object.path = o.des_trimmed_path;
               current_object.object.original_path = o.des_original_path;
            }
            else if (o.des_type == 'texte') {
               current_object.object.text = o.text;
               current_object.object.styles = o.styles;
            }

            user_objects[o.activeZoningName].push(current_object);
         }
      }

      if (config.c2f.model !== undefined) {
         exprod.parentId = config.c2f.id;
         exprod.nom      = config.c2f.name + ' ' + config.c2f.model;
         exprod.model    = config.c2f.model;
         if (config.c2f.decli_id !== undefined)
            exprod.decli_id    = config.c2f.decli_id;
      }
      else {
         exprod.parentId   = config.viewSets[config.nb_pack].parentId;
         exprod.nom        = config.viewSets[config.nb_pack].nom;
         exprod.model      = config.viewSets[config.nb_pack].model;  
      }
      exprod.ref           = config.c2f.identifier;
      exprod.id            = exprod.parentId;

      if (config.c2f.generic === true && config.c2f.decli_id !== undefined)
         exprod.declinaison_id = config.c2f.decli_id;
      else 
         exprod.declinaison_id = config.viewSets[config.nb_pack].id;

      var data = {
         exp        : exp,
         views_infos: views_infos,
         product    : JSON.stringify(exprod),
         zj         : JSON.stringify(config.zonesparams),
         uo         : JSON.stringify(user_objects)
      };

      if (CONFIG_CLIENT['outputMode'] != 'preview') {
         $.extend(data, {

         });
      }
      config.mergeIO(ioToMerge);
      data.io_container = config.io_container;

      data.key = (config.upload_key !== undefined) ? config.upload_key : null;
      data.isModel = true;
      data.idModel = config.model_id;
      data.wantUpdateModel = config.model_want_update;
      data.model_name = config.model_name;
      var res = $.ajax({
         async: false,
         xhr: function() {
            var xhr = new window.XMLHttpRequest();

            xhr.upload.addEventListener("progress", function(evt) {
               if (evt.lengthComputable) {
                  var percentComplete = evt.loaded / evt.total;

                  if (percentComplete >= 1)
                     loadStep(config.useTranslate('save_progress') + "...");
                  else
                     loadStep(config.useTranslate('save_progress') + " " + Math.round(percentComplete * 100) + "%");
               }
            }, false);

            xhr.addEventListener("progress", function(evt) {
               if (evt.lengthComputable) {
                  var percentComplete = evt.loaded / evt.total;

                  if (percentComplete >= 1)
                     loadStep(config.useTranslate('save_progress') + "...");
                  else
                     loadStep(config.useTranslate('save_progress') + " " + Math.round(percentComplete * 100) + "%");
               }
            }, false);

            return xhr;
         },
         type: "POST",
         url: CONFIG_CLIENT[CONFIG_CLIENT['outputMode'] + 'Action'],
         data: data,
         dataType: 'json'
      })
      .done(function(response) {
         if (CONFIG_CLIENT['outputMode'] != 'preview') {
            that.response = response;
            afterSaveFnType = typeof window[CONFIG_CLIENT['afterSaveFn']];

            if (args) that.afterSaveArgs = args;

            if (afterSaveFnType !== undefined && afterSaveFnType === 'function')
               window[CONFIG_CLIENT['afterSaveFn']].call(that);
         }
         else {
            var preJson = $('#pre-json');

            if (!preJson.length)
               preJson = $('<pre id="pre-json">');

            preJson.jsonViewer(response);
            doPopInConfirm({
               id: 'jsonPopin',
               visibles: {
                  body: true,
                  footer: false
               },
               bodyHtml: preJson
            }, undefined);
         }
      })
      .fail(function(response) {
      });
      return res;
   }
