//other functions
function enforceMinMax(el) {
    if (el.value != "") {
        if (parseInt(el.value) < parseInt(el.min)) {
            el.value = el.min;
        }
        if (parseInt(el.value) > parseInt(el.max)) {
            el.value = el.max;
        }
    }
}

function debounce(func, wait) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, args), wait);
  };
}

jQuery(document).ready(function($) {

const DATA_VERSION = {
   uploads: 1,
}

const $document = $(document);
   class ProductIncrement {
	  constructor() {
		 this.bind();
		 this.previous_value;
	  }

	  bind() {
		 const self = this;
		 $document.on('click', '.sgnm-incrament-btn', function() {
			let type = $(this).attr('data-action');
			const $inputElement = self.getInputElement($(this));
			const values = self.getValMinMax($inputElement);
			let oldValue = values.current;

			self.previous_value = oldValue;
			let newValue = oldValue;

			if (type == 'dec') {
			   newValue -= 1;
			} else {
			   newValue += 1;
			}


			let newValueValidated = self.validateValue(
			   newValue,
			   values.min,
			   values.max
			);


			$inputElement.val(newValueValidated);
			// Dispatch custom event with old and new values
			self.dispatchChangeEvent($inputElement, newValueValidated, 'button', values.area);
		 });

		 $document.on('focus', '.sgnm-amount-input', function(e) {
			const value = $(this).val();
			if(value) self.previous_value = parseInt(value);
		 });

		 $document.on('blur', '.sgnm-amount-input', function(e) {
			const value = parseInt($(this).val());
			if(Number.isNaN(value)){
			   $(this).val(self.previous_value);
			}
		 });

		 $document.on('input', '.sgnm-amount-input', function(e) {
			let input = $(this);
			const number = input.val().replace(/[^0-9]/g, '');
			input.val(number); //remove letters
			const oldValue = parseInt(number);
			const values = self.getValMinMax(input);
			const newValue = self.validateValue(values.current, values.min, values.max, 'input');

			if(!Number.isNaN(newValue)){
			   input.val(newValue);
			}

			self.dispatchChangeEvent(input, newValue,'input', values.area);
		 });
	  }

	  dispatchChangeEvent($element, newValue, type, area='') {
		 // Create custom event with detail containing relevant information
		 area = (area === 'cart') ? 'cart' : 'product';
		 const event = new CustomEvent(`${area}:quantityChanged`, {
			bubbles: true, // Allow event to bubble up through the DOM
			detail: {
			   element: $element[0],
			   productId: $element.attr('data-id') || null,
			   oldValue: this.previous_value,
			   newValue: newValue,
			   type: type,
			   input: $element
			}
		 });


		 if(!Number.isNaN(newValue)){
			this.previous_value = newValue;
		 }

		 // Dispatch the event from the input element
		 $element[0].dispatchEvent(event);
	  }

	  getInputElement($buttonElement) {
		 return $buttonElement.siblings('.sgnm-amount-input');
	  }

	  validateValue(value, min, max) {
		 if (value <= min) {
			value = min;
		 }
		 if (value > max) {
			value = max;
		 }
		 return value;
	  }

	  getValMinMax($inputElement = false) {
		 if ($inputElement.length < 1) {
			return { min: 1, max: 1000, current: 1, area: '' }
		 } else {
			//get the values
			return {
			   min: parseInt($inputElement.attr('min')),
			   max: parseInt($inputElement.attr('max')),
			   current: parseInt($inputElement.val()),
			   area: $inputElement.attr('data-area'),
			}
		 }
	  }
   }

   new ProductIncrement();


function deepCopy(object){
	return JSON.parse(JSON.stringify(object));
}

function isNumber(value) {
   if (typeof value === 'number') {
      return true;
   } else if (typeof value === 'string') {
      // Convert string to number
      var num = parseFloat(value);
      // Check if the conversion gives a number
      return !isNaN(num);
   }
   return false;
}




const all_product_settings= studioValues.all_product_settings;
const all_conditions = studioValues.all_conditions
const all_elements = studioValues.all_elements
let currentSettings= studioValues.currentSettings
const customer_type = studioValues.customer_type
const sign_slug = studioValues.slugSign;
let adminLoaded = false;
if(sign_slug){
   my_base_params.base = sign_slug;
}

   class EventBus {
      private events: { [key: string]: Function[] };

      constructor() {
         this.events = {};
      }

      // Subscribe to an event
      on(eventName: string, callback: Function): void {
         if (!this.events[eventName]) {
            this.events[eventName] = [];
         }
         this.events[eventName].push(callback);
      }

      // Unsubscribe from an event
      off(eventName: string, callback: Function): void {
         if (this.events[eventName]) {
            this.events[eventName] = this.events[eventName].filter(cb => cb !== callback);
         }
      }

      // Trigger an event
      emit(eventName: string, data?: any): void {
         if (this.events[eventName]) {
            this.events[eventName].forEach(callback => callback(data));
         }
      }
   }

const eventBus: EventBus = new EventBus();

class DomManager {
    constructor() {
        this.cache = new Map();
    }
    
    // Get element with caching
    get(selector) {
        if (!this.cache.has(selector)) {
            const element = document.querySelector(selector);
            this.cache.set(selector, element);
        }
        return this.cache.get(selector);
    }
    
    // Get multiple elements
    getAll(selector) {
        const cacheKey = `all:${selector}`;
        if (!this.cache.has(cacheKey)) {
            const elements = Array.from(document.querySelectorAll(selector));
            this.cache.set(cacheKey, elements);
        }
        return this.cache.get(cacheKey);
    }
    
    // Utility methods
    addClass(selector, className) {
        const element = this.get(selector);
        if (element) element.classList.add(className);
    }
    
    removeClass(selector, className) {
        const element = this.get(selector);
        if (element) element.classList.remove(className);
    }
    
    show(selector) {
        const element = this.get(selector);
        if (element) element.style.display = '';
    }
    
    hide(selector) {
        const element = this.get(selector);
        if (element) element.style.display = 'none';
    }
    
    // Clear cache when DOM changes
    clearCache() {
        this.cache.clear();
    }
}

class HandleRealDimensionsClass {
   constructor() {
      this.realDimensions = {
         width: 500,
         height: 500,
         material: 1,
         border: 0,
         cornerRadius: null,
         background: "#fff",
         shape: "rect",
         borderColor: "#fff",
         mounting: "none",
         mountingLayout: false,
         mountdim: false,
         engraving: false,
         mount_amount: 0,
         mountImage: false,
         surface: "blank",
         special_sign: false,
         double_sided: false,
         special: false,
         auto_size_h: false,
         auto_size_w: false,
         rotated: false,
      };

      this.sideDims = ['border', 'background', 'borderColor'];

      this.MIN_WIDTH = 30;
      this.MIN_HEIGHT = 30;
   }

   updateDim(key, value) {
      if (key in this.realDimensions) {
         let currentDim = this.realDimensions;
         if (currentDim.double_sided && currentDim.double_sided == 1) {
            if (signSidesManager.getActiveSide() !== 1 && this.sideDims.includes(key)) {
               currentDim = signSidesManager.getSideDim();
            } else {
               //should be side one 
               if (signSidesManager.getSideDim() !== this.realDimensions) {
                  signSidesManager.connectSideOneDim(this.realDimensions);
               }
            }
         }
         currentDim[key] = value;
      } else {
         console.warn(`Key: ${key} not initialized in realDimensions. Consider adding it first.`);
      }
   }

   updateDimsObj(dimensionsObj) {
      Object.entries(dimensionsObj).forEach(([key, value]) => {
         this.updateDim(key, value);
      });
   }

   setRealdimensions(value) {
      if(typeof value === 'object'){
         this.realDimensions = { ...this.realDimensions, ...value };
      }
   }

   getDim(key) {
      if (!key) return false;
      if (Array.isArray(key)) {
         let dimObject = {};
         for (let i = 0; i < key.length; i++) {
            let type = key[i]
            dimObject[type] = this.realDimensions[type];
         }
         return dimObject;
      } else {
         return this.realDimensions[key];
      }
   }
   getAllDim() {
      return this.realDimensions;
   }

   hasDim(key) {
      return key in this.realDimensions;
   }

   updateWidth(value) {
      value = parseInt(value);
      if (value < this.MIN_WIDTH) {
         value = this.MIN_WIDTH;
      }
      this.updateDim('width', value);
   }

   updateHeight(value) {
      value = parseInt(value);
      if (value < this.MIN_HEIGHT) {
         value = this.MIN_HEIGHT;
      }
      this.updateDim('height', value);
   }
}

const handleRealDim = new HandleRealDimensionsClass();

//Classes 
class SignSaveAndLoad {
      public canvas: fabric.Canvas;
      private exportCanvas: fabric.Canvas | null = null;
      private signOperation: SignOperation;
      private BackgroundShape: fabric.Object | null = null;
      private hasStroke: boolean = false;
      private waitingForCart = false;
      private undo_stack = [];
      private redo_stack = [];
      private pause_saving = false;
      private userImages = [];
      private dontSaveSign = {};
      private renderSettings = {};
      private $canvasWrap: JQuery;


	constructor(
         canvas: fabric.Canvas,
         signOperation: SignOperation
      ){
		this.canvas = canvas;
		this.signOperation = signOperation;
        this.$canvasWrap = $("#canvas-wrap");  
	}

   getDontSaveSign() {
      return this.dontSaveSign;
   }

   getQuantity(){
      return toolMenu.getSignQuantity();
   }

	getAjaxParams(){
		let params = {
			'data' : false,
			'addToCart' : false,
			'preview': false,
			'admin' : false, 
			'share' : false,
			'favorite': false,
			'image':false,
			'mountingHoles' : realDimensions.mount_amount,
			'base' : my_base_params.base,
			}
		return JSON.parse(JSON.stringify(params));
	}

    getSignCleanJson(render = false) {
        this.backgroundShape = this.signOperation.getBackgroundShape();
        this.canvas.clipPath = null;

        const { canvas } = this;

        this.canvas.remove(helperLine);
        this.canvas.remove(helperLineV);
        this.canvas.remove(helperLineObjV);
        this.canvas.remove(helperLineObjH);

        //got to set currenct object to selectable otherWise if you are editing text it will set it to selectable false
        let activeObject = this.canvas.getActiveObject();
        let wasUnselectable = false;
        if (activeObject && activeObject.selectable == false) {
            activeObject.set('selectable', true);
            wasUnselectable = true;
        }

        let canvasJSON = canvas.toJSON(saveCustomProperties);
        this.bgGroupSpecial = this.signOperation.getBgGroupSpecial();
        if(this.bgGroupSpecial){
            this.canvas.clipPath = this.bgGroupSpecial;
        }else{
            this.canvas.clipPath = this.backgroundShape;
        }
        let signDistanceToLeft = this.backgroundShape.left;
        let signDistanceToTop = this.backgroundShape.top;

        canvasJSON.objects[0].shadow = null;
        canvasJSON.objects[0].stroke = null;

        for (var i = 0; i < canvasJSON.objects.length; i++) {
            if (canvasJSON.objects[i].hasOwnProperty('removeOnSave')) {
                canvasJSON.objects.splice(i, 1);
            } else {
                var obj = canvasJSON.objects[i];
                if (obj.hasOwnProperty('name') && obj.name === 'bg') {
                    obj.strokeWidth = 0;
                    
                  if(this.renderSettings.renderTransparent){
                     obj.fill = 'transparent';
                  } 
                     
                }
                obj.left -= signDistanceToLeft;
                obj.top -= signDistanceToTop;
            }
        }
        if (wasUnselectable) activeObject.set('selectable', false);

        return canvasJSON;
    }

	saveDesign(sendFinsihed = false){
        if (Object.keys(this.dontSaveSign).length > 0) return;
        let savedSign = false;

         if (signSidesManager.hasSides()) {
            signSidesManager.saveSide();
            let sideData = signSidesManager.getSides();
            savedSign = {
               dim: sideData.original.dim,
               data: sideData.original.data,
               sides: sideData.sides,
            };
         } else {
            let canvasJSON = this.getSignCleanJson(true);
            savedSign = {
               dim: handleRealDim.getAllDim(),
               data: canvasJSON
            };
         }
        
        if (sendFinsihed) {
            //validate that all needed values are correct
            if (!savedSign.dim.background) {
               if(this.bgGroupSpecial){
                  savedSign.dim.background = this.bgGroupSpecial.fill;
               }else{
                  savedSign.dim.background = this.backgroundInnerShape.fill;
               }
            }

            if (!savedSign.dim.shape) {
                savedSign.dim.shape = this.backgroundShape.type;
            }

            if (!savedSign.dim.border) {
                savedSign.dim.border = 0;
            }
        }


       if(!my_base_params.stand_alone){
          localStorage.setItem("designCache", JSON.stringify(savedSign));
       }

        this.saveCanvasForUndoAndRedo(savedSign);
        return savedSign;
    }

    async generateSVG(canvasJSON, raw = false) {
        //let saveCanvas = new fabric.Canvas("save");
        let saveCanvas = new fabric.Canvas(null, { containerClass: 'save' });

        let returnObject = {};

        // Load canvas from JSON and wait for it to complete
        await new Promise((resolve) => {
            saveCanvas.loadFromJSON(canvasJSON, () => {
                saveCanvas.setWidth(
                    canvasJSON.objects[0].width * canvasJSON.objects[0].scaleX
                );
                saveCanvas.setHeight(
                    canvasJSON.objects[0].height * canvasJSON.objects[0].scaleY
                );
                resolve();
            });
        });

        // Replace images with SVGs
        //await this.replaceImagesWithSVGs(saveCanvas);

        // Generate SVG data
        returnObject['svgData'] = saveCanvas.toSVG();

        if(raw){
          returnObject['thumbnailExport'] = await this.getThumbnail(saveCanvas);
        }

        // Additional processing for special signs
        if (handleRealDim.getDim('special_sign') === true) {
            let customBG = saveCanvas.getItemByAttr('name', 'custom_sign_background');
            if (customBG && customBG.type !== "path") {
                customBG._objects.forEach(obj => {
                    customBG.remove(obj);
                });
            }
            saveCanvas.remove(customBG);
            returnObject['svgProd'] = saveCanvas.toSVG();
        }

        // Dispose of the canvas
        saveCanvas.dispose();
        return returnObject;
    }


	async getCompleteSignAsync(thumbnail = true){
        this.preSendSign();
      
        var completeSign = {};
        completeSign.user_images = this.userImages;

        if(studioValues.hasOwnProperty('product_id') && studioValues.product_id){
            completeSign.product_id = studioValues.product_id;
        }

        try {
            completeSign.sign = this.saveDesign(true);
            var canvasJSON = completeSign.sign.data;

            // Get first side
            let signSVG = await this.generateSVG(canvasJSON, thumbnail);
            completeSign.svg = signSVG.svgData;
            completeSign.thumbnailExport = signSVG.thumbnailExport;

            if (signSVG.hasOwnProperty('svgProd')) {
                completeSign.svgProd = signSVG.svgProd;
            }

            // If has sides
            if (completeSign.sign.hasOwnProperty('sides')) {
                let sides = completeSign.sign.sides;
                completeSign.svgSides = {};

                const sidePromises = Object.keys(sides).map(async key => {
                    const tempJSON = sides[key]['data'];
                    const sideSVG = await this.generateSVG(tempJSON);
                    const svgData = { 'svg': sideSVG['svgData'] }
                    if (sideSVG.hasOwnProperty('svgProd')) {
                        svgData['svgProd'] = sideSVG['svgProd'];
                    }
                    completeSign.svgSides[key] = svgData;
                });

                await Promise.all(sidePromises);
            }

            return completeSign;
        } catch (error) {
            console.error("Error in getCompleteSignAsync:", error);
            throw error; // or handle the error as you see fit
        }

	}

    preSendSign(){
        this.backgroundShape = this.signOperation.getBackgroundShape();
        this.backgroundInnerShape = this.signOperation.getbackgroundInnerShape();
        this.hasStroke = (this.backgroundShape.strokeWidth > 0) ? true : false;

        if (this.hasStroke) {
            this.backgroundShape.set({ 'stroke': "transparent", 'strokeWidth': 0 });
        }

        //ger user images
        this.getSignUserImages();
        this.setRenderSettings();
    }

    getSignUserImages(){
        this.userImages = [];
        for (let object of this.canvas.getObjects()) {
            if (object.user_image_id) {
                let id = object.user_image_id;
                if (isNumber(id) && !this.userImages.includes(id)) {
                    this.userImages.push(id);
                }
            }
        }
        return this.userImages;
    }

   setRenderSettings(){
         const renderTransparent = updateProdSettings.getRenderTransparent();
         this.renderSettings = { renderTransparent : renderTransparent};
   }

	afterSendSign(){
		if(this.hasStroke){
			let stroke = signOperation.getTransparentStroke();
			this.backgroundShape.set(stroke);
		}
	}

	addSignToCart(buyNow = false){
		toolMenu.addingToCartAEvent();
		this.preSendSign();
		//get completesign
		this.getCompleteSignAsync().then(completeSign=>{
			let params = this.getAjaxParams();

			params.data = completeSign;
			params.names = this.getNames();
			params.addToCart = true;
            params.quantity = toolMenu.getSignQuantity();
			apiClient.sendFinishedDesign(params)
				.then(async result => {
						this.afterSendSign();
						if(typeof customer_site !== 'undefined' && customer_site){
							await apiClient.customerCreateSign(result);
						}
						if(buyNow){
							window.location.replace(`${my_base_params.site_url}/checkout/`);
						}else{
							toolMenu.addedToCartAEvent();
						}

						updateCartDisplay(result.post_id);
					}
				).catch(error => {
					console.log(error);
				});
		}).catch(error => {
			console.log(error);
		});
	}


	shareSign(){
		this.preSendSign();
		this.getCompleteSignAsync().then(completeSign=>{
			let params = this.getAjaxParams();
			params.data = completeSign;
			params.share = true;
			params.names = this.getNames();
			apiClient.sendFinishedDesign(params)
				.then(result => {
					this.afterSendSign();
					toolMenu.openShareModal(result.id, website_base_url);
				}).catch(error => {
					console.log(error);
				});
			}).catch(error => {
			console.log(error);
		});
	}

	favoriteSign(list){
		this.preSendSign();
		this.getCompleteSignAsync().then(completeSign=>{
			let params = this.getAjaxParams();
			params.data = completeSign;
			params.favorite = true;
			params.names = this.getNames();
			params.list = list;

			apiClient.sendFinishedDesign(params)
				.then(result => {
					this.afterSendSign();
					//if(list == 0){
				    //toolMenu.addToFavorites(true);
					//}
					
					toolMenu.favoritesAdded();
				}).catch(error => {
					console.log(error);
				});
			
		}).catch(error => {
			console.log(error);
		});
	}

	previewSign(){
		toolMenu.openSignPreview(handleRealDim.getDim('material'));
		this.preSendSign();

		this.getCompleteSignAsync(false).then(async completeSign=>{
			let params = this.getAjaxParams();
			params.data = completeSign;
            params.data.sign_png = await this.getPreviewImage();
			params.preview = true;

			apiClient.sendFinishedDesign(params).then(result => {
				this.afterSendSign();

                  if(result.success){
                   let material = params.data.sign.dim.material;
                   let send_data = {
                      id: result.data.id,
                      width: handleRealDim.getDim('width'),
                      height: handleRealDim.getDim('height'),
                      material: material,
                   }

                  if(studioValues.hasOwnProperty('product_id') && studioValues.product_id){
                     send_data['product_id'] = studioValues.product_id;
                  }

                   if (handleRealDim.getDim('material') == 33 || handleRealDim.getDim('material') == 35) {
                      send_data['type'] = "laptop"; 
                   }else if(handleRealDim.getDim('width') > 300 && handleRealDim.getDim('height') > 200){
                      send_data['type'] = "concretewall"; 

                   }
                     this.loadSignPreview(send_data);

                  }else{

                  }



			}).catch(error => {
				console.log(error);
			})
		}).catch(error => {
			console.log(error);
		});
	}

   loadSignPreview(args){
       let {id, width, height, type, material, product_id} = args;

       let params = {
          file: id, 
          w: width, 
          h: height, 
          type : type,
          material : material,
        };

        if(product_id) params['product_id'] = product_id;

		apiClient.getSignPreview(params).then(data => {
           var urlCreator = window.URL || window.webkitURL;
           var imageUrl = urlCreator.createObjectURL(data);
            previewManager.setImage(imageUrl);
		   //toolMenu.appendPreviewImage(imageUrl);
		}).catch(error => {
			console.log(error);
		});
	}

      combineCanvases(
         mainCanvas: fabric.Canvas | null = null,
         add_shadow = true
      ) {
         try{
            if(!mainCanvas){
               mainCanvas = this.canvas;
            }
            //check that export canvas is inited
            let bgDims = $.canvasBgManager.getDim(true);
            let fgDims = $.canvasFgManager.getDim(true);

            let bgCanvas = $.canvasBgManager.getCanvas(); 
            let fgCanvas = $.canvasFgManager.getCanvas(); 


            let newDims = {left: 0, right: 0, top : 0, bottom : 0};
            let newHeight, newWidth;

            for(const dir of Object.keys(newDims)){
               let less = false; 
               if(dir == 'left' || dir == 'top'){
                  less = true; 
               }

               if(less){
                  if(bgDims[dir] < newDims[dir]) newDims[dir] = bgDims[dir]; 
                  if(fgDims[dir] < newDims[dir]) newDims[dir] = fgDims[dir]; 
               }else{
                  if(bgDims[dir] > newDims[dir]) newDims[dir] = bgDims[dir]; 
                  if(fgDims[dir] > newDims[dir]) newDims[dir] = fgDims[dir]; 
               }
            }


            newWidth = Math.abs(newDims.left) + newDims.right;
            newHeight = Math.abs(newDims.top) + newDims.bottom;

            const renderCanvasInstance = new TemporaryCanvas(newWidth, newHeight);
            const renderCanvas = renderCanvasInstance.getCanvas();



            let offesetX = Math.abs(newDims.left);
            let offesetY = Math.abs(newDims.top);


            renderCanvasInstance.copyCanvasContents(bgCanvas, renderCanvas, offesetX, offesetY); 
            renderCanvasInstance.copyCanvasContents(mainCanvas, renderCanvas, offesetX, offesetY, true, add_shadow); 
            renderCanvasInstance.copyCanvasContents(fgCanvas, renderCanvas, offesetX, offesetY); 

            const json = renderCanvas.toJSON(saveCustomProperties);
            renderCanvasInstance.dispose();

            return {
               json,
               new_dims: newDims
            }

         }catch(error){
            console.log("Couldn't combine canvases", error);
            return {
               json: null,
               new_dims: null
            }
         }
      }

      async getThumbnail( mainCanvas: fabric.Canvas | null = null, type='png'){
         try{
            let { json, new_dims } = this.combineCanvases(mainCanvas);

            console.log(json);

            if(!json){
               return false;
            }
            //thumbnail svg
            const canvasClass = new TemporaryCanvas(1100, 1100, '#ffffff'); 
            await canvasClass.addContent(json, 1000, 1000, true);

            let returnData = {
               type: 'png',
               data: ''
            };

            //first we try to get the png
        
            let exportCanvas = (type === 'png') ? canvasClass.getPng() : null;

            if(!exportCanvas){
               //if it doesnt work we send the svg instead 'slower' as we will need to replace
               //urls with base64 or svgs
               returnData.type = 'svg';
               exportCanvas = canvasClass.getSvg();
            }


            canvasClass.dispose();

            returnData['data'] = exportCanvas;

            return returnData;
         }catch(error){
            console.log(error);
         }
      }

      async getPreviewImage(mainCanvas: fabric.Canvas | null = null){
         try{
            if(!mainCanvas){
               mainCanvas = this.canvas;
            }

            let { json, new_dims } = this.combineCanvases(mainCanvas, false);

            if(!json || !new_dims){
               return false;
            }

            //transform to length 2000
            const width = new_dims?.right - new_dims?.left;
            const height = new_dims?.bottom - new_dims?.top;

            let new_width = 0;
            let new_height = 0;

            if(width >= height){
               new_width = 2000;
               new_height = (2000/width) * height;
            }else{
               new_height = 2000;
               new_width = (2000/height) * width;
            }

            const canvasClass = new TemporaryCanvas(new_width, new_height);

            await canvasClass.addContent(json, new_width, new_height);

            let returnData = {
               type: 'png',
               data: ''
            };

            //first we try to get the png
            let exportCanvas = canvasClass.getPng();

            if(!exportCanvas){
               //if it doesnt work we send the svg instead 'slower' as we will need to replace
               //urls with base64 or svgs
               returnData.type = 'svg';
               exportCanvas = canvasClass.getSvg();
            }


            canvasClass.dispose();

            returnData['data'] = exportCanvas;
            return returnData;
            //check that export canvas is inited
         }catch(error){
            console.log(error)
         }
      }


	
	async loadDesignFromCachedData(designCache, saveForUndo = false, args = {updateMenu : true}) {
       $.stdnLoading = true;
		let height = this.$canvasWrap.innerHeight();
		let width = this.$canvasWrap.innerWidth();

        this.canvas.clear();


		toolMenu.setSize(designCache.dim.width,designCache.dim.height);
        handleRealDim.setRealdimensions(designCache.dim);
        updateProdSettings.setMounting(designCache.dim.mounting);
        //setMounting 

        //load sides
         if (designCache.hasOwnProperty('sides') && designCache.sides) {
            signSidesManager.loadSides(designCache);
         } else if (designCache.dim.hasOwnProperty('double_sided') && designCache.dim.double_sided == 1 && !signSidesManager.hasSides()) {
            signSidesManager.createSideOnLoad(designCache);
         }

         const allRealDimensions = designCache.dim;


         //work with colors
		signOperation.setBgColor(handleRealDim.getDim('background'));
		signOperation.setNameTextColor(handleRealDim.getDim('borderColor'));

         //set gravyr id if gravyr
         if(allRealDimensions?.engraving){
           const colorManager = new ManageColors(); 
            const gravyrColorID = colorManager.checkGravyrColorID(designCache.data.objects);
            if(gravyrColorID){
               signOperation.setGravyrColorID(gravyrColorID);
            }
         }

        if (designCache.template) {
            template = designCache.templateData.template;
        } else if (designCache.dim.template) {
            template = designCache.dim.template;
        }

		if (typeof loadAdmin === 'function' && !adminLoaded) {
			loadAdmin(template, canvas, signSaveAndLoad, apiClient, handleRealDim);
            adminLoaded = true;
		} 
	
        if(!("auto_size_h" in allRealDimensions)){
            handleRealDim.updateDimsObj({
                auto_size_h: false,
                auto_size_w: false,
            })
		}
	
        if (isNaN(parseInt(handleRealDim.getDim('border')))) {
            handleRealDim.updateDim("border", 0);
		}
	

		this.canvas.setWidth(width);
		this.canvas.setHeight(height);
        
        const updateMenu = args?.updateMenu ?? true;
		await updateProdSettings.updateProductSettings(true, updateMenu);

		if(saveForUndo){
			this.saveCanvasForUndoAndRedo(designCache);
		}

       fabric.util.clearFabricFontCache();
		 // Collect unique font families from text objects
       for (let v of designCache.data.objects) {
          if (v.type === 'i-text') {
             let fontObject = { family: v.fontFamily, weight: v.fontWeight, style: v.fontStyle };

             try {
                await $.manageFonts.loadFont(fontObject);
                // Font is loaded, continue with your application logic
             } catch (error) {
                console.error('Error loading font:', error);
             }
          }
       }


		//First we edit the objects before adding them to canvas. 
		let objects = designCache.data.objects;
		const keys = Object.keys(objects);
	
		//assing id to link with var
		for(let i = 0; i < keys.length; i++){
			let obj = objects[keys[i]];
			if(objects[keys[i]].name == "bg"){
				if(i == 0){
					objects[keys[i]].id = 'backgroundShape';
					objects[keys[i]].signstudio_standard = true;
	
				}else{
					objects[keys[i]].id = 'backgroundInnerShape';
					objects[keys[i]].signstudio_standard = true;
				}
			}

			//custom bg assigning
			if(obj.name == "custom_sign_background"){
				objects[keys[i]].id = 'custom_sign_background';
				objects[keys[i]].signstudio_standard = true;
			}else if(obj.name == "custom_sign_background_prod"){
				objects[keys[i]].id = 'custom_sign_background_prod';
				objects[keys[i]].signstudio_standard = true;
			}

				
		}	
	
			fabric.util.enlivenObjects(objects, async (objec) => {
				for(let y = 0; y < objects.length; y++){
                    if(!objec[y]){
                        console.log('one object couldnt be loaded');
                        continue;
                    }
                    if(objec[y].type === 'i-text' && objec[y].text === ''){
                        //empty text do not add
                        continue;
                    }

                    if(objec[y].type === 'image') {
                        // Check if crossOrigin is not set or is null/empty
                        if(!objec[y].crossOrigin || objec[y].crossOrigin !== 'anonymous') {

                            // Store the original properties
                            const imageProps = objec[y].toObject();

                            // Create a new image with proper cross-origin
                            await new Promise((resolve, reject) => {
                                fabric.Image.fromURL(objec[y].src, (newImg) => {
                                    // Apply all original properties except src
                                    delete imageProps.src;
                                    newImg.set(imageProps);
                                    newImg.perPixelTargetFind = true;

                                    // Replace the old object with the new one
                                    objec[y] = newImg;
                                    resolve();
                                }, {
                                        crossOrigin: 'anonymous'
                                    });
                            });
                        }
                    }
                    

					objec[y].perPixelTargetFind=true;
					this.canvas.add(objec[y]);
					this.canvas.requestRenderAll();
				}
			
				let newObj = this.canvas.getObjects();
				//Code to run after objects been add
	
			//assisgn names
			for(let y = 0; y < newObj.length; y++){
				//set bacgrkoundshape
				switch (newObj[y].id){
					case 'backgroundShape':
						this.backgroundShape = newObj[y];
						signOperation.setBackgroundShape(this.backgroundShape);
						break;
					case 'backgroundInnerShape':
						this.backgroundInnerShape = newObj[y];
						signOperation.setbackgroundInnerShape(this.backgroundInnerShape);
						break;
					case 'custom_sign_background':
						this.bgGroupSpecial = newObj[y];
						signOperation.setBgGroupSpecial(this.bgGroupSpecial);
						break;
					case 'custom_sign_background_prod':
						this.bgGroupSpecialProduction = newObj[y];
						signOperation.setBgGroupSpecialProduction(this.bgGroupSpecialProduction);
						break;
				}
			}
			this.canvas.clipPath = this.backgroundShape;
	
			if(this.bgGroupSpecial) this.canvas.clipPath = this.bgGroupSpecial;
	
			this.canvas.requestRenderAll();
	
			//Fix sizes
			let scaleXmm;
			let scaleYmm;
			let reduceTop;
			let reduceLeft;
	
			for (var i = 0; i < newObj.length; i++) {
				if (newObj[i].name == "bg") {
					if (i == 0){
						scaleXmm = fabric.util.parseUnit(designCache.dim.width+"mm")/(newObj[i].width*newObj[i].scaleX);
						scaleYmm = fabric.util.parseUnit(designCache.dim.height+"mm")/(newObj[i].height*newObj[i].scaleY) ;
						reduceTop = newObj[i].top;
						reduceLeft = newObj[i].left;
						newObj[i].set({'width':fabric.util.parseUnit(designCache.dim.width+"mm"),
										'height':fabric.util.parseUnit(designCache.dim.height+"mm"),
										'signstudio_standard':true,
										'left': 0,
										'top':0,
										'scaleX': 1,
										'scaleY': 1,
										}).setCoords();
						
						//sets border on transparent
						if(handleRealDim.getDim('shape') == 12){
							newObj[i].set({'stroke': '#D3D3D3', 'strokeWidth': 1, 'fill': 'transparent'});
						}
					}
					else{
						newObj[i].set(this.correctPosition(newObj[i], scaleXmm,scaleYmm,reduceTop,reduceLeft, true)).setCoords();
					}
				}
	
				if(newObj[i].name == "custom_sign_background"){
					//newObj[i].set(correctPosition(newObj[i], scaleXmm,scaleYmm,reduceTop,reduceLeft, true)).setCoords();
					newObj[i].setCoords();
				}else if(newObj[i].name == "custom_sign_background_prod"){
					newObj[i].setCoords();
					//newObj[i].set(correctPosition(newObj[i], scaleXmm,scaleYmm,reduceTop,reduceLeft, true)).setCoords();
				}
				let customShapes = ['bg','custom_sign_background','custom_sign_background_prod']
				if(!customShapes.includes(newObj[i].name)){
					newObj[i].set(this.correctPosition(newObj[i], scaleXmm,scaleYmm,reduceTop,reduceLeft)).setCoords();
				}
			}

			let maxRadius = 0;
			let maxBorder = 0;


			signOperation.setBackgroundDistance();

			if(handleRealDim.getDim('shape') == 'rect-rounded'){
				maxRadius = (handleRealDim.getDim('width') > handleRealDim.getDim('height')) ? Math.floor(handleRealDim.getDim('height')/2) : Math.floor(handleRealDim.getDim('width')/2);
				toolMenu.addRadiusSlider(maxRadius,handleRealDim.getDim('cornerRadius'), handleRealDim.getDim('border'));
			}

               if(!maxBorder) maxBorder = (handleRealDim.getDim('width') > handleRealDim.getDim('height')) ? Math.floor(handleRealDim.getDim('width')/2) : Math.floor(handleRealDim.getDim('height')/2);
               toolMenu.addBorderSlider(maxBorder, handleRealDim.getDim('border'));

               if(this.backgroundShape.fill == 'transparent' && !handleRealDim.getDim('special_sign')){
                  let stroke = signOperation.getTransparentStroke();
                  this.backgroundShape.set(stroke);
               }

               mountingManager.updateMounting();
                $.stdnLoading = false
               toolMenu.debouncedUpdatePrice();
               this.canvas.calcOffset();
               onCanvasGUIHandler.zoomCanvas();
               //onCanvasGUIHandler.centerCanvasView();
               signOperation.requestCenterCanvas();
               this.canvas.requestRenderAll();
               onCanvasGUIHandler.menuCanvasSizeControl();


               //onCanvasGUIHandler.updateDimensionInformation();
            const colorManager = new ManageColors(this.canvas); 
            colorManager.getActiveColors();
               updateUserObjects()
               allLoaded = true;
               this.pause_saving=false;
            });
    }

	
    loadDesignFromCode(code, firstRun) {
      try{
        apiClient.getSignFromCode({'code': code}).then(result => {
          this.loadDesignFromCachedData(result, true);
          //fucntion to show right materials
          if(firstRun == true){
            //undo_stack.push(JSON.parse(JSON.stringify(result)));
          }
        });
      }catch(error){
        console.log(error);
      }
    };

	correctPosition(obj, scaleX, scaleY, rTop, rLeft, fixed){
		let newLeft = obj.left*scaleX-rLeft*scaleX;
		let newTop = obj.top*scaleY-rTop*scaleX;
		let newScaleX = 1;
		let newScaleY = 1;
		let newH;
		let newW;
		if(fixed == true){
			newH = obj.height*obj.scaleY*scaleY;
			newW = obj.width*obj.scaleX*scaleX;
		}else{
			newScaleX = obj.scaleX*scaleX;
			newScaleY = obj.scaleY*scaleY;
		}
		
		let returnSetting = {
							'left': newLeft,
							'top': newTop,
							'scaleX':newScaleX,
							'scaleY':newScaleY}

		if(fixed == true){
			returnSetting.signstudio_standard = true;
			returnSetting.height = newH;
			returnSetting.width = newW;
		}
		return returnSetting;
	}

	saveCanvasForUndoAndRedo(sign){
       return;
		if (!this.pause_saving) {
			this.canvas.remove(helperLine);
			this.canvas.remove(helperLineV);
			this.canvas.remove(helperLineObjV);
			this.canvas.remove(helperLineObjH);
	
			this.undo_stack.push(JSON.parse(JSON.stringify(sign)));
			this.redo_stack = [];
		}
	}

	undo(){
       return;
		this.pause_saving=true;	
		if(Object.keys(this.undo_stack).length > 1){
		this.redo_stack.push(this.undo_stack.pop());
		let previous_state = this.undo_stack[this.undo_stack.length-1];
		if (previous_state == null) {
			previous_state = '{}';
		}else{
			this.canvas.clear();
			this.loadDesignFromCachedData(previous_state);		
		}
		}
		this.pause_saving=false;
	  }
	redo(){
       return;
		if(Object.keys(this.redo_stack).length > 0){
			this.pause_saving=true;

			let state = this.redo_stack.pop();
			console.log(state);
			if (state != null) {
				console.log(state);
				this.undo_stack.push(JSON.parse(JSON.stringify(state)));
				this.canvas.clear();
				this.loadDesignFromCachedData(state);
			}
			this.pause_saving=false;
		}
	}

      clearLocalData() {
         localStorage.removeItem("designCache");
         window.location.href = studioValues.studio_url;
         //window.location.href = ajaxob.current_page;
      }
	getNames(){
		let id = handleRealDim.getDim('material');
		let mountingID = handleRealDim.getDim('mounting');
		let productName = all_product_settings['product_'+id]['name'];
		let mountingName = "";

		if (all_elements['mount_' + mountingID] && all_elements['mount_' + mountingID].hasOwnProperty('display_name')) {
			mountingName = all_elements['mount_' + mountingID]['display_name'];
		}
		
		return {'product_name': productName, 'mounting_name': mountingName}
	}

        unlockObjectsJSON(json){
            if(!json.hasOwnProperty('data') || !json.data.hasOwnProperty('objects')){
                return json;
            }

            for(let i = 0; i < json.data.objects.length; i++){
                if(json.data.objects[i].hasOwnProperty('signstudio_standard')){
                    json.data.objects[i].selectable = false;
                }else{
                    json.data.objects[i].selectable = true;
                }
            }
            return json;
        }
}

let signSaveAndLoad: SignSaveAndLoad;

  class APIClient{
    private root: string = '';
    private nonce: string = '';
    private clientRoot: string = '';
    private clientNonce: string = '';

    constructor(){
      if(typeof customer_site !== 'undefined' && customer_site){
        this.clientRoot = customer_site.root;
        this.clientNonce = customer_site.nonce;
      }
    }

    init(ajaxob){
      this.root = ajaxob.root;
      this.nonce = ajaxob.nonce;
      if(typeof customer_site !== 'undefined' && customer_site){
        this.clientRoot = customer_site.root;
        this.clientNonce = customer_site.nonce;
      }
    }

    request(method: string, url: string, data = null, extraOptions = {}, contentType: string | null = null){
      const options = {
        method: method,
        url: this.root + url,
        headers: {'X-WP-Nonce': this.nonce},
        ...extraOptions,
      }

      if(data){
        if(method === 'GET'){
          options.data = data;
        } else {
          if(contentType){
            if(contentType != 'none'){
              options.contentType = contentType;
            } else {
              options.data = data;
            }
          } else {
               options.contentType = "application/json";
               options.data = (typeof data === 'string') ? data : JSON.stringify(data);
          }
        }
      }

      return new Promise((resolve, reject) => {
        $.ajax(options)
          .done(response => {
            resolve(response);
          })
          .fail((xhr, status, error) => {
            reject(new Error(`API request failed: ${error}`));
          });
      });
    }

    clientRequest(method, url, data = null, extraOptions = {}, contentType = false){
      const options = {
        method: method,
        url: this.clientRoot + url,
        headers: {'X-WP-Nonce': this.clientNonce},
        ...extraOptions,
      }

      if(data){
        if(contentType){
          if(contentType !== 'none'){
            options.contentType = contentType;
          }
        } else {
          options.contentType = "application/json";
          options.data = JSON.stringify(data);
        }
      }

      return new Promise((resolve, reject) => {
        $.ajax(options)
          .done(response => {
            resolve(response);
          })
          .fail((xhr, status, error) => {
            reject(new Error(`API request failed: ${error}`));
          });
      });
    }


    async getSymbols(){
      return this.request('GET', 'skyltar/v1/get_symbols');
    }
    async getPrice(data){
      return this.request('GET', "skyltar/v1/price", data);
    }
    async getSignFromCode(data){
      console.log(data);
      return this.request('GET', 'skyltar/v1/loadSignFromCode', data);
    }
    async sendFinishedDesign(data){
      return this.request('POST', 'skyltar/v1/sendFinisedDesign', data);
    }
    async userUpload(data){
      return this.request('POST', 'skyltar/v1/user_upload', data, {cache: false, processData: false, contentType: false}, 'none');
    }
    async imageQualityCheck(data){
      return this.request('GET', 'skyltar/v1/check_image_quality', data);
    }
    async getSignPreview(data){
      return this.request('POST', 'skyltar/v1/preview', data, {xhrFields: { responseType: 'blob' }},'none');
    }
    async getUserImages(){
      return this.request('GET', 'skyltar/v1/user_images');
    }
    async manageUserImages(data){
      return this.request('POST', 'skyltar/v1/manage_user_images', data);
    }

    async getTempalteData(data) {
      return this.request('POST', 'skyltar/v1/get_template_data', data);
    }

    async getSvgSize(data){
      return this.request('POST', 'skyltar/v1/get_svg_size', data);
    }

    async customerCreateSign(data){
      return this.clientRequest('POST', 'signstudio_extend/v1/new_sign', data);
    }

  }

   const apiClient = new APIClient();

   class SvgCheck {
      constructor() {

      }

      readSVGFile(file) {
         return new Promise((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = (event) => {
               const svgString = event.target.result;
               const isValid = this.validate(svgString);
               resolve(isValid); // resolve the promise with the validation result
            };
            reader.onerror = (error) => {
               reject(error); // reject the promise if reading the file fails
            };
            reader.readAsText(file);
         });
      }

      validate(svgString) {
         const parser = new DOMParser();
         const doc = parser.parseFromString(svgString, 'image/svg+xml');
         const styleColors = this.extractColorsFromStyles(doc);

         //const paths = doc.querySelectorAll('path');
         const paths = doc.querySelectorAll('path, rect, circle, ellipse, line, polyline, polygon', 'g');
         const filteredPaths = Array.from(paths).filter(path => !path.closest('#boolean-operations'));


         const allowedElements = doc.documentElement.querySelectorAll('path, g, defs, style, clipPath, rect, circle, ellipse, line, polyline, polygon, title');
         const totalElements = doc.documentElement.querySelectorAll('*');


         if (totalElements.length !== allowedElements.length) {
            return false;
         }


         let firstFillColor = true;
         let isSingleColor = true;
         let fillColor;

         let colors = new Set();
         for (let index = 0; index < paths.length; index++) {
            const path = paths[index];
            //console.log(path);
            fillColor = this.validateElementColor(path, styleColors);
            //console.log(fillColor);
            if (index === 0) {
               firstFillColor = fillColor;
            } else {
               if (fillColor !== firstFillColor) {
                  isSingleColor = false;
                  break;
               }
            }
         }

         if (!isSingleColor) {
            return false;
         }

         return true;
      }

      extractColorsFromStyles(svgDoc) {
         let styleColors = {};
         const styles = svgDoc.querySelectorAll('style');

         styles.forEach(style => {
            // Firefox often has style.sheet as null for parsed XML documents
            if (style.sheet && style.sheet.cssRules) {
               // Use stylesheet API if available
               const cssRules = style.sheet.cssRules;
               for (let rule of cssRules) {
                  const color = rule.style.fill || rule.style.stroke;
                  if (color) {
                     rule.selectorText.split(",").forEach(selector => {
                        styleColors[selector.trim()] = color;
                     });
                  }
               }
            } else {
               // Fallback: parse the text content manually
               this.parseStyleText(style.textContent || style.innerHTML, styleColors);
            }
         });

         return styleColors;
      }

      parseStyleText(cssText, styleColors) {
         // Simple regex-based CSS parser for fill and stroke properties
         const rules = cssText.split('}').filter(rule => rule.trim());

         rules.forEach(rule => {
            const parts = rule.split('{');
            if (parts.length === 2) {
               const selectors = parts[0].trim();
               const declarations = parts[1].trim();

               const fillMatch = declarations.match(/fill\s*:\s*([^;]+)/);
               const strokeMatch = declarations.match(/stroke\s*:\s*([^;]+)/);

               const color = fillMatch?.[1]?.trim() || strokeMatch?.[1]?.trim();

               if (color) {
                  selectors.split(',').forEach(selector => {
                     styleColors[selector.trim()] = color;
                  });
               }
            }
         });
      }

      validateElementColor(element, styleColors) {
         let fillColor = element.getAttribute('fill');

         if (!fillColor) {
            // Check class and id for a color
            if (element.className.baseVal) {
               fillColor = styleColors['.' + element.className.baseVal];
            }
            if (!fillColor && element.id) {
               fillColor = styleColors['#' + element.id];
            }
         }

         return fillColor;
      }

      async checkSvgSingleColor(url: string){
         // Fetch the SVG file from the URL
         let response = await fetch(url);
         if (!response.ok) {
            alert("Hittade inte filen");
            throw new Error(`HTTP error! status: ${response.status}`);
         }

         // Convert response to Blob
         let blob = await response.blob();
         const maxSize = 500 * 1024; // 500 KB in bytes
         if (blob.size > maxSize) {
            return false;
         }
         // Validate the SVG
         let status = await this.readSVGFile(blob);
         return status;
      }
   }

    class HandleImages {
      private eventBus; 
        constructor(eventBus) {
            this.eventBus = eventBus;
            this.bindEvents();
        }

        bindEvents() {
            this.eventBus.on('user_image:scaled', (data) => {
                const obj = data?.obj;
                if (!obj) return;

                const src = obj.svgURL ?? obj.src;
                const id = obj.user_image_id;

                this.checkImageQuality(id, src, obj);

            });

            this.eventBus.on('user_image:selected', (data) => {
                if(data?.obj){
                    const obj = data.obj;
                    const image_id = obj?.user_image_id;

                    if (obj && obj.stdnCustom) {
                        if(obj.stdnCustom?.quality_score){
                            const imageIdString = image_id ? `image-id-${image_id}` : null;
                            let qualityScore = obj.stdnCustom.quality_score;
                            if (qualityScore < 70) {
                                this.handleLowQuality(imageIdString, true, obj);
                            } else {
                                this.handleGoodQuality(imageIdString, true, obj);
                            }

                        }else{
                            const src = obj.svgURL ?? obj.src;
                            this.checkImageQuality(image_id, src, obj);
                        }
                    }
                };
            });
        }

      checkImageQuality(image_id = false, src=false, obj = false) {
         apiClient.imageQualityCheck(
            {
            'id': image_id,
            'src': src,
            'width_px': obj.width * obj.scaleX,
            'height_px': obj.height * obj.scaleY
         })
            .then(result => {
               if (result.success) {
                  const data = result.data;
                  const imageIdString: string | null = image_id ? `image-id-${image_id}` : null;
                  let previousQuality:number = 100;
                  let updateMessages = true;

                  if (!data.hasOwnProperty('quality_score')) {
                     toastManager.clear();
                     return;
                  }

                  // Update object quality score
                  if (obj && obj.stdnCustom) {
                     if(obj.stdnCustom?.quality_score){
                        previousQuality = obj.stdnCustom.quality_score;
                     }
                     obj.stdnCustom.quality_score = data.quality_score;
                  }

                  const qualityScore = parseFloat(data.quality_score);

                  if (qualityScore < 70) {
                     if(previousQuality < 70) updateMessages = false;
                     this.handleLowQuality(imageIdString, updateMessages, obj);
                  } else {
                     if(previousQuality >= 70) updateMessages = false;
                     this.handleGoodQuality(imageIdString, updateMessages, obj);
                  }
               }
            })
            .catch(error => {
               console.error('Image quality check failed:', error);
            });
      }

        handleLowQuality(imageId: string | null, updateMessages, obj) {
            const errorText = "Risk för dålig bildkvalitet, vid denna bild storlek";
            let lastQualityWarning: string | null = null;

            if(obj){
               lastQualityWarning = obj.last_quality_warning;
               obj.last_quality_warning = 'bad';
            }

            if (imageId) {
                
               if(updateMessages){
                  this.eventBus.emit("object:tool_menu_message", {
                     message_id: imageId,
                     area: 'quality',
                     message: errorText,
                     type: 'error'
                  });
               }

               //only shake once over canvas other wise stressfull
               if(lastQualityWarning !== 'bad'){
                  const existingMessages = toastManager.getMessages(imageId);
                  // Remove success messages and avoid duplicate error messages
                  for (const messageElement of existingMessages) {
                     const msgType = messageElement.getAttribute('data-msg-type');
                     if (msgType === 'success') {
                        messageElement.remove();
                     } else if (msgType === 'error') {
                        return; // Don't show duplicate error
                     }
                  }
                  const errorMessage = toastManager.error(
                     errorText, 
                     imageId
                  );

                  toastManager.autoRemove(errorMessage);
               }
            }


        }

        handleGoodQuality(imageId: string | null, updateMessages, obj) {
            if (!imageId) return;

            let lastQualityWarning: string | null = null;

            if(obj){
               lastQualityWarning = obj.last_quality_warning;
               obj.last_quality_warning = 'good';
            }

            if(lastQualityWarning === 'good' || lastQualityWarning == null){
               return;
            }

            const existingMessages = toastManager.getMessages(imageId);
            const messageText = "Snyggt! Bilden är okej";

            if(updateMessages){
                this.eventBus.emit("object:tool_menu_message", {
                    message_id: imageId,
                    area: 'quality',
                    message: messageText,
                    type: 'success'
                });
            }

            // Only show success if there was a previous error
            for (const messageElement of existingMessages) {
                const msgType = messageElement.getAttribute('data-msg-type');
                if (msgType === 'error') {
                    messageElement.remove();
                }
            }
            toastManager.success(messageText, 3000, imageId);
        }
    }

new HandleImages(eventBus);


class ToolMenu{
      private eventBus;
      private domManager: DomManager;
      private svgCheck: SvgCheck;
      private $wrap: JQuery;
      private $toolwrap : JQuery;
      private $menu : JQuery;

	constructor(canvas, eventBus: EventBus){
        this.eventBus = eventBus;
        this.domManager = new DomManager;

        this.priceDebounceTimer = null;
        this.priceDebounceDelay = 300;


        this.resizeCanvasToOverFold();
		this.$toggle = [];
		this.$alternative = [];
		this.activeMenuTab = {'toggle': 'signtype', 'connection' : false};
		this.activeToolbar = "signtype";
		this.toolBeforeObject = {'tool': false, 'connection' : false};
		this.menuScaled = false;
		this.hiddenToolWrap = false;
		this.oldMaterial = false;
		this.canvas = canvas;
		this.prevSpecialMenu = false;
		this.currentSpecialmenu = false;
		this.svgCheck = null;
        this.signQuantity = 1;

		this.loadingBlack = '<div class="lds-ring black"><div></div><div></div><div></div><div></div></div>';

		//classes
		this.favoritesModule = false;

		//comes from singOperatio
		this.activeSelection 

		this.maxPreviewWidth;
		this.maxPreviewHeight;

         this.$wrap = $('#studio-wrap');
         this.$menu = $('#signbuilder-left-menu');
         this.$toolwrap = $('#toolwrap', this.$menu);

		this.initialize();
		this.bind();
        this.toolMenuMessageHandler();
	}

    resizeCanvasToOverFold(){
        const studioWrap = document.querySelector('#studio-wrap');
        const rect = studioWrap.getBoundingClientRect();
        let scrollTop = window.scrollY; // or window.pageYOffset for older browsers
        scrollTop = rect.top + scrollTop;
        studioWrap.style.height = `calc(100vh - ${scrollTop}px)`;
    }

	initialize(){
        let self = this;
		this.$window = $(window);
        this.menu = document.getElementById('signbuilder-left-menu');
        $('.tools.tab-wrap', this.menu).removeClass('hidden');
        $('#toolwrap', this.menu).removeClass('hidden');

        if(studioValues.customer_type){
            this.tax_type = (studioValues.customer_type == 'business') ? 'excl' : 'incl';
        }else{
            this.tax_type = 'incl';
        }


		this.$menuTabs = $('.tabs', this.$menu);
		this.$toolBars = $('.toolbar', this.$menu);
		this.$toggle = $('[data-tool-toggle]',this.$menu);
		this.$alternative = $('[data-sign-optiontype]', this.$menu);
		this.$menuToggle = $('.toggle-close-menu-wrp', this.$toolwrap);
		this.$editControl = $('.edit-control');
		this.$priceDisplay =$(".price-displays-top");
        this.$priceDisplayBottom = $('.price-displays-bottom');
		this.$productTypeDisplay = $(".stdn-name");
		this.$materialParent = $(".material-parent");
		this.$products = $("[data-sign-optiontype='material']", this.$toolwrap);
		this.$materials = $('.toolbar.t-material [data-connection-display]', this.$toolwrap);
		this.$materialsTabs = $('.toggle-material', this.$menu);
		this.$mountings = $('[data-sign-optiontype="mounting"]', this.$toolwrap);
		this.$shapes = $('[data-sign-optiontype="shape"]', this.$toolwrap);
		this.$dimensions = $('[data-sign-optiontype="dim"][type="number"]',this.$toolwrap);
		this.$sizes = $('[data-sign-optiontype="sizes"]', this.$toolwrap);
        this.$doubleSideWrp = $('#dubble-sided-options', this.$toolwrap);
        this.$doubleSideBtn = $('.amount-of-sign-sides', this.$toolwrap);

		/*upload images */ 
		this.$uploadGrid = $('.grid.upload-grid', this.$toolwrap);

		this.$fontSize = $(".font-size-option-value", this.$toolwrap);
		this.$symbols = $(".new-symbol-grid", this.$toolwrap);
		this.$userUploads = $(".image-upload-form .grid", this.$toolwrap);
        this.$changeImages = $(".change-image-settings .grid", this.$toolwrap);
		this.$backgroundColor = $('[data-sign-optiontype="background"]', this.$toolwrap);
		this.$borderColor = $('[data-sign-optiontype="bordercolor"]', this.$toolwrap);
		this.$gravyrColors = $('[data-sign-optiontype="gravyr-colors"]', this.$toolwrap);
		this.$addCustomShape = $('[data-sign-optiontype="create-shape"]', this.$toolwrap);
		this.$customShapeBg = $('[data-sign-optiontype="shape-bg-color"]', this.$toolwrap);
		this.$customShapeBorderColor = $('[data-sign-optiontype="shape-border-color"]', this.$toolwrap);
		this.$symbolColor = $('[data-sign-optiontype="symbolcolor"]', this.$toolwrap);
		this.$alignObject = $('[data-sign-optiontype="adjust"]');
		this.$rotateObject = $('[data-sign-optiontype="rotate"]', this.$toolwrap);
		this.$flipObject = $('[data-sign-optiontype="flip"]', this.$toolwrap);
		this.$adjustLayer = $('[data-sign-optiontype="move_layer"]', this.$toolwrap);
		this.$selectObjectContainers = $('.text-objects-container', this.$toolwrap).add('.shape-objects-container');
		this.$insertText = $('[data-sign-optiontype="add-text"]', this.$toolwrap);
		this.$copyBtn = $('[data-sign-optiontype="copy"]');
		this.$deleteBtn = $('[data-sign-optiontype="delete"]');
		this.$formatText = $('[data-sign-optiontype="format-text"]', this.$toolwrap);
		this.$autoSizeSettings = $('.size-options-auto input[type="checkbox"]', this.$toolwrap);
		this.$activeColorWraps = $('[data-sign-optiontype-plc]', this.$toolwrap);
		this.$productDetails = $('.product-details', this.$toolwrap);
		this.$eyeDropp = $('.color-picker', this.$toolwrap);
		this.$objectDistanceInput = $('.position-values[data-optiontype]', this.$toolwrap);
		this.$objectDistanceText = $('.position-values[data-optiontype="text"]', this.$toolwrap);
		this.$objectDistanceSymbol = $('.position-values[data-optiontype="symbol"]', this.$toolwrap);
		this.$objectDistanceShape = $('.position-values[data-optiontype="shape"]', this.$toolwrap);

		//other menus gallery,symbols,and object/inner
		this.$shapesObjMenu = $(".new-shape-form");
		this.$shapesObjToggle = $(".new-shape-toggle");
		this.$symbolObjMenu =  $(".symbol-gallery");
		this.$symbolObjToggle = $(".new-symbol-gallery-toggle");
        this.$changeImageMenu = $('.change-image-menu');

	
		this.$productDetailsToggle = $('.category-read-more',this.$toolwrap);

		//nliders
		this.$radiusSlider = $("#radius-range", this.$toolwrap);
		this.$radiusInput = $(".radius-input", this.$toolwrap);
		this.$bgBorderSlider = $("#background-thicc-range", this.$toolwrap);
		this.$bgBorderinput = $("#background-thicc-input", this.$toolwrap);
		this.$shapeRadiusSlider = $("#shape-radius-range", this.$toolwrap);
		this.$shapeRadiusSliderInput = $(".shape-radius-input", this.$toolwrap);
		this.$shapeBorderSlider = $("#shape-border-range", this.$toolwrap);
		this.$shapeBorderInput = $(".shape-border-input", this.$toolwrap);
        this.$opacitySlider = $("#opacity-range", this.$toolwrap);
        this.$opacityInput = $(".opacity-input", this.$toolwrap);


		//fontbuttons and ui
		this.$fontItalic = $(".f-italic", this.$toolwrap);
		this.$fontBold = $(".f-bold", this.$toolwrap);
		this.$fontUnderline = $(".f-underline", this.$toolwrap);
		this.$fontLeft = $(".f-left", this.$toolwrap);
		this.$fontCenter = $(".f-center", this.$toolwrap);
		this.$fontRight = $(".f-right", this.$toolwrap);
		this.$fontPreview = $(".option-font-preview .font-name", this.$toolwrap);

        this.$fixedSizesContainer = $('.size-options-menu .fixed-size-container', this.$toolwrap);


        this.$toggleInsideMenu = $('.toggle-inside-menu');

        this.$changeImageMenu.on('click', () => {
            this.openExpandable('change-image');
            uploadsManager.updateImagesFromServer().then(() => uploadsManager.renderImages());
        });

		this.$signBuilderR = $('#signbuilder-right-content');
		//Buttons for preview,buy/share....
		this.$addToCartBnt = $('.skyltstdn-add-to-cart', this.$signBuilderR);
		this.$buyNow = $('.stdn-buy-now', this.$signBuilderR);
		this.$addToFavorites = $('.skyltstdn-add-favorite', this.$signBuilderR);
        



		//share 
		this.$favListModule = $('#skyltstdn-favorite-list', this.$signBuilderR);

		//splash
		this.$addTocartSplash = $('.adding-to-cart-splash');
		this.$addTocartSplashText = $('.cart-splash-text');

		//gets added on click
		this.$shareWindow;
		this.$previewWindow;
	}


        bind(){
            let self = this;
            //Buttons for buying/sharing/previewing sign
            this.$wrap.on('click', '.skyltsdn-preview', () =>{
                signSaveAndLoad.previewSign();
            });

            this.$addToCartBnt.on('click', () =>{
                signSaveAndLoad.addSignToCart();
            });

            this.$buyNow.on('click', () =>{
                signSaveAndLoad.addSignToCart(true);
            });


            this.$addToFavorites.on('click', () =>{
                this.addToFavorites();
            });



            /*managing the amount that will be added to cart*/
            $('.stdn-incrament-btn').on('click', function(){
                let type = $(this).attr('data-action');
                let min = 1;
                let max = 10000;

                if(type == 'dec'){
                    self.signQuantity -= 1;
                    if(self.signQuantity < min){
                        self.signQuantity = min;
                    }                    
                }else{
                    self.signQuantity += 1;
                    if(self.signQuantity > max){
                        self.signQuantity = max;
                    }
                }

                $('.stdn-amount-input').val(self.signQuantity);
                self.updatePrice();
                //self.updateQuantityElements();
            });

            $('.stdn-amount-input').on('input', function(){
                let input = $(this);

                let newVal = parseInt(input.val());

                if(newVal == 0){
                    newVal = 1;
                }

                let min = parseInt(input.attr('min'));
                let max = parseInt(input.attr('max'));

                if (newVal < min) {
                    newVal = min;
                }
                if (newVal > max) {
                    newVal = max;
                }

                self.signQuantity = newVal; 
                self.updatePrice();
                //self.updateQuantityElements();
            });


            this.$wrap.on('click', '.skyltsdn-share', () => {
                this.openShareModal(null, website_base_url);
                signSaveAndLoad.shareSign();
            });



            this.$wrap.on('click', '.skyltsdn-restart', ()=>{
                signSaveAndLoad.clearLocalData();
            })

            this.$wrap.on('click', '.skyltsdn-undo', function() {
                signSaveAndLoad.undo();
            });

            this.$wrap.on('click', '.skyltsdn-redo',function() {
                signSaveAndLoad.redo();
            });

            //menu tabs
            this.$toggle.on('click', (event) => {
                const item = $(event.currentTarget);
                const type = item.attr('data-tool-toggle');
                const connection = item.attr('data-connection-display');
                this.handelMenuToggle(type,connection);
            })

            this.$menuToggle.on('click', () => {
                this.hideTools();
            });

            $('.toolbar').each(function() {
                var $this = $(this);
                $this.on('scroll', function() {
                    self.updateScrollFade();
                });
            });

            //handle materials display
            this.$materialParent.on('click', (event) =>{
                const item = $(event.currentTarget).attr('data-assoc');
                this.oldMaterial = item;
                this.updateMenuTab('material', item);
                this.changeToolbar('material', item);
                this.handleMaterialToggle(item);
            });

            //handnle material click
            this.$products.on('click', (event) => {
                const itemID = $(event.currentTarget).attr('data-sign-option');
                this.updateProductSelectionInMenu(itemID);
            });

            //edit button under object
            this.$editControl.on('click', ()=>{
                this.changeActiveTool('settings', canvas.getActiveObject());
                this.showTools();
            });

            //this click on mounting 
            this.$mountings.on('click', (event)=>{
                let id = $(event.currentTarget).attr('data-assoc');
                this.selectMounting(id);
            });

            this.$shapes.on('click', (event)=>{
                let ID = $(event.currentTarget).data('assoc');
                let name = $(event.currentTarget).data('sign-option');
                this.selectShape(ID,name);
            });

            this.$dimensions.on('change', function(e) {
                e.preventDefault();
                let sizeObject = {
                    element: $(this),
                    type: $(this).attr('data-sign-option'),
                    value: $(this).val(),
                }

                self.updateSize(sizeObject);
            });

            this.$sizes.on('change', (e) =>{
                let value = $(e.currentTarget).val().split("x");
                let width = value[0]
                let height = value[1];

                let sizeObject = {
                    element: $(this),
                    type: 'fixed',
                    width: width,
                    height: height
                }

                this.updateSize(sizeObject);
            });


            this.$toolwrap.on('click', '.upload-mini button', function(){
                 const $element = $(this); 
                  $element.siblings('input')?.trigger('click');
            })

            this.$toolwrap.on('change', '.upload-mini input', (e) => {
               let fileList = e.target.files;
               let element = $(e.target);
               if (fileList.length > 0) {
                  this.uploadFiles(fileList, element);
               }
           });

            this.$toolwrap.on('dragover', '.upload-mini',(e)=>{
                e.stopPropagation();
                e.preventDefault();
                $(e.currentTarget).addClass("hover");
            });

            this.$toolwrap.on('dragleave', '.upload-mini', (e) =>{
                e.stopPropagation();
                e.preventDefault();
                $(e.currentTarget).removeClass("hover");
            });


            this.$toolwrap.on('drop', '.upload-mini', (e)=>{
                e.preventDefault();
                e.stopPropagation();
                const target = $(e.currentTarget);
                target.removeClass("hover");
                const input = target.find('input');
                this.uploadFiles(e.originalEvent.dataTransfer.files, input);
            });

         this.$toolwrap.on('input', '.upload-search input', (e) => {
            const target = $(e.target); 
            const value = target.val(); 
            const grid = target.parent().siblings('.upload-grid');
            uploadsManager.filterImages(value, grid);
         });

            this.$uploadGrid.on('click', 'li .actions button', (e) =>{
                e.preventDefault();
                e.stopPropagation()
                uploadsManager.handleActions($(e.currentTarget));
                //return false;
            });

            this.$productDetailsToggle.on('click', (e)=>{
                this.openProductDetails($(e.currentTarget));
            });

            //this is for menus for object shapes
            this.$shapesObjToggle.on('click', ()=>{
                this.openShapes();
            });
            this.$symbolObjToggle.on('click', () =>{
                this.openGallery();
            })

            this.$toggleInsideMenu.on('click', ()=>{
                this.closeInnerMenu();
            });

            this.$symbolObjMenu.on('click', ".categories li", (e) => {
                let term = $(e.currentTarget).attr("value");
                symbolMangement.updateSymbolFilterTerm(term);
            });

            this.$symbolObjMenu.on('input', ".symbol-search", (e) => {
                let term = $(e.currentTarget).val().trim();
                symbolMangement.updateSymbolFilterTerm(term);
            });

            this.$symbols.on("dragstart", 'li', (e) => {
                e.originalEvent.dataTransfer.setData("symbol", $(e.currentTarget).attr("data-symbol"));
                let name = $(e.currentTarget).attr("data-name");
                e.originalEvent.dataTransfer.setData("name", name);
                signOperation.setObjectName(name);
                e.originalEvent.dataTransfer.setData("uploadType", 'symbol');
            });


            this.$symbols.on('click','li', (e) => {
                let symbol = $(e.currentTarget).attr("data-symbol");
                let name = $(e.currentTarget).attr("data-name");
                signOperation.setObjectName(name);
                signOperation.addSymbolByFileName(symbol);
            });

            this.$userUploads.on("dragstart", 'li', (e) => {
                e.originalEvent.dataTransfer.setData("symbol", $(e.currentTarget).data("target"));
                e.originalEvent.dataTransfer.setData("item", $(e.currentTarget).data("item"));
                let name = $(e.currentTarget).attr("data-name");
                signOperation.setObjectName(name);
            });

            this.$userUploads.on('click', 'li', (e) =>{
                let url = $(e.currentTarget).attr('data-target');
                let id = $(e.currentTarget).attr('data-item');
                let name = $(e.currentTarget).attr('data-name'); // Corrected the typo here
                signOperation.setObjectName(name);
                this.addUserUploadToSign(url, id);
            });

            this.$changeImages.on('click', 'li', (e) => {
                let url = $(e.currentTarget).attr('data-target');
                let id = $(e.currentTarget).attr('data-item');

                $.studioImageManager.changeImage(url, id);
            });

            this.$changeImages.on("dragstart", 'li', (e) => {
                e.originalEvent.dataTransfer.setData("symbol", $(e.currentTarget).attr("data-target"));
                e.originalEvent.dataTransfer.setData("item", $(e.currentTarget).attr("data-item"));
                e.originalEvent.dataTransfer.setData("changeImage", true);
                let name = $(e.currentTarget).attr("data-name");
                signOperation.setObjectName(name);
            });



            this.$menuTabs.on('click', '[data-tool-toggle="uploads"]', (e)=>{
                //load previous images
                uploadsManager.updateImagesFromServer().then(() => uploadsManager.renderImages());

            });

            this.$toolwrap.on('click', '.fixed-size-option-btn', function() {
                document.querySelectorAll('.fixed-size-option-btn').forEach(function(btn) {
                    btn.classList.remove('active');
                });
                $(this).addClass('active');

                let sizeObject = {
                    element: $(this),
                    width: $(this).data('width'),
                    height: $(this).data('height'),
                    type: 'fixed',
                }

                self.updateSize(sizeObject);
            });


            this.$backgroundColor.on('click', (e) =>{
                signOperation.changeBackgroundColor($(e.currentTarget).attr('data-sign-option'));
            });

            this.$borderColor.on('click', (e) =>{
                signOperation.changeBorderColor($(e.currentTarget).attr('data-sign-option'));
            });
            this.$gravyrColors.on('click', (e) =>{
                signOperation.setGravyrColors($(e.currentTarget));
            });
            this.$addCustomShape.on('click', (e) =>{
                manageCustomObjects.createUserShape($(e.currentTarget).attr('data-sign-option'));
            });
            this.$customShapeBg.on('click', (e) =>{
                manageCustomObjects.updateShapeBackground($(e.currentTarget).attr('data-sign-option'));
            });
            this.$customShapeBorderColor.on('click', (e) =>{
                manageCustomObjects.updateShapeBorderColor($(e.currentTarget).attr('data-sign-option'));
            });
            this.$symbolColor.on('click', (e) =>{
                manageCustomObjects.updateSymbolColor($(e.currentTarget).attr('data-sign-option'), $(e.currentTarget).attr('data-assoc'));
            });
            this.$alignObject.on('click', (e) =>{
                this.alignItems($(e.currentTarget).attr('data-sign-option'));
            })
            this.$rotateObject.on('click', (e) =>{
                this.rotateObject($(e.currentTarget).attr('data-sign-option'));
            })
            this.$flipObject.on('click', (e) =>{
                this.flipObject();
            })
            this.$adjustLayer.on('click', (e) =>{
                this.adjustLayer($(e.currentTarget).attr('data-sign-option'));
            })
            this.$selectObjectContainers.on('click','[data-sign-optiontype="select-obj"]',(e) =>{
                signOperation.selectObjWithBtn($(e.currentTarget).attr('data-sign-option'));
            });
            this.$insertText.on('click', (e) =>{
                manageCustomObjects.insertNewText();
            });

            $document.on('click', '[data-sign-optiontype="select-obj"]', (e) => {
                this.hoverSelect = false;
                signOperation.selectObjWithBtn($(e.currentTarget).attr('data-sign-option'));
            });


            $document.on('click', '[data-sign-optiontype="copy"]', (e) => {
                this.dublicateObject();
            });

            $document.on('click', '[data-sign-optiontype="change-selectable"]', (e) => {
                let element = $(e.currentTarget);
                element.toggleClass('active');
                let state = element.hasClass('active');
                let object = this.canvas.getActiveObject();
                let id = object.objId;
                manageCustomObjects.changeSelectionStatus(state, object);

                let tab = $(`.obj-btn[data-sign-option="${id}"] .lock-toggle`);
                if (!state) {
                    tab.removeClass('active');
                } else {
                    tab.addClass('active');
                }
            });

            $document.on('click', '[data-sign-optiontype="select-obj"] .lock-toggle', (e) => {
                e.preventDefault();
                e.stopPropagation();
                $(e.currentTarget).toggleClass('active');
                let element = $(e.currentTarget);
                let state = element.hasClass('active');
                let objectId = element.parent().attr('data-sign-option');
                manageCustomObjects.changeSelectionStatus(state, objectId);
            });


            this.$deleteBtn.on('click', (e) =>{
                manageCustomObjects.deleteObject();
            });
            this.$formatText.on('click', (e) => {
                let target = $(e.currentTarget);
                if(target.hasClass('disabled')) return;
                let textOption = target.attr('data-sign-option');
                this.formatText(textOption);
            });

            this.$autoSizeSettings.on('click',(e) =>{
                let type = $(e.currentTarget).attr('data-option-type');
                let state = $(e.currentTarget).prop('checked');
                if(type == "W"){
                    this.updateAutoWidth(state);
                }else if(type == "H"){
                    this.updateAutoHeight(state);
                }
            });

            this.$doubleSideBtn.on('click', (e) => {
                let btn = $(e.currentTarget);
                let value = btn.attr("data-sign-optiontype");
                let status = (value === 'double-sided') ? true : false;
                this.updateSignSides(status);
                if (status && !signSidesManager.hasSides()) {
                    handleRealDim.updateDim('double_sided', true);
                    signSidesManager.createNewSides();
                    this.debouncedUpdatePrice();
                } else if (!status && (signSidesManager.hasSides() || handleRealDim.getDim('double_sided'))) {
                    handleRealDim.updateDim('double_sided', false);
                    signSidesManager.removeSides();
                }
            });

            //transform to vanilla js
            this.menu.addEventListener('mouseover', (e) => {
                let { target } = e;

                if (target.matches('[data-sign-optiontype="select-obj"]')) {
                    this.hoverObjectSelect(target);
                }
            })

            this.menu.addEventListener('mouseleave', (e) => {
                this.hoverSelect = false;
                let { target } = e;
            })

            this.menu.addEventListener('click', (e) => {
                this.hoverSelect = false;
                let { target } = e;
            })

            $('.double-sided-options.side-design-area div').on('click', (e) => {
                let side = $(e.currentTarget).attr('data-val');
                this.updateSignSideBtn(side);
                signSidesManager.changeSides(side);
            });



            this.$activeColorWraps.on('click', '[data-sign-optiontype]', (e)=>{
                let element = $(e.currentTarget);
                let type = element.attr('data-sign-optiontype');
                let color = element.attr('data-sign-option');

                this.updateColors(type, color);
            });

            this.$eyeDropp.on('click', (e) =>{
                advancedColors.useEyeDropper($(e.currentTarget).attr('data-eye-optiontype'));
            });

            this.$objectDistanceInput.on("change", (e)=>{
                let target = $(e.currentTarget);
                manageCustomObjects.setObjectDistance(target.attr('data-position'), target.val());
            });

            this.$toolwrap.on('click', '.stdn-template-item', function() {
                let templateId = $(this).data('val');
                apiClient.getTempalteData(JSON.stringify({ templateId: templateId }))
                    .then(result => {
                        signOperation.addTemplateToSign(result);
                    });
            });


            $document.on('change', '.font-weight-select',  function (e) {
                let val = $(this).val();
                let options = {weight : val}
                manageCustomObjects.toggleTextFormat(options, self.canvas.getActiveObject());
            });

            this.addBorderSlider();

            $document.on('click', '.copy-link-btn', function(e){
                e.preventDefault();
                let text = $(this).attr('data-copy');
                let button = $(this);

                if (navigator.clipboard && window.isSecureContext) {
                    return navigator.clipboard.writeText(text).then(() => {
                        button.addClass("copied");
                        return true;
                    }).catch((err) => {
                            console.error('Failed to copy: ', err);
                            return false;
                        });
                }

                // Fallback for older browsers
                try {
                    const textArea = document.createElement('textarea');
                    textArea.value = text;

                    // Make the textarea out of viewport
                    textArea.style.position = 'fixed';
                    textArea.style.left = '-999999px';
                    textArea.style.top = '-999999px';
                    document.body.appendChild(textArea);

                    textArea.focus();
                    textArea.select();

                    // Execute the copy command
                    const successful = document.execCommand('copy');
                    textArea.remove();
                    button.addClass("copied");
                    return successful;
                } catch (err) {
                    console.error('Failed to copy: ', err);
                    return false;
                }
            });

            $document.on('click', '.tax-type-toggle', function(e){
                e.preventDefault();
                e.stopPropagation();

                let tax_type = $(this).attr('data-tax');
                $('.tax-type-toggle').each(function(index, elem){
                    let element = $(elem);
                    if(element.attr('data-tax') == tax_type){
                        element.addClass('active');
                    }else{
                        element.removeClass('active');
                    }
                });

                self.tax_type = tax_type;
                self.updatePrice();
            });

            $document.on('click', '.stdn-price-container', function(e){
                const currentSelection = $(this).attr('data-showing');
                const newSelection = (currentSelection === 'piece') ? 'total' : 'piece';  

                $('.stdn-price-container').attr('data-showing', newSelection);
            });

            $document.on('click', '.sign-option[data-sign-optiontype="orientation"]', function(){
                const manageOrientetionClass = new ManageOrientation(); 
                manageOrientetionClass.setOrientationSelection($(this).attr('data-sign-option'));
            });

            $(".font-size-option-value").change(function(e) {
                let activeObject = canvas.getActiveObject();

                if (activeObject == null || activeObject.type != "i-text") {
                    return;
                }
                var fontSizePt = parseInt($(".font-size-option-value").val());
                if (fontSizePt < 1) {
                    return;
                }

                let transformedPT = fabric.util.parseUnit(`${fontSizePt}pt`);

                activeObject.set({
                    'scaleX': 1,
                    'scaleY': 1,
                });

                activeObject.setCoords();
                activeObject.set('fontSize', transformedPT);
                activeObject.setCoords();


                if(handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h')){
                    signOperation.updateAutoSize({action:'change'});
                }

                canvas.requestRenderAll();

            });

            
        }

        toolMenuMessageHandler(){
            const objectMsgManger = new ManageToastMessages(document.getElementById('object-message-container'));

            this.eventBus.on('object:tool_menu_message', (data) => {
                let existingMessages = objectMsgManger.getMessages();
                let dataType = data.type ?? false;


                if(dataType){
                    let stop = false;
                    for(const messageElement of existingMessages){
                        messageElement.remove();
                    }
                    if(data.type === 'success'){
                        objectMsgManger.success(data.message, 3000, data.message_id);
                    }else{
                        objectMsgManger.error(data.message, data.message_id);
                    } 
                }
            });

            this.eventBus.on('object:tool_menu_message:clear', (data) => {
                objectMsgManger.clear(true);
            });

        }

   getAllElements() {
      if (!this.allElements) {
         this.allElements = elementUpdater.getElement();
      }
      return this.allElements;
   }


	///Setters and getters 
	setActiveSelection(value){
		this.activeSelection = value;
	}

    getSignQuantity(){
        return this.signQuantity;
    }

	//Menu Functions 

	addingToCartAEvent(){
		this.waitingForCart = true;
		this.typingTextSplash();
		//disable buttons
		this.$addToCartBnt.prop("disabled", true);
		this.$buyNow.prop("disabled", true);
		if(lastActiveTool != 'closed'){
			this.$toolwrap.addClass('dont-display');
			onCanvasGUIHandler.menuCanvasSizeControl(true);
		}
	}

	addedToCartAEvent(){
		this.waitingForCart = false;
		this.$addToCartBnt.prop("disabled", false);
		this.$buyNow.prop("disabled", false);
		this.$addTocartSplash.fadeOut( "slow" );
		if(lastActiveTool != 'closed'){
			this.$toolwrap.removeClass('dont-display');
			onCanvasGUIHandler.menuCanvasSizeControl(false);
		}
	}

	handelMenuToggle(type, connection){
		if(this.hiddenToolWrap){
			this.showTools();
		}
		this.updateMenuTab(type,connection);
		this.changeToolbar(type,connection);
		this.closeInnerMenu();
	}

	updateMenuTab(menu = false, connect = false){
		if(!menu && !section){
			menu = this.activeMenuTab.toggle;
			connect = this.activeMenuTab.connection;
		}
		this.activeMenuTab.toggle = menu;
		this.activeMenuTab.connection = connect;

		const $elementsToActivate = this.$toggle.filter((index, element) => {
			element = $(element);
			return (element.attr('data-tool-toggle') === menu) && (!connect || (element.attr('data-connection-display') === connect));
		});
		
		const $elementsToDeactivate = this.$toggle.not($elementsToActivate);
		$elementsToActivate.addClass('active');
		$elementsToDeactivate.removeClass('active');
	}

	handleMaterialToggle(item){
		let showArray = this.getRecursiveParents(item);

		this.$materialsTabs.addClass('dont-display');

		const selector = showArray.map(item => `[data-connection-display='${item}']`).join(', ');

		this.$materialsTabs.filter(selector).removeClass('dont-display');
	}

	updateProductSelectionInMenu(id, onLoad = false){
		const item = $(`[data-sign-option="${id}"]:not(.no-standard-option)`);

		if(!onLoad){
			let change = this.toggleMaterial(id);
           if(!change){
              return;
           }
		}
		this.$products.removeClass('active');
		this.$materialParent.removeClass('active');
		item.addClass('active');

		//show correct tabs and options if nested
		if(onLoad){
			let itemID = item.attr('data-assoc-parent');
			if(itemID){
				this.updateMenuTab('material', itemID);
				this.changeToolbar('material', itemID);
				this.handleMaterialToggle(itemID);
			}
		}

		//updated and shows materials 
		let parent = item.attr('data-assoc-parent');
		if(parent){
			this.activateMaterialParents(parent);
		}else{
			this.$materialsTabs.addClass('dont-display');
		}
		this.oldMaterial = false;
	}

	activateMaterialParents(parent){
		this.$materialParent.removeClass('active');
		this.$materialsTabs.addClass('dont-display');
		this.$materialParent.filter(`[data-assoc='${parent}']`).addClass('active');
		this.$materialsTabs.filter(`[data-connection-display='${parent}']`).removeClass('dont-display');
		let currentParent = this.$materialParent.filter(`[data-assoc='${parent}']`).attr('data-assoc-parent');

		while(currentParent){
			this.$materialParent.filter(`[data-assoc='${currentParent}']`).addClass('active');
			this.$materialsTabs.filter(`[data-connection-display='${currentParent}']`).removeClass('dont-display');
			currentParent = this.$materialParent.filter(`[data-assoc='${currentParent}']`).attr('data-assoc-parent');
		}
	}

	getRecursiveParents(parent){
		let nested = [parent];
		let currentParent = this.$materialParent.filter(`[data-assoc='${parent}']`).attr('data-assoc-parent');
		while(currentParent){
			nested.push(currentParent);
			currentParent = this.$materialParent.filter(`[data-assoc='${currentParent}']`).attr('data-assoc-parent');
		}
		return nested;
	}


	showSpecialMenu(menu,assoc){
		this.prevSpecialMenu = this.$toolBars.filter(`[data-tool-name]:not(.hidden)`);
		this.$toolBars.addClass("hidden");
		this.currentSpecialmenu = this.$toolBars.filter(`[data-tool-name="${menu}"]`).removeClass("hidden");
		if(assoc){
			if(menu == 'details'){
				this.$productDetails.hide();
				this.$productDetails.filter(`[data-assoc="${assoc}"]`).show();
			}
		}
		this.$toggleInsideMenu.addClass('active');
	}
	hideSpeicalMenu(){
		this.currentSpecialmenu.addClass('hidden');
		this.prevSpecialMenu.removeClass('hidden');
		this.prevSpecialMenu = false;
		this.currentSpecialmenu = false;
	}

	updateScrollFade(){
		let activeToolBar = $('.toolbar:not(".hidden")', this.$toolwrap);
		if(!activeToolBar[0]) return;

		let scrollHeight = activeToolBar[0].scrollHeight;
		let scrollTop = $(activeToolBar[0]).scrollTop();
		let height = $(activeToolBar[0]).innerHeight();

		// Checks whether the user has scrolled to the bottom
		if(scrollTop + height + 5 >= scrollHeight) {
			// If so, hides the fade effect
			activeToolBar.addClass('scrolled-to-bottom');
		} else {
			// If not, shows the fade effect
			activeToolBar.removeClass('scrolled-to-bottom');
		}
	}

	changeToolbar(tool, connection = false){
		if(this.activeToolbar !== tool){
			if(this.menuScaled){
				this.minimizeToolBar();
			}
			this.activeToolbar = tool;
			this.$toolBars.each((index, element) => {
				const $elementsToShow = this.$toolBars.filter(`[data-tool-name="${tool}"]`);
				const $elementsToHide = this.$toolBars.not($elementsToShow);
				$elementsToShow.removeClass("hidden");
				$elementsToHide.addClass("hidden");
			})
		}
		if(connection && tool == 'material'){
			this.$materials.each((index,element) => {
				let current = $(element);
				if(current.attr('data-connection-display') == connection){
					current.addClass("active-block");
				}else{
					current.removeClass("active-block");
				}
			});
		}
		if(tool != 'settings' && tool != 'details' ){
			this.toolBeforeObject.tool = tool;
			this.toolBeforeObject.connection = connection;
		}

		if(this.prevSpecialMenu){
			this.prevSpecialMenu=false;
			this.currentSpecialmenu=false;
		}
		this.updateScrollFade();
	}

	revertToPreviousTool(){
		this.changeToolbar(this.toolBeforeObject.tool,this.toolBeforeObject.connection);
	}

      changeActiveTool(tool, obj) {
         if (this.hoverSelect) return;
         if (this.canvas.getActiveObjects().length > 1) {
            onCanvasGUIHandler.menuCanvasSizeControl(true);
            this.hideTools();
            return;
         }
         if (!obj) {
            this.canvas.getActiveObject();
         }
         if (obj.name == 'noEdit') {
            return;
         }


         this.changeToolbar(tool);

         if (obj && obj.type == "i-text") {
            this.updateFontSizeInput(obj);
         }

         let selectButton = $('[data-sign-optiontype="change-selectable"]');
         if (!obj.selectable) {
            selectButton.addClass('active');
         } else {
            selectButton.removeClass('active');
         }

         if (obj) {
            this.eventBus.emit('object:tool_menu_message:clear');
            //**Display correct settings if click object */
            let type;
            let section = false;
            let singleColor = true;
            let hide = [];



            if (obj.name == 'userFabricShape' && obj.type == 'group') {
               $.each(obj._objects, function(i, element) {
                  if (element.hasOwnProperty('name') && element.name == "userShapeInner") {
                     obj = element;
                  }
               });
            }


            if (Object.hasOwn(obj, 'singleColor') && obj.singleColor === false) {
               singleColor = false;
            }

            let checkSwitch = true;

            if (obj.hasOwnProperty('name') && obj.name === 'userImage') {
               type = 'Bild';
               section = "symbol";
               checkSwitch = false;
               
               this.eventBus.emit('user_image:selected', {obj: obj});
            }

            if (checkSwitch) {
               switch (obj.type) {
                  case 'i-text':
                     type = 'Text';
                     section = 'text';
                     break;
                  case 'group':
                     type = 'Bild';
                     section = 'symbol';
                     break;
                  case 'image':
                     type = 'Bild';
                     section = 'symbol';
                     break;
                  case 'path':
                     type = "Bild";
                     section = 'symbol';
                     break;
                  case 'line':
                     type = "Bild";
                     section = 'symbol';
                     break;
                  case 'triangle':
                     type = 'Triangle';
                     section = 'shape';
                     hide.push('shape-setting-radius');
                     break;
                  case 'circle':
                     type = 'Cirkel';
                     section = 'shape';
                     hide.push('shape-setting-radius');
                     break;
                  case 'rect':
                     type = 'Rektangle';
                     section = 'shape';
                     break;
               }
            }

            $('.shape-settings .shape-cond-sett').show();


            for (let i = 0; i < hide.length; i++) {
               $(`.shape-settings .shape-cond-sett.${hide[i]}`).hide();
            }
            //sett type in title 
            $('.obj-sett-heading').html(`${type} inställningar`);
            //show correct settings container
            $('.obj-sett-con').addClass('hidden');
            let sectionSettings = $(`.${section}-settings`);
            sectionSettings.removeClass('hidden');

            //show hide colorsettings 

            if (section === 'symbol' && singleColor === true) {
               $('.tool-colorize', sectionSettings).removeClass('hidden');
            } else if (section === 'symbol' && singleColor !== true) {
               $('.tool-colorize', sectionSettings).addClass('hidden');
            } else {
               $('.tool-colorize', sectionSettings).removeClass('hidden');
            }


            //set correct values for distance and position tool
            this.updateElementDistance(section);


            //show correct settings in container
            switch (section) {
               case 'text':
                  break;
               case 'symbol':
                  toolMenu.addOpacitySlider(obj);
                  break;
               case 'shape':
                  toolMenu.addShapeRadiusSlider(obj);
                  toolMenu.addShapeBorderSlider(obj);
                  break;
            }
         }
      }


	enlargeToolbar(){
		this.$toolwrap.addClass('enlarge-content-menu-studio');
		this.menuScaled = true;
	}
	minimizeToolBar(){
		this.$toolwrap.removeClass('enlarge-content-menu-studio');
		this.menuScaled = false;
	}

	hideTools(){
		this.$toolwrap.addClass('dont-display');
		this.hiddenToolWrap = true;
		//adjust canvas to space
		onCanvasGUIHandler.menuCanvasSizeControl(true);
	}

   hoverObjectSelect(target) {
      this.hoverSelect = true;
      let objectToSelect = target.getAttribute('data-sign-option');
      signOperation.selectObjWithBtn(objectToSelect, true);
   }

	showTools(){
		this.$toolwrap.removeClass('dont-display');
		this.hiddenToolWrap = false;
		//adjust canvas to space
		onCanvasGUIHandler.menuCanvasSizeControl(false);
	}
    
    debouncedUpdatePrice() {
        clearTimeout(this.priceDebounceTimer);
        this.priceDebounceTimer = setTimeout(() => {
            this.updatePrice();
        }, this.priceDebounceDelay);
    }

        updatePrice(){
            loadingSpinner.addLoading(this.$priceDisplay);
            let realDimWithoutCode = deepCopy(handleRealDim.getAllDim());
            delete realDimWithoutCode.signcode;

            const tax = (this.tax_type == 'incl') ? true : false;
            apiClient.getPrice({"product": realDimWithoutCode, "mouting-holes" : realDimWithoutCode.mount_amount, "quantity" : this.signQuantity, 'tax' : tax})
                .then(result => {

                    loadingSpinner.removeLoading(this.$priceDisplay);

                    if(result.price.hasOwnProperty('adminHtml')){
                        $('#admin-prices-all').html(result.price.adminHtml);
                    }

                    const piecePrice = Math.round(result.price.price);
                    const totalPrice = Math.round(result.price.total_price);

                    this.$priceDisplay.find('.piece').html(`${piecePrice} kr/st`);
                    this.$priceDisplay.find('.total').html(`${totalPrice} kr`);

                    let flagQuouta = (result.price && result.price.hasOwnProperty('flag_quota')) ? result.price.flag_quota : false;
                    const basePrice = (result.price.hasOwnProperty('base_price')) ? result.price.base_price : false ;

                    const $discountElement = this.$priceDisplay.find('.stdn-pop-up-hover-wrp');

                    if(result.price.hasOwnProperty('discount_float')){
                        this.$priceDisplay.find('.stdn-pop-up-hover-wrp').html(`<span class="discount-badge stdn-pop-trigger">
Spara ${Math.round(100*(1-result.price.discount_float))}%</span>
<span class="stdn-pop-up stdn-text-disabled"><s>${Math.round(result.price.total_price_no_discount)}kr</s>
</span>`).removeClass('dont-display');

                    }else{
                        $discountElement.addClass('dont-display');
                    }

                    if(flagQuouta){
                        this.$priceDisplayBottom
                            .html(`<span class='stdn-text-discrete'>Kontakta oss för offert</span>`)
                            .removeClass('dont-display');
                    } else if(basePrice){
                        const baseTotal = basePrice.total_price;
                        const basePiece = basePrice.price;

                        const activePriceType = this.$priceDisplay.find('.stdn-price-container').first().attr('data-showing');

                        this.$priceDisplayBottom
                            .html(`<span class='stdn-text-discrete stdn-price-container' data-showing='${activePriceType}'>
                                <span class='total'>Utpris - ${Math.round(baseTotal)} kr</span>
                                <span class='piece'>Utpris - ${Math.round(basePiece)} kr/st</span>
                                </span>`)
                            .removeClass('dont-display');
                    }else{
                        this.$priceDisplayBottom
                            .html('')
                            .addClass('dont-display');
                    }

                }).catch(error => {
                    console.log(error);
                });
        }

	updateSignTitel(name){
		this.$productTypeDisplay.text(name);
	}

        toggleMaterial(product){
            if(handleRealDim.getDim('engraving') == false && all_product_settings["product_"+product].color_type == 1){
                let prompt = `Du byter nu till gravyr, vilket innebär att bilder och symboler med mer än en färg kommer att tas bort.`;
                if (confirm(prompt) != true) {
                    return false;
                }
            }
            handleRealDim.updateDim('double_sided', false);
            signSidesManager.removeSides();

            signOperation.setMaterial(product);
            signOperation.setOldSize({width: handleRealDim.getDim('width'),height: handleRealDim.getDim('height')});
            signOperation.setResizeSignContentStatus(true);
            masterSync({'updateBackgroundShape' : ''}, 'addTrue');
            return true;
        }

	selectMounting(id){
		this.activateMounting(id);
        updateProdSettings.setMounting(id);
		masterSync({
            'updateBackgroundShape' : true,
            'updateMounting':''
         });
	}

	activateMounting(id){
		this.$mountings.removeClass('active');
		this.$mountings.filter(`[data-assoc='${id}']`).addClass('active');
		
	}

	selectShape(id, name){
		this.activateShape(id);
        handleRealDim.updateDim('shape',name);
		masterSync({'updateBackgroundShape' : ''});
	}

	activateShape(id){
		this.$shapes.removeClass('active');
		this.$shapes.filter(`[data-assoc='${id}']`).addClass('active');
	}

	setSize(width, height){
		(width) ? $(this.$dimensions).filter('[data-sign-option="width"]').val(width): '';
		(height) ? $(this.$dimensions).filter('[data-sign-option="height"]').val(height): '';
	}

    updateSize(sizeObject){
        let type = sizeObject.type;

        if(type == 'width' || type == 'height') {
            if(!sizeObject.value){
                //reset cant be none or 0 
                value = (type == 'width') ? handleRealDim.getDim('width') : handleRealDim.getDim('height');
                sizeObject.element.val(value);
                return;
            }
        }

        let resizeStatus = false;
        signOperation.setOldSize({width: handleRealDim.getDim('width'),height: handleRealDim.getDim('height')});


        if(type == 'fixed'){
            $(this.$dimensions).filter('[data-sign-option="width"]').val(sizeObject.width);
            $(this.$dimensions).filter('[data-sign-option="height"]').val(sizeObject.height);

            let width = sizeObject.width;
            let height = sizeObject.height;

            if (width && height) {
                resizeStatus = true;
                let oldWidth = handleRealDim.getDim('width');
                let oldHeight = handleRealDim.getDim('height');
                let border = handleRealDim.getDim('border');
                if (border && border > 0) {
                    let xRatio = width / oldWidth;
                    let yRatio = height / oldHeight;

                    let ratio = (Math.abs(yRatio) >= Math.abs(xRatio)) ? yRatio : xRatio;
                    let newBorder = ratio * border;
                    handleRealDim.updateDim('border', newBorder);
                }
            }



            handleRealDim.updateWidth(width);
            handleRealDim.updateHeight(height);
        }
        else if(type == 'width'){
            handleRealDim.updateWidth(sizeObject.value);
        }else if(type == 'height'){
            handleRealDim.updateHeight(sizeObject.value);
        }

        signOperation.setResizeSignContentStatus(true);


        masterSync({'updateBackgroundShape' : ''}, true);
        //signOperation.updateBackgroundShape();
    }

	closeInnerMenu(){
		this.closeShapes();
		this.closeGallery();
		this.minimizeToolBar();
        this.closeExpandable();
		if(this.prevSpecialMenu){
			this.hideSpeicalMenu();
		}
		this.$toggleInsideMenu.removeClass('active');
	}

	openShapes(){
		this.enlargeToolbar();
		this.$shapesObjMenu.removeClass("hidden");
		this.$shapesObjToggle.addClass("active");
		this.$toggleInsideMenu.addClass('active');
	}

	openGallery(){
		this.enlargeToolbar();
		//show or hide simple colors 
		this.$symbolObjMenu.removeClass("hidden");
		this.$symbolObjToggle.addClass("active");
		this.$toggleInsideMenu.addClass('active');
		symbolMangement.updateSymbolFilter('');
	}

    openExpandable(type) {
        $('.tool-sections.expandable').addClass('hidden');
        $(`.tool-sections.expandable[data-type="${type}"]`).removeClass('hidden');
        this.$toggleInsideMenu.addClass('active');
    }

      closeExpandable(){
        $('.tool-sections.expandable').addClass('hidden');
        this.$toggleInsideMenu.addClass('active');
      }


	closeShapes(){
		this.$shapesObjMenu.addClass("hidden");
		this.$shapesObjToggle.removeClass("active");
	}
	closeGallery(){
		this.$symbolObjMenu.addClass("hidden");
		this.$symbolObjToggle.removeClass("active");
	}

	openProductDetails(item){
		let assoc= item.attr('data-assoc');
		this.showSpecialMenu('details', assoc);
	}

    async uploadFiles(files: FileList, element: JQuery) {
        let data = new FormData();
        let dataType = element.attr('data-type');

        for (const [key, value] of Object.entries(files)) {
            data.append(key, value);
        }

        element.wrap("<form>").closest("form").get(0).reset();
        element.unwrap();

        $(".loader").removeClass("hidden");
        apiClient.userUpload(data)
            .then(result => {
                if (result.success == true) {
                    if(dataType == 'change-image'){
                        let url = USER_UPLOAD_DIR + result.files[0].path;
                        let id = result.files[0].id;
                        
                        $.studioImageManager.changeImage(url,id);
                    }
                    uploadsManager.addNewImageToGallery(result.files);
                } else {
                    console.log("ERRORS: " + result.error);
                }
                $(".image-upload-form .loader").addClass("hidden");
            })
            .catch(errorData => {
                $(".image-upload-form .loader").addClass("hidden");
            });
    }


	hasFiles(formData) {
		let hasImages = false;
		for (let pair of formData.entries()) {
		  if (pair[1] instanceof File) {
			hasImages = true;
			break;
		  }
		}
		return hasImages;
	  }

	updateFontSizeInput(activeObject = false, scaling = false) {
		if(!activeObject){
			activeObject = this.canvas.getActiveObject();
		}
		if (activeObject == null || activeObject.type != "i-text") {
			return;
		}

		let heightPixels = activeObject.height * activeObject.scaleY;

        if(scaling){
            let ratio = activeObject.fontSize*activeObject.scaleX;
            activeObject.set({scaleX: 1, scaleY: 1, fontSize : ratio});
        }

        let fontSizePt = activeObject.fontSize / fabric.util.parseUnit('1pt');
		this.$fontSize.val(Math.round(fontSizePt));
	}

	addUserUploadToSign(url:string, id:number = null){
		let isSingleColor = signOperation.getSimpleColor();
		if (url.endsWith(".svg")) {
			signOperation.addSymbol(url, false, false, id);
		} else {
			if(isSingleColor){
				alert('Kan enbart lägga till svg för enkelfärgade skyltar');
				return;
			}
			signOperation.addImageToCanvas(url, id);
		}
	}

	updateElementDistance(type){
		let bgDistance = signOperation.getBackgroundDistance();
		if(!type){
			type = signOperation.getCurrentObjectSection();
		}
		switch(type){
			case 'text':
				this.setElementDistance(bgDistance, this.$objectDistanceText, this.activeSelection, type);
				break;
			case 'symbol':
				this.setElementDistance(bgDistance, this.$objectDistanceSymbol, this.activeSelection, type);
				break;
			case 'shape':
				this.setElementDistance(bgDistance, this.$objectDistanceShape, this.activeSelection, type);
				break;
		}
	}

	setElementDistance(values, elements, obj, type){
		if(!obj){
			return;
		}

		let distances = obj.getBoundingRect(true)
		let unitPerMM = fabric.util.parseUnit('1mm');
		let top = 0, right = 0, left = 0, bottom = 0;
		for (let key in values) {
			switch(key){
				case'top':
					top = ((values[key] + distances.top)/unitPerMM).toFixed(1);
				break;
				case'right':
					right = ((values[key] - distances.left - distances.width)/unitPerMM).toFixed(1);
				break;
				case'left':
					left = ((values[key] + distances.left)/unitPerMM).toFixed(1);
				break;
				case'bottom':
					bottom = ((values[key] - distances.top - distances.height)/unitPerMM).toFixed(1);
				break;
			}
		}
		elements.filter('[data-position="top"]').val(top);
		elements.filter('[data-position="right"]').val(right);
		elements.filter('[data-position="bottom"]').val(bottom);
		elements.filter('[data-position="left"]').val(left);
	}

      addRadiusSlider(max, standard) {
         if (!this.$radiusSlider.data('initialized')) {
            this.$radiusSlider.slider({
               min: 0,
               max: max,
               value: standard,
               slide: (event, ui) => {
                  let val = ui.value;
                  signOperation.updateCornerRadius(val);
                  mountingManager.calculateHolePlacement();
                  this.$radiusInput.val(val);
               }
            });

            this.$radiusInput.attr({
               "max": max
            });
            // Use .val() for setting the initial value too
            this.$radiusInput.val(standard);

            this.$radiusInput.on("input", (e) => {
               let val = this.checkSliderInputsForCorrectValue(e, this.$radiusSlider);
               signOperation.updateCornerRadius(val);
               mountingManager.calculateHolePlacement();
            });

            this.$radiusSlider.data('initialized', true);
         } else {
            this.$radiusSlider.slider('option', 'max', max);
            this.$radiusSlider.slider('option', 'value', standard);
            this.$radiusInput.attr({
               "max": max
            });
            // Use .val() here as well
            this.$radiusInput.val(standard);
         }
      }


      addBorderSlider(max = 100, value = 0) {
         if (!this.$bgBorderSlider.data('initialized')) {
            this.$bgBorderSlider.slider({
               min: 0,
               max: max,
               value: handleRealDim.getDim('border'),
               slide: (event, ui) => {
                  let val = ui.value;
                  this.$bgBorderinput.val(val);
                  handleRealDim.updateDim('border', val);
                  signOperation.updateBackgroundBorder(false, false, true);
               }
            });

            this.$bgBorderinput.attr({
               "max": max,
               "value": handleRealDim.getDim('border')
            });

            this.$bgBorderinput.on("input", (e) => {
               let val = this.checkSliderInputsForCorrectValue(e, this.$bgBorderSlider);
               handleRealDim.updateDim('border', val);
               signOperation.updateBackgroundBorder(false, false, true);
            });
            this.$bgBorderSlider.data('initialized', true);
         } else {
            this.$bgBorderSlider.slider('option', 'max', max);
            this.$bgBorderSlider.slider('option', 'value', handleRealDim.getDim('border'));
            this.$bgBorderinput.attr({
               "max": max,
            });
            this.$bgBorderinput.val(handleRealDim.getDim('border'));
         }
      }

   addOpacitySlider(obj = false) {
      if (!obj) return;
      let opacity = obj.opacity * 100;

      let updateOpacity = (value) => {
         obj = this.canvas.getActiveObject();
         if(obj.hasOwnProperty('belong')){
            obj = obj.belong;
         }
         let newOpacity = value / 100;
         obj.opacity = newOpacity;
         if(obj.hasOwnProperty('previousOpacity')){
            obj.previousOpacity = newOpacity; 
         }

         this.canvas.requestRenderAll();
      };

      if (!this.$opacitySlider.data('initialized')) {
         this.$opacitySlider.slider({
            min: 0,
            max: 100,
            value: opacity,
            slide: (event, ui) => {
               let val = ui.value;
               this.$opacityInput.attr({
                  "value": val,
               });

               updateOpacity(val);
            }
         });

         this.$opacityInput.attr({
            "max": 100,
            "value": opacity,
         });

         this.$opacityInput.on("input", (e) => {
            let value = e.value;
            this.$opacitySlider.slider('option', 'value', value);
            updateOpacity(value);
         });

         this.$opacitySlider.data('initialized', true);
      } else {
         this.$opacitySlider.slider('option', 'value', opacity);
         this.$opacityInput.attr({
            "max": 100,
            "value": opacity,
         });

      }
   }



	addShapeRadiusSlider(obj){
		if(!obj){
			obj = this.canvas.getActiveObject();
		}

		let curr,max;
		if(obj.type ==  'group' && obj.name == "userFabricShape"){
			obj = JSON.parse(JSON.stringify(obj._objects[0]));
		}
		curr = (obj.rx) ? obj.rx : 0; 
		max = (obj.width < obj.height) ? (obj.width+obj.strokeWidth)/2 : (obj.height+obj.strokeWidth)/2;
	
		curr = Math.round(curr/fabric.util.parseUnit("1mm"));
		max = Math.round(max/fabric.util.parseUnit("1mm"));
		
		if(!this.$shapeRadiusSlider.data('initialized')){
			this.$shapeRadiusSlider.slider({
				range: "min",
				min: 0,
				max: max,
				value: curr,
				slide: ( event, ui )  => {
					let val = ui.value; 
					manageCustomObjects.updateShapeRadius(val);
					this.$shapeRadiusSliderInput.val(val);
				}
			});

			this.$shapeRadiusSliderInput.attr({
				"max" : max,
				"value": curr  
			 });

			this.$shapeRadiusSliderInput.on('input', (e)=>{
				let val = this.checkSliderInputsForCorrectValue(e, this.$shapeRadiusSlider);
				manageCustomObjects.updateShapeRadius(val);
			});
			this.$shapeRadiusSlider.data('initialized', true);
		}else{
			this.$shapeRadiusSlider.slider('option', 'max', max);
			this.$shapeRadiusSlider.slider('option', 'value', curr);
			this.$shapeRadiusSliderInput.attr({
				"max" : max,
			 });
			this.$shapeRadiusSliderInput.val(curr);
		}
	}

	addShapeBorderSlider(obj){
		if(!obj){
			obj = this.canvas.getActiveObject();
		}
		
		let curr,max,type;
		//find current
		curr = 0;
		if(obj.type ==  'group' && obj.name == "userFabricShape"){
			curr = obj.cstBorder;
			type = obj._objects[0].type;	
		}else if(obj.group.name == "userFabricShape"){
			curr = obj.group.cstBorder;
			type = obj.group._objects[0].type;
		}
		let box = obj.getBoundingRect(true,true);
		let margin = 2;
		switch (type){
			case 'triangle':
				margin = 4;
				break;
			case 'circle':
				margin = 2;
				break;
			case 'rect':
				margin = 2
				break;
		}
		let shortest = (box.width < box.height) ? box.width/margin : box.height/margin;
		max = Math.floor(shortest/fabric.util.parseUnit("1mm"));
		if(!this.$shapeBorderSlider.data('initialized')){
			this.$shapeBorderSlider.slider({
				range: "min",
				min: 0,
				max: max,
				value: curr,
				slide: (event, ui) => {
					let val = ui.value; 
					manageCustomObjects.updateBorder(val);
					this.$shapeBorderInput.val(val);
				}
			});

			this.$shapeBorderInput.attr({
				"max" : max,
				"value" : curr
			});

			this.$shapeBorderInput.on('input', (e)=>{
				let val = this.checkSliderInputsForCorrectValue(e, this.$shapeBorderSlider);
				manageCustomObjects.updateBorder(val);
			});

			this.$shapeBorderSlider.data('initialized', true);
		}else{
			this.$shapeBorderSlider.slider('option', 'max', max);
			this.$shapeBorderSlider.slider('option', 'value', curr);
			this.$shapeBorderInput.attr({
				"max" : max,
			});
			this.$shapeBorderInput.val(curr);
		}

		
	}

      checkSliderInputsForCorrectValue(e, slider) {
         let element = $(e.currentTarget);
         let val = parseInt(element.val());
         let max = parseInt(element.attr('max'));

         // Check for NaN (when input is empty or invalid)
         if (isNaN(val)) {
            // You can either set it to 0 or to the current slider value
            val = 0; // or val = slider.slider('option', 'value');
            element.val(val);
         } else if (val < 0) {
            element.val(0);
            val = 0;
         } else if (val > max) {
            element.val(max);
            val = max;
         }

         // Use 'value' instead of 'values' for single-value sliders
         slider.slider('option', 'value', val);

         return val;
      }

	alignItems(option){
		let obj = this.canvas.getActiveObject();
		if (obj == null) return;
		if (option == "horizontal") {
			signOperation.positionHorizontal(obj);
			//centerObjectHorizontally();
		}else if (option == "vertical") {
			signOperation.positionVertical(obj);
			//centerObjectVertically();
		} else {
			signOperation.positionHorizontal(obj);
			signOperation.positionVertical(obj);
		}
		onCanvasGUIHandler.updateObjectControl();
		this.updateElementDistance();
	}
	rotateObject(option){
		if (this.activeSelection  == null) return;
		if (option == "cw") {
			signOperation.rotateObject(90,this.activeSelection);
		} else {
			signOperation.rotateObject(-90,this.activeSelection);
		}
		this.updateElementDistance();
	}
	flipObject(){
		let obj = this.canvas.getActiveObject();
		if (obj == null) return;
		signOperation.flipObject(obj);
		onCanvasGUIHandler.updateFlipButton();
	}
	adjustLayer(option){
		let obj = canvas.getActiveObject();
		if (obj == null) return;
		signOperation.moveLayer(option);
	}
	dublicateObject(){
		let obj = this.canvas.getActiveObject()
		if (obj == null) return;
		manageCustomObjects.dublicateActiveObject(obj);
	}
	formatText(option){
		let obj = canvas.getActiveObject();
		if (obj == null && !obj instanceof fabric.IText) return;

		manageCustomObjects.toggleTextFormat(option, obj);
	}

	updateTextFormatButtons(obj, args = {}) {
        let fontFamily = obj.fontFamily;
        let fontSettings = $.manageFonts.getCurrentFontSettings(fontFamily);

        if(!this.fontWeightDropDown){
            this.fontWeightDropDown = $('.font-weight-select');
        }

        this.fontWeightDropDown.empty();

        let objWeight = obj.fontWeight;
        if(objWeight == 'normal') objWeight = 400;
        if(objWeight == 'bold') objWeight = 700;
        // Dynamically create and add new options
        for (let weight in fontSettings) {
            let option = new Option(weight, weight, weight == objWeight, weight == objWeight); // Create a new Option element
            this.fontWeightDropDown.append(option); // Append it to the select element
        }

        this.fontWeightDropDown.select2();
        //check if italic is allowed
        let disableItalic = true;
        let activateItalic = false;
        if(fontSettings[objWeight]){
            if(fontSettings[objWeight]['italic']){
              disableItalic = false;
              if(obj.fontStyle == 'italic'){
                  activateItalic = true;
                  this.$fontItalic.addClass("active");
              }else{
              }
            }        
        }

        if(disableItalic){
			this.$fontItalic.addClass("disabled");
            this.$fontItalic.removeClass("active");
        }else{
            this.$fontItalic.removeClass("disabled");
            if(activateItalic){
                this.$fontItalic.addClass("active");
            }else{
                this.$fontItalic.removeClass("active");
            }
        }

        if(fontSettings[700]){
			this.$fontBold.removeClass("disabled");
            if(objWeight == 700){
			this.$fontBold.addClass("active");
            }else{
			this.$fontBold.removeClass("active");
            }
        }else{
			this.$fontBold.addClass("disabled");
			this.$fontBold.removeClass("active");
        }

		this.$fontPreview.text(obj['fontFamily']);

		this.$fontLeft.removeClass('active');
		this.$fontCenter.removeClass('active');
		this.$fontRight.removeClass('active');

        let objectTypingDirection = obj.textAlign;
        switch(objectTypingDirection){
            case 'left':
                this.$fontLeft.addClass('active');
                break;
            case 'center':
                this.$fontCenter.addClass('active');
                break;
            case 'right':
                this.$fontLeft.addClass('active');
                break;

        }
	}

	updateAutoWidth(state, onlyUpdateCheck = false){
        handleRealDim.updateDim('auto_size_w', state);
		let input= $(this.$dimensions).filter('[data-sign-option="width"]');
		if(state){
			input.hide();
		}else{
			input.show();
		}

		if(!onlyUpdateCheck){
			if(handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h')){
				signOperation.updateAutoSize({action:'autoWidth'});
			}
		}

		if(onlyUpdateCheck){
			let btn = this.$autoSizeSettings.filter('[data-option-type="W"]');
			if(state){
				btn.prop('checked', true);
			}else{
				btn.prop('checked', false);
			}
		}		
	}

	updateAutoHeight(state, onlyUpdateCheck = false){
        handleRealDim.updateDim('auto_size_h', state);
		let input = $(this.$dimensions).filter('[data-sign-option="height"]');
		if(state){
			input.hide();
		}else{
			input.show();
		}

		if(!onlyUpdateCheck){
			if(handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h')){
				signOperation.updateAutoSize({action:'autoHeight'});
			}
		}

		if(onlyUpdateCheck){
			let btn = this.$autoSizeSettings.filter('[data-option-type="H"]');
			if(state){
				btn.prop('checked', true);
			}else{
				btn.prop('checked', false);
			}
			
		}
	}

	showSizeInput(){
		$(this.$dimensions).show();
	}

   updateSignSides(status = false) {
      if (!status || status == 0) {
         $('[data-sign-optiontype="one-sided"]', this.$doubleSideWrp).addClass('active');
         $('[data-sign-optiontype="double-sided"]', this.$doubleSideWrp).removeClass('active');
         $('.double-sided-options.side-design-area div').addClass('dont-display');
      } else {
         $('[data-sign-optiontype="one-sided"]', this.$doubleSideWrp).removeClass('active');
         $('[data-sign-optiontype="double-sided"]', this.$doubleSideWrp).addClass('active');
         $('.double-sided-options.side-design-area div').removeClass('dont-display');
         let side = signSidesManager.getActiveSide();
         this.updateSignSideBtn(side);
      }
   }

   updateSignSideBtn(val = 1) {
      $(`.double-sided-options.side-design-area  div[data-val]`).removeClass('active');
      $(`.double-sided-options.side-design-area  div[data-val="${val}"]`).addClass('active');
   }

    updateQuantityElements(){

        function addToCartText(insert = false){
            insert = (!insert) ? '' : insert += ' '; 
            return `Lägg ${insert}i varukorgen`;
        }

        function buyNowText(insert = false){
            insert = (!insert) ? '' : insert += ' '; 
            return `Köp ${insert}nu`;
        }
        
        let cartText = '';
        let buyText = '';

        if(this.signQuantity == 1){
            cartText = addToCartText();
            buyText = buyNowText();
        }else{
            let addition_text = `${this.signQuantity}st`;
            cartText = addToCartText(addition_text);
            buyText = buyNowText(addition_text);
        }

        this.$addToCartBnt.html(cartText);
        this.$buyNow.html(buyText); 

    }

	updateColors(type, color){
		switch(type){
			case 'background':
				signOperation.changeBackgroundColor(color);
				break;
			case 'bordercolor':
				signOperation.changeBorderColor(color);
				break;
			case 'symbolcolor':
				manageCustomObjects.updateSymbolColor(color);
				break;
			case 'shape-bg-color':
				manageCustomObjects.updateShapeBackground(color);
				break;
			case 'shape-border-color':
				manageCustomObjects.updateShapeBorderColor(color);
				break;
		}
	}
	
	getColorChangeObject(type){
		let send; 
		let obj;
		switch(type){
			case 'background':
				this.backgroundInnerShape = signOperation.getbackgroundInnerShape();
				return [this.backgroundInnerShape, handleRealDim.getDim('background')];
			case 'bordercolor':
				this.backgroundShape = signOperation.getBackgroundShape();
				return [this.backgroundShape, handleRealDim.getDim('borderColor')];
			case 'symbolcolor':
				obj = canvas.getActiveObject();
				if (obj == null) return;
				return [obj,obj.fill];
			case 'shape-bg-color':
				obj = this.canvas.getActiveObject();
				if (obj == null) return;
				send; 
				$.each(obj._objects, function(){
					if($(this)[0]["name"] == 'userShapeInner'){
						obj = $(this)[0];
						let fill = $(this)[0].fill;
						send = [obj, fill];
					}
				})
				return send;
			case 'shape-border-color':
				obj = canvas.getActiveObject();
				if (obj == null) return;
				send; 
				$.each(obj._objects, function(){
					if($(this)[0]["name"] != 'userShapeInner'){
						obj = $(this)[0];
						let fill = $(this)[0].fill;
						send = [obj, fill];
					}
				})
				return send;
			default:
				return false;
		}
	}
	
	openShareModal(code, website_base_url) {
		if (code != null) {
			let link = `${website_base_url}/skyltstudion/?base=${code}`;
			this.$shareWindow.html(
				`<div class="stdn-rows-wrp">
					<h4>Dela din design med denna länken:</h4>
					<input class="share-url-input" readonly type="text" value="${link}"/>
					<button type="button" class="filled-btn-black-stdn stdn-icon-btn copy-link-btn" data-copy="${link}">
						<i class="material-icons copy-not-done">link</i>
						<i class="material-icons copy-done">done</i>
						<span>Kopiera länk</span>
					</button>
				</div>`
			);
			return;
		}
	
		let shareDom = '<div class="share-window"><div class="loading"><div class="spinner"></div><span>Sparar design....</span></div></div>';
		this.$shareWindow = $.featherlight(shareDom,{'variant': 'shareWindowWrp'}).$content;
	}

	openSignPreview(material = "plastic"){
         previewManager.resetSettings();
         previewManager.init();
         return;
         let type = "door";
         if(material == "sticker" || material == "vinyl"){
            type = "laptop";
         }

         let previewDom = '<div class="preview-window"><div class="preview-image '+type+'"><div class="loading"><div class="spinner"></div><span>Laddar förhandsgranskning....</span></div></div></div>';
         this.$previewWindow = $.featherlight(previewDom, {'variant': 'previewWindowWrp'}).$content;
         this.maxPreviewWidth = (this.$window.innerWidth()*0.8);
         this.maxPreviewHeight = (this.$window.innerHeight()*0.8);
         this.$previewWindow.css("width", this.maxPreviewWidth + "px");
         this.$previewWindow.css("height", this.maxPreviewHeight + "px");
	}

	appendPreviewImage(url){
		let img = new Image();
		const self = this;

		$(img).on("load", function(){
			
			let $prevImage = $('.preview-image', self.$previewWindow);
			$prevImage.append($(this));
			let width = $('img',$prevImage).outerWidth();
			let height = $('img',$prevImage).outerHeight();
			
			if(width > self.maxPreviewWidth || height > self.maxPreviewHeight){
				let ratio;
				if(width > height){
					ratio = self.maxPreviewWidth / width;
					width = self.maxPreviewWidth;
					height = height*ratio;
				}else{
					ratio = height > self.maxPreviewHeight/height;
					height = self.maxPreviewHeight;
					width = width*ratio;
				}
			}

			self.$previewWindow.parent().width(width).height(height);
			$('.preview-image .loading', self.$previewWindow).hide();

		}).attr({
			src:url
		});
	}

	async addToFavorites(newList){
		//first load 
		if(!this.favoritesModule || newList){
			this.favoritesLoading();
			this.favoritesModule = new Favorites;
			let list = await this.favoritesModule.getUserFavoriteLists();
			let html = "";
			this.favoritesDone();
            if(list && list.length > 0 ){
				if(!newList){
					this.$favListModule.show();
				}
				for(let i = 0; i < list.length; i++){
					html += `<button class="stdn-add-fav-btn" type="button" value="${list[i].id}">+ ${list[i].name}</button>`;
				}
				this.$favListModule.html(html);

				$('button', this.$favListModule).on('click', (e)=>{
					this.addSignTofavorite($(e.currentTarget).val());
				});
			}else{
               this.addSignTofavorite(0);
			}

			return;
		}

		//if no list or only one it adds direclty
		if (this.$favListModule.html() === '') {
			//addToList
			return;
		}

		//if more than one list toggles option
		this.$favListModule.toggle();
	}

	favoritesLoading(){
		this.$addToFavorites.prop("disabled", true);
		this.$addToFavorites.html(this.loadingBlack);
	}

	favoritesDone(){
		this.$addToFavorites.prop("disabled", false);
		this.$addToFavorites.html("<i class='icon-favorite'></i>");
	}

	favoritesAdded(){
		//$.showCustomEvent(`Skylten har lagts till i din inköpslista\n
		//				  <a href='https://signmakerr.se/inkopslistor/' style="text-decoration: underline">Gå till dina inköpslistor</a>`);
		this.$favListModule.hide();
		this.$favListModule.html(this.favortieList);
		$('button', this.$favListModule).on('click', (e)=>{
			this.addSignTofavorite($(e.currentTarget).val());
		});
	}

	addSignTofavorite(id){
		signSaveAndLoad.favoriteSign(id);
		this.favortieList = this.$favListModule.html();
		this.$favListModule.html(this.loadingBlack);
	}

   updateSizeMenu(avilableSizes = false, width, height) {
      if (!avilableSizes) return;
      let elements = this.getAllElements();
      this.$fixedSizesContainer.html('');
      for (let i = 0; i < avilableSizes.length; i++) {
         let id = 'size_' + avilableSizes[i];
         let size = elements[id];
         let active = (size.width == width && size.height == height) ? 'active' : '';
         let btn = $(`<button class="fixed-size-option-btn flat ${active}"  data-fixed-size-opt="${id}" data-width="${size.width}" data-height="${size.height}"></button>`);
         let conentWrp = $(`<span>${size.display_name}</span>`);
         btn.append(conentWrp);
         this.$fixedSizesContainer.append(btn);
      }
   }


	async typingTextSplash(){
		this.$addTocartSplash.fadeIn("fast");
		let words = ['SKAPAR DIN SKYLT', 'MÄTER MED VATTENPASS', 'RAKT SKA DET VARA', 'VILKEN SNYGGSKYLT!'];
		let used = [];
		let word
		let index = 0;
		let element = this.$addTocartSplashText;
		while(this.waitingForCart){
			if(words.length == 0){
				words = used;
				used = [];
			}
			//add random 
			await addNew(1000);
			await clear();
			//replace letters
	
			//fade out
		}
		
		//finnish sign message
		element.html("<h3>Färdig!</h3>");
	
		async function addNew(ms){
			let randomObjectIndex = getRandomInt(words.length);
			word = words.splice(randomObjectIndex, 1)[0];
			used.push(word);
			element.html("");
			//creates random letters
			let letter = "<span>" +word+"</span>";
			await addLetter(letter);
			return new Promise(res => setTimeout(res, ms))
		}
		function getRandomInt(max) {
			return Math.floor(Math.random() * max);
		}
	
		function addLetter(letter){
			let ele = $(letter);
			ele.hide().appendTo(element);
			let width = ele.outerWidth();
			ele.css("width", '0px');
			ele.show().animate({ width: width+'px'}, 1000);
			
			return new Promise(res => setTimeout(res, 2000))
		}
	
		function clear(){
			element.find("span").animate({ width: '0px'}, 1000);
			return new Promise(res => setTimeout(res, 1200))
		}	
	}

	hideEyeDrop(){
		this.$eyeDropp.hide();
	}

      updateSelections(value:string, selection:string, clear:boolean = false){
         $(`.sign-option[data-sign-optiontype='${selection}']`).removeClass('active');
         if(clear != true){
            $(`.sign-option[data-sign-optiontype='${selection}'][data-sign-option='${value}']`).addClass('active');
         }	
      }
}

let toolMenu: ToolMenu;

class SignOperation{
      private canvas: fabric.Canvas;
      private backgroundCanvas: fabric.Canvas;
      private foregroundCanvas: fabric.Canvas;

      private backgroundShape: fabric.Object | null = null;

      private _clipboard;
      private currentSettings;
      private needUpdate = false;
      private updating = false;
      private render = false;

      private doUserZoom = false;
      private doCenterCanvas = false;

      private  gravyrColorID: number | null;

   constructor(
         canvas: fabric.Canvas,
         currentSettings,
         backgroundCanvas:fabric.Canvas,
         foregroundCanvas: fabric.Canvas
      ) {

      this.canvas = canvas;
      this.backgroundCanvas = backgroundCanvas;
      this.foregroundCanvas = foregroundCanvas;

      //actions after render 
      this._clipboard;

      this.backgroundInnerShape = false;
      this.bgGroupSpecial = false;
      this.bgGroupSpecialProduction = false;
      this.currentSettings = currentSettings;
      this.oldSize = false;
      this.resizeSignContentStatus= false;

      //shape settings in accordance to db 
      this.shapeTypes = {9 : 'rectangle', 10 : 'rect-rounded', 11:'triangle', 12:'transparent', 13:'circle'};
      this.currentShapeType = false;

      //stateHandler
      this.activeSelection = false;
      this.backgroundSides = {'left': 0, 'top': 0, 'right': 0, 'bottom': 0};
      this.movingVinyl = false;
      this.magnetSnapping = true;
      this.panning = false;
      this.lastColorUsed = false;
      this.nameTextColor = "#000";
      this.bgColor = "#fff";
      this.lastBgColor = "#fff";
      this.textColorID = false;
      this.simpleColor = false;
      this.moving = false;
      this.$firstGravyrColors = $('button.gravyr-colors').first();
      this.$allowedColor = false;
      this.section = false;
      this.isTransparent = false;
      this.wasTransparent = false;

      this.dontSave = false;
      this.clickTimer = null;

       this.activeObject = false;
       this.deselecteObject = false;
       this.addedObject = false;
       this.removedObject = false;
       this.modifiedObject = false;
       this.lastEvent = false;


       this.downedKeys = false;
      //events
       this.mouseDownBefore = false;
       this.mouseDown = false;
       this.mouseMoving = false;
       this.mouseOver = false;
       this.objectIsMoving = false;
       this.objectIsAdded = false;
       this.objectIsRemoved = false;
       this.objectIsRotating = false;
       this.objectIsScaling = false;
       this.objectIsModified = false;
       this.mouseUp = false;
       this.doubleClick = false;
       this.selectionIsCleared = false;
       this.selectionIsUpdated = false;
       this.selectionIsCreated = false;

       this.triggerToggleCrop = false;
       this.triggerMoveImageStatusOnCrop = false;

       this.mousePosition = { x: 0, y: 0 };
       this.movingObjectPosition = { x: 0, y: 0 };

      //

      this.updateEvent = this.updateEvent.bind(this);

      //Cropbinds
      this.clipath = false;
      this.originalPosition = false;
      this.originalCropPosition = null;
      this.fakeControlBox = null;
      this.visualClipPath = false;
      this.cropClick = false;
      this.panningObjects = false;


       this.eventQueue = [];
      this.initialize();
      this.binding();

            this.debouncedUpdateAutoSize = debounce((actionObj = {action:'moving'}) => {
                this.updateAutoSize(actionObj);
            }, 1);
      //requestAnimationFrame(this.updateEvent);
   }

   renderCanvas(setRenderTrue = false){
            if(setRenderTrue){
                this.render = true;
            }

            if(!this.render) return false;

        this.canvas.requestRenderAll();
   }

   initialize(){
      this.$canvasContainer = $("#canvas-wrap");
   }

    setVisibility(state = true){
        this.$canvasContainer.css({'visibility': state ? 'visible' : 'hidden'});
    }

   updateEvent(){
      requestAnimationFrame(this.updateEvent);
   }

   requestUserZoom(){
       this.doUserZoom = true;
   }

   requestCenterCanvas(){
       this.doCenterCanvas = true;
   }

   updateKeyDown(object) {
   
      if(!object){
         this.resetKeyDown();
      }

      this.downedKeys = object;

      let activeObject = this.canvas.getActiveObject();
       if (activeObject) {
           // Check if the Delete key (keyCode 46) or Backspace (keyCode 8) is pressed
           if (this.downedKeys.keyCode === 46 || this.downedKeys.keyCode === 8) {
               if (activeObject.type === 'i-text' && activeObject.isEditing) {
                   // Don't delete if the text object is being edited
                   return;
               }
               // Delete the object from the canvas
               this.canvas.remove(activeObject);
               this.canvas.renderAll();
           }
       }


      if (object.which == 32) {
         //let selectableObjects = this.canvas.getObjects().filter(obj => obj.selectable);
         this.canvas.discardActiveObject();

         this.panningObjects = []; // Initialize panningObjects to store affected objects

         this.canvas.defaultCursor = 'grab';
         this.canvas.hoverCursor = 'grab';
         this.canvas.moveCursor = 'grab';
         this.canvas.setCursor('grab');
         // Disable selection for all selectable objects
         
        /*
         selectableObjects.forEach((obj) => {
            obj.selectable = false;
            this.panningObjects.push(obj); // Add to panningObjects for later restoration
         });
          */
         this.canvas.requestRenderAll();
      }
   }

    async manageDimensionDisplay() {
        if(!this.backgroundShape) return;
        let width = this.backgroundShape.getScaledWidth();
        let height = this.backgroundShape.getScaledHeight();

        let realWidth = handleRealDim.getDim('width');
        let realHeight = handleRealDim.getDim('height');

        // Clear existing dimension lines if any
        let fgCanvas = $.canvasFgManager.getCanvas();
        let zoom = fgCanvas.getZoom();
        let offset =  15/zoom;
        let fontSize = 12;
        let pinLength = 10;
        let xy = height + offset;
        let yx = width + offset;
        let zoomedStroke = 1 /zoom; 
        let zoomedFont = fontSize / zoom;
        let pinHalf = (pinLength /2) / zoom 

        if(true || !this.dimensionWidthLine || !this.dimensionHeightLine){
            fgCanvas.remove(this.dimensionWidthLine);
            fgCanvas.remove(this.dimensionHeightLine);


            //creates the text for the inside sizeing 
            this.widthDimText = new fabric.Text(realWidth + " mm", {
                fontSize: zoomedFont,
                fontFamily: 'Roboto, sans-serif',
                fill: '#000',
                originX: 'center',
                originY: 'center',
                name: 'width-text',
                id: 'dimobj',
                excludeFromExport: true
            });

            this.heightDimText = new fabric.Text(realHeight + " mm", {
                fontSize: zoomedFont,
                fontFamily: 'Roboto, sans-serif',
                fill: '#000',
                angle: 90,
                originX: 'center',
                originY: 'center',
                name: 'height-text',
                selectable: false,
                evented: false,
                id: 'dimobj',
                excludeFromExport: true
            });

            let pinArgs = {
                originX: 'center',
                originY: 'center',
                selectable: false,
                evented: false,
                stroke: '#bbb',
                strokeWidth: zoomedStroke,
                id: 'dimobj',
                excludeFromExport: true
            }
            

            //create lines 
            let widthTextWidth = this.widthDimText.width + 10 / zoom;
            let heightTextHeight = this.heightDimText.width + 10 / zoom;


            this.widthLine1 =  new fabric.Line([0, xy, (width - widthTextWidth) / 2, xy], {
                stroke: '#bbb',
                strokeWidth: zoomedStroke,
                name: 'width-line-1',
                id: 'dimobj',
                excludeFromExport: true
            });
            this.widthLine2 =  new fabric.Line([(width + widthTextWidth) / 2, xy, width, xy], {
                stroke: '#bbb',
                strokeWidth: zoomedStroke,
                name: 'width-line-2',
                id: 'dimobj',
                excludeFromExport: true
            });


            this.heightLine1 =  new fabric.Line([yx, 0, yx, (height - heightTextHeight) / 2], {
                stroke: '#bbb',
                strokeWidth: zoomedStroke,
                name: 'height-line-1',
                id: 'dimobj',
                excludeFromExport: true
            });

            this.heightLine2 =  new fabric.Line([yx, (height + heightTextHeight) / 2, yx, height], {
                stroke: '#bbb',
                strokeWidth: zoomedStroke,
                name: 'height-line-2',
                id: 'dimobj',
                excludeFromExport: true
            });


            //x1,y1,x2,y2
            
            this.widthPin1 = new fabric.Line([0, xy - pinHalf, 0, xy + pinHalf], pinArgs);
            this.widthPin2 = new fabric.Line([width, xy - pinHalf, width, xy + pinHalf], pinArgs);
            this.heightPin1 = new fabric.Line([yx - pinHalf, 0, yx + pinHalf, 0], pinArgs);
            this.heightPin2 = new fabric.Line([yx - pinHalf, height, yx + pinHalf, height], pinArgs);

            //construct 
            this.widthDimText.set({ left: width / 2, top: xy })
            this.heightDimText.set({ left: yx, top: height / 2 })
            
            try{
                this.dimensionWidthLine =  new fabric.Group([this.widthLine1, this.widthLine2, this.widthDimText, this.widthPin1,this.widthPin2], {
                        selectable: false,
                        evented: false,
                        id: 'dimobj',
                        excludeFromExport: true

                });

                this.dimensionHeightLine =  new fabric.Group([this.heightLine1, this.heightLine2, this.heightDimText, this.heightPin1, this.heightPin2 ], {
                    selectable: false,
                    evented: false,
                        id: 'dimobj',
                        excludeFromExport: true
                });

                fgCanvas.add(this.dimensionWidthLine);
                fgCanvas.add(this.dimensionHeightLine);
                this.dimensionWidthLine.setCoords();
                this.dimensionHeightLine.setCoords();
            }catch(error){
                console.log(error)
            }

        }else{
            this.widthDimText.set({ left: width / 2, top: xy, fontSize: zoomedFont, text: realWidth + " mm" }).setCoords();
            this.heightDimText.set({ left: yx, top: height / 2, fontSize: zoomedFont, text: realHeight + " mm" }).setCoords();

            let widthTextWidth = this.widthDimText.width + 10 / zoom;
            let heightTextHeight = this.heightDimText.width + 10 / zoom;

            this.widthLine1.set({
                x1: 0,
                y1: xy,
                x2: (width - widthTextWidth) / 2,
                y2: xy,
                strokeWidth: zoomedStroke,
            }).setCoords();

            this.widthLine2.set({
                x1: (width + widthTextWidth) / 2,
                y1: xy,
                x2: width,
                y2: xy,
                strokeWidth: zoomedStroke,
            }).setCoords();

            this.heightLine1.set({
                x1: yx,
                y1: 0,
                x2: yx,
                y2: (height - heightTextHeight) / 2,
                strokeWidth: zoomedStroke,
            }).setCoords();

            this.heightLine2.set({
                x1: yx,
                y1: (height + heightTextHeight) / 2,
                x2: yx,
                y2: height,
                strokeWidth: zoomedStroke,
            }).setCoords();

            //x1,y1,x2,y2
            this.widthPin1.set({
                x1: 0,
                y1: xy - pinHalf,
                x2: 0,
                y2: xy + pinHalf,
                strokeWidth: zoomedStroke,
            }).setCoords();

            this.widthPin2.set({
                x1: width,
                y1: xy - pinHalf,
                x2: width,
                y2: xy + pinHalf,
                strokeWidth: zoomedStroke,
            }).setCoords();

            this.heightPin1.set({
                x1: yx - pinHalf,
                y1: 0,
                x2: yx + pinHalf,
                y2: 0,
                strokeWidth: zoomedStroke,
            }).setCoords();

            this.heightPin2.set({
                x1: yx - pinHalf,
                y1: height,
                x2: yx + pinHalf,
                y2: height,
                strokeWidth: zoomedStroke,
            }).setCoords();
            this.dimensionWidthLine.setCoords();
            this.dimensionHeightLine.setCoords();
        } 
        $.canvasFgManager.updateDim();
        fgCanvas.requestRenderAll();
    }

    getDimensionLines(){
        return {width : this.dimensionWidthLine, height: this.dimensionHeightLine}
    }


   resetKeyDown() {
      if (this.panningObjects && this.panningObjects.length) {
         this.panningObjects.forEach((obj) => {
            obj.selectable = true;
         });
         this.panningObjects = []; // Clear the panningObjects list

         this.canvas.defaultCursor = 'default';
         this.canvas.hoverCursor = 'default';
         this.canvas.moveCursor = 'default';
         this.canvas.setCursor('default');
      }
   }

   binding() {
      this.canvas.on({
         'object:modified': (e) => {
            this.enqueueEvent({ type: "objectIsModified", event: { ...e }});
         },
         'object:added': (e) => {
            this.enqueueEvent({ type: "objectIsAdded", event: { ...e } });
         },
         'object:removed': (e) => {
            this.enqueueEvent({ type: "objectIsRemoved", event: { ...e } });
         },
         "object:moving": (e) => {
            this.enqueueEvent({ type: "objectIsMoving", event: { ...e }});
         },
         "object:scaling": (e) => {
            this.enqueueEvent({ type: "objectIsScaling", event: { ...e }});
         },
         "selection:created": (e) => {
            this.enqueueEvent({ type: "selectionIsCreated", event: { ...e } });
         },
         "selection:cleared": (e) => {
            this.enqueueEvent({ type: "selectionIsCleared", event: { ...e } });
         },
         'selection:updated': (e) => {
            this.enqueueEvent({ type: "selectionIsUpdated", event: { ...e } });
         }, "mouse:up": (e) => {
            this.enqueueEvent({ type: "mouseUp", event: { ...e } });
         }, "object:rotating": (e) => {
            this.enqueueEvent({ type: "objectIsRotating", event: { ...e } });
         }, "mouse:over": (e) => {
            this.enqueueEvent({ type: "mouseOver", event: { ...e } },true);
         }, "text:changed": (e) => {
            this.objectChanging(e.target);
         }, 'mouse:down:before': (e) => {
            this.enqueueEvent({ type: "mouseDownBefore", event: { ...e } });
         }, "mouse:down": (e) => {
            this.enqueueEvent({ type: "mouseDown", event: { ...e } });
         }, 'mouse:move': (e) => {
            this.enqueueEvent({ type: "mouseMoving", event: { ...e }}, true);
         }, 'mouse:wheel': (e) => {
            this.enqueueEvent({ type: "scrolling", event: { ...e }}, true);
         }, 'mouse:dblclick': (e) => {
             this.enqueueEvent({ type: "doubleClick", event: { ...e } });
         }, 'key:down' : (e) => {
            console.log(e);
         }, 'after:render' :async  (e) => {
             //do actions after render 
             if((this.doUserZoom || this.doCenterCanvas) && !$.stdnLoading){
                await this.manageDimensionDisplay(true); 
                onCanvasGUIHandler.centerCanvasView(true);
             }
             this.doCenterCanvas = false;
             this.doUserZoom = false;
         }
          
      });

      this.$canvasContainer.on('dragover', (e) => {
         e.preventDefault();
         e.stopPropagation();
         this.handleDragOver(e)
         return false;
      })
      this.$canvasContainer.on('drop', (e) => {
         e.preventDefault();
         e.stopPropagation()
         this.handleDragDrop(e);
      });


      let canvasContainer = $('.canvas-container');

      function disableScrolling() {
         window.addEventListener('wheel', preventScroll, {passive: false});
      }

      // Function to enable scrolling
      function enableScrolling() {
         window.removeEventListener('wheel', preventScroll, {passive: false});
      }

      function preventScroll(e){
         e.preventDefault();
      }

      canvasContainer.on('mouseenter', disableScrolling);
      canvasContainer.on('mouseleave', enableScrolling);

      $document.on('mousedown', (e) => {
         if ($(e.target).hasClass('design-area')) {
            if (this.canvas.getActiveObject()) {
               this.canvas.discardActiveObject().requestRenderAll();
            }
         }
      });

   }

    enqueueEvent(eventData, animationFrame = false) {
        if(animationFrame){
            this.eventQueue.push(eventData);
            this.requestUpdate();
        }else{
            this.updateUserInteractions([eventData]);
        }
    }

    requestUpdate() {
        if (!this.needUpdate) {
            this.needUpdate = true;
            requestAnimationFrame(() => this.updateUserInteractions(this.eventQueue));
        }
    }

    updateUserInteractions(fullEvenData = false){
        if (this.needUpdate || fullEvenData.length > 0) {
            if(!fullEvenData ){
                fullEvenData = this.eventQueue; 
            }

            while(fullEvenData.length > 0){
                const eventData = fullEvenData.shift();
                switch(eventData.type){
                    case "mouseDownBefore":
                        this.mouseDownBeforEvent(eventData.event);
                        break;
                    case "mouseDown":
                        this.clickTimer = new Date();
                        this.mouseDownEvent(eventData.event);
                        break;
                    case "mouseMoving":
                        this.mouseMove(eventData.event);
                        break;
                    case "mouseOver":
                        onCanvasGUIHandler.checkforCursorType(eventData.event);
                        break;
                    case "objectIsMoving":
                        this.clickTimer = false;
                        this.objectMoving(eventData.event);
                        break;
                    case "objectIsAdded":
                        this.objectAdded(eventData.event);
                        break;
                    case "objectIsRemoved":
                        this.objectRemoved(eventData.event);
                        break;
                    case "objectIsRotating":
                        this.objectRotating(eventData.event);
                        break;
                    case "objectIsScaling":
                        this.objectScaling(eventData.event);
                        break;
                    case "objectIsModified":
                        this.objectModified(eventData.event);
                        break;
                    case "mouseUp":
                        this.mouseUpEvent(eventData.event);
                        break;
                    case "doubleClick":
                        this.doubleClickEvent(eventData.event);
                        break;
                    case "selectionIsCreated":
                        this.selectionCreated(eventData.event);
                        break;
                    case "selectionIsCleared":
                        this.objectCleared(eventData.event);
                        break;
                    case "selectionIsUpdated":
                        this.objectUpdated(eventData.event);
                        this.selectionFuncs();
                        break;
                    case 'scrolling':
                      this.scrollZoom(eventData);
                      break

                }
            }
            this.needUpdate = false;
        }
    }

   getDontSave() {
      if (!this.dontSave) {
         this.dontSaveSign = signSaveAndLoad.getDontSaveSign();
      }
      return this.dontSaveSign;
   }
   setOldSize(resizeObject) {
      if (!resizeObject) return;
      this.oldSize = resizeObject;
      //oldHeight and oldWidth  
   }
   setResizeSignContentStatus(status = true) {
      this.resizeSignContentStatus = status;
   }

   //setters and getters
   setBackgroundShape(shape){
      this.backgroundShape = shape;
      this.backgroundShape.id = "backgroundShape";
      this.backgroundShape.thumbailShadow = true;
      this.backgroundShape.evented = true;
      this.backgroundShape.perPixelTargetFind = false;
   }
   setbackgroundInnerShape(shape){
      this.backgroundInnerShape = shape;
      this.backgroundInnerShape.id = "backgroundInnerShape";
   }
   setBgGroupSpecial(shape){
      this.bgGroupSpecial = shape;
   }
   setBgGroupSpecialProduction(shape){
      this.bgGroupSpecialProduction = shape;
   }

   setBackgroundDistance() {
      this.backgroundDistances = {
         'left': this.backgroundShape.left,
         'top': this.backgroundShape.top,
         'right': this.backgroundShape.width + this.backgroundShape.left,
         'bottom': this.backgroundShape.height + this.backgroundShape.top
      };
   }

   getOldSize(){
      return this.oldSize;
   }

   setAllowedGravyrColor($gravyrButton){
      this.$allowedColor = $gravyrButton;
   }

   getCurrentObjectSection(){
      return this.section;
   }

   getBackgroundDistance(){
      return this.backgroundDistances;
   }
   getActiveSelection(){
      return this.activeSelection;
   }

   getLastColorUsed(){
      if(!this.lastColorUsed){
         let objects = canvas.getObjects();
         for(let object of objects){
            if(!object.signstudio_standard && object.fill){
               this.lastColorUsed = object.fill;
               break;
            }
         }
      }
      if(!this.lastColorUsed) this.lastColorUsed = "#000";

      return this.lastColorUsed;
   }
   setLastcolorUsed(color){
      this.lastColorUsed = color;
   }

   getNameTextColor(){
      return this.nameTextColor;
   }
   setNameTextColor(color){
      this.nameTextColor = color;
   }

   getGravyrColorID(){
      return parseInt(this.gravyrColorID);
   }

   setGravyrColorID(id : number){
      this.gravyrColorID = id;
   }
   
   getTextColorID(){
      return parseInt(this.textColorID);
   }	

   getBgColor(){
      return this.bgColor;
   }
   setBgColor(color){
      this.bgColor = color;
   }

   setSimpleColor(state){
      this.simpleColor = state;
   }

   getSimpleColor(){
      return this.simpleColor;
   }

   getCanvas(){
      return this.canvas;
   }
   getTransparentStroke(){
      return {'stroke': '#D3D3D3', 'strokeWidth': 1}
   }

   getBackgroundShape() : fabric.Object | null{
      return this.backgroundShape;
   }

   getbackgroundInnerShape(){
      return this.backgroundInnerShape;
   }

   getBgGroupSpecial(){
      return this.bgGroupSpecial;
   }
   getBgGroupSpecialProduction(){
      return this.bgGroupSpecialProduction;
   }

   getMagnetSnapping() {
      return this.magnetSnapping;
   }

   setMagnetSnapping(value) {
      this.magnetSnapping = value;
   }

   setTransparentBackround(bool){
      this.isTransparent = bool;
   }

   objectModified(e) {
      this.moving = false;
      onCanvasGUIHandler.updateObjectControl();
      let obj = e.target;
      this.modifiedObject = false;
      //fix saving and price for when having auto on vinyl size 
      this.movingVinyl = false;

      if (obj.type == 'i-text') {
         obj.cleanStyle('fontFamily');
         let newtxt = `<div>${obj.text}</div>`;
         $(`[data-sign-optiontype='select-obj'][data-sign-option='${obj.objId}'] > div:first`).html(newtxt);
      }

            //Checking image quality on scale
      if(e.action === 'scale' && obj.type === 'image' && obj.hasOwnProperty('user_image_id')){


         eventBus.emit('user_image:scaled', {obj: obj});

      }

      if (allLoaded == true) {
         signSaveAndLoad.saveDesign();
      }
      /*
      if (handleRealDim.getDim('material') == "vinyl") {
         toolMenu.updatePrice();
      }
      */
   }


   objectAdded(event) {
      let object = event.target;
      if (!$.stdnLoading) {
          if (object){
            if(!object.hasOwnProperty('signstudio_standard') || object.signstudio_standard != true) {
                updateUserObjects();
                object = false;
            }
         }
      } 
   }


   objectRemoved(event){
       let obj = event.target;
      if(obj.signstudio_standard != true){
         //update object list 
         let index = UserobjectCollection.indexOf(obj.objId);
         if (index !== -1) {
            UserobjectCollection.splice(index, 1); // Remove the item from the array
         }
         updateUserObjects();
          if (handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h')) {
              this.updateAutoSize({action:'removed'});
          }
      }


   }

   objectMoving(e) {
      onCanvasGUIHandler.updateObjectControl(true);
      let obj = e.target;

      //cropped image management
      if (this.fakeControlBox) {
         this.updateCoordsCropedImage(obj, e);
         if (obj.hasOwnProperty('name') && obj.name === 'userImage') {
            //make sure image is not out side clippath 
            this.maintainImageInCrop(obj);
         }
      }
            const autoSizing = (handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h'));

      //magnetic snipping 
      let trans = e.transform;
      let direction = { x: 0, y: 0 };

      let prevX = trans.lastX - trans.offsetX;
      let currentX = obj.left;
      let prevY = trans.lastY - trans.offsetY;
      let currentY = obj.top;

      direction.x = currentX - prevX;
      direction.y = currentY - prevY;

      //runObjectOutsideDetection(obj);
      this.dontAllowObjectsOutside(obj);
      if (this.magnetSnapping) {
         onCanvasGUIHandler.calculateAndDrawHelperLines(obj, direction, this.backgroundShape, this.moving, this.magnetSnapping);
         this.moving = true;
      }
      //updateSymbolSize();
      if (handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h')) {
         this.debouncedUpdateAutoSize({action:'moving'});
         //this.updateAutoSize({action:'moving'});
         movingVinyl = true;
      }

      toolMenu.updateElementDistance(this.section);
   };


   objectScaling(e) {
      var obj = e.target;
      onCanvasGUIHandler.updateObjectControl(true);
      if (obj.name == 'userFabricShape') {
         let scaleX = obj.scaleX;
         let scaleY = obj.scaleY;
         let gwidth = obj.getScaledWidth();
         let gheight = obj.getScaledHeight();
         let type = obj._objects[0].type;
         let radius = 0;

         //cricle is unisize
         if (type == 'circle') {
            let closest = 0;
            if(scaleX === 1 || scaleY === 1){
                if(scaleY < 1 || scaleX < 1){
                 closest = (gwidth < gheight) ? gwidth : gheight;
                }else{
                 closest = (gwidth > gheight) ? gwidth : gheight;
                }
            }else{
                closest = (gwidth > gheight) ? gwidth : gheight;
            }

            radius = closest / 2;
            obj.set({ scaleX: 1, scaleY: 1, radius: radius, width: closest, height: closest }).setCoords();
         } else {
            obj.set({ scaleX: 1, scaleY: 1, width: gwidth, height: gheight }).setCoords();
         }

         //set coords for objects inside group
         let objects = obj._objects;
         let border = obj.cstBorder;

         for (let i = 0; i < objects.length; i++) {
            if (type != 'circle') {
               let width = objects[i].width * scaleX;
               let height = objects[i].height * scaleY;
               objects[i].set({ scaleX: 1, scaleY: 1, width: width, height: height, left: (-gwidth / 2), top: (-gheight / 2) });
            } else {
               objects[i].set({ scaleX: 1, scaleY: 1, radius: radius, left: -radius, top: -radius });
            }
         }

         if (border > 0) {
            manageCustomObjects.calcUserBorder(border, obj);
         }

         canvas.requestRenderAll();
      }

       if (handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h')) {
           this.debouncedUpdateAutoSize({action:'scalign', obj: obj});
           //this.updateAutoSize({action:'scalign', obj: obj});
       }

      //runObjectOutsideDetection(obj);
      toolMenu.updateFontSizeInput(obj, true);
      toolMenu.updateElementDistance(this.section);
      updateSymbolSize();
   }

   doubleClickEvent(e){
       if (this.fakeControlBox && e.target === this.fakeControlBox
           && (this.clickTimer - new Date()) < 100) {
           this.MoveImageStatusOnCrop();
       }
   }

   selectionCreated() {
      this.selectionFuncs();
   }


   objectCleared(e) {
      let object = (e.deselected) ? e.deselected[0] : false;
      this.selectionCleared();
      onCanvasGUIHandler.updateObjectControl();

      if (handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h')) {
        this.updateAutoSize({action:'cleared'});
        toolMenu.debouncedUpdatePrice();
      }
      this.revertCrop(object);
      if (object.hasOwnProperty('text')) {
         if (object.text.trim().length === 0) {
            manageCustomObjects.deleteObject(object);
         }
      }

   }

   revertCrop(object) {
      if (object.type === 'image' && this.cropClick) {
         this.toggleImageCrop(object, true);
         //this.removeFakeBoxForClipping();
      }
      this.MoveImageStatusOnCrop(object, true);
      this.cropClick = false;
   }

   objectChanging(obj) {
      if (handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h')) {
         this.debouncedUpdateAutoSize({action:'changing'});
         //this.updateAutoSize({action:'changing'});
      }
   }

   selectionFuncs() {
      let obj = this.canvas.getActiveObject();
      (obj) ? this.activeSelection = obj : '';
      //Allow selection on borderbox
      (obj) ? obj.perPixelTargetFind = false : '';

      //sets object to active. It is used because custome shapes are handled differently 
      toolMenu.setActiveSelection(obj);

      toolMenu.closeInnerMenu();
      onCanvasGUIHandler.updateObjectControl();

      updateSymbolSize();

      this.updateCurrentObjectTypeSection(obj);

      if (!obj) return;
      if (obj.get('type') != "i-text") {
         $(".t-symbols").removeClass("deactive");
         toolMenu.changeActiveTool("settings", obj);
         onCanvasGUIHandler.updateFlipButton(obj);
      } else {
         $(".t-text").removeClass("deactive");
         toolMenu.changeActiveTool("settings", obj);
         toolMenu.updateTextFormatButtons(obj);
         onCanvasGUIHandler.updateFlipButton(obj);
      }

      if (obj && (obj.type === 'image' || obj.type === 'path' || obj.type === 'group')
          && obj.clipPath 
          && obj.stdnCustom 
          && obj.stdnCustom.croped
          && !this.cropClick) {
         this.drawFakeBoxForClipping(obj);
      }
     
      if(obj.name == 'userFabricShape'){
          for(let i = 0; i < obj['_objects'].length; i++){
              if(!obj['_objects'][i].fill !== 'transparent'){
                  if(obj['_objects'][i].name == 'userShapeInner'){
                      toolMenu.updateSelections(obj['_objects'][i].fill, 'shape-bg-color');
                  }else{
                      toolMenu.updateSelections(obj['_objects'][i].fill, 'shape-border-color');
                  }
              }
          }
      } 

      toolMenu.updateSelections(obj.fill, 'symbolcolor');
   }



   scrollZoom(e){
       let event = e.event.e;
       let deltaMode = event.deltaMode;
       let isTrackpad = (deltaMode === WheelEvent.DOM_DELTA_PIXEL); // Assuming trackpad scroll is in pixel mode.

       if(isTrackpad) {
           //we do not want to sroll on trackpad only with mouse
           return;
       }

      let currentScroll = e.event.e.deltaY; 
      let newScroll = 0;

      if(currentScroll > 0){
         newScroll = Math.max((currentScroll /100), 5);
         if(newScroll < 1) newScroll = 1;
      }else{
         newScroll = Math.min((currentScroll /100), -5);
         if(newScroll > -1) newScroll = -1;
      }
      let currentZoom = parseInt($('#zoom-per span').text());
      newScroll = currentZoom + newScroll;
      onCanvasGUIHandler.userZoom(newScroll, true);
   }

    objectUpdated(e) {
        let deselection = e.deselected;
        let newSelection = e.selected;
        //check crop click, if it was then revert the crop and then select the other object
        if(deselection[0]){
            deselection = deselection[0]
        }else{
            deselection =  false;
        }  

        if(deselection && 
            ((deselection.name === "userImage" && deselection.clipPath)
                || (deselection.hasOwnProperty('belong') && deselection.belong !== newSelection[0]))) {
            this.revertCrop(deselection);
        }
    }



   selectionCleared() {
      (this.activeSelection) ? this.activeSelection.perPixelTargetFind = true : '';
      //remove text object if empty due to error
      if (this.activeSelection.type === 'i-text' && this.activeSelection.text.trim() == "") {
         canvas.remove(this.activeSelection);
      }
      toolMenu.revertToPreviousTool();

      toolMenu.updateSelections('', 'symbolcolor', true);
      //runCompleteObjectOutsideDetection();
      this.activeSelection = false;
      this.section = false;
      toolMenu.setActiveSelection(false);
      canvas.requestRenderAll();
   }


   updateCurrentObjectTypeSection(obj) {
      if (!obj) return;
      if (obj.hasOwnProperty('name') && obj.name == 'userFabricShape' && obj.type == 'group') {
         $.each(obj._objects, function(i, element) {
            if (element.hasOwnProperty('name') && element.name != "userShapeInner") {
               obj = element;
            }
         });
      }

      let objType = obj.get('type');
      switch (objType) {
         case 'i-text':
            this.section = 'text';
            break;
         case 'group':
            this.section = 'symbol';
            break;
         case 'image':
            this.section = 'symbol';
            break;
         case 'path':
            this.section = 'symbol';
            break;
         case 'triangle':
            this.section = 'shape';
            break;
         case 'circle':
            this.section = 'shape';
            break;
         case 'ellipse':
            this.section = 'shape';
            break;
         case 'rect':
            this.section = 'shape';
            break;
      }
   }


   mouseUpEvent(e) {
       this.mouseDown = false;
      let object = e.currentTarget;
      let activeObject = this.canvas.getActiveObject();

      onCanvasGUIHandler.checkforCursorType(e);
      if (this.panning == true) {
         this.canvas.forEachObject(function(o) {
            o.setCoords();
         });
      }
      this.panning = false; //removes panning
      //removeHelperlines
      onCanvasGUIHandler.removeHelperLines();

      //update cropped images 
      if (activeObject && activeObject === this.fakeControlBox) {
         let moved = (e.transform && e.transform.action === 'drag' && e.transform.actionPerformed);
         if (moved) {
            this.updateClipPathPosition(this.fakeControlBox);
         }
      } else if (activeObject && activeObject.type === 'image' && this.cropClick) {
         this.toggleImageCrop(activeObject, true);
      }
      this.originalPosition = null;

      this.canvas.requestRenderAll();

      if (object != null) {
         //if (realDimensions.auto_size_w || realDimensions.auto_size_h) {
            //updateAutoSize(activeObject);
            //}
         //manageCustomObjects.bringToFront(object);
         //runObjectOutsideDetection(activeObject);
         //runCompleteObjectOutsideDetection();
         updateGUI();
      }
      this.canvas.selection = true;
   }


   objectRotating(e){
      var obj = e.target;
      onCanvasGUIHandler.updateObjectControl(true);
      //runObjectOutsideDetection(obj);

      obj.set({snapAngle: 90, snapThreshold: 5});
   }

    mouseDownBeforEvent(e){
        let object = e.target;
        if (!object) return;
        this.originalPosition = { left: object.left, top: object.top };
        if (object && object.type === 'image' && object.clipPath && this.cropClick && this.cropClick == object.clipPath) {
            this.toggleImageCrop(object);
        }
    }

   mouseDownEvent(e){
      if((e.target != null && e.target.selectable == false) || e.button == 2){ //Operation for panning if object is not movable
         this.panning = true; // removes on mouse up
         this.lastPosX = e.e.clientX;
         this.lastPosY = e.e.clientY;
      }
   }

   mouseMove(opt) {
      if (this.panning && opt && opt.e) { //Pans canvas
         // get max width and height from all canvases
         this.canvas.selection = false;
         let threshold = 50;
         let rect = this.backgroundShape.getBoundingRect(true, true);
         let objWidth = rect.width;
         let objHeight = rect.height;

         let bgCanvasDim = $.canvasBgManager.getDim();
         let fgCanvasDim = $.canvasFgManager.getDim();
         let bgWidth = objWidth;
         let bgHeight = objHeight;
         let bgCW = bgWidth;
         let bgCH = bgHeight;
         let fgCW = bgWidth;
         let fgCH = bgHeight;

         if (bgCanvasDim) {
            if (bgCanvasDim.width > bgWidth) bgWidth = bgCanvasDim.width;
            if (bgCanvasDim.height > bgHeight) bgHeight = bgCanvasDim.height;
            bgCW = (objWidth > bgCanvasDim.width) ? objWidth : bgCanvasDim.width;
            bgCH = (objHeight > bgCanvasDim.height) ? objHeight : bgCanvasDim.height;
         }

         if (fgCanvasDim) {
            if (fgCanvasDim.width > bgWidth) bgWidth = fgCanvasDim.width;
            if (fgCanvasDim.height > bgHeight) bgHeight = fgCanvasDim.height;
            fgCW = (objWidth > fgCanvasDim.width) ? objWidth : fgCanvasDim.width;
            fgCH = (objHeight > fgCanvasDim.height) ? objHeight : fgCanvasDim.height;
         }

         let yRatio = (objHeight * this.canvas.getZoom()) / this.canvas.getHeight();
         let xRatio = (objWidth * this.canvas.getZoom()) / this.canvas.getWidth();
         let e = opt.e;
         let vpt = this.canvas.viewportTransform;

         let vptBG = $.canvasBgManager.getCanvas().viewportTransform;
         let vptFG = $.canvasFgManager.getCanvas().viewportTransform;
         let zoom = this.canvas.getZoom();


         let body = this.backgroundShape;
         let iMtx = fabric.util.invertTransform(vpt);
         const bpt = new fabric.Point(body.aCoords.tl.x, body.aCoords.tl.y);
         const bwh = new fabric.Point(body.aCoords.br.x, body.aCoords.br.y);
         const xbpt = fabric.util.transformPoint(bpt, iMtx);
         const xbwh = fabric.util.transformPoint(bwh, iMtx);
         //Keeps panning within range Y range
         if (opt.target) {
            if (yRatio > 1) {
               opt.target.hoverCursor = "grabbing";
               let movementY = e.clientY - this.lastPosY;
               if (movementY < 0) {
                  let newYPos = this.canvas.getHeight() - this.backgroundShape.getScaledHeight() * zoom - threshold;
                  vpt[5] = Math.max(vpt[5] + movementY, newYPos);
                  vptBG[5] = vpt[5];
                  vptFG[5] = vpt[5];
                  /*
                  if ((this.canvas.getHeight() - this.backgroundShape.getScaledHeight() * zoom) < vpt[5]) {
                     vpt[5] += movementY;
                     vptBG[5] += movementY;
                     vptFG[5] += movementY;
                  } else {
                     vpt[5] = this.canvas.getHeight() - this.backgroundShape.getScaledHeight() * zoom;
                     vptBG[5] = this.canvas.getHeight() - this.backgroundShape.getScaledHeight() * zoom;
                     vptFG[5] = this.canvas.getHeight() - this.backgroundShape.getScaledHeight() * zoom;
                  }*/
               } else if (movementY > 0) {
                  vpt[5] = Math.min(vpt[5] + movementY, threshold);
                  vptBG[5] = vpt[5];
                  vptFG[5] = vpt[5];

                  /*if (xbpt.y > 0) {
                     vpt[5] += movementY;
                     vptBG[5] += movementY;
                     vptFG[5] += movementY;
                  } else {
                     vpt[5] = 0;
                     vptBG[5] = 0;
                     vptFG[5] = 0;
                  }

                  */
               }
            }

            //keeps panning within range X 
            if (xRatio > 1) {
               //this.canvas.selection = false;
               opt.target.hoverCursor = "grabbing";
               let movementX = e.clientX - this.lastPosX;
               if (movementX < 0) {
                  let newXPos = this.canvas.getWidth() - this.backgroundShape.getScaledWidth() * zoom - threshold;
                  vpt[4] = Math.max(vpt[4] + movementX, newXPos);
                  vptBG[4] = vpt[4];
                  vptFG[4] = vpt[4];
                  /*
                  if ((this.canvas.getWidth() - this.backgroundShape.getScaledWidth() * zoom) < vpt[4]) {
                     vpt[4] += movementX;
                     vptBG[4] += movementX;
                     vptFG[4] += movementX;
                  } else {
                     vpt[4] = this.canvas.getWidth() - this.backgroundShape.getScaledWidth() * zoom;
                     vptBG[4] = this.canvas.getWidth() - this.backgroundShape.getScaledWidth() * zoom;
                     vptFG[4] = this.canvas.getWidth() - this.backgroundShape.getScaledWidth() * zoom;
                  }*/
               } else if (movementX > 0) {
                  vpt[4] = Math.min(vpt[4] + movementX, threshold);
                  vptBG[4] = vpt[4];
                  vptFG[4] = vpt[4];
                  /*
                  if (xbpt.x > 0) {
                     vpt[4] += movementX;
                     vptBG[4] += movementX;
                     vptFG[4] += movementX;
                  } else {
                     vpt[4] = 0;
                     vptBG[4] = 0;
                     vptFG[4] = 0;
                  }*/

               }
            }

            this.canvas.setViewportTransform(vpt);
            this.backgroundCanvas.setViewportTransform(vptBG);
            this.foregroundCanvas.setViewportTransform(vptFG);

         } else {
            this.panning = false;
         }

          // Re-render the canvases
          this.canvas.requestRenderAll();
          this.backgroundCanvas.requestRenderAll();
          this.foregroundCanvas.requestRenderAll();
         this.lastPosX = e.clientX;
         this.lastPosY = e.clientY;
      }
   }

   drawFakeBoxForClipping(object, setActive = true) {
      if (this.fakeControlBox) {
         this.canvas.remove(this.fakeControlBox);
      }
      let width = object.clipPath.width * object.clipPath.scaleX;
      let height = object.clipPath.height * object.clipPath.scaleY;

      object.set({
         hasControls: false,
         evented: false,
         selectable: false
      });

      let type = object.clipPath.type;
      let zoom = this.canvas.getZoom();

      if (type === 'circle') {
         let radius = (width > height) ? width / 2 : height / 2;
         this.fakeControlBox = new fabric.Circle({
            left: object.clipPath.left,
            top: object.clipPath.top,
            radius: radius,
            fill: 'transparent',
            stroke: 'green',
            strokeWidth: 2 / zoom,
            name: 'noEdit',
            hasControls: true,
            hasBorders: true,
            originX: 'center',
            originY: 'center',
            belong: object,
            strokeUniform: true,
            removeOnSave: true
         });
         this.afterCreatingFakebox(object, setActive);
      } else if (type === 'rect') {
         this.fakeControlBox = new fabric.Rect({
            left: object.clipPath.left,
            top: object.clipPath.top,
            width: width,
            height: height,
            fill: 'transparent',
            stroke: 'green',
            strokeWidth: 2 / zoom,
            name: 'noEdit',
            hasControls: true,
            hasBorders: true,
            originX: 'center',
            originY: 'center',
            belong: object,
            strokeUniform: true,
            removeOnSave: true
         });
         this.afterCreatingFakebox(object, setActive);
      } else if (type === 'path' || type === 'group') {
         object.clipPath.clone((clonedPath) => {
            this.fakeControlBox = clonedPath;
            // Set cloned path properties
            this.fakeControlBox.set({
               fill: 'transparent',
               stroke: 'green',
               strokeWidth: 2 / zoom,
               name: 'noEdit',
               hasControls: true,
               hasBorders: true,
               originX: 'center',
               originY: 'center',
               belong: object,
               strokeUniform: true,
               removeOnSave: true,
                excludeFromExport: true
                });
            this.afterCreatingFakebox(object, setActive);
         });
      }

   }

   afterCreatingFakebox(object, setActive = true) {
      this.canvas.add(this.fakeControlBox);

      this.fakeControlBox.on('scaling', (e) => {
         let object = e.transform.target;
         let scaleX = object.scaleX;
         let scaleY = object.scaleY;

         // Check if the clipPath is a circle
         let isCircle = object.type === 'circle';

         let newWidth = null;
         let newHeight = null;
         let newRadius = null;

         // Calculate the new width, height, and radius based on the scale factors
         newWidth = object.width * scaleX;
         newHeight = object.height * scaleY;
         if (isCircle) {
            newRadius = object.width / 2;
            object.radius = object.width / 2;
         } else {
            object.set({
               width: object.width,
               height: object.height,
            });
         }

         object.set({
            scaleX: object.scaleX,
            scaleY: object.scaleY
         });


         // Now apply these new dimensions to the clipPath of the object this fake control box belongs to
         let originalObject = object.belong; // this should reference back to your original object
         if (originalObject && originalObject.clipPath) {
            let clippath = originalObject.clipPath;
            clippath.absolutePositioned = true;

            // If the clipPath is a circle, set the radius. Otherwise, set the width and height.
               if (isCircle) {
                  clippath.set({
                     radius: object.radius,
                     left: object.left,
                     top: object.top,
                     scaleX: object.scaleX,
                     scaleY: object.scaleY,
                  });
               } else {
                  clippath.set({
                     width: object.width,
                     height: object.height,
                     left: object.left,
                     top: object.top,
                     scaleX: object.scaleX,
                     scaleY: object.scaleY,
                  });
               }
         }
         this.canvas.renderAll();
         originalObject.clipPath.absolutePositioned = false;
      });

      if (setActive) {
         this.canvas.setActiveObject(this.fakeControlBox);
      } else {
         this.canvas.setActiveObject(object);
      }
      this.originalPosition = {
         left: this.fakeControlBox.left,
         top: this.fakeControlBox.top
      };

      this.cropClick = object.clipPath;
   }

   removeFakeBoxForClipping() {
      if (this.fakeControlBox) {
         this.canvas.remove(this.fakeControlBox);
      }
      this.fakeControlBox = null;
      this.cropClick = false;
      this.canvas.requestRenderAll();
   }

   toggleImageCrop(object, revert = false) {
      let dontSaveObj = this.getDontSave();
      if (revert) {
         object.clipPath = this.cropClick;
         if(object.hasOwnProperty('previousOpacity')){
            object.opacity = object.previousOpacity;
         }else{
            object.opacity = 1;
         }
         if (dontSaveObj.hasOwnProperty('crop')) delete dontSaveObj.crop;
      } else {
         object.previousOpacity = object.opacity;
         object.clipPath = null;
         object.opacity = 0.40;
         dontSaveObj.crop = true;
      }
   }

    MoveImageStatusOnCrop(object = false, removeCrop = false) {
        if (!object) {
            object = this.canvas.getActiveObject();
        }

        if (!object) return;

        let changeEditArg = (editCrop) => {
            return {
                lockScalingX: (true != editCrop),
                lockScalingX: (true != editCrop),
                hasControls: (false != editCrop),
                hasBorders: (false != editCrop),
                evented: (false != editCrop)
            }
        }

        let removeFakeControls = () => {
            if (this.fakeControlBox) {
                let cropedObject = this.fakeControlBox.belong;
                cropedObject.set({
                    hasControls: true,
                    evented: true,
                    selectable: true,
                    perPixelTargetFind: true
                })
                this.canvas.remove(this.fakeControlBox);
            }
            this.fakeControlBox = null;
        }


        if (removeCrop && this.cropClick) {
            //Allow the movement of crop instead of image, reverting
            this.cropClick.absolutePositioned = true;
            removeFakeControls();
            signSaveAndLoad.saveDesign();
        } else if (object === this.fakeControlBox && !removeCrop) {
            //Allow moving of image instead of croppox
            let CropedObject = this.fakeControlBox.belong;
            CropedObject.set(changeEditArg(true));
            this.canvas.setActiveObject(CropedObject);
            CropedObject.clipPath.absolutePositioned = true;
            this.fakeControlBox.set({
                "selectable": false,
                "evented": false
            });
        }
        this.canvas.requestRenderAll();
    }


   updateCoordsCropedImage(object, e) {
      if (this.originalPosition && object === this.fakeControlBox) {
         // Calculate the distance moved
         let croppedObject = this.fakeControlBox.belong;
         croppedObject.clipPath.absolutePositioned = false;

         if (!this.originalPosition.objectLeft) {
            this.originalPosition.objectLeft = croppedObject.left;
            this.originalPosition.objectTop = croppedObject.top;
         }
         const deltaX = object.left - this.originalPosition.left;
         const deltaY = object.top - this.originalPosition.top;

         croppedObject.left = this.originalPosition.objectLeft + deltaX;
         croppedObject.top = this.originalPosition.objectTop + deltaY;
         this.originalPosition.newLeft = deltaX;
         this.originalPosition.newTop = deltaY;
         this.canvas.requestRenderAll();
      }
   }

   maintainImageInCrop(obj) {
      // Ensures that the center of the image remains inside the clipPath
      if (!obj || !this.fakeControlBox) return;

      // Get the bounding rect of the clipPath
      var clipPathBounds = this.fakeControlBox.getCenterPoint(true, true);

      let objRect = obj.getBoundingRect(true, true);
      // Calculate the center of the object
      var objectCorners = {
         lx: objRect.left,
         rx: objRect.left + objRect.width,
         ty: objRect.top,
         by: objRect.top + objRect.height
      };
      //br,br,tl,tr
      // Check if the center of the object is outside the clipPath bounds
      // Adjust horizontally
      let left = false;
      let top = false;

      if (objectCorners.lx > clipPathBounds.x) left = clipPathBounds.x;
      if (objectCorners.rx < clipPathBounds.x) left = clipPathBounds.x - objRect.width;

      if (objectCorners.ty > clipPathBounds.y) top = clipPathBounds.y;
      if (objectCorners.by < clipPathBounds.y) top = clipPathBounds.y - objRect.height;

      if (left || top) {
         let centerPoint = {};
         centerPoint.x = (left) ? left : objRect.left;
         centerPoint.y = (top) ? top : objRect.top;
         obj.setPositionByOrigin(centerPoint, 'left', 'top');
      }

      // Apply the changes
      obj.setCoords();
      this.canvas.requestRenderAll();
   }

   updateClipPathPosition() {
      if (this.originalPosition && this.fakeControlBox) {
         let object = this.fakeControlBox.belong;
         object.clipPath.left = object.clipPath.left + this.originalPosition.newLeft;
         object.clipPath.top = object.clipPath.top + this.originalPosition.newTop;
         object.clipPath.absolutePositioned = true;
         this.canvas.requestRenderAll();
      }
   }

   mouseScroll(opt){
      let delta = opt.e.deltaY;
      let direction = ((0.999 ** delta) > 1) ? 3 : -3;
      let newZoom = parseInt($('#zoom-per span').text())+ direction;
      if(newZoom < 50) newZoom = 50;
      if(newZoom > 500) newZoom = 500;
      onCanvasGUIHandler.userZoom(newZoom, true);
   }

   rotateObject(deg,obj = false){
      if(!obj){
         obj = canvas.getActiveObject();
      }
      obj.rotate(obj.angle + deg);
      obj.setCoords();
      this.canvas.requestRenderAll();
   }

   alingCrop(object = false, centerCoord = false) {
      if (!object || !centerCoord) return;
      let clippath = (object.hasOwnProperty('clipPath') && object.clipPath) ? object.clipPath : false;

      object.setPositionByOrigin(centerCoord, 'center', 'center').setCoords();
      if (clippath) {
         clippath.setPositionByOrigin(centerCoord, 'center', 'center').setCoords();
      }
   }


   positionHorizontal(object) {
      let canvas = this.canvas;
      let backgroundShape = this.backgroundShape;

      if (!object) {
         object = canvas.getActiveObject();
      }
      let belong = (object.hasOwnProperty('belong')) ? object.belong : false;
      let centerPointY = (backgroundShape.aCoords.tl.y + backgroundShape.aCoords.bl.y) / 2
      let centerPointX = object.getCenterPoint().x;
      let newCoords = { "x": centerPointX, "y": centerPointY }
      object.setPositionByOrigin(newCoords, 'center', 'center');
      object.setCoords();
      if (belong) this.alingCrop(belong, newCoords);
      canvas.requestRenderAll();
   }

   positionVertical(object) {
      if (!object) object = this.canvas.getActiveObject();
      let belong = (object.hasOwnProperty('belong')) ? object.belong : false;
      let centerPointX = (this.backgroundShape.aCoords.tl.x + this.backgroundShape.aCoords.tr.x) / 2
      let centerPointY = object.getCenterPoint().y;
      let newCoords = { "x": centerPointX, "y": centerPointY }
      object.setPositionByOrigin(newCoords, 'center', 'center');
      object.setCoords();
      if (belong) this.alingCrop(belong, newCoords);
      canvas.requestRenderAll();
   }


   setMaterial(option) {
      handleRealDim.updateDim('material',option);

      if (all_product_settings["product_"+option].special != 0){
         handleRealDim.updateDim('special_sign',true);
      }else{
         handleRealDim.updateDim('special_sign',false);
      } 
   }

   async updateBackgroundShape(addsign = false){
      let wasSpecialSign = false;
      let realBg = handleRealDim.getDim('background');
      let realBorderColor = handleRealDim.getDim('borderColor');

      //checks if special sign/svg bg
      if(handleRealDim.getDim('special_sign') == true){
         if(addsign){
            await this.addCustomSignBg();
         }
            const colorManager = new ManageColors(this.canvas); 
            colorManager.getActiveColors();
         //update color selection
         toolMenu.updateSelections(handleRealDim.getDim('background'), 'background');
         toolMenu.updateSelections(handleRealDim.getDim('borderColor'), 'bordercolor');
         return;
      }

      //makes sure specialSign is removed
       
        if(this.bgGroupSpecial){
         this.canvas.remove(this.bgGroupSpecial);
         this.canvas.remove(this.bgGroupSpecialProduction);
         this.bgGroupSpecial = false;
         this.bgGroupSpecialProduction = false;
         wasSpecialSign = true;
      }


      //rounded should be removed and not handled here 
      if(handleRealDim.getDim('shape') == 'rect-rounded'){
         $('.only-rect-rounded').show();
      }else{
         $('.only-rect-rounded').hide();
      }

      switch(handleRealDim.getDim('shape')){
         case 'triangle':
            if(this.backgroundShape.get('type') != 'triangle' || wasSpecialSign) {
               this.addStandardBackgroundShape(handleRealDim.getDim('shape'));
            }
            break;
         case 'transparent':
            if(this.backgroundShape.get('type') != 'rect' || wasSpecialSign){
               this.addStandardBackgroundShape(handleRealDim.getDim('shape'));
            }
            break;
         case 'circle':
            if(this.backgroundShape.get('type') != 'rect' || wasSpecialSign){
               this.addStandardBackgroundShape(handleRealDim.getDim('shape'));
            }
            break;
         default:
            if(this.backgroundShape.get('type') != 'rect' || wasSpecialSign){
               this.addStandardBackgroundShape(handleRealDim.getDim('shape'));
            }
            break;
      }

      
      if(this.isTransparent && !this.wasTransparent){
         this.updateTransparent();
      }else if(!this.isTransparent && this.wasTransparent){
         this.resetTransparent();
         this.wasTransparent = false;
      }

      //update size and border
      this.updateBackgroundSize();

      this.canvas.calcOffset();

      $(".shape-specific").show();
      $(".shape-specific.not-" + handleRealDim.getDim('shape')).hide();

      this.dontAllowObjectsOutside();
         const colorManager = new ManageColors(this.canvas); 
         colorManager.getActiveColors();
      //update color selection
      toolMenu.updateSelections(realBg, 'background');
      toolMenu.updateSelections(realBorderColor, 'bordercolor');
      this.canvas.requestRenderAll();
      this.canvas.clipPath = this.backgroundShape;
      //onCanvasGUIHandler.menuCanvasSizeControl();
      toolMenu.debouncedUpdatePrice();
   }

   finishRendering(){
      updateGUI();
      //onCanvasGUIHandler.updateDimensionInformation();
      //onCanvasGUIHandler.zoomCanvas();
    //  onCanvasGUIHandler.centerCanvasView();
     signOperation.requestCenterCanvas();
      this.dontAllowObjectsOutside();
   }

   updateBackgroundSize(){
      let realWidth = handleRealDim.getDim('width');
      let realHeight = handleRealDim.getDim('height');
      let bgWidth = fabric.util.parseUnit(handleRealDim.getDim('width')+'mm');
      let bgHeight = fabric.util.parseUnit(handleRealDim.getDim('height')+"mm");

      this.backgroundShape.set('width', bgWidth).setCoords();
      this.backgroundShape.set('height', bgHeight).setCoords();
      this.backgroundShape.set({'left': 0, 'top' : 0});
      this.updateBackgroundBorder(bgWidth,bgHeight);

      let maxBorder = (realWidth < realHeight) ? realWidth / 2 : realHeight / 2;
      toolMenu.addBorderSlider(maxBorder);
      signOperation.setBackgroundDistance();
      //update radius quick fix
      this.updateBackgroundRadius();
      //update mounting
      mountingManager.updateMounting();
      this.canvas.requestRenderAll();
      $.canvasBgManager.updateDim();
      $.canvasFgManager.updateDim();

      //checkIfsignContentShouldBeResized
      if (this.resizeSignContentStatus) {
         this.resizeSignContent();
      }
      //update dimensions gui
      //onCanvasGUIHandler.updateDimensionInformation();
      //centercanvas
      //onCanvasGUIHandler.centerCanvasView();

      signOperation.requestCenterCanvas();
      //update canvas width with menu
      onCanvasGUIHandler.menuCanvasSizeControl();
   }

   updateBackgroundBorder(bgWidth = false, bgHeight = false, render= false){

      if(handleRealDim.getDim('special_sign')){
         return;
      }

      let realBorder = handleRealDim.getDim('border');
      let border = (handleRealDim.getDim('shape') != 'triangle') ? fabric.util.parseUnit(realBorder + 'mm') * 2 : 0;

      if (!bgWidth && !bgHeight) {
         bgWidth = fabric.util.parseUnit(handleRealDim.getDim('width') + 'mm');
         bgHeight = fabric.util.parseUnit(handleRealDim.getDim('height') + "mm");
      }
      
      if(border > bgWidth) border = bgWidth;
      if(border > bgHeight) border = bgHeight;

      this.backgroundInnerShape.set('width', bgWidth - border).setCoords();
      this.backgroundInnerShape.set('height', bgHeight - border).setCoords();

      if (realBorder == 0) {
         this.backgroundShape.set("fill", 'transparent');
      } else {
         let borderColor = handleRealDim.getDim('borderColor');
         if (!borderColor) {
            borderColor = '#000';
            handleRealDim.updateDim('borderColor', borderColor);
         }
         this.backgroundShape.set("fill", borderColor);
      }

      //center the innershape 
      if (realBorder == 0) {
         this.backgroundInnerShape.set({ 'left': 0, 'top': 0 });
      } else {
         let centerPoint = this.backgroundShape.getCenterPoint();
         this.backgroundInnerShape.setPositionByOrigin(centerPoint, 'center', 'center');
      }


      if (realBorder > 0) this.updateBackgroundRadius();

      if (render) this.canvas.requestRenderAll();

   }

   addStandardBackgroundShape(update = false){
      //remove old 
      this.canvas.remove(this.backgroundShape);
      this.canvas.remove(this.backgroundInnerShape);

      //standard shape options 
      let shapeOptions = {
         left: 0,
         top: 0,
         fill: handleRealDim.getDim('borderColor'),
         width: 300,
         height: 300,
         ry: 0,
         rx: 0,
         name: "bg",
         signstudio_standard:true,
      };

      if (handleRealDim.getDim('shape') == 'triangle') {
         this.backgroundShape = new fabric.Triangle(shapeOptions);
         signOperation.setBackgroundShape(this.backgroundShape);
         this.backgroundInnerShape = new fabric.Triangle(shapeOptions);
         signOperation.setbackgroundInnerShape(this.backgroundInnerShape);
      }else{
         this.backgroundShape = new fabric.Rect(shapeOptions);
         signOperation.setBackgroundShape(this.backgroundShape);
         this.backgroundInnerShape = new fabric.Rect(shapeOptions);
         signOperation.setbackgroundInnerShape(this.backgroundInnerShape);
      }

      noResizing(this.backgroundShape);
      noResizing(this.backgroundInnerShape);

      this.backgroundShape.selectable = false;
      this.backgroundInnerShape.selectable = false;

      this.backgroundInnerShape.set('fill', handleRealDim.getDim('background'));
      this.backgroundShape.set('fill', handleRealDim.getDim('borderColor'));

      this.canvas.add(this.backgroundShape);
      this.canvas.add(this.backgroundInnerShape);

      this.canvas.sendToBack(this.backgroundInnerShape);
      this.canvas.sendToBack(this.backgroundShape);

      this.canvas.clipPath = this.backgroundShape;

      if(update){
         //used mainly for restarting sign;
         this.updateBackgroundSize();
         onCanvasGUIHandler.zoomCanvas();
         //onCanvasGUIHandler.centerCanvasView();
         signOperation.requestCenterCanvas();
         this.canvas.requestRenderAll();
         onCanvasGUIHandler.menuCanvasSizeControl();
      }
   }

   async addCustomSignBg(){
      this.currentSettings = updateProdSettings.getCurrentSetting();
      let bredd = this.currentSettings.special.width;
      let hojd = this.currentSettings.special.height;
      let img = this.currentSettings.special.image;
      let prodImg = this.currentSettings.special.prodimg;

         let shapeOptions = {left: 0,top: 0,	width: bredd, height: hojd, ry: 0,rx: 0,name: "bg", signstudio_standard: true};

         let shapeOptionsInner = {left: 0,top: 0, width: bredd, height: hojd, ry: 0,  rx: 0, name: "bg", signstudio_standard: true};

         handleRealDim.updateDimsObj({
            special_sign: true,
            shape: "rectSpecial",
            width: bredd,
            height: hojd,
         });


         //Cleares canvas 
         this.canvas.remove(this.backgroundShape);
         this.canvas.remove(this.backgroundInnerShape);
         this.canvas.remove(this.bgGroupSpecial);
         this.canvas.remove(this.bgGroupSpecialProduction);

         //Creates standard backgrounds as placeholders 
         this.backgroundShape = new fabric.Rect(shapeOptions);
         this.setBackgroundShape(this.backgroundShape);
         this.backgroundInnerShape = new fabric.Rect(shapeOptionsInner);
         this.setbackgroundInnerShape(this.backgroundInnerShape);
         //add transparent backgroundshape for special sign background
         //Adds placeholder backgrounds
         this.canvas.add(this.backgroundShape);
         this.canvas.add(this.backgroundInnerShape);
         //gets size for scaled canvas 
         let realWidth = fabric.util.parseUnit(handleRealDim.getDim('width')+"mm");
         let realHeight = fabric.util.parseUnit(handleRealDim.getDim('height')+"mm");
         //Sets size on canvas 
         this.backgroundShape.set("width", realWidth).setCoords();
         this.backgroundShape.set("height",realHeight).setCoords();
         this.backgroundInnerShape.set("width", realWidth).setCoords();
         this.backgroundInnerShape.set("height",realHeight).setCoords();

         this.backgroundShape.set("strokeWidth", 0).setCoords();

         this.backgroundShape.set("left", 0).setCoords();
         this.backgroundShape.set("top", 0).setCoords();
         this.backgroundInnerShape.set("left", 0).setCoords();
         this.backgroundInnerShape.set("top", 0).setCoords();

         this.backgroundShape.set('fill', 'transparent');
         this.backgroundInnerShape.set('fill', 'transparent');

         this.backgroundShape.set({ rx: 0, ry: 0 });
         this.backgroundInnerShape.set({ rx: 0, ry: 0 });

         if(!prodImg){
            prodImg = img;
         }
         //load normal special sign
        try{
            this.bgGroupSpecial = await this.loadImageAsBG('custom_sign_background', img, true);
            this.bgGroupSpecialProduction = await this.loadImageAsBG('custom_sign_background_prod', prodImg);
        }catch(error){
            console.log(error);
        }
         //load produciton sign

         this.dontAllowObjectsOutside();
         this.finishCustomSign();
   }

   finishCustomSign(){
      //Orders backgrounds in right order 
      this.canvas.sendToBack(this.bgGroupSpecial);
      this.canvas.sendToBack(this.bgGroupSpecialProduction);
      this.canvas.sendToBack(this.backgroundInnerShape);
      this.canvas.sendToBack(this.backgroundShape);

      this.bgGroupSpecial.selectable = false;
      this.backgroundShape.selectable = false;
      this.backgroundInnerShape.selectable = false;
      this.bgGroupSpecialProduction.selectable = false;
      mountingManager.removeMountingHoles(this.canvas);
      onCanvasGUIHandler.updateDimensionInformation();
      this.updateBackgroundSize();
      this.noResizing(this.bgGroupSpecial);
      this.noResizing(this.backgroundInnerShape);
      this.noResizing(this.backgroundShape);
      this.noResizing(this.bgGroupSpecialProduction);	
      onCanvasGUIHandler.zoomCanvas();
      //onCanvasGUIHandler.centerCanvasView();
      signOperation.requestCenterCanvas();
      //canvas.clipPath = null;
      this.canvas.clipPath = this.bgGroupSpecial;
      this.canvas.calcOffset();
      this.canvas.requestRenderAll();
      $(".sign-option-width").val(handleRealDim.getDim('width'));
      $(".sign-option-height").val(handleRealDim.getDim('height')); 
      onCanvasGUIHandler.renderCanvas();
      signSaveAndLoad.saveDesign();
   }

      async loadImageAsBG(name, image, style) {
         let self = this;
         return new Promise((resolve, reject) => {
            fabric.loadSVGFromURL(
               `${my_base_params.image_content}/${image}`,
               function (objects, options) {
                  try {
                     let loadedBG = fabric.util.groupSVGElements(objects, options);
                     loadedBG.scaleToWidth(fabric.util.parseUnit(handleRealDim.getDim('width') + "mm"));
                     loadedBG.scaleToHeight(fabric.util.parseUnit(handleRealDim.getDim('height') + "mm"));

                     self.canvas.add(loadedBG);
                     self.positionVertical(loadedBG);
                     self.positionHorizontal(loadedBG);

                     if (style) {
                        loadedBG.set({ "fill": "#ffffff", "dirty": true });
                     }

                     loadedBG.name = name;
                     loadedBG.id = name;
                     loadedBG.signstudio_standard = true;
                     resolve(loadedBG);
                  } catch (error) {
                     console.error('Error loading SVG:', error);
                     reject(error);
                  }
               },
               null,
               {crossOrigin: 'anonymous'}
            );
         });
      }

   updateBackgroundRadius(){
      let radius = { rx: 0, ry: 0 }
      let realWidth = 0;
      let realHeight = 0;

      switch (handleRealDim.getDim('shape')) {
         case 'circle':
            realWidth = handleRealDim.getDim('width');
            realHeight = handleRealDim.getDim('height');

            radius = {
               rx: (fabric.util.parseUnit(realWidth + 'mm')) / 2,
               ry: (fabric.util.parseUnit(realHeight + 'mm')) / 2,
            };
            this.backgroundShape.set(radius);
            this.backgroundInnerShape.set(radius);
            break;
         case 'rect-rounded':
            let maxRadius
            let standardRadius

            realWidth = handleRealDim.getDim('width');
            realHeight = handleRealDim.getDim('height');
            let borderThickness = handleRealDim.getDim('border');

            maxRadius = (realWidth > realHeight) ? Math.floor(realHeight / 2) : Math.floor(realWidth / 2);

            let realCorner = handleRealDim.getDim('cornerRadius');
            if (realCorner == null) {
               standardRadius = Math.round(maxRadius * 0.1)
               handleRealDim.updateDim('cornerRadius', standardRadius);
            } else {
               standardRadius = realCorner;
            }
            toolMenu.addRadiusSlider(maxRadius, standardRadius, borderThickness);
            handleRealDim.updateDim('cornerRadius', standardRadius);
            this.updateCornerRadius(standardRadius);
            break;
         default:
            this.backgroundShape.set(radius);
            this.backgroundInnerShape.set(radius);
      }

   }

   clearCanvas() {
      let objects = this.canvas.getObjects();
      for (let i = 0; i < objects.length; i++) {
         let object = objects[i];
         if (!object.hasOwnProperty('signstudio_standard') || object.signstudio_standard == false) {
            this.canvas.remove(object);
         }
      }
   }


   dontAllowObjectsOutside(activeObj){
      let backgroundShape = this.backgroundShape;
      let canvas = this.canvas;

      let objects;
      if (activeObj != null) {
         objects = [activeObj];
      } else {
         objects = canvas.getObjects();
      }
      let [minX, maxX, minY, maxY] = [0, backgroundShape.aCoords.br.x, 0, backgroundShape.aCoords.br.y];
      $.each(objects, function(i, obj) {
         let centerPoint = obj.getCenterPoint();
         let outside = false;

         let autoW = handleRealDim.getDim('auto_size_w');
         let autoH = handleRealDim.getDim('auto_size_h');
         if (obj.signstudio_standard != true) {
            if (centerPoint.x < minX && autoW != true) {
               centerPoint.x = minX;
               outside = true;
            }
            if (centerPoint.x > maxX && autoW != true) {
               centerPoint.x = maxX;
               outside = true;
            }
            if (centerPoint.y < minY && autoH != true) {
               centerPoint.y = minY;
               outside = true;
            }
            if (centerPoint.y > maxY && autoH != true) {
               centerPoint.y = maxY;
               outside = true;
            }

            if (outside) {
               obj.setPositionByOrigin(centerPoint, 'center', 'center');
               canvas.requestRenderAll();
            }
         }
      })

   }

   noResizing(shape) {
      shape.setControlVisible("mb", false);
      shape.setControlVisible("ml", false);
      shape.setControlVisible("mt", false);
      shape.setControlVisible("mr", false);
   }
   addObjectToSign(obj) {
      this.addCustomObjectData(obj, 'objectName', this.objectName);
      this.objectName = false;
      this.positionHorizontal(obj);
      this.positionVertical(obj);
      this.canvas.add(obj);
      obj.setCoords();
      this.canvas.setActiveObject(obj);
      this.canvas.requestRenderAll();
      onCanvasGUIHandler.updateObjectControl();
      signSaveAndLoad.saveDesign();
      return obj;
   }

   addCustomObjectData(obj = false, key, value) {
      if (!obj || !key || !value) return;
      if (!obj.hasOwnProperty('stdnCustom')) {
         obj.stdnCustom = {};
      }

      obj.stdnCustom[key] = value;
   }


   scaleToFitSign(shape) {
      let scale = 1.0;

      let smallestScale = this.backgroundShape.getScaledWidth() / shape.width;

      if (this.backgroundShape.height / shape.height < smallestScale)
         smallestScale = this.backgroundShape.getScaledHeight() / shape.height;

      if (smallestScale < 1.0) scale = smallestScale;

      if (smallestScale > 1.0) {
         let largestScale = this.backgroundShape.getScaledWidth() / shape.width;
         if (this.backgroundShape.height / shape.height < largestScale)
            largestScale = this.backgroundShape.getScaledHeight() / shape.height;
         shape.scale(largestScale * 0.5);
      } else {
         shape.scale(scale * 0.5);
      }
   }

   addSymbolByFileName(symbol) {
      let singlecolor = true;
      let symbolGallery = symbolMangement.getSymbolGallery();
      for (var i = 0; i < symbolGallery.length; i++) {
         if (symbolGallery[i].file == symbol) {
            singlecolor = symbolGallery[i].c;
            break;
         }
      }
      this.addSymbol(SYMBOL_ROOT_URL + symbol + ".svg", singlecolor);
   }

   async addSymbol(url, colorize, loadFromString = false, userimage) {

      const svgCheckerClass = new SvgCheck();

      if(signOperation.getSimpleColor() && !loadFromString){
         let isSingleColor = await svgCheckerClass.checkSvgSingleColor(url);
         if(!isSingleColor) return;
      }
      if (loadFromString) {
         fabric.loadSVGFromString(url, (objects, options) => this.afterSVGImport(objects, options, colorize,userimage));
      } else {
         colorize = await svgCheckerClass.checkSvgSingleColor(url); 
         //If it is single color add it as a fabric object
         if(colorize){
            fabric.loadSVGFromURL(
                  url,
                  (objects, options) => this.afterSVGImport(objects, options, colorize,userimage),
                  null,
                  {crossOrigin: 'anonymous'}
               );
         }else{
            //if not sigle color add it as an image with svg as base 
            this.addSVGAsImage(url,userimage);
         }
      }
   }

/*async addSVGAsImage(url, userimage) {
    let svgSize  = await apiClient.getSvgSize(JSON.stringify({image_url:url}));
    console.log(svgSize);
    const img = new fabric.Image();
    console.log('next step');
    img.set({
        originX: 'center',
        originY: 'center',
        dirty: true,
        svgURL: url,
        ownCaching: true,
        perPixelTargetFind: true,
        centeredScaling: true,
        centeredRotation: true,
        singleColor: false,
        width: svgSize.width,
        height: svgSize.height,
    });

    img.setCoords();
    if (userimage) {
        img.name = "userImage";
        img.user_image_id = userimage;
    }

    img.setSrc(url, (img) => {
        this.scaleToFitSign(img);
        this.noResizing(img);
        this.addObjectToSign(img);
    }, {
        crossOrigin: 'anonymous' // Optional: needed if the SVG is from a different domain
    });
}

*/

    async addSVGAsImage(url, userimage){
        let svgSize  = await apiClient.getSvgSize(JSON.stringify({image_url:url}));

        fabric.Image.fromURL(url, (img) => {
            if(svgSize.unit !== 'px'){
                svgSize.width = fabric.util.parseUnit(svgSize.width + svgSize.unit); 
                svgSize.height = fabric.util.parseUnit(svgSize.height + svgSize.unit); 
            }
            img.set({
                originX: 'center',
                originY: 'center',
                dirty: true,
                svgURL: url,
                ownCaching: true,
                perPixelTargetFind: true,
                centeredScaling: true,
                centeredRotation: true,
                singleColor: false,
                width: svgSize.width,
                height: svgSize.height,

            });


            img.setCoords();
            if (userimage) {
                img.name = "userImage";
                img.user_image_id = userimage;
            }
            this.scaleToFitSign(img);
            this.noResizing(img);
            this.addObjectToSign(img);
        }, {crossOrigin: 'anonymous'});
    }

   afterSVGImport(objects, options, colorize, userimage) {
      options.perPixelTargetFind = true;

      let shape = fabric.util.groupSVGElements(objects, options);


      if (signOperation.getSimpleColor() == true && colorize == 1) {
         let colorSet = this.getLastColorUsed();
         manageCustomObjects.applySVGcolor(shape, colorSet);
      }

      shape.centeredScaling = true;
      shape.centeredRotation = true;
      shape.perPixelTargetFind = true;
      shape.singleColor = (colorize == 1 || signOperation.getSimpleColor()) ? true : false;
      if (userimage) {
         shape.name = "userImage";
         shape.user_image_id = userimage;
      }
      this.scaleToFitSign(shape);
      this.noResizing(shape);
      this.addObjectToSign(shape);

   }

   addImageToCanvas(url: string, id: number | null = null) {
      fabric.Image.fromURL(url, (img) => {
         let oImg = img.set({});
         oImg.name = 'userImage';
         oImg.user_image_id = id;
         oImg.centeredScaling = true;
         oImg.perPixelTargetFind = true;
         this.scaleToFitSign(oImg);
         this.noResizing(oImg);

         this.addObjectToSign(oImg);
         this.canvas.setActiveObject(oImg);
      }, { crossOrigin: 'anonymous' });
   }

      setObjectName(objectName: string) {
         this.objectName = objectName;
      }

      handleDragOver(e){
         e.originalEvent.dataTransfer.dropEffect = "copy";
      }

      handleDragDrop(e) {
         let dropData = e.originalEvent.dataTransfer.getData("symbol");
         let dropType = e.originalEvent.dataTransfer.getData("uploadType");
         let changeStatus = e.originalEvent.dataTransfer.getData("changeImage");
         let dropID = e.originalEvent.dataTransfer.getData("item");

         if(changeStatus){
            $.studioImageManager.changeImage(dropData, dropID);
            return;
         }

         if (!dropData) return;

         if (dropType === 'symbol') {
            let symbolName = e.originalEvent.dataTransfer.getData("title");
            this.addSymbolByFileName(dropData, symbolName);
         } else {
            if (dropData.endsWith(".svg")) {
               this.addSymbol(dropData, false, false, dropID);
            } else {
               this.addImageToCanvas(dropData, dropID);
            }
         }
      }

      changeBackgroundColor(color) {
         handleRealDim.updateDim('background', color);
         this.lastBgColor = color;
         this.bgColor = color;

         if (color === 'transparent'){
            this.isTransparent = true;
         }else{
            this.isTransparent = false;
         } 

         if (handleRealDim.getDim('special_sign') != true) {
            this.backgroundInnerShape.set("fill", color);
         } else {
            this.bgGroupSpecial.set("fill", this.bgColor);
         }

         const colorManager = new ManageColors(this.canvas); 
         colorManager.getActiveColors();
         toolMenu.updateSelections(color, 'background');

         if(this.isTransparent){
            this.updateTransparent();
         }

         this.canvas.requestRenderAll();
      }


   updateTransparent(){
      if(handleRealDim.getDim('special_sign') == true) return;
      let zoom = this.canvas.getZoom();
       
      this.backgroundShape.set({'stroke': '#D3D3D3', 'strokeWidth': 1 / zoom, 'fill': 'transparent'});
      this.backgroundInnerShape.set("fill", "transparent");
      handleRealDim.updateDim('background',"transparent");
      handleRealDim.updateDim('borderColor',"transparent");
      this.wasTransparent = true;
   }

   resetTransparent(){
      if (handleRealDim.getDim('background') === 'transparent') {
         let bgcolor = '#fff';
         let borderColor = "000";
         handleRealDim.updateDim('background', bgcolor);
         handleRealDim.updateDim('borderColor', borderColor);
         this.backgroundInnerShape.set('fill', bgcolor);
         this.backgroundShape.set('fill', borderColor);
      }
      this.backgroundShape.set({ 'stroke': "transparent", 'strokeWidth': 0 });
      this.isTransparent = false;

   }

   changeBorderColor(color){
      handleRealDim.updateDim('borderColor', color);
      if (handleRealDim.getDim('border') > 0) {
         this.backgroundShape.set("fill", color);
      } else {
         this.backgroundShape.set("fill", 'transparent');
      }
         const colorManager = new ManageColors(this.canvas); 
         colorManager.getActiveColors();
      toolMenu.updateSelections(color, 'bordercolor');
      this.canvas.requestRenderAll();

   }

   updateSimpleColors(bg, text){
      this.nameTextColor = text;
      this.bgColor = bg;

      let borderColor = (this.bgColor === 'transparent') ? this.bgColor : text;
      //sets bg color
      signOperation.changeBackgroundColor(this.bgColor);
      signOperation.changeBorderColor(borderColor)


      this.canvas.getObjects().forEach((obj) => {
         if (obj.signstudio_standard != true) {
            if (obj.name == "userFabricShape") {
               //get inner outer
               let inner;
               let outer;
               $.each(obj._objects, function(i, element) {
                  if (element.hasOwnProperty('name') && element.name == "userShapeInner") {
                     inner = element;
                  } else {
                     outer = element
                  }
               });

               if (obj.cstBorder == 0) {
                  outer.fill = this.nameTextColor;
                  inner.fill = this.nameTextColor;
               } else {
                  outer.fill = this.nameTextColor;
                  outer.fill = bg;
               }
            } else {
               if ((obj.singleColor !== undefined && obj.singleColor == false)) {
                  this.canvas.remove(obj);
               } else {
                  if (obj.type == 'group') {
                     manageCustomObjects.applySVGcolor(obj, this.nameTextColor);
                  } else {
                     obj.set('fill', this.nameTextColor);
                  }
               }
            }
         }
      });

      this.lastColorUsed = this.nameTextColor;
      this.canvas.requestRenderAll();
   }

   updateCornerRadius(newRadius){
      handleRealDim.updateDim('cornerRadius', newRadius);
      newRadius = fabric.util.parseUnit("1mm") * newRadius;
      let borderThickness = fabric.util.parseUnit("1mm") * handleRealDim.getDim('border');
      this.backgroundShape.set({
         rx: newRadius,
         ry: newRadius
      });
      if (newRadius - borderThickness > 0) {
         this.backgroundInnerShape.set({
            rx: newRadius - borderThickness,
            ry: newRadius - borderThickness,
         });
      } else {
         this.backgroundInnerShape.set({
            rx: 0,
            ry: 0,
         });
      }
      this.canvas.requestRenderAll();

   }

   flipObject(obj) {
      obj = (obj) ? obj : this.canvas.getActiveObject();

      if(!obj) return;
      if (obj) {
         obj.set('flipX', !obj.flipX);
         this.canvas.requestRenderAll();
      }
   }

   moveLayer(move, obj = false) {
      if (!obj) {
         obj = this.canvas.getActiveObject();
      }
      if (obj == null) {
         return;
      }

      if (move == "bottom") {
         this.canvas.sendToBack(obj);
      }

      if (move == "down") {
         this.canvas.sendBackwards(obj);
      }

      if (move == "up") {
         manageCustomObjects.bringForward(obj);
      }

      if (move == "top") {
         manageCustomObjects.bringToFront(obj);
      }

      let layersTooFar = this.canvas.getObjects().indexOf(obj) - 2;

      while (layersTooFar < 0) {
         this.canvas.bringForward(obj);
         layersTooFar = canvas.getObjects().indexOf(obj) - 2;
      }

      this.canvas.requestRenderAll();
   }


   selectObjWithBtn(id, hover = false) {
      let objects = this.canvas.getObjects();
      if (!hover) this.canvas.discardActiveObject();
      for (let index in objects) {
         if (objects[index].objId == id) {
            if (objects[index].selectable) {
               this.canvas.setActiveObject(objects[index]);
            } else if (!hover) {
               this.canvas.setActiveObject(objects[index]);
            }
            this.canvas.requestRenderAll();
            return;
         }
      }
   }


   setGravyrColors($allowedColor, speicalSign = false){
      let colorElement = this.$firstGravyrColors

      if($allowedColor){
         colorElement = $($allowedColor);
      }

      this.bgColor = colorElement.attr('data-sign-option');
      this.nameTextColor = colorElement.attr('data-sign-text');
      this.gravyrColorID = parseInt(colorElement.attr('data-assoc'));


      this.updateSimpleColors(this.bgColor,this.nameTextColor);
   }

   async updateAutoSize(args) {	
      let action = args.action;

      let canvasWidht,canvasHeight;
      let objects = this.canvas.getObjects();
      let addedObj = [];
      let margin = fabric.util.parseUnit('5mm');
      let bounds = [];
      let xLeft = Number.POSITIVE_INFINITY;
      let xRight = Number.NEGATIVE_INFINITY;
      let yTop =  Number.POSITIVE_INFINITY;
      let yBottom =  Number.NEGATIVE_INFINITY;

      $.each(objects, function(index, obj){
         if(obj.signstudio_standard != true){ 
             addedObj = [...addedObj, obj];
             xLeft = Math.min(xLeft, obj.left);
             xRight = Math.max(xRight, obj.getScaledWidth() + obj.left);
             yTop = Math.min(yTop, obj.top);
             yBottom = Math.max(yBottom, obj.getScaledHeight() + obj.top);
         };
      });


      if(addedObj.length == 0){
         //if no objects add text
         handleRealDim.updateDim('auto_size_w',true);
         handleRealDim.updateDim('auto_size_h',true);

         let inserted_object = await manageCustomObjects.insertNewText(); 
         addedObj.push(inserted_object);

         canvasWidht = addedObj[0].getBoundingRect(true,true).width + margin*2;
         canvasHeight = addedObj[0].getBoundingRect(true,true).height + margin*2;

      }else if(addedObj.length == 1){
         canvasWidht = addedObj[0].getBoundingRect(true,true).width + margin*2;
         canvasHeight = addedObj[0].getBoundingRect(true,true).height + margin*2;
      }else{
         //get min max
          xLeft -= margin;
          xRight += margin;
          yTop -= margin;
          yBottom += margin;
        
          $.each(addedObj, function(index, obj){
              if(handleRealDim.getDim('auto_size_w') == true){
                  obj.left = obj.left - xLeft;
                  obj.setCoords()
              }
              if(handleRealDim.getDim('auto_size_h') == true){
                  obj.top = obj.top - yTop;
                  obj.setCoords()
              }
          });

         canvasWidht = xRight - xLeft;
         canvasHeight = yBottom - yTop;
      }
    

    
      let previousWidth = handleRealDim.getDim('width');
      let previousHeight = handleRealDim.getDim('height');

      let newWidth = Math.ceil(canvasWidht / fabric.util.parseUnit('1mm'));
      if(newWidth < 30) newWidth = 30;

      handleRealDim.updateWidth(newWidth);

      let newHeight = Math.ceil(canvasHeight / fabric.util.parseUnit('1mm'));
      if(newHeight < 30) newHeight = 30;

      handleRealDim.updateHeight(newHeight);
    

      //sets fixed size if choosen
      if(handleRealDim.getDim('auto_size_w') == false){
         handleRealDim.updateWidth($(".sign-option-width").val());
      }
      if(handleRealDim.getDim('auto_size_h') == false){
         handleRealDim.updateHeight($(".sign-option-height").val());
      }

      await masterSync({'updateBackgroundShape' : ''}, true);

      //if only has one object center
      let hasAuto = false;

      if(handleRealDim.getDim('auto_size_w') == true){
         //(addedObj.length == 1)? centerObjectVertically(addedObj[0]) : '';
         (addedObj.length == 1) ? signOperation.positionVertical(addedObj[0]) : '';
          hasAuto  = true;
      }
      if(handleRealDim.getDim('auto_size_h') == true){
         //(addedObj.length == 1) ? centerObjectHorizontally(addedObj[0]): '';
         (addedObj.length == 1) ? signOperation.positionHorizontal(addedObj[0]) : '';
          hasAuto  = true;

      }

      let dontCenterCanvas = (!hasAuto || (hasAuto && newWidth == previousWidth && previousHeight == newHeight));
      this.canvas.calcOffset();
      //onCanvasGUIHandler.updateDimensionInformation();
      //onCanvasGUIHandler.zoomCanvas();
      if(!dontCenterCanvas){
          signOperation.requestCenterCanvas();
      }
      //onCanvasGUIHandler.centerCanvasView();
      this.canvas.requestRenderAll(); 
   }

addTemplateToSign(templateObject) {
   if (!templateObject || !templateObject.hasOwnProperty('sign')) return
   this.clearCanvas();

   let realDimensions = handleRealDim.getAllDim();
   let scaleXmm = realDimensions.width / templateObject.width;
   let scaleYmm = realDimensions.height / templateObject.height;

   //set valid dim 
   if (templateObject.border) {
      handleRealDim.updateDim('border', parseInt(templateObject.border));
   } else {
      handleRealDim.updateDim('border', 0);
   }


   let scaleToFit = 1;
   scaleToFit = (scaleYmm < scaleYmm) ? scaleXmm : scaleYmm;
   let index = 0;
   fabric.util.enlivenObjects(templateObject.sign.objects, async (enlivenedObjects) => {
      try {
         for (const obj of enlivenedObjects) {
            let objectID = (obj.hasOwnProperty('id')) ? obj.id : false;
            if (obj.hasOwnProperty('name') && obj.name === 'bg') {
               if (objectID === 'backgroundShape' || index === 0) {
                  let bg = this.backgroundShape;
                  obj.set({
                     width: obj.width * scaleXmm,
                     height: obj.width * scaleYmm,
                     scaleX: bg.scaleX,
                     scaleY: bg.scaleY,
                  });
                  this.canvas.remove(this.backgroundShape);
                  this.backgroundShape = obj;
                  this.canvas.add(obj);
               } else if (objectID === "backgroundInnerShape" || index === 1) {
                  let bgInner = this.backgroundInnerShape;
                  obj.set({
                     width: obj.width * scaleXmm,
                     height: obj.height * scaleYmm,
                     scaleX: bgInner.scaleX,
                     scaleY: bgInner.scaleY,
                  });
                  this.canvas.remove(this.backgroundInnerShape);
                  this.backgroundInnerShape = obj;

                  this.canvas.add(obj);
               }
            } else {
               //modify objects
               if (scaleToFit !== false) {
                  obj.set(this.correctPositionLoad(obj, index, scaleXmm, scaleYmm, 0, 0));
               }
               //check if object is text than load font
               if (obj.type === 'i-text') {
                  if (obj.text.trim().length !== 0) {
                     let font = {family: obj.fontFamily, weight: obj.fontWeight, style: obj.fontStyle}
                     try {
                        await $.manageFonts.loadFont(font);
                     } catch (error) {
                        console.error("Error loading font:", error);
                     }
                  }
               }
               this.canvas.add(obj);
            }
            if (obj) obj.setCoords();
            index++;
         };
      } catch {

      }


      this.updateBackgroundShape();
      this.updateBackgroundBorder();
      //toolMenu.addBorderSlider();
      this.canvas.requestRenderAll();
   });
}


correctPositionLoad(obj, index, scaleXNew, scaleYNew, rTop = 0, rLeft = 0, fixed = false) {
   let newLeft = obj.left * scaleXNew - rLeft * scaleXNew;
   let newTop = obj.top * scaleYNew - rTop * scaleXNew;

   let scaleX = 1;
   let scaleY = 1;

   scaleX = fixed ? 1 : obj.scaleX * scaleXNew;
   scaleY = fixed ? 1 : obj.scaleY * scaleYNew;

   if (!fixed) {
      if (obj) {
         let originalScaleX = obj.scaleX;
         let originalScaleY = obj.scaleY;

         scaleX = originalScaleX * scaleXNew;
         scaleY = originalScaleY * scaleYNew;
         let scaleRatio = 1;
         if (scaleX < scaleY) {
            scaleRatio = originalScaleY / originalScaleX;
            scaleY = scaleX;
            scaleY *= scaleRatio;
         } else if (scaleY > scaleX) {
            scaleRatio = originalScaleX / originalScaleY;
            scaleX = scaleY;
            scaleX *= scaleRatio;
         }

         //Check if has clippatt if so we need to update the clippath as well
         if (obj.clipPath) {
            let clip = obj.clipPath;

            let originalClipScaleX = clip.scaleX;
            let originalClipScaleY = clip.scaleY;

            let clipScaleX = originalClipScaleX * scaleXNew;
            let clipScaleY = originalClipScaleY * scaleYNew;

            let newClipLeft = clip.left * scaleXNew;
            let newClipTop = clip.top * scaleYNew;

            let scaleRatioClip = 1;
            if (scaleX < scaleY) {
               scaleRatioClip = originalClipScaleY / originalClipScaleX;
               clipScaleY = clipScaleX;
               clipScaleY *= scaleRatioClip;
            } else if (scaleX > scaleY) {
               scaleRatioClip = originalClipScaleX / originalClipScaleY;
               clipScaleX = clipScaleY;
               clipScaleX *= scaleRatioClip;
            }
            clip.set({
               left: newClipLeft,
               top: newClipTop,
               scaleX: clipScaleX,
               scaleY: clipScaleY,
               width: clip.width,
               height: clip.height
            });
         }
      };

   }

   let returnSetting = {
      'left': newLeft,
      'top': newTop,
      'scaleX': scaleX,
      'scaleY': scaleY,
      'width': obj.width,
      'height': obj.height,
   };

   if (fixed) {
      returnSetting.signstudio_standard = true;
      returnSetting.height = obj.height * obj.scaleY * scaleY;
      returnSetting.width = obj.width * obj.scaleX * scaleX;
   }
   return returnSetting;
}



  resizeSignContent() {
   if (!this.resizeSignContentStatus) return;

   this.resizeSignContentStatus = false;

   if (handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h')) return;


   let realDimensions = handleRealDim.getDim(['height', 'width']);
   
   let scaleXmm = realDimensions.width / this.oldSize.width;
   let scaleYmm = realDimensions.height / this.oldSize.height;

   let ratioSame = parseInt(realDimensions.width / realDimensions.height) == parseInt(this.oldSize.width / this.oldSize.height); 


   let group = new fabric.Group([], {});


   let objects = this.canvas.getObjects();

   for (let i = 0; i < objects.length; i++) {
      let obj = objects[i];
      let backgroundObjectNames = ['bg', "custom_sign_background_prod","custom_sign_background"];
      if (!obj.hasOwnProperty('name') || (obj.hasOwnProperty('name') && !backgroundObjectNames.includes(obj.name))) {
            this.canvas.remove(obj);
            group.addWithUpdate(obj);
      }
   }

    // Initial canvas dimensions, stored in your object
    let initialCanvasWidth = fabric.util.parseUnit(this.oldSize.width + 'mm');
    let initialCanvasHeight = fabric.util.parseUnit(this.oldSize.height + 'mm');


    let groupWidth = group.getBoundingRect().width;
    let groupHeight = group.getBoundingRect().height;


    let newWidth = fabric.util.parseUnit(realDimensions.width + 'mm');
    let newHeight = fabric.util.parseUnit(realDimensions.height + 'mm');

    // Calculate the proportions of the group relative to the initial canvas size
    let widthProportion = groupWidth / initialCanvasWidth;
    let heightProportion = groupHeight / initialCanvasHeight;

    // Determine the target size of the group based on new dimensions and initial proportions
    let targetGroupWidth = newWidth * widthProportion;
    let targetGroupHeight = newHeight * heightProportion;

    // Calculate scaling factors based on the target size
    let scaleX = targetGroupWidth / groupWidth;
    let scaleY = targetGroupHeight / groupHeight;

    // Choose the maximum scale that doesn't exceed the new dimensions
    let maxScale;
    if (scaleX < scaleY) {
        // If scaling by width is smaller, check if this fits in height when applied
        if (groupHeight * scaleX <= newHeight) {
            maxScale = scaleX;
        } else {
            maxScale = scaleY;
        }
    } else {
        // If scaling by height is smaller, check if this fits in width when applied
        if (groupWidth * scaleY <= newWidth) {
            maxScale = scaleY;
        } else {
            maxScale = scaleX;
        }
    }
    

    group.scaleX = group.scaleY = maxScale;
    

    group.set({
        left: (group.left + groupWidth / 2) * scaleX - group.getScaledWidth() / 2,
        top: (group.top + groupHeight / 2) * scaleY - group.getScaledHeight() / 2
    });

    // Decompose the group back to individual objects if necessary
    group.forEachObject((obj) => {
        group.removeWithUpdate(obj);
        obj.setCoords(); // Update coordinates after removing from group
        this.canvas.add(obj);
    });

    this.canvas.remove(group);

   this.resizesigncontentstatus = false;
}

}

let signOperation : SignOperation;


   class Symbols {
      constructor() {
         this.symbolSelection = [];
         this.symbolGallery = [];
         this.symbolTags = [];
         this.debouncedUpdateSymbolFilter = this.debounce(this.updateSymbolFilter.bind(this), 300);

      }

       debounce(func, wait) {
           let timeout;

           return function executedFunction(...args) {
               const later = () => {
                   clearTimeout(timeout);
                   func(...args);
               };

               clearTimeout(timeout);
               timeout = setTimeout(later, wait);
           };
       }

      updateSymbolFilter(filter) {
         this.symbolSelection = [];


         if (filter.length < 1) {
            this.symbolSelection = this.symbolGallery;
            this.updateSymbolGalleryView();
            return;
         }

         for (var i = 0; i < this.symbolGallery.length; i++) {
            if (this.searchForTagMatch(filter, this.symbolGallery[i])) {
               this.symbolSelection.push(this.symbolGallery[i]);
            }

         }

         this.updateSymbolGalleryView();
      }

      getSymbolGallery() {
         return this.symbolGallery;
      }

      updateSymbolFilterTerm(term) {
       if (term.length < 2) {
            this.debouncedUpdateSymbolFilter("");
            return;
        }
        this.debouncedUpdateSymbolFilter(term);
      }

      updateSymbolGalleryView() {
         $(".new-symbol-grid").empty();

         for (var i = 0; i < this.symbolSelection.length; i++) {
            let multiColor = 'multi-color';
            var symbol = this.symbolSelection[i];

            if (symbol.c == "1" || symbol.c == 1) {
               multiColor = '';
            }
            let src = SYMBOL_THUMB_URL + symbol.file + '.svg';
            let newSymbol = `<li 
								data-symbol="${symbol.file}" 
								data-name="${symbol.name}" 
								id="a${i}"
								draggable="true" 
								class="${multiColor}">
								<img src="${src}" 
									alt="${symbol.name}" 
									title="${symbol.name}" 
                                    loading="lazy"  
									class="bloop">
							</li>`;

            $(".new-symbol-grid").append(newSymbol);
         }

         let allmultiColors = $('.multi-color', this.$symbolObjMenu);
         updateProdSettings.setSymbolsMulticolor(allmultiColors);
         //update multicolor, if simple color is true than multi color symbols should be hidden
         let show = true;
         if (signOperation.getSimpleColor()) {
            show = false;
         }
         updateProdSettings.updateAlternativesInMenu({ 'show': show, 'class': allmultiColors });
         //updateProdSettings.setSymbolsMulticolor($('.multi-color', this.$symbolObjMenu));
         //updateProdSettings.updateProductSettings(realDimensions.material);
      }

      searchForTagMatch(filter, symbol) {
         let tags = symbol.tags;
         filter = filter.toLowerCase();
         for (var j = 0; j < tags.length; j++) {
            if (tags[j] != null) {
               let check = tags[j].toLowerCase();
               if (check.startsWith(filter)) {
                  return true;
               }
            }
         }
         return false;
      }

      addAllSymbolsToGallery(data) {
         for (var i = 0; i < data.length; i++) {
            this.addSymbolToGallery(data[i][0], data[i][1], data[i][2], data[i][3]);
         }
         this.addSymbolCat();
      }

      addSymbolToGallery(file, name, singlecolor, tags) {
         let tagNames = tags.map(tag => tag.name);
         this.symbolGallery.push({ file: file, name: name, c: singlecolor, tags: tagNames });
         let self = this;
         $.each(tags, function(index, value) {
            if (self.symbolTags.indexOf(value.name) === -1 && value.name != null && value.menu == 1) {
               self.symbolTags.push(value.name);
            }
         });
         this.symbolTags.sort();
      }

      addSymbolCat() {
         $.each(this.symbolTags, function(index, value) {
            $('.symbol-left .categories ul').append(`
			 <li value="${value}">${value.substring(0, 1).toUpperCase() + value.substring(1)}</li>
			`);
         });
      }
   }


   class ImageManager {

      constructor(canvas, signOperation) {
         this.canvas = canvas;
         this.signOperation = signOperation;
         this.bind();
         this.events();
      }

      bind() {
         this.toolWrap = $('#toolwrap');
         this.uploadBtn = $('#upload-image-crop', this.toolWrap);
      }



      events() {
         this.toolWrap.on('click', '.img-crop-alt', (e) => {
            let crop = $(e.currentTarget).data('type');
            //if type is own shape validate the shape first and then add it, can only be path
            if (crop === 'upload') {
               this.uploadBtn.click();
            } else {
               this.cropimage(crop);
            }
         });

         this.uploadBtn.on('change', (e) => {
            this.addImageAsCrop(e);
         });
      }

      addImageAsCrop(e) {
         let fileList = e.target.files;
         if (fileList.length > 0) {
            const file = fileList[0];
            const reader = new FileReader();

            reader.onload = (event) => {
               const svgString = event.target.result;

               // Validate and Convert the SVG to Fabric Object
               fabric.loadSVGFromString(svgString, (objects, options) => {
                  let svgObject = fabric.util.groupSVGElements(objects, options);
                  svgObject.perPixelTargetFind = true;
                  if (typeof svgObject === 'object' && Object.keys(svgObject).length > 0) {
                     this.cropimage('upload', svgObject);
                  } else {
                     // Handle invalid SVG (not a single path or not allowed format)
                     console.error("Uploaded SVG is not in a valid format for clipPath.");
                  }
               });
            };

            reader.readAsText(file);
         }
      }

      cropimage(type, clipPathObject = false) {
         let image = this.customGetActiveObject();
         if (!image) {
            return;
         }

         let width = image.width * image.scaleX;
         let height = image.height * image.scaleY;
         let clipPath = null; // Using null to indicate no clipping path
         let center = image.getCenterPoint(); // Getting the center point of the image

         switch (type) {
            case 'none':
               if (image.clipPath && image.stdnCustom && image.stdnCustom.croped) {
                  // If you've added the clipPath as a separate object on the canvas, 
                  // you should remove it like this:
                  this.canvas.remove(image.clipPath);
                  this.signOperation.removeFakeBoxForClipping();
                  // Then, remove the clipPath from the image
                  delete image.clipPath;
                  image.set({
                     lockScalingX: false,
                     lockScalingX: false,
                     hasControls: true,
                     hasBorders: true,
                     evented: true,
                     selectable: true,
                  });

                  image.stdnCustom.croped = false;
                  this.canvas.setActiveObject(image);
               }
               this.canvas.requestRenderAll();
               return;
               break;
            case 'rectangle':
               clipPath = new fabric.Rect({
                  width: width,
                  height: height,
                  left: center.x,  // use top-left corner instead of center
                  top: center.y,
                  originX: 'center',  // adjust origin to top-left corner
                  originY: 'center',
                  absolutePositioned: true,
                  layout: 'clip-path',
                  controlsAboveOverlay: true,
               });
               break;
            case 'circle':
               let radius = (width > height) ? width / 2 : height / 2;
               clipPath = new fabric.Circle({
                  radius: radius,
                  left: center.x,  // for a circle, the center is still appropriate
                  top: center.y,
                  originX: 'center',  // keep the origin at center for the circle
                  originY: 'center',
                  absolutePositioned: true,
                  layout: 'clip-path',
                  controlsAboveOverlay: true,
               });
               break;
            case 'upload':
               if (clipPathObject) {
                  // Calculate the scale factors
                  let scaleX = width / clipPathObject.width;
                  let scaleY = height / clipPathObject.height;

                  let scaleedRatio = (scaleX > scaleY) ? scaleY : scaleX;

                  // Apply scaling to the clippath
                  clipPathObject.scaleX = scaleedRatio;
                  clipPathObject.scaleY = scaleedRatio;

                  // Set the position of the clippath
                  clipPathObject.set({
                     left: center.x,
                     top: center.y,
                     originX: 'center',
                     originY: 'center',
                     absolutePositioned: true,
                     layout: 'clip-path',
                     controlsAboveOverlay: true,
                  });
                  clipPath = clipPathObject;
               }
               break;
            default:
               return;
         }

         image.clipPath = clipPath;
         image.perPixelTargetFind = true;
         if(!image.stdnCustom){
            image.stdnCustom = {};
         }

         if(type !== 'none'){
            image.stdnCustom.croped = true;
         }

         this.signOperation.drawFakeBoxForClipping(image, false);
         this.canvas.requestRenderAll();
      }

      customGetActiveObject() {
         let obj = this.canvas.getActiveObject();
         if (obj && obj.hasOwnProperty('name') && obj.name === 'noEdit') {
            obj = obj.belong;
         }
         this.activeObject = obj;

         return this.activeObject;
      }

      async changeImage(url: string, id: number) {
         if(!url || !id){
            alert('Något gick fel');
         }

         let self = this;
         if (url.endsWith(".svg")) {
            const svgCheckerClass = new SvgCheck();
            let load_as_svg = await svgCheckerClass.checkSvgSingleColor(url); 

            if(load_as_svg){
               fabric.loadSVGFromURL(url, (objects, options) => {
                  let svg = fabric.util.groupSVGElements(objects, options);
                  self.modifieImage(svg, id);
                  self.canvas.requestRenderAll();
               },null,{crossOrigin: 'anonymous'});
            }
         } 

         fabric.Image.fromURL(url, function(img) {
            //modifie image 
            self.modifieImage(img, id);
            self.canvas.requestRenderAll();
         },{crossOrigin: 'anonymous'});
      }

      modifieImage(img, id) {
         //first get previous image size and center
         let activeObject = this.canvas.getActiveObject();
         let oldImage = this.customGetActiveObject()

         //get old size and position
         let oldWidth = oldImage.width * oldImage.scaleX;
         let oldHeight = oldImage.height * oldImage.scaleY;
         let center = oldImage.getCenterPoint();

         let imgWidth = img.width;
         let imgHeight = img.height;

         let newscale;

         let oldImageIndex = this.canvas.getObjects().indexOf(oldImage);

         //use the largest in the square to keep the ratio
         let widthScale = oldWidth / imgWidth;
         let heightScale = oldHeight / imgHeight;
         newscale = Math.min(widthScale, heightScale);

         img.name = oldImage.name
         img.user_image_id = id;
         //keep id
         if (oldImage.hasOwnProperty('objId')) {
            img.objID = oldImage.objId
         }

         //keep template variables
         if (oldImage.hasOwnProperty('templateName')) {
            img.templateName = oldImage.templateName;
         }

         if (oldImage.hasOwnProperty('name')) {
            img.name = oldImage.name;
         }
         //updateCrop if cropped
         if (oldImage.hasOwnProperty('clipPath') && oldImage.clipPath) {
            img.clipPath = oldImage.clipPath;
            oldImage.clipPath = null;
         }

         if (activeObject.hasOwnProperty('belong') && activeObject.belong) {
            activeObject.belong = img;
         }

         this.canvas.add(img);
         img.scale(newscale);

         img.templateName = this.templateImage;
         img.set({
            originX: 'center',
            originY: 'center',
            left: center.x,
            top: center.y,
            selectable: true
         });
         img.setCoords();

         img.setControlsVisibility({
            mt: true,
            mb: true,
            ml: true,
            mr: true,
            bl: true,
            br: true,
            tl: true,
            tr: true,
            mtr: true,
         });

         this.canvas.moveTo(img, oldImageIndex);
         this.canvas.setActiveObject(img);
         this.canvas.remove(oldImage);
         this.canvas.requestRenderAll();
      }
   }



class OnCanvasGUIHandler{
	constructor(canvas, signOperations,backgroundCanvas, foregroundCanvas){
		this.canvas = canvas;
        this.backgroundCanvas = backgroundCanvas;
        this.foregroundCanvas = foregroundCanvas;
		this.signOperations = signOperations;
		this.isMobile = false;
		this.zoomedHeight = 1;
		this.zoomedWidth = 1;

        this.lastZoomValue = 100;
        this.animationFrameId = null;
        this.timeoutId = null;
        this.handleZoom = this.handleZoom.bind(this);
        
        // Configuration options
        this.zoomConfig = {
            delay: 10,
            animationDuration: 150,
            useRequestAnimationFrame: true
        };

		this.initialize();

		this.allCoords;

	}

	initialize(){
		this.$window = $(window);
		this.getWindowHeight();

		this.objectControlElement = $(".object-controls"); 
		this.$magenetSnappingBtn = $('.magnet-snapping');
		this.$zoomRange = $( "#zoomrange");
		this.$zoomRangeText = $('#zoom-per span');
		this.$zoomRangeFullScreen = $("#zoomrange-full");
		this.$zoomRangeFullScreenText = $('#zoom-per-full span');
		this.$designArea = $('.design-area');
		this.$canvasContainer = $(".canvas-container");
		this.$canvasWrap = $('#canvas-wrap');
		this.$toolWrap = $('#toolwrap');
		this.$flipButton = $(".option-flip");
		this.binding();
	}

	binding(){
		this.$magenetSnappingBtn.on('click', () => {
			this.updateMagneticSnapping();
		  });

		this.$window.on('resize', () => {
			this.windowResize();
		});
	}

	getWindowHeight(){
		this.$window.on('load', () => {
			// Code that needs to wait for the page to fully load goes here
			//this.vh = window.innerHeight * 0.01;
			//document.documentElement.style.setProperty('--vh', `${vh}px`);
		  });
	}

	renderCanvas(){
		this.updateCanvasSize();
		//this.zoomCanvas();
        this.signOperations.requestCenterCanvas();
		//this.centerCanvasView();
		this.canvas.renderAll();
		this.menuCanvasSizeControl();
	}

	updateMagneticSnapping(){
		if (this.$magenetSnappingBtn.is('.active')) {
			this.$magenetSnappingBtn.removeClass('active');
			this.signOperations.setMagnetSnapping(false);
		  } else {
			this.$magenetSnappingBtn.addClass('active');
			this.signOperations.setMagnetSnapping(true);
		  }
	}


	updateDimensionInformation(){
		let distance = 55;
		let distance2 = 40;

		if(this.$window.width()<768){
			distance = 55;
			distance2 = 10;
		}
		//setting upp height info gui
        this.signOperations.manageDimensionDisplay();

		//updates top nav size
		$('.top-tool-bar-width span').html(handleRealDim.getDim('width')+"mm");
		$('.top-tool-bar-height span').html(handleRealDim.getDim('height')+"mm");
	
		var maxDim = handleRealDim.getDim('width');
        if (handleRealDim.getDim('height') < handleRealDim.getDim('width'))
            maxDim = handleRealDim.getDim('height');

        var maxBorderThickness = Math.floor(maxDim / 4);
        $(".thicc-slider").attr("max", maxBorderThickness);
    }

    updateHeightText(height){
        this.$heightInfoWrpText.text(height + " mm");
    }

    updateWidthText(width){
        this.$widthInfoWrpText.text(width + " mm");
    }

    centerCanvasView(lastRun = false) {
        let backgroundShape = this.signOperations.getBackgroundShape();
        if (!backgroundShape) return;

        if(!lastRun){
           this.signOperations.manageDimensionDisplay();
        }

        let objWidth = backgroundShape.getScaledWidth();
        let objHeight = backgroundShape.getScaledHeight();

        let dimensionsLines = this.signOperations.getDimensionLines();

        if(dimensionsLines.width){
            let lineGroupWidth = dimensionsLines.width.getScaledHeight();
            let additionalHeight = lineGroupWidth + dimensionsLines.width.top + lineGroupWidth/2 - objHeight;
            objHeight += additionalHeight;
        }
        if(dimensionsLines.height){
            let lineGroupHeight = dimensionsLines.height.getScaledWidth();
            let additionalWidth = lineGroupHeight + dimensionsLines.height.left + lineGroupHeight/2 - objWidth;
            objWidth += additionalWidth;
        }

        let bgCanvasDim = $.canvasBgManager.getDim();
        let fgCanvasDim = $.canvasFgManager.getDim();

        let bgWidth = objWidth;
        let bgHeight = objHeight;

        let bgCW = objWidth;
        let bgCH = objHeight;
        let fgCW = objWidth;
        let fgCH = objHeight;

        if (bgCanvasDim) {
            if (bgCanvasDim.width > objWidth) bgWidth = bgCanvasDim.width;
            if (bgCanvasDim.height > objHeight) bgHeight = bgCanvasDim.height;
            bgCW = (objWidth > bgCanvasDim.width) ? objWidth : bgCanvasDim.width;
            bgCH = (objHeight > bgCanvasDim.height) ? objHeight : bgCanvasDim.height;
        }

        if (fgCanvasDim) {
            if (fgCanvasDim.width > bgWidth) bgWidth = fgCanvasDim.width;
            if (fgCanvasDim.height > bgHeight) bgHeight = fgCanvasDim.height;
            fgCW = (objWidth > fgCanvasDim.width) ? objWidth : fgCanvasDim.width;
            fgCH = (objHeight > fgCanvasDim.height) ? objHeight : fgCanvasDim.height;
        }

        let absolutCanvas = {
            left: Math.min(bgCanvasDim.left, fgCanvasDim.left, backgroundShape.left),
            right: Math.max(backgroundShape.width, fgCanvasDim.right, bgCanvasDim.right),
            top: Math.min(bgCanvasDim.top, fgCanvasDim.top, backgroundShape.top),
            bottom: Math.max(backgroundShape.height, fgCanvasDim.bottom, bgCanvasDim.bottom),
        }

        absolutCanvas['width'] = absolutCanvas['right'] - absolutCanvas['left'];
        absolutCanvas['height'] = absolutCanvas['bottom'] - absolutCanvas['top'];

        let zoomWidth = this.canvas.width / (bgWidth);
        let zoomHeight = this.canvas.height / (bgHeight);
        zoomWidth = this.canvas.width / (absolutCanvas['width']);
        zoomHeight = this.canvas.height / (absolutCanvas['height']);


        let newzoom = Math.min(zoomWidth, zoomHeight);


        if(backgroundShape.strokeWidth && backgroundShape.strokeWidth > 0){
            backgroundShape.strokeWidth = 1 / newzoom;
        }

        newzoom *= 0.95;


        let panX = 0
        let panY = 0

        //get center
        panX = ((this.canvas.getWidth() / newzoom / 2) - (absolutCanvas.width / 2) - absolutCanvas.left) * newzoom
        panY = ((this.canvas.getHeight() / newzoom / 2) - (absolutCanvas.height / 2) - absolutCanvas.top) * newzoom

        //panX = ((this.canvas.getWidth() / newzoom / 2) - (objWidth / 2)) * newzoom
        //panY = ((this.canvas.getHeight() / newzoom / 2) - (objHeight / 2)) * newzoom

        this.canvas.setViewportTransform([newzoom, 0, 0, newzoom, panX, panY]);
        this.backgroundCanvas.setViewportTransform([newzoom, 0, 0, newzoom, panX, panY]);
        this.foregroundCanvas.setViewportTransform([newzoom, 0, 0, newzoom, panX, panY]);

        $('#zoom-per span').text(95);
        this.$zoomRange.slider({ value: 95 });
        this.$zoomRangeFullScreenText.slider({ value: 95 });

        this.updateDimensionInformation();
        this.canvas.requestRenderAll();

        /*remove jump in render do not know where the bug is */
            setTimeout(() => {
                signOperation.setVisibility();
            }, 100);
    }


	checkforCursorType(event){
		let backgroundShape = this.signOperations.getBackgroundShape();
		//checks for panning
		if(event.target != null && event.target.selectable == false){
			let yRatio = (backgroundShape.height+10)*canvas.getZoom()/canvas.getHeight();
			let xRatio = (backgroundShape.width+10)*canvas.getZoom()/canvas.getWidth();
			if(xRatio > 1 || yRatio > 1){
				event.target.hoverCursor = "grab";
				return true;
			}else{
				return event.target.hoverCursor = "default";
			}
		}else{
			if (event.target == null) return;
	
			if (event.target.type == "i-text") {
				event.target.hoverCursor = "pointer";
			}else if(event.target.selectable == true){
				event.target.hoverCursor = "move";
			}
		}
	}
	
	updateObjectControl(dontdisplay){
		let object = this.canvas.getActiveObject();
		//objectControlElement set in init 
		if(object){
			if(dontdisplay == true){
				this.objectControlElement.addClass('dont-display');
			}else{
				this.objectControlElement.removeClass('dont-display');
				let box = object.getBoundingRect();
				let vpt = this.canvas.viewportTransform;
				let zoom = this.canvas.getZoom();

				let controlWidthHalf = this.objectControlElement.outerWidth(true)/2;
				let top = box.top + box.height+ 10;
				let left = box.left + box.width/2 - controlWidthHalf;
				let right = box.left + box.width/2 + controlWidthHalf; 

				let canvasLeft = this.$canvasContainer.offset().left;
				let canvasWrpRight = this.$canvasWrap.outerWidth();

				if(this.isMobile){
					if (left < 0 && (canvasLeft + left) < 10){
						left = 10 - canvasLeft;
					}else if(right > canvasWrpRight-10){
						left = canvasWrpRight-controlWidthHalf*2;
					}
				}
				
				if(top > this.$canvasWrap.height()){
					top = this.$canvasWrap.height();
				}
	
				this.objectControlElement.css({
					'left': left +"px",
					'top' :top +"px"
				});
			}
		}else{
			this.objectControlElement.addClass('dont-display');
		}
	}
    removeHelperLines(){
        this.canvas.remove(helperLine);
        this.canvas.remove(helperLineV);
        this.canvas.remove(helperLineObjV);
        this.canvas.remove(helperLineObjH);
    }

    zoomCanvas() {
        return;
        let backgroundShape = this.signOperations.getBackgroundShape();
        let canvas = this.canvas;

        let bgWidth = backgroundShape.width;
        let bgHeight = backgroundShape.height;

        let bgCanvasDim = $.canvasBgManager.getDim();
        let fgCanvasDim = $.canvasFgManager.getDim();

        if (bgCanvasDim) {
            if (bgCanvasDim.width > bgWidth) bgWidth = bgCanvasDim.width;
            if (bgCanvasDim.height > bgHeight) bgHeight = bgCanvasDim.height;
        }

        if (fgCanvasDim) {
            if (fgCanvasDim.width > bgWidth) bgWidth = fgCanvasDim.width;
            if (fgCanvasDim.height > bgHeight) bgHeight = fgCanvasDim.height;
        }


       //set to 85% as we dont have the function to get the widht and height of dimension displays
       let zoomWidth = canvas.width / (bgWidth + 10);
       let zoomHeight = canvas.height / (bgWidth + 10);


        var newzoom = 0;

        if (zoomWidth < zoomHeight) {
            newzoom = zoomWidth;
        } else {
            newzoom = zoomHeight;
        }


        let zoomPoint = new fabric.Point(canvas.width / 2, canvas.height / 2);

        canvas.zoomToPoint(zoomPoint, newzoom);
        if (bgCanvasDim) {
            this.backgroundCanvas.zoomToPoint(zoomPoint, newzoom);
        }
        this.foregroundCanvas.zoomToPoint(zoomPoint, newzoom);
        canvas.requestRenderAll();

        this.$zoomRange.slider({ range: "min", value: 85 });
        this.$zoomRangeText.text(85);

        this.$zoomRangeFullScreen.slider({ range: "min", value: 85 });
        this.$zoomRangeFullScreenText.text(85);

        this.updateObjectControl();
    }

	
    userZoom(zoom = false, updatesliders = false) {
        if (!zoom) zoom = parseInt($('#zoom-per span').text());
        
        if(zoom < 50) zoom = 50;
        if(zoom > 500) zoom = 500;

        let backgroundShape = this.signOperations.getBackgroundShape();
        if (!backgroundShape) return;
        let canvas = this.canvas;
        let zoomPercentage = 100 / zoom;
        this.$zoomRangeText.text(Math.round(zoom));
        this.$zoomRangeFullScreenText.text(Math.round(zoom));

        if (updatesliders) {
            this.$zoomRange.slider({ value: zoom });
            this.$zoomRangeFullScreenText.slider({ value: zoom });
        }

        let rect = backgroundShape.getBoundingRect(true, true);
        let objWidth = rect.width;
        let objHeight = rect.height;

        let bgCanvasDim = $.canvasBgManager.getDim();
        let fgCanvasDim = $.canvasFgManager.getDim();

        let bgWidth = objWidth;
        let bgHeight = objHeight;

        if (bgCanvasDim) {
            if (bgCanvasDim.width > bgWidth) bgWidth = bgCanvasDim.width;
            if (bgCanvasDim.height > bgHeight) bgHeight = bgCanvasDim.height;
        }

        if (fgCanvasDim) {
            if (fgCanvasDim.width > bgWidth) bgWidth = fgCanvasDim.width;
            if (fgCanvasDim.height > bgHeight) bgHeight = fgCanvasDim.height;
        }

        //absolute values from center 
        let absolutCanvas = {
            left: Math.min(bgCanvasDim.left, fgCanvasDim.left, backgroundShape.left),
            right: Math.max(backgroundShape.width, fgCanvasDim.right, bgCanvasDim.right),
            top: Math.min(bgCanvasDim.top, fgCanvasDim.top, backgroundShape.top),
            bottom: Math.max(backgroundShape.height, fgCanvasDim.bottom, bgCanvasDim.bottom),
        }

        absolutCanvas['width'] = absolutCanvas['right'] - absolutCanvas['left'];
        absolutCanvas['height'] = absolutCanvas['bottom'] - absolutCanvas['top'];
        let zoomWidth = canvas.width / (absolutCanvas.width);
        let zoomHeight = canvas.height / (absolutCanvas.height);

        let newzoom = 0;

        if (zoomWidth < zoomHeight) {
            newzoom = zoomWidth;
        } else {
            newzoom = zoomHeight;
        }

        let setZoom = (newzoom * (zoom / 100));
        let zoomingIn = (canvas.getZoom() < setZoom);
        //let newZoomPoint = new fabric.Point(canvas.width / 2, canvas.height / 2);
        let vpt = canvas.viewportTransform;

        let xVPT = false;
        let yVPT = false;

        let screenX = 0;
        let screenY = 0;

        let canvasWidth = canvas.getWidth();
        let canvasHeight = canvas.getHeight();

        let zoomedWidth = absolutCanvas.width * setZoom;
        //zoomedWidth = objWidth * setZoom;

        let centerX = absolutCanvas.left + absolutCanvas.width / 2;
        screenX = (centerX * vpt[0]) + vpt[4];

        if (zoomedWidth > canvasWidth) {
            if (!zoomingIn) { //IF we are zooming in we do not need to care about keeping edges in view
                let right = vpt[4] + (absolutCanvas.right) * setZoom;
                let left = vpt[4] + (absolutCanvas.left) * setZoom;

                let bothOutOfBoundX = (left < 0 && right > canvasWidth);

                //if not both is outof bound take closes to edge to zero
                if (!bothOutOfBoundX && right < canvasWidth) {
                    //out of bound left side
                    xVPT = vpt[4] + (canvasWidth - right);
                } else if (!bothOutOfBoundX && vpt[4] >= left) {
                    //out of bound left side
                    xVPT = vpt[4] - left;
                }
            }
        } else {
            xVPT = ((canvasWidth / setZoom / 2) - (absolutCanvas.width / 2) - absolutCanvas.left) * setZoom;
            xVPT = ((canvasWidth / setZoom / 2) - (objWidth / 2)) * setZoom;
            //let orginVPTx = ((canvasWidth) - (objWidth*setZoom))/2;
            //screenX = canvas.width/2*setZoom;
            //xVPT = orginVPTx;
        }

        let zoomedHeight = absolutCanvas.height * setZoom;
        // zoomedHeight = objWidth * setZoom;


        let centerY = (absolutCanvas.height / 2) + absolutCanvas.top;
        screenY = (centerY * vpt[0]) + vpt[5];

        if (zoomedHeight > canvasHeight) {
            if (!zoomingIn) {
                let bottom = vpt[5] + (absolutCanvas.bottom) * setZoom;
                let top = vpt[5] + (absolutCanvas.top) * setZoom;
                let bothOutOfBound = (top < 0 && bottom > canvasHeight);

                if (!bothOutOfBound && top <= 0 && bottom < canvasHeight) { //if bottom is out of bound
                    yVPT = vpt[5] + (canvasHeight - bottom);
                } else if (!bothOutOfBound && top >= 0 && bottom > canvasHeight) { // if top is out of bound
                    yVPT = vpt[5] - top;
                }
            }
        } else {
            yVPT = ((canvasHeight / setZoom / 2) - (absolutCanvas.height / 2) - absolutCanvas.top) * setZoom;
           yVPT = ((canvasHeight / setZoom / 2) - (objHeight / 2)) * setZoom;
            //let orginVPTy = ((canvasHeight)-(zoomedHeight))/2;
            //yVPT = orginVPTy;
        }


        let newZoomPoint = new fabric.Point(screenX, screenY);
        canvas.zoomToPoint(newZoomPoint, setZoom);
        this.backgroundCanvas.zoomToPoint(newZoomPoint, setZoom)
        this.foregroundCanvas.zoomToPoint(newZoomPoint, setZoom)
        if (!zoomingIn && (yVPT || xVPT || yVPT === 0 || xVPT === 0)) {
            vpt = canvas.viewportTransform;

            if (yVPT || yVPT === 0) {
                vpt[5] = yVPT;
            }
            if (xVPT || xVPT === 0) {
                vpt[4] = xVPT;
            }
            canvas.setViewportTransform(vpt);
            this.backgroundCanvas.setViewportTransform(vpt);
            this.foregroundCanvas.setViewportTransform(vpt);
        }

        //doesnt allow left of canvas to be outside
        canvas.requestRenderAll();
        this.backgroundCanvas.requestRenderAll();
        this.foregroundCanvas.requestRenderAll();
        onCanvasGUIHandler.updateDimensionInformation();
    }

    
    animateZoom(startZoom, targetZoom, startTime, duration) {
        const currentTime = Date.now();
        const elapsed = currentTime - startTime;
        
        if (elapsed < duration) {
            const progress = elapsed / duration;
            const easedProgress = 0.5 * (1 - Math.cos(Math.PI * progress));
            const currentZoom = startZoom + (targetZoom - startZoom) * easedProgress;
            
            this.userZoom(currentZoom);
            
            this.animationFrameId = requestAnimationFrame(() => 
                this.animateZoom(startZoom, targetZoom, startTime, duration)
            );
        } else {
            this.userZoom(targetZoom);
            this.animationFrameId = null;
        }
    }

    handleZoom(newZoomValue) {
        // Cancel any ongoing animations
        if (this.animationFrameId) {
            cancelAnimationFrame(this.animationFrameId);
            this.animationFrameId = null;
        }
        if (this.timeoutId) {
            clearTimeout(this.timeoutId);
            this.timeoutId = null;
        }

        // Debounce the zoom action
        this.timeoutId = setTimeout(() => {
            if (this.zoomConfig.useRequestAnimationFrame) {
                this.animateZoom(
                    this.lastZoomValue, 
                    newZoomValue, 
                    Date.now(), 
                    this.zoomConfig.animationDuration
                );
            } else {
                this.userZoom(newZoomValue);
            }
            this.lastZoomValue = newZoomValue;
        }, this.zoomConfig.delay);
    }


	menuCanvasSizeControl(hide){
		if(this.isMobile) return;
		if(!hide){
			hide = (this.$toolWrap.hasClass('dont-display')) ? true : false;
		}

       //let zoom = this.$zoomRangeText.text();
		if(hide){
			this.$designArea.css('padding-left', 0+"px");
		}else{
           let pad = this.$toolWrap.outerWidth() + 40;
           this.$designArea.css('padding-left', pad+"px");
		}
		this.updateCanvasSize();
        let zoom = $('#zoom-per span').text(); 
        
        let canvasZoom = this.canvas.getZoom();
        //let backgroundShape = this.canvas.getBackgroundShape();
        signOperation.requestCenterCanvas();
	//	this.userZoom(zoom);
	}

	getBackgroundLeft(){
		let object = this.signOperations.getBackgroundShape();;
		let zoom = this.signOperations.getCanvas().getZoom();
		let transform = this.canvas.viewportTransform;
		let objectLeft = object.left
		let objectLeftPx = objectLeft * zoom + transform[4];
		return objectLeftPx;
	}

	updateCanvasSize() {
		this.canvas.setHeight(0);
		this.canvas.setWidth(0);
		let height = this.$canvasContainer.outerHeight();
		let width = this.$canvasContainer.outerWidth();
		this.canvas.setHeight(height);
		this.canvas.setWidth(width);
	
		MAX_SIGN_HEIGHT = this.canvas.getHeight();
		MAX_SIGN_WIDTH = this.canvas.getWidth();
        this.backgroundCanvas.setHeight(height).setWidth(width);
        this.foregroundCanvas.setHeight(height).setWidth(width);

	}

	updateFlipButton(shape = false) {
		if(!shape){
			shape = this.canvas.getActiveObject();
		}
		if (shape["flipX"]) {
			this.$flipButton.addClass("active");
		} else {
			this.$flipButton.removeClass("active");
		}
	}


	windowResize(){
        toolMenu.resizeCanvasToOverFold();
		const MIN_KEYBOARD_HEIGHT = 250 // N.B.! this might not always be correct
		let isKeyboardOpen = false;
  	    this.isMobile = (this.$window.innerWidth() < 768) ? true : false;

		//alert(screen.height);
		//let isKeyboardOpen = isMobile && ($(window).innerHeight() - MIN_KEYBOARD_HEIGHT > $(window).height());
		if(this.isMobile){
			//isKeyboardOpen = isMobile &&  screen.height - MIN_KEYBOARD_HEIGHT >  visualViewport.height;
			if($(document.activeElement).prop('type') === 'textarea') {
				isKeyboardOpen = true;
			}
		}

		//this.vh = this.$window.innerHeight() * 0.01;
		//document.documentElement.style.setProperty('--vh', `${this.vh}px`);

		if(!isKeyboardOpen){
			this.renderCanvas();
		}
	}


	
	calculateAndDrawHelperLines(object, direction, backgroundShape, moving, magnetSnapping) {
		let objBounding = object.getBoundingRect(true,true);
		let objectCenterV = object.getCenterPoint().x;
		let objectCenterH = object.getCenterPoint().y;
		let objLeft = objBounding.left;
		let objWidth = objBounding.width;
		let objRight = objBounding.left + objWidth;
		let objTop = objBounding.top;
		let objHeight = objBounding.height;
		let objBottom = objBounding.top + objHeight;
		let objectMarCor = (objHeight > objWidth) ? objWidth*0.08 : objHeight*0.08;
        let zoom = this.canvas.getZoom();
		let newCoords = {"x" :0, "y":0}
		let objectCoords = {'lefts' : objLeft, 
							'rights' : objRight,
							'tops' : objTop,
							'bottoms' : objBottom,
							'centersV' : objectCenterV,
							'centersH' : objectCenterH}
		var centerLine  = backgroundShape.getCenterPoint().x;
		
		var distanceFromHorizontalCenter = Math.abs(centerLine - objectCenterV);

        this.allCoords = {lefts : [], tops : [], rights :[], bottoms : [], centersV: [], centersH: []}

        const auto_size_h = handleRealDim.getDim('auto_size_h');
        const auto_size_w = handleRealDim.getDim('auto_size_w');
		//only check objects once if no auto size
		if(!moving && auto_size_h != true && auto_size_w != true){
			//get all objects for snapping to right, middle, left
			let allObjects = this.canvas.getObjects();
			let exludeObj = ['bg', 
							"custom_sign_background_prod", 
							"custom_sign_background",
							"helperlineH",
							"helperlineV",
							"helperLineObjV",
							"helperLineObjH"]

			$.each(allObjects, (index, obj) => {
				if(object != obj && obj.signstudio_standard != true && !exludeObj.includes(obj.name)){
						let bounding = obj.getBoundingRect(true,true);
						this.allCoords.lefts.push(bounding.left);
						this.allCoords.tops.push(bounding.top);
						this.allCoords.rights.push(bounding.left + bounding.width);
						this.allCoords.bottoms.push(bounding.top + bounding.height);
						this.allCoords.centersV.push(bounding.left + bounding.width/2);
						this.allCoords.centersH.push(bounding.top + bounding.height/2);
				}
			});
		}


        let lineThickness = 1 / zoom;
	
		if (distanceFromHorizontalCenter < 8 && auto_size_h != true) {

			if (helperLineV.canvas == null){
				this.canvas.add(helperLineV);
			} 
			helperLineV.set({ left: centerLine-(lineThickness/2), width: lineThickness, height: backgroundShape.height});
			if(magnetSnapping){
				//centerObjectVertically(object);
				signOperation.positionVertical(object);
			}
		} else {
			this.canvas.remove(helperLineV);
		}
	
		var centerLine  = backgroundShape.getCenterPoint().y;
		var distanceFromVerticalCenter = Math.abs(centerLine - objectCenterH);

		if (distanceFromVerticalCenter < 8 && auto_size_w != true) {

			if (helperLine.canvas == null){
				this.canvas.add(helperLine);
			} 
			helperLine.set({ top: centerLine-(lineThickness/2), height: lineThickness, width:backgroundShape.width});
			if(magnetSnapping){
				signOperation.positionHorizontal(object);
				//centerObjectHorizontally(object);
			}
		} else {
			this.canvas.remove(helperLine);
		}
	
		if(magnetSnapping){
			//align with objects
			let checkDirections = [];
			if(direction.x != 0){
				checkDirections.push('lefts','rights', 'centersV');
			}
			if(direction.y != 0){
				checkDirections.push('tops','bottoms','centersH');
			}
	
	
			let snapToObj = {'lefts' : {'state' : false, 'distance': 0, 'coords': 0, 'align' : 'x', 'func' : 'left'}, 
							'rights' : {'state' : false, 'distance': 0, 'coords': 0, 'align' : 'x', 'func' : 'right'},
							'centersV' : {'state' : false, 'distance': 0, 'coords': 0, 'align' : 'x', 'func' : 'cenV'}, 
							'tops' :{'state' : false, 'distance': 0, 'coords': 0, 'align' : 'y', 'func' :'top' },
							'bottoms' : {'state' : false, 'distance': 0 , 'coords': 0, 'align' : 'y', 'func' : 'bot'},
							'centersH' :{'state' : false, 'distance': 0, 'coords': 0, 'align' : 'y', 'func':'cenH'}}
	
			$.each(checkDirections, (ind, obj) => {
				snapToObj[obj].state = this.allCoords[obj].some((value) => {
					snapToObj[obj].distance = Math.abs(value - objectCoords[obj]);
					snapToObj[obj].coords = value;
					return (Math.abs(value - objectCoords[obj]) <= objectMarCor);	   
				});
			});
	
			let lowestXDistance = Object.values(snapToObj).filter(obj => obj.state == true && obj.align == 'x');
			(lowestXDistance.length !== 0) ? lowestXDistance = lowestXDistance.reduce((prev,curr) => (prev.distance < curr.distance) ? prev : curr) : false;
	
			let lowestYDistance = Object.values(snapToObj).filter(obj => obj.state == true && obj.align == 'y');
			(lowestYDistance.length !== 0) ? lowestYDistance = lowestYDistance.reduce((prev,curr) => (prev.distance < curr.distance) ? prev : curr) : false;
	
			//snapping is being done
			if(Object.keys(lowestXDistance).length > 0){
				let left = 0; 
				switch(lowestXDistance.func){
					case 'left':
						left = lowestXDistance.coords + objWidth/2;
						break;
					case 'right':
						left = lowestXDistance.coords - objWidth/2;
						break;
					case 'cenV':
						left = lowestXDistance.coords;
						break;	
				}
				if (helperLineObjV.canvas == null){
					canvas.add(helperLineObjV);
				}
				helperLineObjV.set({x1 : lowestXDistance.coords, 
									y1: 0, 
									x2: lowestXDistance.coords, 
									y2: backgroundShape.height})
				let LcentY = object.getCenterPoint().y
				newCoords = {"x" :left, "y":LcentY}
				object.setPositionByOrigin(newCoords, 'center', 'center');
				this.canvas.requestRenderAll();
			}else{
				this.canvas.remove(helperLineObjV);
			}

			if(Object.keys(lowestYDistance).length > 0){
				let top = 0;
				switch(lowestYDistance.func){
					case 'top':
						top = lowestYDistance.coords + objHeight/2;
						break;
					case 'bot':
						top = lowestYDistance.coords - objHeight/2;
						break;
					case 'cenH':
						top = lowestYDistance.coords;	
						break;
				}
				if (helperLineObjH.canvas == null){
					this.canvas.add(helperLineObjH);
				}
				helperLineObjH.set({x1 : 0, 
					y1: lowestYDistance.coords, 
					x2: backgroundShape.width, 
					y2: lowestYDistance.coords});
				//
				//object.set("top", top).setCoords();
				let LcentX = object.getCenterPoint().x
				newCoords = {"x" :LcentX, "y":top}
				object.setPositionByOrigin(newCoords, 'center', 'center');
				this.canvas.renderAll();
			}else{
				this.canvas.remove(helperLineObjH);
			}
		}
		this.canvas.renderAll();
	}
	
}

   class ProductSettingsUpdater {
      private $toolWrap: JQuery;
      //realdimensions is needed and are outside class
      constructor(all_product_settings, currentSettings, currentselections, SimpleColor) {
         //this.all_product_settings = all_product_settings;

         Object.defineProperty(this, 'all_product_settings', {
            value: all_product_settings,
            writable: false,
            enumerable: true,
            configurable: false,
         });

         this.currentselections = currentselections;
         this.currentSettings = currentSettings;
         this.settings;
         this.toolSelections = {};
         this.simpleColor = SimpleColor;
         this.isGravyr = false;
         this.hasColors = false;
         this.isTransparent = false;
         this.renderTransparent = false;
         this.allowTransparent = false;
         this.allowedColor = false;
         this.loadSign = false;
         this.wasAutoSize = false;
         this.specialSign = handleRealDim.getDim('special_sign');
         this.fixed_sizes = false;
         this.symbolsMultiColor = false;
         this.actions = [];

         this.$toolWrap = $('#signbuilder-left-menu');
         this.initialize();
      }

      getAutoSizeStatus() {
         return this.wasAutoSize;
      }

      initialize() {
         //this.$mountingOptions = $('.sign-option[data-sign-optiontype="mounting"]');
         this.mountingOptions = document.querySelectorAll('.sign-option[data-sign-optiontype="mounting"]');
         this.sizeOptions = document.querySelectorAll('.sign-option[data-sign-optiontype="sizes"] option');
         //this.$sizeOptions = $('.sign-option[data-sign-optiontype="sizes"] option');
         //this.$shapeOptions = $(".sign-option[data-sign-optiontype='shape']");
         this.shapeOptions = document.querySelectorAll(".sign-option[data-sign-optiontype='shape']");
         //this.$colorOption = $('.sign-option[data-sign-collection="color"]');
         this.colorOption = document.querySelectorAll('.sign-option[data-sign-collection="color"]');
         this.getjQueryMenu();
      }

      setSymbolsMulticolor(symbols) {
         this.symbolsMultiColor = symbols;
      }

      getjQueryMenu() {
         this.standard_sizes = $('.standard-sizes', this.$toolWrap);
         this.custom_sizes = $('#size-options-container', this.$toolWrap);
         this.auto_size_opt = $(".size-options-auto", this.$toolWrap);
         this.bg_radius = $(".custom-radius-option", this.$toolWrap);
         this.bg_shapes = $(".bg_shapes", this.$toolWrap);
         this.bg_surface = $(".bg-surface", this.$toolWrap);
         this.bg_color = $(".bg_color", this.$toolWrap);
         this.bg_transparent = $(".bg_color .normal-color [data-sign-option='transparent']", this.$toolWrap);
         this.normal_color = $(".normal-color", this.$toolWrap);
         this.bg_gravyr = $(".gravyr-color", this.$toolWrap);
         this.border = $(".bg_border", this.$toolWrap);
         this.border_color = $(".border-color", this.$toolWrap);
         this.uploads = $(".toggle-uploads", this.$toolWrap);
         this.customColor = $('.custom-color, .color-picker', this.$toolWrap);
         this.mounting = $(".toggle-mounting", this.$toolWrap);
         this.doubleSided = $(".double-sided-options", this.$toolWrap);
         this.fixedSizesContainer = $(".size-options-menu", this.$toolWrap);
         this.sizesToggle = $(".size-options-menu", this.$toolWrap);
         this.fixedSizesMenu = $(".fixed-sizes", this.$toolWrap);
         this.customSizesMenu = $(".custom-sizes", this.$toolWrap);
         this.sizeInBg = $('.t-background .minitool.tool-size', this.$toolWrap);
         this.orientation = $('.orientation-options',this.$toolWrap);
      }

      getToolSelections() {
         let toolSelections = {
            'standard_sizes': { 'show': true, 'class': this.standard_sizes },
            'custom_sizes': { 'show': true, 'class': this.custom_sizes },
            'auto_size_opt': { 'show': false, 'class': this.auto_size_opt },
            'bg_radius': { 'show': true, 'class': this.bg_radius },
            'bg_shapes': { 'show': true, 'class': this.bg_shapes },
            'bg_surface': { 'show': true, 'class': this.bg_surface },
            'bg_color': { 'show': true, 'class': this.bg_color },
            'bg_transparent': { 'show': false, 'class': this.bg_transparent },
            'normal_color': { 'show': true, 'class': this.normal_color },
            'bg_gravyr': { 'show': true, 'class': this.bg_gravyr },
            'border': { 'show': true, 'class': this.border },
            'custom_color': { 'show': true, 'class': this.customColor },
            'border_color': { 'show': true, 'class': this.border_color },
            'multi_color_symbol': { 'show': true, 'class': this.symbolsMultiColor },
            'uploads': { 'show': true, 'class': this.uploads },
            'mounting': { 'show': true, 'class': this.mounting },
            'allow_multi_side': { 'show': false, 'class': this.doubleSided },
            'size_menu': { 'show': true, 'class': this.sizesToggle },
            'size_menu_custom': { 'show': true, 'class': this.customSizesMenu },
            'size_menu_fixed': { 'show': false, 'class': this.fixedSizesMenu },
            'size_in_bg': { 'show' : true, 'class': this.sizeInBg },
            'orientation' : {'show' : false, 'class' : this.orientation },
         };
         return toolSelections;
      }

      getCurrentSetting() {
         return this.currentSettings;
      }

      getRenderTransparent(){
         return this.renderTransparent;
      }

      setShape(index, name = false) {
         this.currentselections.shape = parseInt(index);
         if (name) handleRealDim.updateDim('shape', name);
         toolMenu.activateShape(index);
      }

      setMounting(id) {
         if(!id) return;
         this.currentselections.mount = parseInt(id);
         handleRealDim.updateDim('mounting', id);
         toolMenu.activateMounting(id);
      }


        async updateProductSettings(updateMaterial = false, updateMenu = true) {
            let infoToSend = {};
            this.backgroundItems = [];
            this.forGroundItems = [];
            return new Promise((resolve, reject) => {
                let realDimensions = handleRealDim.getAllDim();
                this.loadSign = updateMaterial;
                let running = true;
                let material = realDimensions.material;
                this.settings = JSON.parse(JSON.stringify(this.all_product_settings["product_" + material]));

                let fixed_size = false;
                this.actions = [];
                this.prolongedActions = [];

                while (running) {
                    //get selected product
                    //Get settings for selected product
                    //get tools UI 
                    this.toolSelections = this.getToolSelections();

                    this.currentselections.product = this.settings.id;
                    this.currentselections["product-group"] = this.settings['belong'] ?? false;

                    for (let key in this.settings) {
                        if (this.settings.hasOwnProperty(key)) {
                            let value = this.settings[key];
                            switch (key) {
                                case "sizes":
                                    this.updateSizeOptions(key, value);
                                    break;
                                case "mounting":
                                    this.updateMountingOptions(key, value);
                                    break;
                                case "mount_image":
                                    this.updateMountingImage(key, value);
                                    break;
                                case "only_fixed":
                                    fixed_size = this.updateFixed(value);
                                    break;
                                case "shape":
                                    this.updateShapes(value);
                                    break;
                                case "color":
                                    this.updateColorDisplay(value);
                                    break;
                                case "allow_transparent":
                                    this.updateTransparentStatus(value);
                                    break;
                                case "include_transparent_bg":
                                    this.toggleTransparentBgOption(value);
                                    break;
                                case "render_transparent":
                                    this.setRenderTransparent(value);
                                    break;
                                case "color_type":
                                    this.updateColorType(value);
                                    break;
                                case 'special':
                                    this.updateSpecialStatus(value);
                                    break;
                                case 'auto_size':
                                    this.updateAutoSizeStatus(value);
                                    break;
                                case 'allow_multi_side':
                                    this.updateDoublesided(value);
                                    break;
                                case 'fixed_backgrounds':
                                    if (value && !this.backgroundItems.includes(value)) {
                                        this.backgroundItems.push(value);
                                    }
                                    break;
                                case 'allow_orientation':
                                    this.updateOrientation(value);
                                    break;
                                default:
                                    break;
                            }
                        }
                    }


                    //hides and shows depending on product uses this.Toolselection or input
                    if(!this.hasColors && this.allowTransparent){
                        this.isTransparent = true;
                        signOperation.setTransparentBackround(true);
                        this.toolSelections.bg_color.show = false;
                        this.toolSelections.border_color.show = false;
                        this.toolSelections.border.show = false;
                        this.toolSelections.custom_color.show = false;
                    }

                    this.updateAlternativesInMenu();
                    //calls a different class to get the new conditions and element settings
                    let update = elementUpdater.updateElementsAndConditions(this.currentSettings, this.currentselections);
                    running = update[0];
                    this.currentSettings = update[1];
                }

                //sets simple color
                if (this.isGravyr) {
                    this.updateSimpleColorStatus(true);
                } else {
                    this.updateSimpleColorStatus(false);
                }


                //update restrictions

                toolMenu.updateSignSides(handleRealDim.getDim('double_sided'));
                //console.log(this.currentSettings);
                if (fixed_size) {
                    this.updateFixedSizeSignDimension();
                    toolMenu.updateSizeMenu(this.currentSettings.sizes, realDimensions.width, realDimensions.height);
                }
                //run stored actions that needs to be runned afterwards e.x. gravyrcolor update
                if (this.actions) {
                    for (let action of this.actions) {
                        action();
                    }
                }

                //Check if fixedBackground meets criteria
                let currentKey = realDimensions.width + "x" + realDimensions.height;
                let bgs = [];
                for (let y = 0; y < this.backgroundItems.length; y++) {
                    if (this.backgroundItems[y][currentKey]) {
                        bgs.push(this.backgroundItems[y][currentKey]);
                    }
                }
                $.canvasBgManager.setBgs(bgs);

                let nameOfProduct = this.all_product_settings["product_" + realDimensions.material]['name'];
                toolMenu.updateSignTitel(nameOfProduct);

                if (updateMaterial && updateMenu) {
                    toolMenu.updateProductSelectionInMenu(realDimensions.material, true);
                }
                this.loadSign = false;
                resolve();
            });

        }

      updateAlternativesInMenu(item = false) {
         if (!item) {
            //Does the normal operation generated in this functions 
            $.each(this.toolSelections, function() {
               let element = this.class;
               let alt = this.show;


               if (element) {
                  alt ? element.removeClass('dont-display') : element.addClass('dont-display');
               }

            });
         } else {
            //calls for specific update with formated input
            item.show ? item.class.removeClass('dont-display') : item.class.addClass('dont-display');
         }

      }

      updateFixedSizeSignDimension() {
         if (!this.fixed_sizes) {
            this.fixed_sizes = Object.entries(all_elements)
               .filter(([sizeId, sizeValue]) => sizeId.startsWith('size_') && sizeValue.fixed_sizes == null)
               .reduce((fixedSizes, [sizeId, sizeValue]) => {
                  fixedSizes[sizeId.substring(5)] = {
                     width: sizeValue.width,
                     height: sizeValue.height
                  };
                  return fixedSizes;
               }, {});
         }
         let allowedSizes = this.currentSettings.sizes;
         let width = parseInt(handleRealDim.getDim("width"));
         let height = parseInt(handleRealDim.getDim("height"));

         if (!allowedSizes.some(size => parseInt(this.fixed_sizes[size].width) === width && parseInt(this.fixed_sizes[size].height) === height)) {
            // Set the dimensions to the first allowed size
            let firstAllowedSize = this.fixed_sizes[allowedSizes[0]];
            if (firstAllowedSize) {
               handleRealDim.updateWidth(parseInt(firstAllowedSize.width));
               handleRealDim.updateHeight(parseInt(firstAllowedSize.height));
            }
         }

      }


      setSizeRestrict(value) {
         if (value !== 0 && value !== null) {
            return value;
         } else {
            return null;
         }
      }

      updateSizeOptions(key, value) {
         if (Array.isArray(value) && value.length != 0) {
            // Iterate over sizeOptions and update display based on 'data-assoc' attribute
            this.sizeOptions.forEach(option => {
               const optionValue = option.getAttribute('data-assoc');
               if (value.includes(Number(optionValue))) {
                  // Show the option if it's in the value array
                  option.classList.remove('dont-display');
               } else {
                  // Hide the option if it's not in the value array
                  option.classList.add('dont-display');
               }

               if(value.length == 1){

                  this.toolSelections.size_menu_custom.show = false;
                  this.toolSelections.size_menu_fixed.show = false;
               }
            });
         } else {
            this.toolSelections.standard_sizes.show = false;
            this.sizeOptions.forEach(option => {
               option.classList.add('dont-display');
            });
         }
      }

      updateMountingOptions(key:string, value) {
         let hideMountings = true; // This variable is declared but not used in the provided snippet
         this.mountingOptions.forEach(option => {
            if (Array.isArray(value)) {
               if (value.length <= 1) {
                  this.toolSelections.mounting.show = false;
               } else {
                  let attribute = option.getAttribute('data-assoc');
                  // Ensure the attribute is converted to the correct type for comparison
                  if (value.includes(Number(attribute))) {
                     option.classList.remove('dont-display');
                  } else {
                     option.classList.add('dont-display');
                  }
               }
            } else {
               option.classList.add('dont-display');
               this.toolSelections.mounting.show = false;
            }
         });

      }

      updateMountingImage(key, value) {
         this.currentSettings[key] = value;
      }

      updateFixed(value) {
         if (value == 1) {
            $('.sign-option-size option[value=0x0]').addClass('dont-display');
            $('div.adjust-to-content-size').addClass('dont-display');
            $('.custom-size-option-vals').addClass('dont-display');
            this.toolSelections.custom_sizes.show = false;
            this.toolSelections.size_in_bg.show = false;
            this.toolSelections.size_menu_custom.show = false;
            this.toolSelections.size_menu_fixed.show = true;
            return true;
         } else {
            $('.sign-option-size option[value=0x0]').removeClass('dont-display');
            $('div.adjust-to-content-size').removeClass('dont-display');
            $('.custom-size-option-vals').removeClass('dont-display');
            this.toolSelections.size_menu_custom.show = true;
            this.toolSelections.size_menu_fixed.show = false;
            this.toolSelections.custom_sizes.show = true;
         }
         return false;
      }

      updateShapes(value) {
         //set rect as standard if nothing is set
         //using both id and name 
         let setShape = 'rect';
         let matchingShape = false;
         let realShape = handleRealDim.getDim("shape");
         if (!value || value.length == 0 || this.shapeOptions.length < 2) {
            this.toolSelections.bg_shapes.show = false;
         } else if (value.length == 1) {
            this.toolSelections.bg_shapes.show = false;
            let selectedShapeOption = Array.from(this.shapeOptions).find(element => element.dataset.assoc === value[0]);
            if (selectedShapeOption) {
               setShape = selectedShapeOption.dataset.signOption;
            }
            this.setShape(value[0], setShape);
         } else {
            this.shapeOptions.forEach(option => {
               let attribute = option.getAttribute('data-assoc');
               if (value.includes(Number(attribute))) {
                  option.classList.remove('dont-display');
                  let selection = option.getAttribute('data-sign-option');
                  if (selection == realShape) {
                     matchingShape = attribute;
                  }
               } else {
                  option.classList.add('dont-display');
               }
            });

            // Checks if current shape is available in option, else sets to none or other option
            if (!matchingShape) {
               handleRealDim.updateDim("shape", setShape);
               realShape = setShape;
               this.setShape(value[0], setShape);
            } else {
               this.setShape(matchingShape);
            }
         }

         if (realShape != 'rect-rounded') {
            this.toolSelections.bg_radius.show = false;
         }
      }


      updateTransparentStatus(value) {
         if (value == 1) {
            this.allowTransparent = true;
            //this.isTransparent = true;
            //signOperation.setTransparentBackround(true);
            //this.toolSelections.bg_color.show = false;
            //this.toolSelections.border_color.show = false;
            //this.toolSelections.border.show = false;
            //this.toolSelections.custom_color.show = false;
         } else {
            signOperation.setTransparentBackround(false);
            this.allowTransparent = false;
            this.isTransparent = false;
         }
      }

      toggleTransparentBgOption(value){
        this.toolSelections.bg_transparent.show = (value == 1) ? true : false;
      }

      setRenderTransparent(value){
         if(value && value == 1){
            this.renderTransparent = true;
         }else{
            this.renderTransparent = false;
         }
      }

      updateColorDisplay(values) {
         this.allowedColor = false;
         if (values && values.length === 0) {
            this.hasColors = false;
            this.colorOption.forEach(element => {
               element.classList.remove('dont-display');
            })
         } else {
            this.hasColors = true;
            let chosenColors = [signOperation.getGravyrColorID(), signOperation.getTextColorID()];

            this.colorOption.forEach(option => {
               let id = option.getAttribute('data-assoc');

               // Show the option if no specific values are provided or if it matches one of the values
               if ((!values || values.length === 0) || values.includes(Number(id))) {
                  option.classList.remove('dont-display');

                  // Update allowedColor if it's the first match or if it's one of the chosen colors
                  if (!this.allowedColor || chosenColors.includes(parseInt(id))) {
                     this.allowedColor = option;
                  }
               } else {
                  option.classList.add('dont-display');
               }
            });
         }
      }

      updateColorType(value) {
         if (value == 1) {
            this.isGravyr = true;
            this.toolSelections.border_color.show = false;
            this.toolSelections.normal_color.show = false;
            this.toolSelections.bg_gravyr.show = true;
            handleRealDim.updateDim("engraving", true);
            //Updates all object to first color scheme
            if (!this.loadSign) {
               this.prolongedActions.push(() => {
                  signOperation.setGravyrColors(this.allowedColor, this.specialSign);
               });
            }
         } else {
            this.isGravyr = false;
            this.toolSelections.bg_gravyr.show = false;
            handleRealDim.updateDim("engraving", false);
         }
      }
      updateSpecialStatus(value) {
         if (value != 0) {
            this.specialSign = true;
            handleRealDim.updateDim("special_sign", true);
         } else {
            this.specialSign = false;
            handleRealDim.updateDim("special_sign", false);
         }
      }

      updateSimpleColorStatus(bool) {
         signOperation.setSimpleColor(bool);
         this.simpleColor = bool;
         this.toolSelections.multi_color_symbol.show = false;
      }

      updateAutoSizeStatus(value) {
         //check if autoSize
         if (parseInt(value) === 1) {
            this.wasAutoSize = true;
            this.toolSelections.auto_size_opt.show = true;

            if (handleRealDim.getDim('auto_size_w') || handleRealDim.hasDim("auto_size_h")) {
               let autoWidth = (handleRealDim.getDim('auto_size_w') == true) ? true : false;
               let autoHeight = (handleRealDim.getDim('auto_size_h') == true) ? true : false;
               this.actions.push(() => {
                  toolMenu.updateAutoWidth(autoWidth, true);
                  toolMenu.updateAutoHeight(autoHeight, true);
               });

                this.prolongedActions.push(() => {
                    //signOperation.updateAutoSize({action:'prodSettingsUpdate'});
                })
            }
         } else {
            this.wasAutoSize = false;
            handleRealDim.updateDim("auto_size_h", false)
            handleRealDim.updateDim("auto_size_w", false)
            this.toolSelections.auto_size_opt.show = false;

            this.actions.push(() => {
               toolMenu.showSizeInput();
            });
         }
      }

      updateDoublesided(value) {
         if (value == 1) {
            this.toolSelections.allow_multi_side.show = true;
         }
      }
        
        updateOrientation(value){
            if(value == 1){
                this.toolSelections.orientation.show = true;
                this.actions.push(() => {
                    const manageOrientationClass = new ManageOrientation();
                    manageOrientationClass.renderSelection();
                });
            }else{
                this.toolSelections.orientation.show = false;
                const manageOrientationClass = new ManageOrientation();
                manageOrientationClass.turnOfOrientation();
            }
        }

      activateProlongedActions(){
          if(this.prolongedActions){
              for (let action of this.prolongedActions) {
                  action();
              }
          }

          this.prolongedActions = false;
      }
   }


   class ElementAndConditionsUpdater {
      //realdimensions is needed and are outside class
      constructor(currentSettings, currentselections, all_conditions, all_elements, all_product_settings) {
         this.currentselections = currentselections;
         this.currentSettings = currentSettings;
         this.all_conditions = all_conditions;
         this.all_elements = all_elements;


         //this.all_product_settings = all_product_settings;
         this.activeConditions = {};
         this.activeElementsArray = [];
         this.update = false;
         this.initialize();

         Object.defineProperty(this, 'all_product_settings', {
            value: all_product_settings,
            writable: false,
            enumerable: true,
            configurable: false,
         });
      }

      initialize() {
         this.allOptions = $('[data-assoc]');
         
      }


      getElement() {
         return this.all_elements;
      }

      updateElementsAndConditions(currentSettings) {
         this.currentSettings = currentSettings;
         this.update = false;
         this.activeConditions = {};
         this.getCurrentConditions();
         this.updateSettings(this.activeConditions);
         return [this.update, this.currentSettings];
      }

      getCurrentConditions() {
         //this.currentselections.group = toolMenu.getRecursiveParents(this.currentselections.material);
         let currentselections = this.currentselections;

         let tempCondition;

         this.activeElementsArray = Object.entries(currentselections)
            .filter(([key, value]) => value !== false && !Number.isNaN(value)) // Filter out entries where value is false or 'NaN'
            .map(([key, value]) => `${key}_${value}`); // Get keys 

         for (let key in currentselections) {
            let conditionOwner = key + "_" + currentselections[key];
            if (conditionOwner in this.all_conditions) {
               tempCondition = this.all_conditions[conditionOwner];
            }
            for (const property in tempCondition) {
               let obj = tempCondition[property];
               let sett = obj.settings;


               if (sett.oper === '==') {
                  if (this.activeElementsArray.includes(sett.value)) {
                     this.storeConditions(obj);
                  }
               } else if (sett.oper === '!=') {
                  if (!this.activeElementsArray.includes(sett.value)) {
                     this.storeConditions(obj);
                  }
               }
            }
         }
      }

      updateSettings(activeConditions) {
         let product = this.all_product_settings['product_' + handleRealDim.getDim("material")];


         for (let setts in this.currentSettings) {
            let newSetting = false;
            newSetting = product?.[setts];

            this.activeElementsArray.forEach(activeElementAssoc => {
               let obj = this.all_elements[activeElementAssoc];

               if (obj?.[setts]) {
                  newSetting = (obj[setts]);
               }
            });

            if (setts in activeConditions) {
               newSetting = activeConditions[setts];
            }


            if (this.currentSettings[setts] !== newSetting) {
               this.currentSettings[setts] = newSetting;
               this.update = true;
            }
         }
      }

      storeConditions(options) {
         for (const property in options) {
            if (property !== 'settings') {
               this.activeConditions[property] = Array.isArray(options[property]) && options[property].length === 1
                  ? options[property][0]
                  : options[property];
            }
         }
      }
   }

   class ManageCustomObjects {
      constructor(canvas, signOperation) {
         this.canvas = canvas;
         this._clipboard;
         this.signOperation = signOperation;
         this.event();
      }

      event() {
         let self = this;
            
         $document.on("change",'.shape-setting-size input', function(e) {
            let { inner, outer, group } = self.getUserShapeObjects()
            if (!inner) {
               return;
            }
            let newWidth = (e.target.dataset.signOption == "width") ? fabric.util.parseUnit(`${e.target.value}mm`) : false;
            let newHeight = (e.target.dataset.signOption == "height") ? fabric.util.parseUnit(`${e.target.value}mm`) : false;
            if(outer.type == 'circle'){
                (newWidth) ? newHeight = newWidth : newWidth = newHeight;

            }

            let scaleX = group.scaleX;
            let scaleY = group.scaleY;
            let gwidth = group.width * scaleX;
            let gheight = group.height * scaleY;
            let type = inner.type;



            group.set({ scaleX: 1, scaleY: 1 });


            if(newWidth && newHeight){
               group.set({ width: newWidth, height: newHeight })
               gwidth = newWidth;
               gheight = newHeight;
            } else if(newWidth) {
               group.set({ width: newWidth, height: gheight })
               gwidth = newWidth;
            } else {
               group.set({ width: gwidth, height: newHeight });
               gheight = newHeight;
            }

            if (type != 'circle') {
               inner.set({ scaleX: 1, scaleY: 1, width: gwidth, height: gheight, left: (-gwidth / 2), top: (-gheight / 2) });
               outer.set({ scaleX: 1, scaleY: 1, width: gwidth, height: gheight, left: (-gwidth / 2), top: (-gheight / 2) });
            } else {
               let radius = gwidth / 2;
               inner.set({ scaleX: 1, scaleY: 1, radius: radius, left: (-gwidth / 2), top: (-gheight / 2) });
               outer.set({ scaleX: 1, scaleY: 1, radius: radius, left: (-gwidth / 2), top: (-gheight / 2) });
               updateSymbolSize();
            }

            let border = inner.cstBorder;

            self.canvas.requestRenderAll();
         });


            $document.on("change", ".symbol-setting-size input", function(e) {
                let activeObject = canvas.getActiveObject();
                if (activeObject == null) {
                    return;
                }

                let objectCenter = activeObject.getCenterPoint(true,true);

                let scaler = 1;
                if (e.target.dataset.signOption == "width") {
                    let currentWidth = (activeObject.width * activeObject.scaleX);
                    let newWidth = fabric.util.parseUnit(e.target.value+"mm");
                    scaler = newWidth / currentWidth;
                }else if(e.target.dataset.signOption == "height"){
                    let currentHeight = (activeObject.height * activeObject.scaleY);
                    let newHeight = fabric.util.parseUnit(e.target.value+"mm");
                    scaler = newHeight / currentHeight;
                }else{
                    return;
                }

                activeObject.scaleX = activeObject.scaleX * scaler * 1;
                activeObject.scaleY = activeObject.scaleY * scaler * 1;

                activeObject.setPositionByOrigin(objectCenter, 'center', 'center');

                if(handleRealDim.getDim('auto_size_w') || handleRealDim.getDim('auto_size_h')){
                    signOperation.updateAutoSize({action:'change'});
                }
                canvas.requestRenderAll();
                updateSymbolSize();
            });
      }

      getUserShapeObjects(obj = false) {
         if (!obj) {
            obj = this.canvas.getActiveObject();
         }

         let inner = false;
         let outer = false;
         $.each(obj._objects, function(i, element) {
            if (element.hasOwnProperty('name') && element.name == "userShapeInner") {
               inner = element;
            } else {
               outer = element
            }
         });

         if (inner && outer) {
            return { inner: inner, outer: outer, group: obj };
         }

         return false;
      }

      createUserShape(type) {
         let backgroundShape = signOperation.getBackgroundShape();
         let object, inner;
         let width = backgroundShape.width / 3;
         let height = backgroundShape.height / 3;
         let squared = (width < height) ? width : height;
         let color;
         squared = Math.floor(squared);
         if (signOperation.getSimpleColor() == true) {
            color = signOperation.getNameTextColor();
         } else {
            let lastColorUsed = signOperation.getLastColorUsed();
            (!(lastColorUsed)) ? lastColorUsed = '#000' : '';
            color = lastColorUsed;
         }

         let shapeName = '';

         switch (type) {
            case 'rectangle':
               shapeName =  'Rektangle';
               object = new fabric.Rect({
                  width: squared,
                  height: squared,
                  fill: color,
                  objectName : 'Rektangle',
                  opacity: 1,
               });
               inner = new fabric.Rect({
                  width: squared,
                  height: squared,
                  name: 'userShapeInner',
                  objectName : 'Rektangle',
                  fill: color,
                  opacity: 1,
               });
               break;
            case 'circle':
               shapeName = 'Cirkel';
               object = new fabric.Circle({
                  radius: squared/2,
                  fill: color,
                  objectName : 'Cirkel',
                  lockUniScaling: true,
               });
               inner = new fabric.Circle({
                  radius: squared/2,
                  fill: color,
                  name: 'userShapeInner',
                  objectName : 'Cirkel',
                   lockUniScaling: true,
               });
               break;
            case 'elipse':
                 object = new fabric.Ellipse({
                     width: squared,
                     height: squared,
                     rx: squared,
                     ry: squared,
                     fill: color,
                     objectName : 'Cirkel'
                 });
                 inner = new fabric.Ellipse({
                     width: squared,
                     height: squared,
                     rx: squared,
                     ry: squared,
                     fill: color,
                     name: 'userShapeInner',
                     objectName : 'Cirkel'
                 });

             case 'triangle':
                 shapeName = 'Triangel';
                 object = new fabric.Triangle({
                     width: squared,
                     height: squared,
                     fill: color,
                     objectName : 'Triangel'
                 });
                 inner = new fabric.Triangle({
                     width: squared,
                     height: squared,
                  name: 'userShapeInner',
                  fill: color,
                  objectName : 'Triangel'
               });
               break;
         }

         inner.cstBorder = 0;

         let creationDetails = { excludeFromExport: false }
         if(type == 'circle'){
             creationDetails.lockUniScaling = true;
         }
          let newGroup = new fabric.Group([object, inner], creationDetails);
         newGroup.set({width: squared, height: squared});
         newGroup.setCoords();
         newGroup.name = 'userFabricShape';
         newGroup.cstBorder = 0;
         newGroup.stdnCustom = {objectName : shapeName};
         signOperation.addObjectToSign(newGroup);
      }

      updateShapeBackground(color) {
         this.canvas.getActiveObject()._objects[1].set('fill', color);
         const colorManager = new ManageColors(this.canvas); 
         colorManager.getActiveColors();
         toolMenu.updateSelections(color, 'shape-bg-color');
         this.canvas.requestRenderAll();
      }

      updateShapeBorderColor(color) {
         if (!color) {
            color = "#fff";
         }
         this.canvas.getActiveObject()._objects[0].set('fill', color);
         const colorManager = new ManageColors(this.canvas); 
         colorManager.getActiveColors();
         toolMenu.updateSelections(color, 'shape-border-color');
         this.canvas.requestRenderAll();
      }

      updateSymbolColor(color, textColorID) {
         let obj = this.canvas.getActiveObject();
         if(obj.type !== 'text'){
            if (obj == null || (Object.hasOwn(obj, 'singleColor') && !obj.singleColor)) return;
         }
         this.textColorID = textColorID;
         this.setSymbolColor(obj, color);

         const colorManager = new ManageColors(this.canvas); 
         colorManager.getActiveColors();
         toolMenu.updateSelections(color, 'symbolcolor');
         this.canvas.requestRenderAll();
      }

      setSymbolColor(shape, colorSet) {
         this.applySVGcolor(shape, colorSet);
         let backgroundShape = signOperation.getBackgroundShape();
         if (signOperation.getSimpleColor() == true) {
            signOperation.updateSimpleColors(backgroundShape.fill, colorSet);
         }
         this.canvas.requestRenderAll();

         const colorManager = new ManageColors(this.canvas); 
         colorManager.getActiveColors();
         signOperation.setLastcolorUsed(colorSet);
      }

      applySVGcolor(shape, colorSet) {
         if (shape.type === 'path' || shape.type === 'i-text' || shape.type === 'text') {
            shape.set("fill", colorSet);
         } else {
            for (const path of shape._objects) {
               let fillColor = path.fill.toLowerCase();
               let whiteColors = ['#fff', '#ffffff'];
               if (path.fill !== "transparent" && !whiteColors.includes(fillColor) && !whiteColors.includes(colorSet)) {
                  path.set('fill', colorSet);
               } else if (signOperation.getSimpleColor()) {
                  path.set('fill', colorSet);
               }
            }
         }
      }


      updateShapeRadius(value, keepRadius = false) {
         let objects = this.canvas.getActiveObject();


         let [border,outer, inner] = [objects.cstBorder, objects._objects[0], objects._objects[1]];

         let outRadius = 0;
         let innRadius = 0;

         if(keepRadius){
            border = value;
            outRadius = outer.rx;
            innRadius = outRadius - fabric.util.parseUnit(border + "mm");
         }else{
             outRadius = fabric.util.parseUnit(value + "mm");
             innRadius = fabric.util.parseUnit((value - border) + "mm");
         }

          outer.set({ rx: outRadius, ry: outRadius });
         
         if (innRadius < 0) innRadius = 0;
         inner.set({ rx: innRadius, ry: innRadius });
         this.canvas.requestRenderAll();
      }

      updateBorder(val, obj) {
         obj = this.canvas.getActiveObject();
         if (!(obj)) {
            return;
         }
         if (obj.type == 'group' && obj.name == "userFabricShape") {
            this.calcUserBorder(val, obj)
         }
      }

      calcUserBorder(val, obj) {
         if (obj.type == 'group' && obj.name == "userFabricShape") {
            let inner, outer;
            obj.cstBorder = val;
            let mmBorder = val;
            val = fabric.util.parseUnit(val + "mm");

            for (let i = 0; i < obj._objects.length; i++) {
               if (obj._objects[i].name == "userShapeInner") {
                  inner = obj._objects[i];
                  inner.cstBorder = mmBorder;
               } else {
                  outer = obj._objects[i];
               }
            }
            let namntextcolor = signOperation.getNameTextColor();
            let bgcolor = signOperation.getBgColor();
            if (signOperation.getSimpleColor() == true) {
               if (val == 0) {
                  outer.fill = namntextcolor;
                  inner.fill = namntextcolor;
               } else {
                  outer.fill = namntextcolor;
                  inner.fill = bgcolor;
               }
            }

            let width, height, center, radius;
            let yPostion = 'center';
            switch (inner.type) {
               case 'rect':
                  [width, height, center] = [outer.width, outer.height, outer.getCenterPoint(true, true)];
                  if (val > outer.width / 2) {
                     val = outer.width / 2;
                  }
                  inner.set({
                     width: width - val * 2,
                     height: height - val * 2,
                  });
                  break;
               case 'circle':
                  [radius, center] = [outer.radius, outer.getCenterPoint(true, true)];
                  if (val > outer.radius) {
                     val = outer.radius;
                  }
                  inner.set({
                     radius: radius - val,
                  });
                  break;
               case 'triangle':
                  [width, height, center] = [outer.width, outer.height, outer.getCenterPoint(true, true)];
                  let top = outer.top;
                  let shortest = (width > height) ? height : width;
                  if (val > shortest / 4) {
                     val = shortest / 4;
                  }

                  let newWidth = width - 2 * Math.sqrt(3) * val;
                  let newHeight = height - 3 * val;

                  inner.set({
                     width: newWidth,
                     height: newHeight,
                     stroke: 0,
                  });
                  yPostion = 'top';
                  center.y = top + height - val - newHeight;
                  break;
            }
            if (obj.cstBorder > 0) {
                this.updateShapeRadius(obj.cstBorder , true);
            }
            inner.setPositionByOrigin(center, 'center', yPostion);
            this.canvas.requestRenderAll();
         }

      }

      changeSelectionStatus(state, idOrObject) {
         let selectStatus = !state;

         if (typeof idOrObject === 'object') {
            idOrObject.set('selectable', selectStatus);
            this.canvas.requestRenderAll();
            return;
         }

         let objects = this.canvas.getObjects();
         for (let index in objects) {
            if (objects[index].objId == idOrObject) {
               objects[index].set('selectable', selectStatus);
               this.canvas.requestRenderAll();
               if (state) this.canvas.discardActiveObject();
               return;
            }
         }
      }

      async insertNewText() {
         let lastColorUsed = signOperation.getLastColorUsed();

         let realW = handleRealDim.getDim("width");
         let realH = handleRealDim.getDim("height");

         let fontsize = (realW > realH) ? realW / 2 : realH / 2;
         //check that font has been loaded before adding 

         try{
             await $.manageFonts.loadFont(selectedFontProperties)
         }catch(error){
             console.log(error);
         }
         let options = {
            left: 0,
            top: 0,
            fontFamily: selectedFontProperties.family,
            fontStyle: selectedFontProperties.style,
            fontWeight: selectedFontProperties.weight,
            fill: lastColorUsed,
            lineHeight: 1.1,
            styles: {},
            fontSize: fontsize,
            centeredScaling: true,
            editable: true,
            selectable: true,
            perPixelTargetFind: true,
            targetFindTolerance: 4
         };
         if (handleRealDim.getDim("material") == "vinyl" && canvas.getObjects().length <= 2) {
            var newOptions = {
               //lockMovementX: true,
               //lockMovementY: true,
               //lockRotation: true
            };
         }
         options = Object.assign({}, options, newOptions);

         let text = new fabric.IText("Text", options);

         let obj = signOperation.addObjectToSign(text);


         const colorManager = new ManageColors(this.canvas); 
         colorManager.getActiveColors();
         text.selectAll();
         this.canvas.requestRenderAll();
         return obj;
      }

      dublicateActiveObject(obj = false, copy = false) {
         if (!obj) {
            obj = this.canvas.getActiveObject();
         }

         // Clone the object
         obj.clone((cloned) => {
            // Now inside the callback, cloned is available and fully loaded

            // Custom properties to be cloned along with the object
            if(obj.hasOwnProperty('cstBorder')){
               cloned.set('cstBorder', obj.cstBorder);
            }
            if(obj.hasOwnProperty('singleColor')){
               cloned.set('singleColor', obj.singleColor);
            }

            if (copy != true) {
               cloned.set("top", cloned.top + 10);
               cloned.set("left", cloned.left + 10);
               this.canvas.add(cloned); // Add the cloned object to the canvas
               cloned.setCoords(); // Update the cloned object's coordinates
               this.canvas.requestRenderAll(); // Re-render the canvas
               this.canvas.calcOffset(); // Recalculate the canvas offset
               signSaveAndLoad.saveDesign(); // Save the design, ensure this function can handle async operations
            } else {
               this._clipboard = cloned; // If copy is true, store the cloned object for later use
            }
         }, ['cstBorder', 'singleColor']); // Specify properties to include in the clone if needed
      }

      deleteObject(obj) {
         if (!obj) {
            obj = this.canvas.getActiveObject();
         }
         if (!obj) return;

         let belong = false;
         if (obj.hasOwnProperty('belong')) {
            belong = obj.belong;
         }
         this.canvas.remove(obj);
         if (belong) this.canvas.remove(belong);
         this.canvas.requestRenderAll();
         toolMenu.revertToPreviousTool();
         signSaveAndLoad.saveDesign();
      }

      moveKeyPressed(e, moveX, moveY) {
         if (this.canvas.getActiveObject() == null) return;
         e.preventDefault();
         let obj = this.canvas.getActiveObject();
         obj.left += moveX;
         obj.top += moveY;
         obj.setCoords();
         toolMenu.updateElementDistance();
         this.canvas.requestRenderAll();
      }

      async toggleTextFormat(option, obj) {
          if(option == 'left' || option == 'center' || option == 'right'){
              let boundingRect = obj.getBoundingRect(true, true);
               switch (option) {
                  case 'left':
                     obj.left = boundingRect.left;
                     break;
                  case 'center':
                     obj.left = boundingRect.left + boundingRect.width / 2;
                     break;
                  case 'right':
                     obj.left = boundingRect.left + boundingRect.width;
                     break;
               }
              obj.textAlign = option;
              obj.originX = option;
          }else{
              let weight, style;
              let styleCheck = false;

              if(option == 'bold'){
                let boldButton = $('.sign-option[data-sign-option="bold"]'); 
                weight = (boldButton.hasClass('active')) ? 400 : 700;
                style =  obj.fontStyle;
              }else if(option == 'italic'){
                let italicButton = $('.sign-option[data-sign-option="italic"]'); 
                style = (italicButton.hasClass('active')) ? 'Normal' : 'Italic';
                weight =  obj.fontWeight;
              }else{
                  weight  = (option.weight) ? option.weight : obj.fontWeight; 
                  style  = (option.style) ? option.style : obj.fontStyle; 

                  if(option.weight){
                      //we need to check exuslivty for the italic normal
                      styleCheck = $.manageFonts.checkForSingleStyle(obj.fontFamily, weight);
                      if(styleCheck){
                          style = styleCheck;
                      }
                  }
              }


              if(weight == 'normal' || weight == "Normal") weight = 400;
              if(weight == 'bold' || weight == "Normal") weight = 700
              let closestFont = {family : obj.fontFamily, weight: obj.fontWeight, style: obj.fontStyle}

              try{
                 closestFont =  await $.manageFonts.loadNewFont(obj.fontFamily, { preferredWeight: weight, preferredStyle: style });
              }catch(error){
                  console.log(error);
              }

              if(option.weight || option == 'bold'){
                  obj.fontWeight = closestFont.weight;
              }

              if(option.style || option == 'italic' || styleCheck){
                  obj.fontStyle = closestFont.style;
              }


              if(option.underline){
                  if (obj.textDecoration == "underline") obj.textDecoration = "";
                  else obj.textDecoration = "underline";

                  //stupid hax
                  let scl = obj.scaleX;
                  obj.scale(scl * 0.999);
                  this.canvas.requestRenderAll();
                  obj.scale(scl);
                  this.canvas.requestRenderAll();
              }

          }

         toolMenu.updateTextFormatButtons(obj);

         const colorManager = new ManageColors(this.canvas); 
         colorManager.getActiveColors();
         this.canvas.requestRenderAll();
      }

      bringToFront(obj) {
         if (!obj) {
            obj = this.canvas.getActiveObject();
         }
         let mount_amnt = handleRealDim.getDim("mount_amount");
         let moveIndex = (mount_amnt) ? mount_amnt : 0;

         if (moveIndex == 0) {
            this.canvas.bringToFront(obj);
         } else {
            obj.moveTo(this.canvas.getObjects().length - 1 - moveIndex);
         }
      }

      bringForward(obj) {
         if (!obj) {
            obj = this.canvas.getActiveObject();
         }
         let mount_amnt = handleRealDim.getDim("mount_amount");
         let moveIndex = (mount_amnt) ? mount_amnt : 0;
         if (moveIndex == 0) {
            this.canvas.bringForward(obj);
         } else {
            let objects = this.canvas.getObjects();
            if ((objects.length - 1 - moveIndex) < objects.indexOf(obj)) { // Change "<=" to "<"
               this.canvas.bringForward(obj);
            }
         }
      }

      pasteCopy() {
         if (!(this._clipboard)) return;
         this._clipboard.clone((clonedObj) => {
            this.canvas.discardActiveObject();

            let inside = false;
            // Check if the mouse pointer is outside the canvas
            var pointer = this.canvas.getPointer(this.canvas.lowerCanvasEl);


            clonedObj.set({
               left: clonedObj.left + 10,
               top: clonedObj.top + 10,
               evented: true,
            });
            if (clonedObj.type === 'activeSelection') {
               // active selection needs a reference to the canvas.
               clonedObj.canvas = this.canvas;
               clonedObj.forEachObject((obj) => {
                  this.canvas.add(obj);
               });
               // this should solve the unselectability
               clonedObj.setCoords();
            } else {
               this.canvas.add(clonedObj);
            }
            this._clipboard.top += 10;
            this._clipboard.left += 10;
            this.canvas.setActiveObject(clonedObj);
            this.canvas.requestRenderAll();
         }, ['cstBorder', 'singleColor', 'name']);
      }

      setObjectDistance(direction, distance, obj) {
         if (!obj) {
            obj = this.canvas.getActiveObject();
         }

         let objDistance = obj.getBoundingRect(true,true);
         distance = fabric.util.parseUnit(`${distance}mm`);
         let backgroundDistances = signOperation.getBackgroundDistance();
         let move = 0;


         switch (direction) {
            case 'left':
               move = backgroundDistances[direction] + distance;
               if (distance < 0) move = 0;
               if (backgroundDistances['right'] < (move + objDistance.width)) move = backgroundDistances['right'] - objDistance.width;
               obj.set('left', move + objDistance.width * (obj.originX === 'center' ? 0.5 : obj.originX === 'right' ? 1 : 0));
               break;
            case 'top':
               move = backgroundDistances[direction] + distance;
               if (distance < 0) move = 0;
               if (backgroundDistances['bottom'] < (move + objDistance.height)) move = backgroundDistances['bottom'] - objDistance.height;
               obj.set('top', move + objDistance.height * (obj.originY === 'center' ? 0.5 : obj.originY === 'bottom' ? 1 : 0));
               break;
            case 'right':
               move = backgroundDistances[direction] - distance - objDistance.width;
               if (backgroundDistances[direction] < (move + objDistance.width)){
                  move = backgroundDistances[direction] - objDistance.width;
               } 
               if (move < 0) move = 0;

               obj.set('left', move + objDistance.width * (obj.originX === 'center' ? 0.5 : obj.originX === 'left' ? 0 : 1));
               break;
            case 'bottom':
               move = backgroundDistances[direction] - distance - objDistance.height;
               if (backgroundDistances[direction] < (move + objDistance.height)) move = backgroundDistances[direction] - objDistance.height;
               if (move < 0) move = 0;
               obj.set('top', move + objDistance.height * (obj.originY === 'center' ? 0.5 : obj.originY === 'top' ? 0 : 1));
               break;
         }
         this.canvas.requestRenderAll();
         toolMenu.updateElementDistance();
         onCanvasGUIHandler.updateObjectControl();
      }
   }


    class UploadsManager {
        constructor() {
            this.images = false;
            this.loggedIn = false;
            this.loaded = false;
        }

        checkDataVersion() {
            const currentVersion = JSON.parse(localStorage.getItem('dataVersions') || '{}');

            if (!currentVersion.hasOwnProperty('uploads') || currentVersion['uploads'] !== DATA_VERSION.uploads) {
                localStorage.removeItem("designUploads");
                currentVersion['uploads'] = DATA_VERSION.uploads;
                localStorage.setItem('dataVersions', JSON.stringify(currentVersion));
                console.log('Data version updated, cleared old uploads');
                return true; // Indicates data was reset
            }
            return false; // No reset needed
        }

        getImagesFromLocalStorage() {
            let images = localStorage.getItem("designUploads");
            if(this.checkDataVersion()){
                this.images = [];
                return;
            }

            if (images) {
                // TEMPORARY CODE TO REMOVE BAD FORMATTING CAN BE REMOVE LATER...  2023-07-20
                images = JSON.parse(images);
                // Filter out images that don't have 'id' property or 'id' is not a number
                images = images.filter(image => typeof image.id === 'number');
                // Update the local storage
                localStorage.setItem("designUploads", JSON.stringify(images));
            } else {
                images = [];
            }
            this.images = images;
        }

        async updateImagesFromServer() {
            //check for first run
            if(this.images === false){
                this.images = [];
                try {
                    const owned = await apiClient.getUserImages();

                    if(owned.hasOwnProperty('notLoggedIn')){
                        this.getImagesFromLocalStorage();
                    }else if(owned && owned.length > 0){
                        this.loggedIn = true;
                        for(let image of owned) {
                            const index = this.images.findIndex(item => item.path === image.path);
                            if(index === -1) {
                                this.images.push({ path: image.path, name: image.image_name, id: image.id, placeholder_path: image.placeholder_path, thumbnail_path: image.thumbnail_path});
                            }
                        }
                    }
                } catch(error) {
                    console.error('Error:', error);
                }
            }
        }

        saveImagesToLocalStorage() {
            localStorage.setItem("designUploads", JSON.stringify(this.images));
        }

        renderImages() {
            if (this.images.length > 0) {
                $(".image-upload-form").addClass("has-files");
            } else {
                $(".image-upload-form").removeClass("has-files");
                return;
            }

            if(this.loaded){
                return;
            }
            let dom = "";
            for (let i = 0; i < this.images.length; i++) {
                let path = USER_UPLOAD_DIR + this.images[i].path;
                let imageNameWithoutExt = this.images[i]['name'].split('.').slice(0, -1).join('.');
                let id = this.images[i].id ? this.images[i].id : i;					
                let image_args = {
                    path: path,
                    imageNameWithoutExt: imageNameWithoutExt,
                    id: this.images[i].id,
                    name: this.images[i].name,
                    thumbnail: (this.images[i].hasOwnProperty('thumbnail_path')) ? this.images[i].thumbnail_path : false, 
                    placeholder: (this.images[i].hasOwnProperty('placeholder_path')) ? this.images[i].placeholder_path : false
                }
                dom += this.generateImageInGallery(image_args);
            }

            $(".upload-grid ul").append(dom);
            this.loaded = true;
        }

        generateImageInGallery(args){
            let image_path = (args.hasOwnProperty('placeholder') && args.placeholder) ? USER_UPLOAD_DIR + args.placeholder : args.path;
            let thumbnail = (args.hasOwnProperty('thumbnail') && args.thumbnail) ? USER_UPLOAD_DIR + args.thumbnail : image_path;
            let image = 
                `<li data-target="${image_path}" data-name="${args.name}" data-item="${args.id}" draggable="true">
<div class="actions">
<button class="del" data-action="rename" data-target='${args.id}'>
<i class="material-icons">edit</i>
</button>
<button class="del" data-action="del" data-target='${args.id}'>
<i class="material-icons">remove</i>
</button>
</div>
<img src="${thumbnail}">
<div class="upload-name">${args.imageNameWithoutExt}</div>
</li>`;
            return image;
        }

        handleActions(element){
            let action = element.data('action');

            if(action == 'rename'){
                this.renameImage(element);
            }else if(action == 'del'){
                this.deleteImage(element);
            }
        }

        async deleteImage(element){
            let li = element.closest('li');
            let id = element.data('target');
            let result = false;

            let signUserImages = signSaveAndLoad.getSignUserImages();

            if(this.loggedIn){
                let args = {'action': 'del', 'image_id': id};
                if(signUserImages.includes(id.toString())){
                    args.set_null = true;
                }
                result = await apiClient.manageUserImages(args);
            }

            let stored = JSON.parse(localStorage.getItem("designUploads"));
            if(stored){
                for(let i = 0; i < stored.length; i++){
                    if(stored[i].id == id){
                        stored.splice(i, 1);
                        break;
                    }
                }
                localStorage.setItem("designUploads", JSON.stringify(stored)); // update the stored data
            }


            li.fadeOut(function() {
                li.remove();
            });

        }

        addNewImageToGallery(files){
            // get all files
            let uploads = [];
            let cahceLength = 0;

            for (var i = 0; i < files.length; i++) {
                let transformedObject = {
                    'name': files[i].image_name,
                    'id': files[i].id,
                    'path': files[i].path
                };

                uploads.push(transformedObject);
            }

            if(this.loggedIn == false){
                // For guest users, just store the uploads with their API-provided IDs
                let stored = localStorage.getItem("designUploads");
                let cacheUploads = stored ? JSON.parse(stored) : [];

                // Simply add the new uploads to the cache
                cacheUploads = cacheUploads.concat(uploads);
                localStorage.setItem("designUploads", JSON.stringify(cacheUploads));
            }

            //Now we add it to the gallery cahce is only for non logged in users
            $(".image-upload-form").addClass("has-files");

            let newImages = "";
            for(let i = 0; i < uploads.length; i++){
                let path = USER_UPLOAD_DIR + uploads[i].path;
                let imageNameWithoutExt = uploads[i].name.split('.').slice(0, -1).join('.');
                let image_args = {
                    path: path,
                    imageNameWithoutExt: imageNameWithoutExt,
                    id: uploads[i].id,
                    name: uploads[i].name,
                    thumbnail: (uploads[i].hasOwnProperty('thumbnail_path')) ? uploads[i].thumbnail_path : false, 
                    placeholder: (uploads[i].hasOwnProperty('placeholder_path')) ? uploads[i].placeholder_path : false,
                }
                newImages += this.generateImageInGallery(image_args);

            }

            $(".upload-grid ul").prepend(newImages);
        }

        renameImage(element){
            let li = element.closest('li');
            let currentNameObj = $('.upload-name', li);
            let currentName = currentNameObj.text();
            let id = element.data('target');

            //removes it if already exists 
            if($('.rename-form', li).length > 0){
                let obj = $('.rename-form', li);
                obj.css('transform', 'translateY(100%)');
                setTimeout(() => {
                    obj.remove();
                    obj.off('click.renameForm'); // Remove the document click event handler
                }, 300);
                return;
            }

            // Create the form
            let form = $(
                `<form class="rename-form" transition: transform 0.3s ease;" draggable="false">
<input type="text" value="${currentName}">
<div class="flex-between-row">
<button type="submit">Spara</button>
<button type="button" class="close-rename"><i class="material-icons">close</i></button>
</div>
</form>`
            );
            form.on('click', function(){
                e.stopPropagation();
            });
            // Attach event handlers
            form.on('submit', async (e)  =>{
                e.preventDefault();
                e.stopPropagation();
                let name = $('input[type="text"]', form).val();
                //removes extension
                let newName = $('input[type="text"]', form).val().split('.').slice(0, -1).join('.');
                newName = newName ? newName : name;

                let updatedName
                // IF USER SAVES THAN WE UPDATE THE NAME
                if(this.loggedIn){
                    let result = await apiClient.manageUserImages({
                        action : 'rename',
                        image_id : id,
                        new_name: newName
                    })
                    updatedName = result.image_name;
                }else{
                    let extension = li.data('name').split('.').slice(-1);
                    updatedName = newName +'.'+extension;
                    let images = JSON.parse(localStorage.getItem("designUploads") || "[]");
                    let imageIndex = images.findIndex(image => image.id === id);
                    if (imageIndex !== -1) {
                        images[imageIndex].name = updatedName;
                    }
                    localStorage.setItem("designUploads", JSON.stringify(images));
                }

                li.data('name', updatedName);
                currentNameObj.text(updatedName.split('.').slice(0, -1).join('.'));
                closeForm();
            });

            form.find('input').on('click', function(e) {
                e.stopPropagation();
            });

            form.find('button[type="submit"]').on('click', function(e) {
                e.stopPropagation();
                //changes the name 

            });

            form.find('.close-rename').on('click', function(e) {
                e.preventDefault();
                e.stopPropagation();
                closeForm();
            });

            $document.on('click.renameForm', function(e) {
                closeForm();
            });

            function closeForm() {
                form.css('transform', 'translateY(100%)'); // Slide down
                setTimeout(() => {
                    form.remove();
                    $document.off('click.renameForm'); // Remove the document click event handler
                }, 300); // Remove the form after it has slid down
            }

            // Append the form to the li and slide it up
            li.append(form);
            setTimeout(() => form.css('transform', 'translateY(0)'), 0); // Slide up
        }

      filterImages(searchTerm:string, grid: JQuery) {
         grid.find('li').each(function() {
            let li: JQuery = $(this);
            const name = li.data("name").toLowerCase();
            if (name.indexOf(searchTerm.toLowerCase()) === -1) {
               li.hide();
            } else {
               li.show();
            }
         });
      }
    }

   let uploadsManager = new UploadsManager();

class AdvancedColorFunctions{
      private canvas: fabric.Canvas;
      private colorPickerVal;

	constructor(canvas){
		//Not very advanced jet heheh
		this.canvas = canvas;
		this.colorPickerVal = {'prev':'', 'change':false, 'obj': {}};
		this.initalize();
		this.eyeDropper;
	}

	initalize(){
		this.$colorPickers = $(".colorpicker-input");
        this.extendSpectrum();
		this.addEyeDrop();
	}

    extendSpectrum() {
        $.fn.spectrum.defaults.replacerClassName += ' with-rgb-hex-cmyk';
        var originalHtml = $.fn.spectrum.defaults.replacerHTML;
        $.fn.spectrum.defaults.replacerHTML = function() {
            var html = $(originalHtml.apply(this, arguments));
            html.append(
                '<div class="sp-color-inputs">' +
                '<div class="sp-input-group sp-rgb-inputs">' +
                '<input class="sp-rgb-r" type="number" min="0" max="255" />' +
                '<input class="sp-rgb-g" type="number" min="0" max="255" />' +
                '<input class="sp-rgb-b" type="number" min="0" max="255" />' +
                '</div>' +
                '<div class="sp-input-group sp-hex-input">' +
                '<input class="sp-hex" type="text" />' +
                '</div>' +
                '<div class="sp-input-group sp-cmyk-inputs">' +
                '<input class="sp-cmyk-c" type="number" min="0" max="100" />' +
                '<input class="sp-cmyk-m" type="number" min="0" max="100" />' +
                '<input class="sp-cmyk-y" type="number" min="0" max="100" />' +
                '<input class="sp-cmyk-k" type="number" min="0" max="100" />' +
                '</div>' +
                '</div>'
            );
            return html;
        };
    }

	addColorPicker(){
		let self = this;

		this.$colorPickers.spectrum({
			type: "flat",
			preferredFormat: "hex",
			color: "#f00",
			showInput: true,
			showAlpha:false,
			cancelText: 'Avbryt',
			chooseText: 'Välj',
			clickoutFiresChange: false,  //Important
			beforeShow: function (color) {
			   self.colorPickerVal.change = false;
				let type = $(this).data('sign-optiontype');
				let objOpt = toolMenu.getColorChangeObject(type);
				self.colorPickerVal.prev = objOpt[1];
				self.colorPickerVal.obj = objOpt[0];

				self.$colorPickers.spectrum("set", self.colorPickerVal.prev);
			},
			move: (color) => {
				this.colorPickerVal.obj.set('fill', color.toHexString());
				this.canvas.renderAll();
			},
			change: () => {
			   this.colorPickerVal.change = true;
               this.updateColors();
			},
			hide: () => {
				if(this.colorPickerVal.change == false){
                  this.colorPickerVal.obj.set('fill', this.colorPickerVal.prev);
                  this.canvas.renderAll();
                  this.colorPickerVal.change = false;
                  this.updateColors();
				}
			},
		});


		this.$colorPickers.on('dragstop.spectrum', (e) => {
            this.updateColors();
         });

		this.$colorPickers.on('change', (e) => {
			let element = $(e.currentTarget);
			let optionType = element.data("sign-optiontype");
			let option = element.val();

			if (optionType == "background") {
				signOperation.changeBackgroundColor(option);
                  this.updateColors();
			} else if (optionType == "bordercolor") {
				signOperation.changeBorderColor(option);
                  this.updateColors();
			} else if (optionType == "symbolcolor") {
				if (canvas.getActiveObject() == null) return;
				manageCustomObjects.setSymbolColor(this.canvas.getActiveObject(), option);
                  this.updateColors();
			}
		});
	}

      updateColors(){
         const colorManager = new ManageColors(this.canvas); 
         colorManager.getActiveColors();
      }

    hexToCMYK(hex) {
        // Remove the # if present
        hex = hex.replace(/^#/, '');

        // Parse the hex values
        let r = parseInt(hex.substr(0, 2), 16) / 255;
        let g = parseInt(hex.substr(2, 2), 16) / 255;
        let b = parseInt(hex.substr(4, 2), 16) / 255;

        // Find the maximum of RGB
        let k = 1 - Math.max(r, g, b);

        // Calculate CMY
        let c = (1 - r - k) / (1 - k);
        let m = (1 - g - k) / (1 - k);
        let y = (1 - b - k) / (1 - k);

        // Convert to percentages
        return {
            c: Math.round(c * 100),
            m: Math.round(m * 100),
            y: Math.round(y * 100),
            k: Math.round(k * 100)
        };
    }

	addEyeDrop(){
		if (window.EyeDropper) {
			this.eyeDropper = new EyeDropper();
		  } else {
			toolMenu.hideEyeDrop();
			this.eyeDropper = false;
		  }
	}

	useEyeDropper(assoc){
		if(!this.eyeDropper) return;
		this.eyeDropper.open().then(res => {
        if (res && res.sRGBHex) {
			toolMenu.updateColors(assoc, res.sRGBHex);
        }
		}).catch(err => {
			console.error(err);
		});
	}
}


    class KeyDownManager{

        constructor(canvas){
            this.canvas = canvas;
            this.$canvasWrap = $("#canvas-wrap");
            this.initEventListeners();
            this.keyDown = false;
        }

        initEventListeners(){
            $document.on('keydown', (e) => this.onKeyDown(e))
            $document.on('keyup', (e) => this.onKeyUp(e))
        }

        onKeyDown(e){
            //e.preventDefault();
            let keys = {keyCode: e.keyCode, which : e.which, ctrlKey : e.ctrlKey, metaKey : e.metaKey}; 
            let newEvent = false;

            const activeElement = document.activeElement;
            const ignoredTags = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'];
            if (activeElement && ignoredTags.includes(activeElement.tagName)) {
                return; // Exit the function if an ignored element is focused
            }

            if(JSON.stringify(keys) !== JSON.stringify(this.keyDown)){
                newEvent = true;
                signOperation.updateKeyDown(keys);
            }

            let activeObject =  this.canvas.getActiveObject();
            if(activeObject && activeObject.type == 'i-text' && activeObject.isEditing){
                return;
            }

            this.keyDown = keys;
            if(e.ctrlKey || e.metaKey){	
                switch (e.which) {
                    case 90: // Z
                        //signSaveAndLoad.undo();
                        break;
                    case 89: // Y
                        //signSaveAndLoad.redo();
                        break;
                    case 83: // S
                        e.preventDefault();
                        this.testings();
                        //signSaveAndLoad.redo();
                        break;
                    case 68: // D
                        if(newEvent){
                            let obj = this.canvas.getActiveObject();
                            if (obj == null) return;
                            manageCustomObjects.dublicateActiveObject(obj);
                        }
                        break;
                    case 79: // O
                        this.test();
                        e.preventDefault();
                        break;
                    case 67: // C
                        let obj2 = this.canvas.getActiveObject();
                        if (obj2 == null) return;
                        manageCustomObjects.dublicateActiveObject(obj2, true);
                        break;
                    case 86: // V
                        manageCustomObjects.pasteCopy();
                        break;
                    default:
                        // Other key events with ctrlKey or metaKey pressed
                        break;
                }
            }else{
                switch (this.keyDown.keyCode) {
                    case 46:
                    case 37:
                        manageCustomObjects.moveKeyPressed(e, -1, 0);
                        break;
                    case 38:
                        manageCustomObjects.moveKeyPressed(e, 0, -1);
                        break;
                    case 39:
                        manageCustomObjects.moveKeyPressed(e, 1, 0);
                        break;
                    case 40:
                        manageCustomObjects.moveKeyPressed(e, 0, 1);
                        break;
                }
            }
        }
        onKeyUp(){
            this.keyDown = false;
            signOperation.updateKeyDown(false);
        }

        async test(){
            //console.log(canvas.toSVG());

            let activeObj = canvas.getActiveObject();

            if(activeObj){
                console.log(activeObj);
            }else{
                console.log(canvas.getObjects());
            }
            //console.log(await signSaveAndLoad.getThumbnail(false, true));
        }

        path() {
            //   signSaveAndLoad.getThumbnail();
            //   return;
            let activeObj = canvas.getActiveObject();
            if(activeObj){
                console.log(activeObj);
            }else{
                let allLayers = {
                    bg: $.canvasBgManager.getCanvas().getObjects(),
                    pl: canvas.getObjects(),
                    fg: $.canvasFgManager.getCanvas().getObjects()
                }; 
                console.log(allLayers); 
            }
        }

        async testings() {
            signSaveAndLoad.getCompleteSignAsync().then(completeSign => {
                const { type, data } = completeSign.thumbnailExport;

                const container = document.createElement('div');
                container.style.cssText = 'position:fixed;top:10px;right:320px;width:300px;height:300px;z-index:99999;border:2px solid blue;background:#fff;overflow:hidden;';

                if (type === 'png') {
                    const img = document.createElement('img');
                    img.src = data; // getPng() returns a data URL directly
                    img.style.cssText = 'width:100%;height:100%;object-fit:contain;';
                    container.appendChild(img);
                } else {
                    container.innerHTML = data;
                    const svg = container.querySelector('svg');
                    if (svg) {
                        svg.style.cssText = 'width:100%;height:100%;';
                    }
                }

                document.body.appendChild(container);
            });
        }

    }

class Favorites{
	constructor(){
		this.favoriteList = false;
	}

	async getUserFavoriteLists(){
		if(!this.favoriteList){
           const response = await fetch(studioValues.site_url + '/ajax-api/get_fav_stdn/9');
		   const json = await response.json();
           this.favoriteList = json;
		}
		return this.favoriteList;
	}

	getList(){
		return this.favoriteList;
	}
}

   class RightMenu{

      constructor(){
         this.bind();
         this.events();
      }

      bind(){
         this.$toggleBtnsWrp = $('#stdn-slide-outbtns');
         this.$menuWrp = $('#right-side-menu');

         console.log(this.$menuWrp);
         this.$closeMenuBtn = $('.close-right-menu');

         this.$downloadImagesToggle = $('#download-img-toggle', this.$toggleBtnsWrp);
         this.$donwloadImageWrp = $('#download-img-wrp', this.$menuWrp);

         this.$signListToggle = $('#created-signs-list-toggle', this.$toggleBtnsWrp);
         this.$singListWrp = $('#created_signs_list', this.$menuWrp);
         this.$adminPricesToggle = $('#stdn-admin-prices-toggle');
         this.$adminPrices = $('#stdn-admin-prices');

      }
      events(){
         //STANDARD BUTTONS NOT ADMIN 
         this.$closeMenuBtn.on('click', () => { this.hideMenu(); });
         this.$downloadImagesToggle.on('click', () =>{ this.toggleImageDownload(); });
         this.$signListToggle.on('click', () => {this.toggleSignList()})
         this.$adminPricesToggle.on('click', () => { this.togglePrices() });
      }

      toggleImageDownload(){
         let changed = (this.$donwloadImageWrp.css('display') == 'none');
         this.hideAllModules();
         this.$donwloadImageWrp.show();
         this.showMenu(changed);
      }

      toggleSignList(){
         let changed = (this.$singListWrp.css('display') == 'none');
         this.hideAllModules();
         this.$singListWrp.show();
         console.log(this.$singListWrp);
         this.showMenu(changed);

         if(changed && !this.signListLoaded){
            let container = $('.right-menu-cont' ,this.$singListWrp);
            loadingSpinner.addLoading(container, false, 0.05);
            this.signListLoaded = true;
            apiClient.request('GET', 'signstudio/v1/get_users_signs_studio/').then((result) =>{
               container.html(result);
            });
         }
      }

      togglePrices(){
         let changed = (this.$adminPrices.css('display') == 'none');
         this.hideAllModules();
         this.$adminPrices.show();
         this.showMenu(changed);
      }

      hideAllModules(){
         this.$menuWrp.children('div').hide();
      }

      showMenu(changed = false){
         if(this.$menuWrp.css('right') !== '0px'){
            this.$menuWrp.animate({right: "0px"}, 300);
         }else if(!changed){
            this.hideMenu();
         }
      }

      hideMenu(){
         this.$menuWrp.animate({right: "-20rem"}, 300);
      }
   }

class LoadingSpinner{
	constructor(){
	}
 
    getLoadingSpinner(height, isBlack, thickness) {
    // Calculate the border width based on the height
    const borderWidth = height * thickness;
    const innerHeight = height - borderWidth * 2; // Adjust inner height to exclude border width

    // Using innerHeight for both width and height to create a square loader based on the height
    return `<div class="lds-ring${isBlack ? ' black' : ''}" style="width: ${height}px; height: ${height}px; display: flex; align-items: center; justify-content: center;">` +
            `<div style="width: ${innerHeight}px; height: ${innerHeight}px; border-width: ${borderWidth}px;"></div>` +
            `<div style="width: ${innerHeight}px; height: ${innerHeight}px; border-width: ${borderWidth}px;"></div>` +
            `<div style="width: ${innerHeight}px; height: ${innerHeight}px; border-width: ${borderWidth}px;"></div>` +
            `<div style="width: ${innerHeight}px; height: ${innerHeight}px; border-width: ${borderWidth}px;"></div>` +
           `</div>`;
    }



    addLoading(elements, white, thickness = 0.15) {
        elements.each((index, element) => {
            element = $(element);
            if (element.find('.pre-load').length > 0) {
                // Spinner already exists, don't add another
                return;
            }

            let height = element.innerHeight(); // Get the current height of the element
            let width = element.innerWidth(); // Get the current height of the element

            let paddingTop = 0, paddingBottom = 0;
            if(height < width){
                paddingTop = parseFloat(element.css('padding-top')) || 0;
                paddingBottom = parseFloat(element.css('padding-bottom')) || 0;
            }else{
                paddingTop = parseFloat(element.css('padding-left')) || 0;
                paddingBottom = parseFloat(element.css('padding-right')) || 0;
                height = width;
            }

            if (height === 0) {
                // Handle cases where height might be 0 (hidden elements)
                height = 100; // Default to 100px if no height detected
            } else {
                const paddingTotal = paddingTop + paddingBottom;
                height -= paddingTotal; // Subtract padding from the height
            }

            let inner = element.html();
            let loading = this.getLoadingSpinner(height, !white, thickness);

            element.html(loading + `<div class="pre-load" style="display:none;">${inner}</div>`);
        });
    }

    removeLoading(elements) {
        elements.each((index, element) => {
            element = $(element);
            let original = element.find('.pre-load').html();
            element.html(original);
        });
    }
}

    class DownloadImage{
        constructor(){
            this.$downloadImageWrp = $('#download-img-wrp');
            this.events();
        }

        events(){
            this.$downloadImageWrp.on('click', '.stdn-download-img-btn', (e)=>{
                let element = $(e.currentTarget);
                this.downloadImage(element)
            });
        }

        downloadImage(element){
            let type = element.val();
            loadingSpinner.addLoading(element);

            signSaveAndLoad.getCompleteSignAsync().then(completeSign=>{
                let params = signSaveAndLoad.getAjaxParams();
                params.data = completeSign;
                params.names = signSaveAndLoad.getNames();
                params.image = type;

                apiClient.sendFinishedDesign(params)
                    .then(result => {
                        let fileType;
                        let blob;
                        let filename;

                        if (result instanceof Document) {
                            let serializer = new XMLSerializer();
                            let svgString = serializer.serializeToString(result);
                            svgString = svgString.replace(/hookGeo\(\)/g, '');  // Remove hookGeo()
                            fileType = 'image/svg+xml';
                            blob = new Blob([svgString], {type: fileType}); // directly create a blob from the SVG text
                            filename = "sign.svg";
                        } else if (typeof result === 'string' && result.indexOf("data:") === 0) { // it's a data URL
                            let dataUrl = result;  // This is your base64 data url
                            let byteCharacters = atob(dataUrl.split(',')[1]);
                            let byteNumbers = new Array(byteCharacters.length);
                            for (let i = 0; i < byteCharacters.length; i++) {
                                byteNumbers[i] = byteCharacters.charCodeAt(i);
                            }
                            let byteArray = new Uint8Array(byteNumbers);
                            fileType = dataUrl.split(',')[0].split(':')[1].split(';')[0]; // Extract the file type from the data url

                            if (fileType === 'application/pdf') {
                                blob = new Blob([byteArray], {type:"application/pdf"});
                                filename = "sign.pdf";
                            } else if (fileType === 'image/png') {
                                blob = new Blob([byteArray], {type:"image/png"});
                                filename = "sign.png";
                            } else {
                                console.error('Unexpected file type:', fileType);
                                return;
                            }
                        } else {
                            console.error('Unexpected data type:', typeof result);
                            return;
                        }

                        let link = document.createElement('a');
                        link.href = window.URL.createObjectURL(blob);
                        link.download = filename;
                        link.click();
                        loadingSpinner.removeLoading(element);
                    });
            });
        }
    }

   class SignSidesMangerClass {
      constructor() {
         this.sides = {};
         this.activeSide = 1;
         this.hasSidesStatus = false;
      }

      hasSides() {
         return this.hasSidesStatus;
      }

      getActiveSide() {
         return this.activeSide;
      }

      loadSides(data) {
         this.sides = {
            1: { data: data.data, dim: data.dim },
            2: { data: data.sides[2]['data'], dim: data.sides[2]['dim'] },
         };
         this.activeSide = 1;
         this.hasSidesStatus = true;
      }

      createSideOnLoad(signData) {
         let newDim = this.createNewDim(signData);
         let newData = deepCopy(signData.data);

         this.sides = {
            1: { data: signData.data, dim: signData.dim },
            2: { data: newData, dim: newDim }
         };
         this.activeSide = 1;
         this.hasSidesStatus = true;
      }

      saveSide() {
         //save previous sign
         let newJson = signSaveAndLoad.getSignCleanJson();
         this.sides[this.activeSide].data = newJson;
      }

      getSideDim() {
         return this.sides[this.activeSide].dim;
      }

      connectSideOneDim(dim) {
         this.sides[1].dim = dim;
      }

      createNewDim(signData) {
         let newDim = {
            border: signData.dim.border,
            background: signData.dim.background,
            borderColor: signData.dim.borderColor,
         }
         return newDim;
      }

      createNewSides() {
         let signData = {
            data: signSaveAndLoad.getSignCleanJson(),
            dim: handleRealDim.getAllDim(),
         }
         let newData = deepCopy(signData.data);

            console.log('creating new side')

            for(let i = 0; i < newData.objects.length; i++){
                let curr = newData.objects[i];
                if(curr.hasOwnProperty('id') &&
                    (
                    (curr.id == "custom_sign_background_prod" || curr.id == "custom_sign_background") ||
                    (curr.name == "custom_sign_background_prod" || curr.name == "custom_sign_background")
                    )
                ){
                    console.log('has custom and flipping');
                    newData.objects[i].flipX = true;
                }
            }
         /*
          *
         newData.objects = newData.objects.filter(object => {
            return object.hasOwnProperty('signstudio_standard') && object.signstudio_standard === true;
         });*/

         //dims might change in the future but for now just the most relevant as it will be a copy
         let newDim = this.createNewDim(signData);

         this.sides = {
            1: { data: signData.data, dim: signData.dim },
            2: { data: newData, dim: newDim },
         };
         this.activeSide = 1;
         this.hasSidesStatus = true;
      }


      copySide() {

      }

      removeSides() {
         if (Object.keys(this.sides).length > 0) {
                if(this.activeSide != 1){
                    this.sides[this.activeSide].dim = { ...this.sides[1].dim, ...this.sides[this.activeSide].dim };
                }

            signSaveAndLoad.loadDesignFromCachedData(this.sides[this.activeSide], false, {updateMenu: false});
         }
         this.sides = {};
         this.activeSide = 1;
         this.hasSidesStatus = false;
      }

      configureData(side) {
         this.saveSide();
         //get previous
         if (side != 1) {
            let configuredDim = { ...this.sides[1].dim, ...this.sides[side].dim };
            let data = { data: this.sides[side].data, dim: configuredDim };
            return data;
         }

         return this.sides[1];
      }

      changeSides(side) {
         if (this.activeSide == side) return;
         let data = this.configureData(side);
         this.activeSide = side;
         canvas.clear();
         signSaveAndLoad.loadDesignFromCachedData(data, false, {updateMenu: false});
      }

      getSides() {
         if (Object.keys(this.sides).length > 0) {

            const sides = Object.entries(this.sides).reduce((acc, [key, value]) => {
               if (key !== '1') {  // Assuming the keys are strings. If not, use if (key !== 1)
                  acc[key] = value;
               }
               return acc;
            }, {});

            return {
               original: this.sides[1],
               sides: sides,
            }
         }
      }
   }

   class TemporaryCanvas {
      private width: number = 0;
      private height: number = 0;
      private canvas: fabric.Canvas;
      private background: fabric.Rect | null = null; 

      constructor(
         widthPx: number | null = null,
         heightPx: number | null = null,
         bg: string | null = null
      ) {

         this.canvas = new fabric.Canvas(document.createElement('canvas'));

         if (widthPx && heightPx) {
            this.setCanvasSize(widthPx, heightPx);
         }

         if(bg){
            this.addBg(bg);
         }
      }

      getCanvas() {
         return this.canvas;
      }

      setCanvasSize(widthPx: number, heightPx: number) {
         this.canvas.setDimensions({ width: widthPx, height: heightPx });
         this.width = widthPx;
         this.height = heightPx;
      }

      addBg(color: string){
         if(this.background){
            this.canvas.remove(this.background);
         }

         if(!this.width || !this.height){
            console.log('cant set background no size');
            return;
         }

         const rect = new fabric.Rect({
            left: 0,
            top: 0,
            width: this.width,
            height: this.height,
            fill: color,
            selectable: false,
            evented: false,
            excludeFromExport: false
         });

         this.background = rect;
         this.canvas.add(rect);
         this.canvas.sendToBack(rect);
      }



      getSvg() {
         return this.canvas.toSVG();
      }

      getPng(){
         try {
            let dataURL = this.canvas.toDataURL({
               format: "png",
               left: 0,
               quality: 1,
               top: 0,
               width: this.width ,
               height: this.height ,
            });

            return dataURL;

         }catch(error){
            console.log('Error', error);
            return false;
         }
      }

      initPNGDownload(dataURL:string){
         function dataURLToBlob(dataURL: string) {
            const arr = dataURL.split(',');
            const mime = arr[0].match(/:(.*?);/)[1];
            const bstr = atob(arr[1]);
            let n = bstr.length;
            const u8arr = new Uint8Array(n);
            while (n--) {
               u8arr[n] = bstr.charCodeAt(n);
            }
            return new Blob([u8arr], { type: mime });
         }

         // Test download
         const blob = dataURLToBlob(dataURL);
         const url = URL.createObjectURL(blob);
         const a = document.createElement('a');
         a.href = url;
         a.download = 'fabric-export.png';
         a.click();
      }

      dispose() {
         this.canvas.dispose();
      }

        async addContent(
            jsonContent,
            maxWidth: number | null = null,
            maxHeight: number | null = null,
            addBgShadow = false
        ) {

            if (!jsonContent) {
                console.log('No content provided for adding to tempcanvas');
                return;
            }

            // Set max dimensions
            if (!maxWidth || maxWidth > this.width) {
                maxWidth = this.width;
            }
            if (!maxHeight || maxHeight > this.height) {
                maxHeight = this.height;
            }

            // Parse the JSON content
            const parsedContent = typeof jsonContent === 'string' ? JSON.parse(jsonContent) : jsonContent;

            // Wrap enlivenObjects in a Promise
            return new Promise((resolve, reject) => {
                fabric.util.enlivenObjects(parsedContent.objects, (objects) => {
                    console.log('parsed');
                    console.log(deepCopy(objects));

                    try {
                        // Calculate the bounding box of all objects
                        let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
                        let transparentAdded = false;

                        objects.forEach(obj => {
                            if (obj?.name === 'signGroup') {
                                console.log('signGroup clipPath after enliven:', obj.clipPath);
                            }

                            if (addBgShadow && obj?.id) {
                                const bounds = obj.getBoundingRect(true, true);
                                if (
                                    (obj.id === "backgroundShape" ||
                                        obj.id === "backgroundInnerShape" ||
                                        obj.id === 'addShadow')
                                        && obj.fill !== 'transparent'
                                        && !transparentAdded
                                ) {
                                    const scaledWidth = bounds.width;
                                    const scaledHeight = bounds.height;
                                    const diagonal = Math.sqrt(scaledWidth * scaledWidth + scaledHeight * scaledHeight);
                                    const shadowSize = diagonal * 0.01;
                                    transparentAdded = true;
                                    obj.set('shadow', new fabric.Shadow({
                                        color: 'rgba(0,0,0,0.4)',
                                        blur: shadowSize * 2.5,
                                        offsetX: shadowSize,
                                        offsetY: shadowSize * 1.2
                                    }));
                                }
                            }

                            let bounds;

                            if (obj?.name === 'signGroup' && obj?.clipPath) {
                                // Use clipPath bounds to avoid the huge unclipped group dimensions
                                // Group origin is center-based, so center = left + width/2
                                const cp = obj.clipPath;
                                const groupCenterX = obj.left + (obj.width * (obj.scaleX || 1)) / 2;
                                const groupCenterY = obj.top + (obj.height * (obj.scaleY || 1)) / 2;
                                bounds = {
                                    left: groupCenterX + cp.left * (obj.scaleX || 1),
                                    top: groupCenterY + cp.top * (obj.scaleY || 1),
                                    width: cp.width * (obj.scaleX || 1),
                                    height: cp.height * (obj.scaleY || 1),
                                };
                                console.log('signGroup clipPath bounds:', bounds);
                            } else {
                                bounds = obj.getBoundingRect(true, true);
                            }

                            minX = Math.min(minX, bounds.left);
                            minY = Math.min(minY, bounds.top);
                            maxX = Math.max(maxX, bounds.left + bounds.width);
                            maxY = Math.max(maxY, bounds.top + bounds.height);
                        });

                        const contentWidth = maxX - minX;
                        const contentHeight = maxY - minY;

                        console.log('content dims:', contentWidth, contentHeight, 'minX:', minX, 'minY:', minY);

                        // Calculate scaling factor to fit within maxWidth/maxHeight
                        const scaleX = maxWidth / contentWidth;
                        const scaleY = maxHeight / contentHeight;
                        const scale = Math.min(scaleX, scaleY, 1); // Don't scale up, only down

                        // Calculate center position
                        const centerX = this.width / 2;
                        const centerY = this.height / 2;

                        // Calculate offset to center the content
                        const scaledWidth = contentWidth * scale;
                        const scaledHeight = contentHeight * scale;
                        const offsetX = centerX - scaledWidth / 2 - (minX * scale);
                        const offsetY = centerY - scaledHeight / 2 - (minY * scale);

                        // Apply transformations and add objects to canvas
                        objects.forEach(obj => {
                            obj.scaleX = (obj.scaleX || 1) * scale;
                            obj.scaleY = (obj.scaleY || 1) * scale;
                            obj.left = (obj.left * scale) + offsetX;
                            obj.top = (obj.top * scale) + offsetY;

                            // Fix absolutePositioned clipPath on the object itself
                            if (obj.clipPath && obj.clipPath.absolutePositioned) {
                                obj.clipPath.left = (obj.clipPath.left * scale) + offsetX;
                                obj.clipPath.top = (obj.clipPath.top * scale) + offsetY;
                                obj.clipPath.scaleX = (obj.clipPath.scaleX || 1) * scale;
                                obj.clipPath.scaleY = (obj.clipPath.scaleY || 1) * scale;
                            }

                            // Fix absolutePositioned clipPaths on children (e.g. image inside signGroup)
                            if (obj._objects) {
                                obj._objects.forEach(child => {
                                    if (child.clipPath && child.clipPath.absolutePositioned) {
                                        child.clipPath.left = (child.clipPath.left * scale) + offsetX;
                                        child.clipPath.top = (child.clipPath.top * scale) + offsetY;
                                        child.clipPath.scaleX = (child.clipPath.scaleX || 1) * scale;
                                        child.clipPath.scaleY = (child.clipPath.scaleY || 1) * scale;
                                    }
                                });
                            }

                            this.canvas.add(obj);
                        });

                        this.canvas.renderAll();
                        resolve(objects);
                    } catch (error) {
                        reject(error);
                    }
                });
            });
        }

      copyCanvasContents(
         sourceCanvas: fabric.Canvas,
         targetCanvas: fabric.Canvas,
         offsetX: number,
         offsetY: number,
         isSign = false,
         addShadow = false,
      ) {

         let cropObject: fabric.Object | null = null;
         let layerGroup = new fabric.Group([], {left: 0, top:0});
         let extrasOffsetSign = {left: 0, top: 0};

         //manage crop offsets for sign
         if(isSign){
            let signCoords = {left: 0, top: 0, right: 0, bottom: 0};
            let signCoordsDiff = {left: 0, top: 0, right: 0, bottom: 0};
            sourceCanvas.forEachObject(function(obj) {
               const bb = obj.getBoundingRect(true, true);

               if(!cropObject && obj?.fill && obj?.fill !== "transparent"){
                  cropObject = obj;

                  signCoords = {
                     left: bb.left,
                     top: bb.top,
                     right: bb.left + bb.width,
                     bottom: bb.top + bb.height
                  }

                  signCoordsDiff = {...signCoords}

                  return;
               }

               if(cropObject){
                  signCoordsDiff = {
                     left: Math.min( bb.left, signCoordsDiff.left),
                     top: Math.min( bb.top, signCoordsDiff.top),
                     right: Math.max( (bb.left + bb.width), signCoordsDiff.right),
                     bottom: Math.max( (bb.top + bb.height), signCoordsDiff.bottom),
                  }
               }
            });

            const leftDiff = signCoords.left - signCoordsDiff.left;
            const topDiff = signCoords.top - signCoordsDiff.top;
            const rightDiff = signCoordsDiff.right - signCoords.right;
            const bottomDiff = signCoordsDiff.bottom - signCoords.bottom;

            extrasOffsetSign = {
               left: leftDiff - rightDiff,
               top: topDiff - bottomDiff,
            }

            cropObject = false;
         }

         sourceCanvas.forEachObject(function(obj: fabric.Object) {
            //dont add things that are for the studio
                if (obj.excludeFromExport) return;

                var clonedObj = fabric.util.object.clone(obj);
                clonedObj.set({
                    left: clonedObj.left + offsetX,
                    top: clonedObj.top + offsetY,
                    objectCaching: false
                });

                // If object has an absolutePositioned clipPath, offset it too
                if (clonedObj.clipPath && clonedObj.clipPath.absolutePositioned) {
                    clonedObj.clipPath = fabric.util.object.clone(clonedObj.clipPath);
                    clonedObj.clipPath.set({
                        left: clonedObj.clipPath.left + offsetX,
                        top: clonedObj.clipPath.top + offsetY,
                    });
                }

                clonedObj.setCoords();
            if(isSign && !cropObject && obj?.fill && obj?.fill !== "transparent" && addShadow){
               //adding crop
               //crop
               cropObject = fabric.util.object.clone(obj);
               delete cropObject.id;
               //shadow
               let shadow = fabric.util.object.clone(obj);
               shadow.id = 'addShadow'
               targetCanvas.add(shadow);
            }

            layerGroup.addWithUpdate(clonedObj);
         });

         if(cropObject){
            layerGroup.clipPath = cropObject;
            //cropObject.absolutePositioned = true;
            cropObject.left = - (cropObject.width - extrasOffsetSign.left) / 2;
            cropObject.top = - (cropObject.height - extrasOffsetSign.top) / 2;
            layerGroup.set('name', 'signGroup');
         }

         targetCanvas.add(layerGroup);
         targetCanvas.requestRenderAll();
      }
   }





//define variables for classe 

let canvasFgManager
let onCanvasGUIHandler;
let symbolMangement;
let manageCustomObjects;
let advancedColors;
let keyDownManager;
let rightMenu;
let loadingSpinner;
let downloadImage;
let signSidesManager;
let previewManager;





var MAX_SIGN_WIDTH = 2000;
var MAX_SIGN_HEIGHT = 1000;
var SIGN_SAVE_SIZE = 2000;
var SIGN_CANVAS_SCALE = 0.85;
var EXPORT_SCALE = 3.533;
var USER_UPLOAD_DIR = my_base_params.site_url + "/wp-content/uploads/user-uploads";
var SYMBOL_ROOT_URL = my_base_params.site_url + "/symbols/";
var SYMBOL_ROOT_URL = my_base_params.site_url + "/wp-content/uploads/symbols/";
let SYMBOL_THUMB_URL = my_base_params.site_url + "/wp-content/uploads/symbols/";
var FONT_ROOT_URL = my_base_params.site_url + "/wp-content/plugins/skyltvaruhuset/assets/misc/";
var website_base_url = window.location.origin;
var gravyrColors = false;
var amtMountingHoles = 0; 
var resizeTimer;
var preResizeWidth;
var preResizeHeight;
//Vars for undo redo
let pause_saving = false;
let undo_stack = []
let redo_stack = []
let allLoaded = false;
let activeDropdown;
let isMobile = false; //initiate as false
let panning = false;
let SimpleColor = false;
let bgcolor;
let namntextcolor;
let movingDirection;
let lastBGColor = "#fff";
let movingVinyl = false;
let activeSelection = false;
let lastActiveTool= 'signtype';
let UserobjectCollection = [];
let keyDown = false;
let waitingForCart = false;
let materialMenu = {'ongoing': false, 'prev': 0};
let magnetSnapping = true;
let saveCustomProperties = [
        'signstudio_standard',
        'id',
        'stdnCustom',
        'realSize',
        'cstBorder',
        'singleColor',
        'templateName',
        'thumbailShadow',
        'user_image_id',
        'svgUrl',
        'removeOnSave',
        //'clipPath'
    ];
let fullscreen ={'state': false};

//
let vh = window.innerHeight * 0.01;

//Create classes to manage settings and conditions
let elementUpdater;
let updateProdSettings;
 
var canvas;

if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent) 
    || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4))) { 
    isMobile = true;
}

let realDimensions = {
	width: 148,
	height: 210,
	material: 1,
	border: 0,
	cornerRadius:null, 
	background: "#fff",
	shape: "rect",
	borderColor: "#000",
	mounting: "none",
	mountingLayout: false,
	mountdim : false,
	engraving : false,
	mount_amount: 0,
	mountImage: false,
	surface: "blank",
	special_sign: false,
	special: false,
	auto_size_h: false,
	auto_size_w:false,
};

let template = {}

var pixelsPerMm = 1;
var vinylScaler = 1;

var backgroundShape;
var backgroundInnerShape;
var bgGroupSpecial;
var bgGroupSpecialProduction;



var selectedFontProperties = {
	family: "Open Sans",
    weight: 400,
    style: 'normal',
};
var lastColorUsed = '#000';
var term = 0;

let currentselections = {
	'product': false,
	'mount' : false,
	'product-group' : false,
	'shape' : false,
}


//Global max min sizes 
var GmaxWidth = {'product' : null, 'mounting' : null},
GmaxHeight = {'product' : null, 'mounting' : null}, 
GminWidth = {'product' : null, 'mounting' : null}, 
GminHeight = {'product' : null, 'mounting' : null};
 


fabric.Object.prototype.toObject = (function(toObject) {
	return function(properties) {
		return fabric.util.object.extend(toObject.call(this, properties), {
			name: this.name,
			selectable: this.selectable
		});
	};
})(fabric.Object.prototype.toObject);

fabric.Object.NUM_FRACTION_DIGITS = 6;

fabric.PathGroup = {};
fabric.PathGroup.fromObject = function(object, callback) {
  var originalPaths = object.paths;
  delete object.paths;
      if (typeof originalPaths === 'string') {
         fabric.loadSVGFromURL(
            originalPaths,
            function (elements) {
               var pathUrl = originalPaths;
               var group = fabric.util.groupSVGElements(elements, object, pathUrl);
               object.paths = originalPaths;
               group.type = "group";
               callback(group);
            },
            null,
            {crossOrigin: 'anonymous'}
         );
      }
  else {
    fabric.util.enlivenObjects(originalPaths, function(enlivenedObjects) {
      enlivenedObjects.forEach(function(obj) {
        obj._removeTransformMatrix();
      })
      var group = new fabric.Group(enlivenedObjects, object);
      object.paths = originalPaths;
      group.type = "group";
      callback(group);
    });
  }
};

   //fixing the rounding that fabric does
   const originalImageToSVG = fabric.Image.prototype.toSVG;
   fabric.Image.prototype.toSVG = function(reviver) {
      // Get the exact dimensions from the object
      const exactWidth = this.width;
      const exactHeight = this.height;

      // Call original method
      let svg = originalImageToSVG.call(this, reviver);

      // Fix the rounded dimensions in the image element
      svg = svg.replace(
         /(<image[^>]*width=")(\d+)("[^>]*height=")(\d+)("[^>]*>)/g, 
         function(match, p1, currentWidth, p3, currentHeight, p5) {
            return p1 + exactWidth + p3 + exactHeight + p5;
         }
      );

      return svg;
   };

/*
const originalImageToSVG = fabric.Image.prototype.toSVG;
fabric.Image.prototype.toSVG = function(reviver) {
    // Get the exact dimensions
    console.log("HERE!!!");
    const exactWidth = this.width;
    const exactHeight = this.height;
    
    // Call original method
    let svg = originalImageToSVG.call(this, reviver);
    
    // Fix the rounded dimensions
    svg = svg.replace(
        /width="(\d+)"/g, 
        `width="${exactWidth}"`
    );
    svg = svg.replace(
        /height="(\d+)"/g, 
        `height="${exactHeight}"`
    );
    
    return svg;
};
*/

fabric.Text.prototype.setFontSize = function(size) {
	this.fontSize = fabric.util.parseUnit(size+"pt");
	return this;
}

//update gui for selection boz
fabric.Object.prototype.set({
    transparentCorners: false,
    cornerColor: '#4169e1',
    cornerStrokeColor: '#fff',
    cornerStyle: 'circle',
    borderColor: '#4169e1',
    cornerSize: 12,
    padding: 0,
    //borderScaleFactor: 2, // increase this for a thicker border
});


//Get item by name 

/**
 * Non-unique attributes
 */
 fabric.Canvas.prototype.getItemsByAttr = function(attr, val) {
    var objectList = [];
    traverseObjects(this.getObjects(), attr, val, objectList);
    return objectList;
};

/**
 * Unique attribute
 */
fabric.Canvas.prototype.getItemByAttr = function(attr, val) {
    var objectList = [];
    traverseObjects(this.getObjects(), attr, val, objectList);
    return objectList[0];
};

/**
 * Traverse objects in groups (and subgroups)
 */
function traverseObjects(objects, attr, val, objectList){
    for(let i in objects){
        if(objects[i]['type'] == 'group'){
			if(objects[i][attr] == val){
				objectList.push(objects[i]);
			}
            traverseObjects(objects[i].getObjects(),attr,val, objectList);
        } else if(objects[i][attr] == val){
            objectList.push(objects[i]);
        }
    }    
}

/**
 *   Only allow resizing that scales properly
 */

function noResizing(shape) {
	shape.setControlVisible("mb", false);
	shape.setControlVisible("ml", false);
	shape.setControlVisible("mt", false);
	shape.setControlVisible("mr", false);
}

/**
 *   Create the background shape (sign base)
 */



/**
 *	Make it half the size of the sign (background shape)
 */
function scaleToFitSign(shape) {
	var scale = 1.0;

	var smallestScale = backgroundShape.getScaledWidth() / shape.width;
	if (backgroundShape.height / shape.height < smallestScale)
		smallestScale = backgroundShape.getScaledHeight() / shape.height;

	if (smallestScale < 1.0) scale = smallestScale;

	if (smallestScale > 1.0) {
		var largestScale = backgroundShape.getScaledWidth() / shape.width;
		if (backgroundShape.height / shape.height < largestScale)
			largestScale = backgroundShape.getScaledHeight() / shape.height;
		shape.scale(largestScale * 0.5);
	} else {
		shape.scale(scale * 0.5);
	}
}

var edgedetection = 16; //pixels to snap

var helperLine = new fabric.Rect({
	name: "helperlineH",
	left: 0,
	top: 0,
	fill: "rgba(0,0,0,0.2)",
	width: 1600,
	height: 1,
	selectable: false,
	signstudio_standard: true,
});
var helperLineV = new fabric.Rect({
	name: "helperlineV",
	left: 0,
	top: 0,
	fill: "rgba(0,0,0,0.2)",
	width: 1,
	height: 1600,
	selectable: false,
	signstudio_standard: true,
});

var helperLineObjV = new fabric.Line([0,0,0,0],{
	stroke: "rgba(0,0,0,0.2)",
	strokeWidth: 1,
	selectable: false,
	strokeUniform: true,
	signstudio_standard: true,
});

var helperLineObjH = new fabric.Line([0,0,0,0],{
	stroke: "rgba(0,0,0,0.2)",
	strokeWidth: 1,
	selectable: false,
	strokeUniform: true,
	signstudio_standard: true,
});

/**
 *   Grid helper things
 */





/*
MAIN FUNCTION TO ASYNC RENDERING OF SIGNS AND OPTIONS
1. Add/load sign
2. UpdateButtons
3. UpdateConditions
4. Updatebackgrounshape 
5. UpdateUi 
*/
async function masterSync(actions, specific){
	//creates object if not passed
	if(typeof actions !== 'object'){
		actions = {};
	}

	//LOAD OR UPDATE SIGNS
	if(actions.hasOwnProperty('loadDesignFromCachedData')){
        let cacheData = (actions.hasOwnProperty('unlockObjects')) ? signSaveAndLoad.unlockObjectsJSON(actions['loadDesignFromCachedData']) : actions['loadDesignFromCachedData'];
		signSaveAndLoad.loadDesignFromCachedData(cacheData, true);
		return Promise.resolve("Done");
	}
	if(actions.hasOwnProperty('addBackgroundShape')){
		signOperation.addStandardBackgroundShape();
		signOperation.updateBackgroundShape();
	}

	if(actions.hasOwnProperty('loadDesignFromCode')){
		signSaveAndLoad.loadDesignFromCode(actions['loadDesignFromCode']);
		return Promise.resolve("Done");;
	}

	let updateSign = false;
	if(actions.hasOwnProperty('updateSelection')){
		updateSign = true;
	}

    await updateProdSettings.updateProductSettings(updateSign);

	if(actions.hasOwnProperty('resetSign')){
		signOperation.addStandardBackgroundShape();
       await signOperation.updateBackgroundShape(actions['resetSign']);
	}

	await applyConditions();
	//loads all elements correctly after all settings are defined
	if(actions.hasOwnProperty('updateMounting')){
		await mountingManager.updateMounting();
	}

	if(actions.hasOwnProperty('updateBackgroundShape')){
		await signOperation.updateBackgroundShape(specific);
	}
    //loads actions that needs to happen after sign and moutning actions
    updateProdSettings.activateProlongedActions();

	if(!movingVinyl){
		signSaveAndLoad.saveDesign();
		toolMenu.debouncedUpdatePrice();
	}

	canvas.renderAll();
	
	$(".sign-option-width").val(handleRealDim.getDim('width'));
	$(".sign-option-height").val(handleRealDim.getDim('height'));
	return Promise.resolve("Done");
}
/*
function openShapes() {
	closeGallery();

	$(".new-shape-form").removeClass("hidden");
	$(".new-shape-toggle").addClass("active");
}*/

function openCart() {
	$(".post-design").addClass("open");
	$(".cart-mobile-button").addClass("active");
}

function closeCart() {
	$(".post-design").removeClass("open");
	$(".cart-mobile-button").removeClass("active");
}

/**
 *   Add text to canvas
 */

/**
 *   Add correct properties to format buttons based on options on the specific obj
 */

/**
 *
 */



/*


function isCoordinateOutsideAllowedArea(x, y) {
	if (x < backgroundShape.oCoords.tl.x || x > backgroundShape.oCoords.tr.x) {
		return true;
	}
	if (y < backgroundShape.oCoords.tl.y || y > backgroundShape.oCoords.bl.y) {
		return true;
	}
	return false;
}*/


/*
function fitToContent() {
	var objects = canvas.getObjects();

	var minX = 1000000,
		minY = 1000000;
	var maxX = -100000,
		maxY = -100000;

	for (var o in objects) {
		var object = objects[o];
		if (object.name != null) continue;

		for (c in object.oCoords) {
			var corner = object.oCoords[c];
			if (corner.x < minX) {
				minX = corner.x;
			}
			if (corner.y < minY) {
				minY = corner.y;
			}
			if (corner.x > maxX) {
				maxX = corner.x;
			}
			if (corner.y > maxY) {
				maxY = corner.y;
			}
		}
	}
*/



function scaleSignToSize(sign, width, height) {
	var largestObjectWidth = 0;
	var largestObjectHeight = 0;

	for (var i = 0; i < 2; i++) {
		if (sign.objects[i].name != "bg") continue;

		var obj = sign.objects[i];

		if (obj.width * obj.scaleX > largestObjectWidth) {
			largestObjectWidth = obj.width * obj.scaleX;
			preResizeWidth = largestObjectWidth;
		}

		if (obj.height * obj.scaleY > largestObjectHeight) {
			largestObjectHeight = obj.height * obj.scaleY;
			preResizeHeight = largestObjectHeight;
		}
	}

	var scaleFactor = 1;

	var signRatio = largestObjectWidth / largestObjectHeight;
	var restrictingRatio = width / height;

	if (signRatio > restrictingRatio) scaleFactor = width / largestObjectWidth;
	else scaleFactor = height / largestObjectHeight;

	for (var i = 0; i < sign.objects.length; i++) {
		var obj = sign.objects[i];
		obj.scaleX *= scaleFactor;
		obj.scaleY *= scaleFactor;
		obj.top *= scaleFactor;
		obj.left *= scaleFactor;
	}
}


function updateCanvasSize() {
	canvas.setHeight(0);
	canvas.setWidth(0);
	let height = $(".canvas-container").outerHeight();
	let width = $(".canvas-container").outerWidth();
	canvas.setHeight(height);
	canvas.setWidth(width);

	MAX_SIGN_HEIGHT = canvas.getHeight();
	MAX_SIGN_WIDTH = canvas.getWidth();
}

function addShapeRadiusSlider(){
	let obj = canvas.getActiveObject();
	let curr,max;
	if(obj.type ==  'group' && obj.name == "userFabricShape"){
		obj = obj._objects[0];
	}
	curr = (obj.rx) ? obj.rx : 0; 
	max = (obj.width < obj.height) ? (obj.width+obj.strokeWidth)/2 : (obj.height+obj.strokeWidth)/2;

	curr = Math.round(curr/fabric.util.parseUnit("1mm"));
	max = Math.round(max/fabric.util.parseUnit("1mm"));

	$("#shape-radius-range").slider({
		range: "min",
		min: 0,
		max: max,
		value: curr,
		slide: function( event, ui ) {
			let val = ui.value; 
			updateShapeRadius(val);
			$(".shape-radius-input").attr({
				"value" : val,   
			 });
		}
	});

	$(".shape-radius-input").attr({
		"max" : max,
		"value": curr  
	 });

	 function updateShapeRadius(value){
		let objects = canvas.getActiveObject();

		let [border,outer,inner] = [objects.cstBorder, objects._objects[0], objects._objects[1]];

		outRadius = fabric.util.parseUnit(value +"mm");
		outer.set({rx: outRadius, ry:outRadius});

		innRadius =  fabric.util.parseUnit((value - border)+"mm");
		if(innRadius < 0) innRadius = 0;
		inner.set({rx: innRadius, ry:innRadius});
		canvas.requestRenderAll();
	 }
}

/*
function addShapeBorderSlider(){
	let obj = canvas.getActiveObject();

	let curr,max,type;
	//find current
	curr = 0;
	if(obj.type ==  'group' && obj.name == "userFabricShape"){
		curr = obj.cstBorder;
		type = obj._objects[0].type;
	}
	let box = obj.getBoundingRect(true,true);
	let margin 
	switch (type){
		case 'triangle':
			margin = 4;
			break;
		case 'circle':
			margin = 2;
			break;
		case 'rect':
			margin = 2
			break;
	}	

	let shortest = (box.width < box.height) ? box.width/margin : box.height/margin;
	max = Math.floor(shortest/fabric.util.parseUnit("1mm"));

	$("#shape-border-range").slider({
		range: "min",
		min: 0,
		max: max,
		value: curr,
		slide: function( event, ui ) {
			let val = ui.value; 
			console.log("hrhr");
			updateBorder(val);
			$(".shape-border-input").attr({
				"value" : val,   
			 });
			 //updateSymbolSize(true);
		}
	});

	$(".shape-border-input").attr({"value" : curr});

	function updateBorder(val){
		let obj = canvas.getActiveObject();

		if (!(obj)){
			return;
		}
		if(obj.type ==  'group' && obj.name == "userFabricShape"){
			calcUserBorder(val, obj)
		}
	}
}*/

function calcUserBorder(val, obj){
	if(obj.type ==  'group' && obj.name == "userFabricShape"){
		let inner, outer;
		obj.cstBorder = val;
		val = fabric.util.parseUnit(val+"mm");

		for(let i = 0; i < obj._objects.length; i++){
			if(obj._objects[i].name=="userShapeInner"){
				inner = obj._objects[i];
			}else{
				outer = obj._objects[i];
			}
		}


		if(SimpleColor == true){
			if(val == 0){
				outer.fill = namntextcolor;
				inner.fill = namntextcolor;
			}else{
				outer.fill = namntextcolor;
				inner.fill = bgcolor;
			}
		}

		let width, height, center, radius;
		let yPostion = 'center';
		switch(inner.type){
			case 'rect':
				[width, height, center] = [outer.width, outer.height, outer.getCenterPoint(true,true)];
				if(val > outer.width/2){
					val = outer.width/2;
				}
				inner.set({
					width: width-val*2,
					height: height-val*2,
				});
				break;
			case 'circle':
				[radius, center] = [outer.radius, outer.getCenterPoint(true,true)];
				if(val > outer.radius){
					val = outer.radius;
				}
				inner.set({
					radius: radius-val,
				});
				break;
			case 'triangle':  
				[width, height, center] = [outer.width, outer.height, outer.getCenterPoint(true,true)];
				let top = outer.top;
				let shortest = (width > height) ? height: width;
				if(val > shortest/4){
					val = shortest/4;
				}

				let newWidth = width - 2*Math.sqrt(3)*val;
				let newHeight = height - 3*val;

				inner.set({
					width: newWidth,
					height: newHeight,
					stroke: 0,
				});
				yPostion = 'top';
				center.y = top + height - val - newHeight;
				break;
		}
		inner.setPositionByOrigin(center, 'center', yPostion);
		canvas.requestRenderAll();
	}
}

function updateCornerRadius(newRadius){
    handleRealDim.updateDim('cornerRadius',newRadius);
	newRadius = fabric.util.parseUnit("1mm") * newRadius;
	let borderThickness = fabric.util.parseUnit("1mm")*handleRealDim.getDim('border');
	backgroundShape.set({ rx: newRadius, 
						ry: newRadius});
	if(newRadius - borderThickness > 0){
		backgroundInnerShape.set({
			rx: newRadius - borderThickness,
			ry: newRadius - borderThickness,
			});
	}else{
		backgroundInnerShape.set({
			rx: 0,
			ry: 0,
			});
	}
	
	canvas.renderAll();
}

   function updateUserObjects() {

      let objects = canvas.getObjects();
      let id;
      for (let i = 0; i < objects.length; i++) {
         let obj = objects[i];
         let selectable = (obj.selectable) ? '' : ' active';
         if (!(obj.signstudio_standard == true) && obj.name !== 'noEdit') {
            if (obj.type === 'i-text') {
               if (obj.objId) {
                  let tmpText = $(`[data-sign-optiontype='select-obj'][data-sign-option='${obj.objId}']`);
                  if (tmpText) {
                     tmpText.addClass("adding");
                  } else {
                     id = getNextAvailableId(UserobjectCollection);
                     obj.objId = id
                     loadTextObj(id, obj.text, selectable);
                  }
               } else {
                  id = getNextAvailableId(UserobjectCollection);
                  obj.objId = id
                  loadTextObj(id, obj.text, selectable);
               }
            } else {
               let objectName = (obj.hasOwnProperty('stdnCustom') && obj.stdnCustom.hasOwnProperty('objectName')) ? obj.stdnCustom.objectName : 'Ditt object';
               if (obj.objId) {
                  let tempObj = $(`[data-sign-optiontype='select-obj'][data-sign-option='${obj.objId}']`);
                  if (tempObj) {
                     tempObj.addClass("adding");
                  } else {
                     obj.objId = id
                     loadShapeObj(id, objectName, selectable);
                  }
               } else {
                  id = getNextAvailableId(UserobjectCollection);
                  obj.objId = id
                  loadShapeObj(id, objectName, selectable);
               }
            }
         }
      }
      //remove straglers 

      $('.text-obj-btn').not('.adding').remove();
      $('.text-obj-btn').removeClass('adding');
      $('.shape-obj-btn').not('.adding').remove();
      $('.shape-obj-btn').removeClass('adding');

      function loadTextObj(id, text, selectable) {
         let svg = studioValues.icons.lock;
         UserobjectCollection.push(id);
         let textObj = $(`<div class="sign-option no-standard-option button-border obj-btn text-obj-btn adding" data-sign-optiontype='select-obj' data-sign-option="${id}">
<div class='obj-btn-text'>${text}</div>
<div class='lock-toggle ${selectable}'>${svg}</div>
</div>`);
         $('.text-objects-container').append(textObj);
      }

      function loadShapeObj(id, text, selectable) {
         let svg = studioValues.icons.lock;
         UserobjectCollection.push(id);
         let shapeObj = `<div class="sign-option no-standard-option button-border obj-btn shape-obj-btn adding" data-sign-optiontype='select-obj' data-sign-option="${id}">
<div class='obj-btn-text'>${text}</div>
<div class='lock-toggle ${selectable}'>${svg}</div>
</div>`;
         $('.shape-objects-container').append(shapeObj);
      }

      function getNextAvailableId(ids) {
         let id = 1;
         while (ids.includes(id)) {
            id++;
         }
         return id;
      }
   }


function getBackgroundLeft(){
	let object = backgroundShape;
	let zoom = canvas.getZoom();
	let transform = canvas.viewportTransform;
	let objectLeft = object.left
	let objectLeftPx = objectLeft * zoom + transform[4];
	return objectLeftPx;
}

async function Path(){
	let activeobject = canvas.getActiveObject();
	let background = signOperation.getBackgroundShape();

	let left = fabric.util.parseUnit("20mm");

	let move = background.left + left;
	activeobject.set('left', move);
	canvas.renderAll();

}

function addEndStrokePan(position){
	$('.canvas-container').css(`border-${position}`, '2px solid red');
}

function setDimensionsToStanard(){
	//StudioValues enqued
	let standards = deepCopy(studioValues['standards']);
	let colors = studioValues['colors'];
	//background color
	let backgroundColorID = standards['background'];
	if(backgroundColorID && colors[backgroundColorID]){
		standards['background'] = colors[backgroundColorID]['color'];
	}else{
		standards['background'] = "#ffffff";
	}
	let shapeID = standards['shape'];
	//shape
	if(shapeID && all_elements[`shape_${shapeID}`]){
		standards['shape'] = all_elements[`shape_${shapeID}`]['studio_name'];
	}
	
	realDimensions = {...realDimensions, ...standards}
	
}

function setDimensionsToStanard() {
   //StudioValues enqued
   let standards = deepCopy(studioValues['standards']);
   let realDimensions = handleRealDim.getAllDim();

   if (standards) {
      let colors = studioValues['colors'];
      //background color
      let backgroundColorID = standards['background'];
      if (backgroundColorID && colors[backgroundColorID]) {
         standards['background'] = colors[backgroundColorID]['color'];
      } else {
         standards['background'] = "#ffffff";
      }
      let shapeID = standards['shape'];
      //shape
      if (shapeID && all_elements[`shape_${shapeID}`]) {
         standards['shape'] = all_elements[`shape_${shapeID}`]['studio_name'];
      }
      realDimensions = { ...realDimensions, ...standards }
   }

   handleRealDim.setRealdimensions(realDimensions);

}



    async function init() {
        //assing specific modules on init
        let touchListener = document.getElementById('playground');
            touchListener.addEventListener('touchstart', function(e){
        });

        apiClient.init(ajaxob);

        var mainCanvasHTML = document.getElementById("playground");
        var ctx = mainCanvasHTML.getContext('2d', { willReadFrequently: true });


        canvas = new fabric.Canvas(mainCanvasHTML, {
            preserveObjectStacking: true,
            renderOnAddRemove: false,
            controlsAboveOverlay: true,
            fireRightClick: true,
            fireMiddleClick: true,
            stopContextMenu: true,
            isDrawingMode: false,
            allowTouchScrolling: true,
            selection: false,
            willReadFrequently: true,
            selectionLineWidth : 10,
            targetFindTolerance : 10,
        });

        var bgCanvasHTML = document.getElementById("background");
        let backgroundCanvas = new fabric.Canvas(bgCanvasHTML, {
            interactive: false, 
            containerClass: ' canvas-container overlay-canvases',
        });

        var fgCanvasHTML = document.getElementById("foreground");
        let foregroundCanvas = new fabric.Canvas(fgCanvasHTML, {
            interactive: false, 
            containerClass: ' canvas-container overlay-canvases',
        });


        fabric.Object.prototype.transparentCorners = false;
        fabric.Object.prototype.objectCaching = false;
        let renderCanvas = document.getElementById('render-canvas');

        symbolMangement = new Symbols();
        signSidesManager = new SignSidesMangerClass();

        toolMenu = new ToolMenu(canvas, eventBus);
        $.manageFonts = new ManageFonts(studioValues.font_data, studioValues.font_url);
        $.canvasBgManager = new ManageBackgroundCanvas(backgroundCanvas, canvas);
        $.canvasFgManager = new ManageForegroundCanvas(foregroundCanvas, canvas);
        signOperation = new SignOperation(canvas, currentSettings, backgroundCanvas, foregroundCanvas);
        signSaveAndLoad = new SignSaveAndLoad(canvas, signOperation);
        onCanvasGUIHandler = new OnCanvasGUIHandler(canvas, signOperation, backgroundCanvas, foregroundCanvas);
        manageCustomObjects = new ManageCustomObjects(canvas, signOperation, backgroundCanvas, foregroundCanvas);
        keyDownManager = new KeyDownManager(canvas);
        rightMenu = new RightMenu();
        loadingSpinner = new LoadingSpinner();
        previewManager = new PreviewWindow();

        $.studioImageManager = new ImageManager(canvas, signOperation);

        //new FabricHistory(canvas);
        $.fn.fabricHistory = function(canvas) {
        };

        downloadImage = new DownloadImage();

        window.loadingSpinner = loadingSpinner;
        $.studioLoadingSpinner = loadingSpinner;


        updateCanvasSize();
        //canvas.selection = false;


        let actions = {};				  

        if (my_base_params.base != null) {
            //Load premade sign 
            actions['loadDesignFromCode'] = my_base_params.base;
        }else if(my_base_params.baseID != null){
            actions['loadDesignFromCode'] = my_base_params.baseID;
        }else if(my_base_params.template != null){
            actions['loadDesignFromCode'] = {'getTemplate' : my_base_params.template};
        }else if(my_base_params.edit != null){
            actions['loadDesignFromCode'] = {'getEdit' : my_base_params.edit};
        }else {

            let dis;

            if(my_base_params.quick != null){
                dis = localStorage.getItem("designFromQuick");
                actions['unlockObjects'] = true;
            }else{
                dis = localStorage.getItem("designCache");
            }


            if (dis == null || my_base_params.stand_alone) {
                //load brand new sign 
                setDimensionsToStanard();
                //updateProdSettings.updateProductSettings(true);
                actions['resetSign'] = (my_base_params.stand_alone) ? true : false;
                actions['updateSelection'] = true;

                if (typeof loadAdmin === 'function' && !adminLoaded) {
                    loadAdmin(template, canvas, signSaveAndLoad, apiClient, handleRealDim);
                    adminLoaded = true;
                } 
            }else {
                //load cached sign
                var designCache = JSON.parse(dis);

                if(all_product_settings.hasOwnProperty(`product_${designCache.dim.material}`)){
                    actions['loadDesignFromCachedData'] = designCache;
                }else{
                    setDimensionsToStanard();
                    actions['resetSign'] = true;
                    actions['updateSelection'] = true;
                }
            }
            if(window.signType){
                //setMaterial(window.signType);
            }
        }
        //load classes for settings 
        elementUpdater = new ElementAndConditionsUpdater(currentSettings, currentselections, all_conditions, all_elements, all_product_settings);
        updateProdSettings = new ProductSettingsUpdater(all_product_settings, currentSettings, currentselections, SimpleColor);
        mountingManager = new Mounting(canvas,renderCanvas,currentSettings,signOperation);
        //Function to load everything
        await masterSync(actions);
        updateUserObjects();

        //get symbols
        apiClient.getSymbols().then(data =>{
            symbolMangement.addAllSymbolsToGallery(data);
        }).catch(error => {
                console.log(error);
            });


        advancedColors = new AdvancedColorFunctions(canvas);
        advancedColors.addColorPicker();

        onCanvasGUIHandler.windowResize();
    }

function scaleObjectAroundCenter(object, scale) {
	let objectCenter = object.getCenterPoint(true,true);
	object.scaleX = scale;
	object.scaleY = scale;

	object.setPositionByOrigin(objectCenter, 'center', 'center');
}

function updateSymbolSize() {
	let activeObject = canvas.getActiveObject();
	if (activeObject == null) {
		return;
	}
	let width = Math.round(((activeObject.width+activeObject.strokeWidth) * activeObject.scaleX) / fabric.util.parseUnit("1mm"));
	let height = Math.round(((activeObject.height+activeObject.strokeWidth) * activeObject.scaleY)/ fabric.util.parseUnit("1mm"));
	
	$('[data-sign-optiontype="shape-sett"][data-sign-option="width"]').val(width);
	$('[data-sign-optiontype="shape-sett"][data-sign-option="height"]').val(height);
}


function updateGUI() {
	var dom = "<ul>";

	var objects = canvas.getObjects();

	for (var o in objects) {
		if (objects[o].name != null) continue;

		var obj = objects[o];

		dom += "<li>";
		if (obj.type == "i-text") {
			dom += "Text: " + obj.text;
		} else if (obj.type == "image") {
			dom += "Bild";
		}
		dom +=
			'<button class="layer-action" data-action="remove" data-objid="' +
			o +
			'">Ta bort</button>';
		dom += "</li>";
	}

	dom += "</ul>";

	$(".layers").html(dom);
}


function updateBorder() {
	$(".thicc-input").val(handleRealDim.getDim('border'));
	$(".thicc-slider").val(handleRealDim.getDim('border'));
}

function setSurfaceType(option) {
    handleRealDim.updateDim('surface',option);
	updateSurfaceButtons();
	toolMenu.debouncedUpdatePrice();
}

function updateSurfaceButtons() {
	$(".surface").removeClass("active");
	$(".surface-" + handleRealDim.getDim('surface')).addClass("active");
}



class Mounting {
    constructor(canvas, renderCanvas, currentSettings, signOperation) {
        this.canvas = canvas;
        this.foregroundCanvas = $.canvasFgManager.getCanvas();
        this.renderCanvas = renderCanvas;
        this.currentSettings = currentSettings;
        this.backgroundShape;
        this.backgroundInnerShape;
        this.bgGroupSpecial;
        this.bgGroupSpecialProduction;
        this.signOperation = signOperation;
        this.mountings = [];
        this.realDimensions;

        //first render check will be no mounting 
        this.firstRenderCheck = true;

        this.mountingLayout;

        this.objarray = [];
        this.amount = 0;
        this.special;

        //margins
        this.left
        this.right
        this.top
        this.bottom

        //auto distance
        this.autoDistance;
        //used for guides when using radius
        this.marginMounts = { x: 0, y: 0 }
    }

    async updateMounting() {
        //#1 check if it sign suppose to have no mouning but has mounting 
        this.currentSettings = updateProdSettings.getCurrentSetting();

        if (!this.currentSettings.mount_image) {
            if (this.mountings.length > 0) {
                this.removeMountingHoles();
                handleRealDim.updateDim('mountingLayout', false);
            }
        } else {
            this.realDimensions = handleRealDim.getAllDim();
            ///If it has mounting check if it has the correct amount
            let layout = this.mountingLayoutInstructions();

            if (!layout) {
                if (Object.keys(this.mountings).length > 0) {
                    this.removeMountingHoles();
                }
                handleRealDim.updateDim('mountingLayout', layout);
                return;
                //EXIST NO HOLES
            }

            this.mountingLayout = JSON.parse(JSON.stringify(layout));
            this.amount = this.mountingLayout.amount;

            if (JSON.stringify(this.mountingLayout) !== JSON.stringify(handleRealDim.getDim('mountingLayout'))) {
                handleRealDim.updateDim('mountingLayout', JSON.parse(JSON.stringify(this.mountingLayout)));
                //If the layout is not the same update accordingly
                if (handleRealDim.getDim('mountingLayout').amount == 0) {
                    this.removeMountingHoles();
                } else {
                    await this.addMountingHoles();
                }
            } else {
                //if the moutning layout is correct its all about recalculating hole placement
                if (this.firstRenderCheck) {
                    let canvasAmount = 0;
                    //check if the canvas has the mounts 
                    let activeMounts = this.foregroundCanvas.getObjects();

                    $.each(activeMounts, (index, obj) => {
                        if (obj.hasOwnProperty('name') && obj.name.includes('mountinghole')) {
                            canvasAmount += 1;
                        }
                    })

                    if (this.mountingLayout.amount !== canvasAmount || canvasAmount === 0) {
                        await this.addMountingHoles();
                    }
                    this.firstRenderCheck = false;
                } else {
                    await this.calculateHolePlacement();
                }
            }
        }

        this.foregroundCanvas.requestRenderAll();
    }

    async addMountingHoles() {

        //First remove all mountingholes 
        await this.removeMountingHoles();
        return new Promise(async (resolveTop, reject) => {
            //look if image is used
            if (this.currentSettings.mount_image != false) {
                this.special = 'special';
            } else {
                this.special = false;
            }

            let newMountAmount = (this.amount) ? this.amount : 0;
            handleRealDim.updateDim("mount_amount", newMountAmount);

            //adds mounting
            if (this.mountingLayout.amount) {
                await this.renderMounting();
                await this.calculateHolePlacement();
            }

            resolveTop('done');
        });
    }

    async removeMountingHoles() {
        return new Promise((resolve, reject) => {
            amtMountingHoles = 0;
            let activeCanvas = this.foregroundCanvas;
            let objects = activeCanvas.getObjects();

            for (let i = objects.length - 1; i >= 0; i--) {
                if (objects[i].name == null) continue;

                if (objects[i].name.startsWith("mountinghole")) {
                    activeCanvas.remove(objects[i]);
                }
            }
            handleRealDim.updateDim("mount_amount", 0);
            this.mountings = [];
            $.canvasFgManager.updateDim();
            resolve();
        });
    }

    async renderMounting() {
        this.mountCache = false;
        return new Promise(async (resolve) => {
            for (var i = 0; i < this.amount; i++) {
                let name = "mountinghole" + i;
                await this.mountGenerator(name);
            }
            resolve();
        });
    }

    async mountGenerator(name) {
        let activeCanvas = this.foregroundCanvas;

        // Load the SVG only once and store it in 'svgObjectCache'
         if (this.special == 'special' && !this.mountCache) {
            this.mountCache = await new Promise(
               (resolve, reject) => {
                  fabric.loadSVGFromURL(
                     `${my_base_params.image_content}/${this.currentSettings.mount_image}`,
                     (objects, options) => {
                        resolve(fabric.util.groupSVGElements(objects, options));
                     },
                     null,
                     {crossOrigin: 'anonymous'}
                  );
               }
            );
        }


        return new Promise((resolve, reject) => {
            if (this.special == 'special') {
                // Clone the cached SVG object
                this.mountCache.clone((cloned) => {
                    cloned.set({ selectable: false, signstudio_standard: true, name: name });
                    let w = this.currentSettings.mount_image_width;
                    let h = this.currentSettings.mount_image_height;
                    handleRealDim.updateDim('mountdim', { w: w, h: h });
                    handleRealDim.updateDim('mountImage', this.currentSettings.mount_image);
                    cloned.scaleToWidth(fabric.util.parseUnit(`${w}mm`))
                        .scaleToHeight(fabric.util.parseUnit(`${h}mm`));
                    activeCanvas.add(cloned);
                    this.mountings.push(cloned);
                    resolve(cloned);
                });
            } else {
                let mount = new fabric.Rect({
                    width: fabric.util.parseUnit("3mm"),
                    height: fabric.util.parseUnit("3mm"),
                    top: 0,
                    fill: "white",
                    stroke: "black",
                    rx: fabric.util.parseUnit("3mm"),
                    ry: fabric.util.parseUnit("3mm"),
                    strokeWidth: fabric.util.parseUnit("0.5mm"),
                    left: 0,
                    selectable: false,
                    signstudio_standard: true,
                    signstudio_image: currentSettings.mount_image,
                    name: name
                });
                activeCanvas.add(mount);
                this.mountings.push(mount);
                resolve(mount);
            }

        });
    }

    mountingLayoutInstructions() {
        if (this.currentSettings.mount_layout) {
            let instructions = "";
            let send;
            let settings = JSON.parse(JSON.stringify(this.currentSettings.mount_layout));

            // check first if 0 0 exist which means it could be only one that should count for all
           let use = "";
           // checks if more exists
           let lowestWidth = 0;
           let lowestHight = 0;

            if (settings['0x0']) {
                instructions = settings['0x0'];
                use = '0x0';
            }

            for (let property of Object.keys(settings)) {
                let test = property.split('x');
                let w = parseInt(test[0]);
                let h = parseInt(test[1]);
                if (handleRealDim.getDim("width") <= w && handleRealDim.getDim("height") <= h) {
                    if (lowestWidth == 0 && lowestHight == 0) {
                        lowestWidth = w;
                        lowestHight = h;
                        use = property;
                    } else if (w < lowestWidth || h < lowestHight) {
                        w = lowestWidth;
                        h = lowestHight;
                        use = property;
                    }
                }
            }
            //if found value apply amount 
            let amount = 0;
            if (use) {
                send = JSON.parse(JSON.stringify(settings[use]));
                //check if automatic placement
                if (send['auto-amnt']) {
                    let corners = [];
                    let hasSet = [];
                    let hasDeg = [];
                    let interval = fabric.util.parseUnit(send['auto-amnt']);

                    for (let i = 1; i < 9; i++) {
                        if (send[i]) {
                            if ([1, 3, 6, 8].includes(i)) {
                                amount++;
                                corners.push(i)
                            } else {
                                let tempAmnt = send[i].amount;
                                if (tempAmnt) {
                                    hasSet.push(i)
                                    amount += parseInt(tempAmnt)
                                } else {
                                    hasDeg.push(i);
                                }
                            }
                        }
                    }
                    //get amount width and Height 
                    let amntWidth = Math.floor(handleRealDim.getDim("width") / interval);
                    let amntHeight = Math.floor(handleRealDim.getDim("height") / interval);

                    //check to added quantity
                    for (let y = 3; y < 7; y += 3) {
                        //left top left
                        let uneven = (y == 3) ? 2 : 4;
                        if (!(hasSet.includes(uneven)) && corners.includes(y) && corners.includes(1)) {
                            if (hasDeg.includes(uneven)) {
                                send[uneven].amount = ((uneven) == 2) ? amntWidth : amntHeight;
                            } else {
                                send[uneven] = { amount: ((uneven) == 2) ? amntWidth : amntHeight }
                            }

                            (send[uneven].amount == 0) ? delete send[uneven] : amount += send[uneven].amount;

                        }
                        //right bottom
                        let uneven2 = (y == 3) ? 5 : 7;
                        if (!(hasSet.includes(uneven2)) && corners.includes(y) && corners.includes(8)) {
                            if (hasDeg.includes(uneven2)) {
                                send[uneven2].amount = (uneven2 == 7) ? amntWidth : amntHeight;
                            } else {
                                send[uneven2] = { amount: (uneven2 == 7) ? amntWidth : amntHeight }
                            }

                            (send[uneven2].amount == 0) ? delete send[uneven2] : amount += send[uneven2].amount;
                        }
                    }

                    send.amount = amount;
                }
            }
            return send;
        } else {
            return false;
        }
    }

    async calculateHolePlacement() {
        if (!this.mountingLayout) return;

        let instructions = JSON.parse(JSON.stringify(this.mountingLayout));
        //if no mounting removes the mountingholes
        if (!instructions) {
            this.removeMountingHoles();
        } else {
            //sets correct bg 
            this.backgroundShape = this.signOperation.getBackgroundShape();
            this.backgroundInnerShape = this.signOperation.getbackgroundInnerShape();

            //operation for changing or adding mounting holes
            let checkImageAndSize = false;
            let checkAmount = false;
            //check if it remains the same or if gonna update amount of holes or image
            let checkPlacement = !(
                JSON.stringify(handleRealDim.getDim('mountingLayout')) === JSON.stringify(instructions)) &&
                handleRealDim.getDim('mountingLayout') === 'object' &&
                Object.keys(handleRealDim.getDim('mountingLayout')).length > 0;

            if ('mountdim' in this.realDimensions) {
                checkImageAndSize = this.realDimensions.mountdim.w != currentSettings.mount_image_width || this.realDimensions.mountdim.h != currentSettings.mount_image_height;
            }
            if ('mount_amount' in realDimensions) {
                checkAmount = this.realDimensions.mount_amount != instructions.amount;
            }

           delete instructions['amount'];
            delete instructions['width'];
            delete instructions['height'];

            //gets margins
            (instructions.ml) ? this.left = fabric.util.parseUnit(instructions.ml + "mm") : this.left = 0;
            (instructions.mr) ? this.right = fabric.util.parseUnit(instructions.mr + "mm") : this.right = 0;
            (instructions.mt) ? this.top = fabric.util.parseUnit(instructions.mt + "mm") : this.top = 0;
            (instructions.mb) ? this.bottom = fabric.util.parseUnit(instructions.mb + "mm") : this.bottom = 0;

            (instructions['auto-amnt']) ? this.autoDistance = fabric.util.parseUnit(instructions['auto-amnt'] + "mm") : this.autoDistance = false;

            delete instructions['mb'];
            delete instructions['mr'];
            delete instructions['ml'];
            delete instructions['mt'];
            delete instructions['auto-amnt'];

            //used for guides when using radius
            this.marginMounts = { x: 0, y: 0 }

            /* POSTIONS 
            1  2  3
            4     5
            6  7  8 
                */

                let index = 0;
            for (let inst in instructions) {
                //position
                let item = instructions[inst];
                let amount
                let deg

                (item.amount) ? amount = parseInt(item.amount) : amount = 1;
                (item.deg) ? deg = parseInt(item.deg) : deg = 0;

                switch (parseInt(inst)) {
                    case 1:
                        //left top
                        this.positionHoles(index, 'left', 'top', amount, deg);
                        break;
                    case 2:
                        //middle top 
                        this.positionHoles(index, 'middle', 'top', amount, deg);
                        break;
                    case 3:
                        //right top
                        this.positionHoles(index, 'right', 'top', amount, deg);
                        break;
                    case 4:
                        //left middle
                        this.positionHoles(index, 'left', 'middle', amount, deg);
                        break;
                    case 5:
                        // right middle
                        this.positionHoles(index, 'right', 'middle', amount, deg);
                        break;
                    case 6:
                        //bottom left
                        this.positionHoles(index, 'left', 'bottom', amount, deg);
                        break;
                    case 7:
                        // bottom middle
                        this.positionHoles(index, 'middle', 'bottom', amount, deg);
                        break;
                    case 8:
                        //bottom right
                        this.positionHoles(index, 'right', 'bottom', amount, deg);
                        break;
                }
                //rotation
                index += amount;
            }
            //move mounts to correct postion if has radius
            if (this.backgroundShape.rx > 0) {
                this.renderCanvas.width = this.canvas.width;
                this.renderCanvas.height = this.canvas.height;
                let renderCtx = this.renderCanvas.getContext('2d', { willReadFrequently: true });
                let centerBG = this.backgroundShape.getCenterPoint(true, true);
                let gWidth = this.backgroundShape.width - this.left - this.right - this.marginMounts.x * 2;
                let gHeight = this.backgroundShape.height - this.top - this.bottom - this.marginMounts.y * 2;
                let rx = this.backgroundShape.rx;
                let ry = this.backgroundShape.ry;

                let gRxL
                let gRxY
                let gRyT
                let gRyB

                if (gWidth < this.backgroundShape.width) {
                    gRxL = (rx - this.left > 0) ? rx - this.left : 0;
                    gRxY = (rx - this.right > 0) ? rx - this.right : 0;
                } else {
                    gRxL = rx + this.left;
                    gRxY = rx + this.right;
                }

                if (gHeight < this.backgroundShape.height) {
                    gRyT = (ry - this.top > 0) ? ry - this.top : 0;
                    gRyB = (ry - this.bottom > 0) ? ry - this.bottom : 0;
                } else {
                    gRyT = ry + this.top;
                    gRyB = ry + this.bottom;
                }
                this.drawMountingPath(renderCtx,
                    this.backgroundShape.left + this.left + this.marginMounts.x,
                    this.backgroundShape.top + this.top + this.marginMounts.y,
                    gWidth,
                    gHeight,
                    gRxL,
                    gRxY,
                    gRyT,
                    gRyB);


                for (let property in this.mountings) {
                    let tempObj = this.mountings[property];
                    let centerMount = tempObj.getCenterPoint(true, true);
                    let newCoords = this.mountOnPath(renderCtx, centerMount, centerBG);
                    if (newCoords) {
                        tempObj.setPositionByOrigin(newCoords, 'center', 'center');
                    }

                }
            }
        }
        $.canvasFgManager.updateDim(); //recalculate foreground sizing 
    }

    positionHoles(currObj, x, y, amount, rotation) {
        let xSpread;
        let ySpread;

        if (x == 'middle') {
            let tempObj = this.mountings[currObj];
            xSpread = (this.backgroundShape.width -
                this.left - this.right - ((tempObj.width * tempObj.scaleX))) / (1 + amount);
        } else if (y == 'middle') {
            let tempObj = this.mountings[currObj];
            ySpread = (this.backgroundShape.height -
                this.top - this.bottom -
                (tempObj.height * tempObj.scaleY)) / (1 + amount);
        }

        for (let i = 0; i < amount; i++) {
            //position & rotate, rotate is first due to gettings correct bounding box height
            if (rotation != 0) {
                this.rotate(this.mountings[currObj + i], rotation);
            }
            this.positionHori(this.mountings[currObj + i], x, xSpread, i);
            this.postionVert(this.mountings[currObj + i], y, ySpread, i);
        }
    }

    positionHori(obj, position, spread, order) {
        let xCoord = 0;
        let yCoord = obj.getCenterPoint().y;
        let objWidth = obj.width * obj.scaleX;
        let disToCenMnt = objWidth / 2;
        if (position == 'left' || position == 'right') {
            if (position == 'left') {
                xCoord = this.backgroundShape.left + this.left + disToCenMnt;
            } else {
                xCoord = this.backgroundShape.left +
                    this.backgroundShape.width -
                    this.right -
                    disToCenMnt;
            }
        } else {
            //span in between
            xCoord = this.backgroundShape.left + spread * (order + 1) + this.left + disToCenMnt;

        }
        let Coords = { "x": xCoord, "y": yCoord }
        obj.setPositionByOrigin(Coords, 'center', 'center');
        this.marginMounts.x = disToCenMnt;
    }

    postionVert(obj, position, spread, order) {
        let yCoord = 0;
        let xCoord = obj.getCenterPoint().x;
        let objHeight = obj.height * obj.scaleY;
        let disToCenMnt = objHeight / 2;

        if (position == 'top' || position == 'bottom') {
            if (position == 'top') {
                yCoord = this.backgroundShape.top + this.top + disToCenMnt;
            } else {
                yCoord = this.backgroundShape.top +
                    this.backgroundShape.height -
                    this.bottom -
                    disToCenMnt;
            }
        } else {
            yCoord = this.backgroundShape.top + spread * (order + 1) + this.top + disToCenMnt;
        }
        let Coords = { "x": xCoord, "y": yCoord };
        obj.setPositionByOrigin(Coords, 'center', 'center');
        this.marginMounts.y = disToCenMnt;
    }

    rotate(obj, deg) {
        obj.set('angle', deg);
    }

    drawMountingPath(ctx, x, y, width, height, rxL, rxR, ryT, ryB) {
        ctx.beginPath();
        ctx.moveTo(x + rxL, y);
        ctx.arcTo(x + width, y, x + width, y + ryT, rxR);
        ctx.arcTo(x + width, y + height, x + width - rxR, y + height, ryB);
        ctx.arcTo(x, y + height, x, y + height - ryB, rxL);
        ctx.arcTo(x, y, x + rxL, y, ryT);
        ctx.closePath();
        return ctx;
    }

    mountOnPath(ctx2, point, bgCenter) {
        let x = point.x;
        let y = point.y;

        if (!(ctx2.isPointInPath(x, y))) {

            if (bgCenter.x > point.x) {
                for (let i = point.x; i < bgCenter.x; i += 0.1) {
                    let y = this.getY(i, point, bgCenter);
                    if (ctx2.isPointInPath(i, y)) {
                        return { x: i, y: y }
                    }
                }
                return false;
            } else if (bgCenter.x < point.x) {
                for (let i = point.x; i > bgCenter.x; i -= 0.1) {
                    let y = this.getY(i, point, bgCenter);
                    if (ctx2.isPointInPath(i, y)) {
                        return { x: i, y: y }
                    }
                }
            } else {
            }
        }
        return false;
    }

    getY(x, point1, point2) {
        let x1 = point1.x;
        let y1 = point1.y;
        let x2 = point2.x;
        let y2 = point2.y;

        let m = (y2 - y1) / (x2 - x1);
        let b = y1 - m * x1;

        return m * x + b;
    }

}
   let mountingManager: Mounting;

    function calculateCanvasDimensions(canvas, clean = false) {
        let backgroundShape = signOperation.getBackgroundShape();
        if (!backgroundShape) return;

        let rect = backgroundShape.getBoundingRect(true, true);
        let lefts = [rect.left];
        let rights = [rect.left + rect.width];
        let tops = [rect.top];
        let bottoms = [rect.top + rect.height];

        if (canvas) {
            let objects = canvas.getObjects();
            if (objects.length) {
                objects.forEach((obj) => {
                    if (clean && obj?.excludeFromExport && obj.excludeFromExport === true) {
                        return;
                    }

                    let boundingRect = obj.getBoundingRect(true, true);
                    lefts.push(boundingRect.left);
                    rights.push(boundingRect.left + boundingRect.width);
                    tops.push(boundingRect.top);
                    bottoms.push(boundingRect.top + boundingRect.height);
                });
            }
        }

        const coords = {
            left: Math.min(...lefts),
            top: Math.min(...tops),
            right: Math.max(...rights),
            bottom: Math.max(...bottoms),
        };

        const width = coords.right - coords.left;
        const height = coords.bottom - coords.top;

        return { ...coords, width: width, height: height };
    }


    class ManageBackgroundCanvas {
        constructor(backgroundCanvas, canvas) {
            this.backgroundCanvas = backgroundCanvas;
            this.canvas = canvas;
            this.dim = false;
            this.bgs = false;
            this.hashedBGs = false;
        }

        getCanvas() {
            return this.backgroundCanvas;
        }

        getDim(getClean = false) {
            if (!this.dim || getClean) {
                return this.updateDim(getClean);
            }
            return this.dim;
        }

        updateDim(clean = false) {
            const newCoords = calculateCanvasDimensions(this.backgroundCanvas, clean);

            if (clean) {
                return newCoords;
            } else {
                this.dim = newCoords;
            }

            return this.dim;
        }

        setBgs(objects) {
            this.bgs = objects;
        }

        updateBackground() {
            let objects = this.bgs;
            let newHash = this.hashObjectArray(objects);
            //if no images to add and no images on canvas do nothing
            if (newHash === this.hashedBGs) {
                return;
            } else {
                if (objects.length === 0) {
                    this.removeBackgrund();
                    this.updateDim();
                    this.hashedBGs = newHash;
                    //            onCanvasGUIHandler.addCanvasShadow();
                    //           onCanvasGUIHandler.centerCanvasView();
                    signOperation.requestCenterCanvas();
                } else {
                    this.removeBackgrund();
                    let updated = this.addBackground(objects[0]);
                    if (updated) {
                        onCanvasGUIHandler.removeCanvasShadow();
                        this.hashedBGs = newHash;
                    }
                }
            }
        }

        hashObjectArray(objArray) {
            // Simple hashing function - for illustration purposes
            // Convert the entire array to a JSON string first
            return JSON.stringify(objArray).split('').reduce((prev, curr) => ((prev << 5) - prev) + curr.charCodeAt(0), 0);
        }

        removeBackgrund() {
            let objects = this.backgroundCanvas.getObjects();
            for (let i = 0; i < objects.length; i++) {
                let curr = objects[i];
                if (curr.name === 'product-bg') this.backgroundCanvas.remove(curr);
            }
            this.backgroundCanvas.requestRenderAll();
        }

        addBackground(item) {
            let backgroundShape = signOperation.getBackgroundShape();
            if (!backgroundShape) {
                return false;
            }
            let url = my_base_params.image_content + "/" + item.image;
            let width = fabric.util.parseUnit(item.width + 'mm');
            let height = fabric.util.parseUnit(item.height + 'mm');
            let mt = fabric.util.parseUnit(item.mt + 'mm');
            let mr = fabric.util.parseUnit(item.mr + 'mm');
            let mb = fabric.util.parseUnit(item.mb + 'mm');
            let ml = fabric.util.parseUnit(item.ml + 'mm');

            let bgWidth = backgroundShape.getScaledWidth();
            let bgHeight = backgroundShape.getScaledHeight();

            let left = (bgWidth - width) / 2; // Default to center if margins are 0
            let top = (bgHeight - height) / 2; // Default to center if margins are 0

            // Adjust for margins
            if (mt !== 0) top = mt;
            if (mb !== 0) top = 0 - mb;
            if (ml !== 0) left = ml;
            if (mr !== 0) left = 0 - mr;

            fabric.Image.fromURL(url, (img) => {
                // Set position and scale
                img.set({
                    left: parseInt(left),
                    top: parseInt(top),
                    originX: 'left',
                    originY: 'top',
                    name: 'product-bg',
                });

                img.scaleToWidth(width);
                img.scaleToHeight(height);

                // Additional settings (if needed)
                // Add to canvas
                this.backgroundCanvas.add(img);
                this.updateDim();
                //onCanvasGUIHandler.centerCanvasView();
                //
                signOperation.requestCenterCanvas();
            }, {
                    crossOrigin: 'anonymous' // If the image is from a different domain
                });
            return true;
        }

        test() {
            let width = fabric.util.parseUnit(1300 + "mm");
            let height = fabric.util.parseUnit(1300 + "mm");
            let backgroundShape = signOperation.getBackgroundShape();
            let bgWidth = backgroundShape.getScaledWidth();
            let bgHeight = backgroundShape.getScaledHeight();
            let left = (bgWidth - width);
            let top = (bgHeight - height);
            let rect = new fabric.Rect({
                width: width,
                height: height,
                left: left,
                top: top,
                fill: '#fff',
                stroke: '#000',
                strokeWidth: 20
            });
            this.backgroundCanvas.add(rect);
            this.updateDim();
        }
    }


   class ManageForegroundCanvas {
      constructor(foregroundCanvas, canvas) {
         this.foregroundCanvas = foregroundCanvas;
         this.canvas = canvas;
         this.dim = false;
      }

      getCanvas() {
         return this.foregroundCanvas;
      }

      getDim(getClean = false) {
         if (!this.dim || getClean) {
            return this.updateDim(getClean);
         }
         return this.dim;
      }

        updateDim(clean = false) {
            const newCoords = calculateCanvasDimensions(this.backgroundCanvas, clean);

            if (clean) {
                return newCoords;
            } else {
                this.dim = newCoords;
            }

            return this.dim;
      }
   }

    class FabricHistory {
        constructor() {
            this.db;

            this.init();
        }

        init() {
            const DBOpenRequest = window.indexedDB.open('FabricHistory', 1);

            DBOpenRequest.onerror = (event) => {
            };

            DBOpenRequest.onsuccess = (event) => {

                // Store the result of opening the database in the db variable. This is used a lot below
                this.db = DBOpenRequest.result;
                // Run the displayData() function to populate the task list with all the to-do list data already in the IndexedDB
            };
        }

        async getStoredSession(){
            const session = this.sessionPersistence.restoreSession();

            if (session) {
                return await this.restoreFromSession(session);
            }

            return false;
        }

        async Save(dim, json, side = 1) {
            // Save full project to IndexedDB

            await this.storage.saveProject({
                dimensions: this.dimensions,
                canvas: this.canvas.toJSON(saveCustomProperties),
                history: Array.from(this.memoryCache.values()),
                lastModified: Date.now()
            });

            // Update session in localStorage
            this.sessionPersistence.saveCurrentState(
                this.dimensions,
                this.canvas.toJSON(saveCustomProperties)
            );
        }


        async clearHistory() {
            // Keep only last 100 entries in IndexedDB
            await this.storage.cleanupOldEntries(100);
        }

        async cleanupOldHistory() {
            // Keep only last 100 entries in IndexedDB
            await this.storage.cleanupOldEntries(100);
        }
    }

class ManageFonts{

   constructor(fontSettings, fontUrl){
      this.loadedFonts = {};
      this.fontSettings = fontSettings;
      this.fontUrl = fontUrl;
   }

     async loadFont(fontFamily) {
        if (!Array.isArray(fontFamily)) fontFamily = [fontFamily];
        // Use Promise.all to wait for all fonts to load
        return Promise.all(fontFamily.map(fontSettings => this.loadSingleFont(fontSettings)));
    }

    async loadNewFont(family, args = { preferredWeight: '400', preferredStyle: 'normal' }) {
        if (!this.fontSettings[family]) {
            throw new Error(`Font family ${family} is not available.`);
        }

        let fontSettings = this.findClosestFontSettings(family, args);
        if (!fontSettings) {
            throw new Error(`No suitable font settings found for ${family}.`);
        }

        try {
            await this.loadSingleFont(fontSettings);
            return fontSettings;
        } catch (error) {
            console.error(`Failed to load font: ${fontSettings.family} ${fontSettings.weight} ${fontSettings.style}`, error);
            throw error;
        }
    }

        findClosestFontSettings(family, args) {
        const { preferredWeight, preferredStyle } = args;
        let fontObject = this.fontSettings[family];

        // First, try to match the preferred weight and style exactly
        if (fontObject[preferredWeight] && fontObject[preferredWeight][preferredStyle]) {
            return { family, weight: preferredWeight, style: preferredStyle };
        }

        // If not found, try any style with the preferred weight
        for (let weight in fontObject) {
            if (fontObject[weight][preferredStyle]) {
                return { family, weight, style: preferredStyle };
            }
        }

        // Lastly, return any available weight and style
        for (let weight in fontObject) {
            for (let style in fontObject[weight]) {
                return { family, weight, style };
            }
        }

        return null; // No match found
    }

    async loadSingleFont(fontOptions) {
        if(fontOptions.weight == 'normal') fontOptions.weight = 400;
        if(fontOptions.weight == 'bold') fontOptions.weight = 700;
        fontOptions.style = fontOptions.style.charAt(0).toUpperCase() + fontOptions.style.slice(1);

        const { family, weight, style } = fontOptions;

        // Check if the font has already been loaded
        if (this.isFontLoaded(family, weight, style)) {
            return;
        }

        let args = { preferredWeight: weight, preferredStyle: style}; 
        fontOptions = this.findClosestFontSettings(family, args);
        
        // Construct the FontFace and load it
        let url = this.getFontPath(fontOptions);
        fabric.util.clearFabricFontCache(family);
        const fontFace = new FontFace(family, `url(${url})`, { weight: weight, style: style });
        document.fonts.add(fontFace);

        try {
            await fontFace.load();
            this.markFontAsLoaded(family, weight, style);
        } catch (error) {
            console.error(`Failed to load font: ${family} ${weight} ${style}`, error);
            this.markFontAsLoaded(family, weight, style);
            throw error; // rethrow to be caught by the caller if needed
        }
    }

   isFontLoaded(family, weight, style) {
        return !!this.loadedFonts[family]?.[weight]?.[style];
   }

   markFontAsLoaded(family, weight, style) {
      if (!this.loadedFonts[family]) this.loadedFonts[family] = {};
      if (!this.loadedFonts[family][weight]) this.loadedFonts[family][weight] = {};
      this.loadedFonts[family][weight][style] = true;
   }

   getFontPath(fontOptions){
      const { family, weight, style } = fontOptions;

      this.markFontAsLoaded(family, weight, style);
      let url =this.fontUrl + this.fontSettings[family][weight][style]; 
      return encodeURI(url);
   }

   getCurrentFontSettings(fontFamily){
     return this.fontSettings[fontFamily];
   }
    checkForSingleStyle(family, weight){
        if(this.fontSettings[family]){
            if(this.fontSettings[family][weight]){
                let fontStyles = [];
                for(let style in this.fontSettings[family][weight]){
                    fontStyles.push(style);
                }
                if(fontStyles.length == 1){
                    return fontStyles[0];
                }
            }
        }

        return false;
    }
}


   class PreviewWindow {
	  fancyBox = null;
	  content = "<div class='spinner'></div>";

	  setContent(content) {
		 this.content = content;
	  }

      resetSettings(){
         this.content = "<div class='spinner'></div>";
      }

      init() {
         // Create an empty container with loading spinner
         this.fancyBox = new Fancybox([{
            src: `<div id='fancybox-dynamic-content' class='loading studio-full'><div id='stdn-preview-container'>${this.content}</div></div></div>`,
            type: "html",
         }], {
               backdropClick: false,
               dragToClose: false,
               contentClick: false,
               closeButton: "top",
               on: {
                  done: (fancybox) => {
                     // Add class to container after init
                     if (fancybox.container) {
                        fancybox.container.classList.add('fullscreen-fancybox');
                     }
                  },
                  destroy: () => {
                     // Clean up when Fancybox is closed
                     this.fancyBox = null;
                  }
               }
            });

         return this.fancyBox;
      }

	  setImage(imageUrl) {
		 // If Fancybox isn't open yet, open it
		 if (!this.fancyBox) {
			this.init();
		 }

		 // Create image element
		 const img = new Image();
		 img.className = 'preview-image';

		 // Get the container
		 const container = document.getElementById('stdn-preview-container');
		 if (container) {
			// Show loading spinner while image loads
			container.innerHTML = "<div class='spinner'></div>";

			// When image loads, replace spinner with image
			img.onload = () => {
			   container.innerHTML = '';
			   container.appendChild(img);
			};

			// Handle load errors
			img.onerror = () => {
			   container.innerHTML = '<p>Error loading image</p>';
			};

			// Start loading the image
			img.src = imageUrl;
		 }
	  }

   }

    class ManageToastMessages {
        constructor(toastContainer = null) {
            this.toastContainer = toastContainer;
            this.animationDuration = 300;
        }

        getToastContainer() {
            if (!this.toastContainer) {
                this.toastContainer = document.getElementById('toast-message-area');
            }
            return this.toastContainer;
        }

        getMessages(id: number | null = null, type: string | null = null) : HTMLElement[]{
            const toastContainer = this.getToastContainer();

            let selector = '.stdn-form-message';

            if (id && type) {
                selector += `[data-id='${id}'][data-msg-type='${type}']`;
            } else if (id) {
                selector += `[data-id='${id}']`;
            } else if (type) {
                selector += `[data-msg-type='${type}']`;
            }

            return Array.from(toastContainer.querySelectorAll(selector));
        }

        createMessage(type, message, icon, id = false) {
            const messageElement = document.createElement('div');
            messageElement.className = `stdn-form-message stdn-form-message-${type}`;

            if (id) {
                messageElement.setAttribute('data-id', id);
            }
            if (type) {
                messageElement.setAttribute('data-msg-type', type);
            }

            messageElement.innerHTML = `
<div class="stdn-form-message-icon">${icon}</div>
<div class="stdn-form-message-text">${message}</div>
`;

            const messageBox = this.getToastContainer();
            messageBox.appendChild(messageElement);

            // Add click handler for manual removal
            messageElement.addEventListener('click', () => {
                this.autoRemove(messageElement, 0);
            });

            // Trigger animation
            setTimeout(() => messageElement.classList.add('stdn-show'), 10);

            return messageElement;
        }

        loading(message = 'Jobbar på det') {
            const loadingIcon = '<div class="stdn-loading-spinner"></div>';
            return this.createMessage('loading', message, loadingIcon);
        }

        error(message, id = false) {
            const errorIcon = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#f44336" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="15" y1="9" x2="9" y2="15"></line>
<line x1="9" y1="9" x2="15" y2="15"></line>
</svg>`;

            const messageElement = this.createMessage('error', message, errorIcon, id);

            // Shake animation for errors
            messageElement.classList.add('stdn-shake');
            messageElement.addEventListener('animationend', () => {
                messageElement.classList.remove('stdn-shake');
            }, { once: true });

            return messageElement;
        }

        success(message, removeAfter = 5000, id: string | null = null) {
            const successIcon = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#4caf50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
<polyline points="22 4 12 14.01 9 11.01"></polyline>
</svg>`;

            const messageElement = this.createMessage('success', message, successIcon, id);
            const messageBox = this.getToastContainer();

            // Auto-remove after the specified time
            setTimeout(() => {
                messageElement.classList.remove('stdn-show');

                // Wait for fade-out animation to complete before removing from DOM
                setTimeout(() => {
                    messageElement.remove();
                    //messageBox.innerHTML = ''; // Clear all messages
                }, this.animationDuration);
            }, removeAfter);

            return messageElement;
        }

        message(message) {
            const infoIcon = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#9e9e9e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="16" x2="12" y2="12"></line>
<line x1="12" y1="8" x2="12.01" y2="8"></line>
</svg>`;

            return this.createMessage('info', message, infoIcon);
        }

        clear(instant = false) {
            const messageBox = this.getToastContainer();

            if(instant){
                messageBox.innerHTML = '';
                return;
            }

            const messages = messageBox.querySelectorAll('.stdn-form-message');

            messages.forEach(message => {
                message.classList.remove('stdn-show');
            });

            setTimeout(() => {
                messageBox.innerHTML = '';
            }, time);
        }

        autoRemove(messageElement, timeout = 5000) {
            setTimeout(() => {
                messageElement.classList.remove('stdn-show');
                setTimeout(() => {
                    if (messageElement.parentNode) {
                        messageElement.remove();
                    }
                }, this.animationDuration);
            }, timeout);
        }
    } 

        let toastManager: ManageToastMessages = new ManageToastMessages();

    class ManageOrientation{

        getCurrentOrientation(){
            let width = parseInt(handleRealDim.getDim('width'));
            let height = parseInt(handleRealDim.getDim('height'));

            if(width > height){
                //original orientation laying
                return 'laying';
            }

            return 'standing';
        }
        
        requireRotation(){
            let width = parseInt(handleRealDim.getDim('width'));
            let height = parseInt(handleRealDim.getDim('height'));

            let maxWidht = parseInt(currentSettings['max_width'])
            let maxHeight = parseInt(currentSettings['max_height'])
            
            let rotated = this.shiftStatus();
            
            if(!rotated && (width > maxHeight || height > maxWidht)){
                return true;
            }
            
            return false;
        }


        setOrientationSelection(orientation = false){
            if(!orientation){
                handleRealDim.updateDim('rotated', 0);
                return;
            }

            const currentOrientation = this.getCurrentOrientation();
            const requireRotation = this.requireRotation();

            if(requireRotation){
                handleRealDim.updateDim('rotated', true);
            }else{
                handleRealDim.updateDim('rotated', false);
            }
            

            let newHeight = parseInt(handleRealDim.getDim('width'));
            let newWidth = parseInt(handleRealDim.getDim('height'));
        

            handleRealDim.updateHeight(newHeight);
            handleRealDim.updateWidth(newWidth);

            masterSync({'updateBackgroundShape' : ''}, true);
        }

        renderSelection(){
            const currentOrientation = this.getCurrentOrientation();

            let target = (currentOrientation == 'laying') ? 'laying' : 'standing';
            
            $('.sign-option[data-sign-optiontype="orientation"]').each(function(){
                let elem = $(this);
                let orientation = elem.attr('data-sign-option');

                if(target == orientation){
                    elem.addClass('active');
                }else{
                    elem.removeClass('active');
                }
            });
        }
        
        turnOfOrientation(){
            handleRealDim.updateDim('rotated', false);
        }

        shiftStatus(){
            //return orientation status
            let rotated = handleRealDim.getDim('rotated');

            if(!rotated){
                return false;
            }

            if(rotated == 1 || rotated ) return true;

            return false;
        }
    }

   class ManageColors{
      private canvas:fabric.Canvas;

      constructor(canvas: fabric.Canvas | null = null){
         if(canvas){
            this.canvas = canvas;
         }
      }

      public getActiveColors(canvas: fabric.Canvas | null = null){
         if(!canvas && !this.canvas){
            console.log('no canvas provided');
            return false;
         }
         if(!canvas){
            canvas = this.canvas;
         }
         const objects = canvas.getObjects();
         let colors: string[] = [];

         $.each(objects, function(index, obj: fabric.Object){
            if(obj.name == "bg" || obj?.signstudio_standard != true ){
               if(obj.name == 'userFabricShape'){
                  for(let i = 0; i < obj['_objects'].length; i++){
                     if(obj['_objects'][i].fill !== 'transparent'){ // Fixed: removed double negative
                        colors.push(obj['_objects'][i].fill);
                     }
                  }
               }else{
                  if(obj?.fill && obj.fill !== 'transparent'){
                     colors.push(obj.fill);
                  }
               }
            }
         });

         let colors_set = new Set(colors);
         if(colors_set.has('transparent')){
            colors_set.delete('transparent');
         }

         let activeColors = $('.active-colors');

         if(colors_set.size > 0){
            activeColors.empty();
            activeColors.each(function(){ // Changed from $.each
               const $el = $(this);
               const type = $el.data('sign-optiontype-plc');
               const title = $('<div>')
               .addClass('active-colors_set-title')
               .html('Aktiva färger');
               $el.append(title);

               const color_wrap = $('<div>') // Create new color_wrap for each element
               .addClass('color-opt-wrp');

               colors_set.forEach(function(v){ // Changed from for...of
                  const button = $('<button>')
                  .attr('data-sign-optiontype', type)
                  .attr('data-sign-option', v)
                  .css('background', v)
                  .addClass('sign-option')
                  .html(v);
                  color_wrap.append(button);
               });

               $el.append(color_wrap);
            });
         }else{
            activeColors.empty();
         }
      }

      checkGravyrColorID(objects: object[]){
         let colors = new Set();
         let gravyr_colors = {bg_color: null, text_color: null}
         for(let i = 0; i < objects.length; i++){
            let obj = objects[i];
            const fill = obj?.fill;

            if(fill){
               if(fill.toLowerCase() !== 'transparent'){
                  if(!gravyr_colors.bg_color){
                     gravyr_colors.bg_color = fill;
                  }else if(!gravyr_colors.text_color){
                     gravyr_colors.text_color = fill;
                  }

                  colors.add(fill);
               }
            }

         }

         if(colors.size < 2){
            return false;
         }

         //get all availble gravyr colors and find the id
         const available_gravyr_colors = $('.only-sign.gravyr-color');

         if(available_gravyr_colors.length > 0){
            let first_color_instance = $(available_gravyr_colors[0]);
            let color_alts = first_color_instance.find('.gravyr-colors'); 
            for(let i = 0; i < color_alts.length; i++){
               const element = color_alts[i];
               const bg = element.getAttribute('data-sign-option');
               const text = element.getAttribute('data-sign-text');
               if(bg == gravyr_colors.bg_color && text == gravyr_colors.text_color){
                  const gravyr_id = element.getAttribute('data-assoc');
                  if(gravyr_id){
                     return parseInt(gravyr_id);
                  }
               }
            }
         }

         return false;
      }
   }

function mountingLayoutInstructions(){
	if(currentSettings.mount_layout){
		let instructions = "";
		let send;
		let settings =  JSON.parse(JSON.stringify(currentSettings.mount_layout));
		//check first if 0 0 exist which means it could be only one that should count for all
		if(settings['0x0']){
			instructions = settings['0x0'];
		}
		//checks if more exists
		let lowestWidth = 0;
		let lowestHight = 0;
		let use = "";
		for(propery in settings){
			let test = propery.split('x');
			let w = parseInt(test[0]);
			let h = parseInt(test[1]);
			if( handleRealDim.getDim('width') <= w && handleRealDim.getDim('height') <= h){
				if(lowestWidth == 0 && lowestHight == 0){
					lowestWidth = w;
					lowestHight = h;
					use = propery;
				}else if(w < lowestWidth || h < lowestHight){
					w = lowestWidth;
					h = lowestHight;
					use = propery;
				}
			} 
		}
		//if found value apply amount 
		let amount = 0;
		if(use){
			send = JSON.parse(JSON.stringify(settings[use]));
			//check if automatic placement
			if(send['auto-amnt']){
				let corners = [];
				let hasSet = [];
				let hasDeg = [];
				let interval = fabric.util.parseUnit(send['auto-amnt']);

				for(let i = 1; i < 9; i++){
					if(send[i]){
						if([1,3,6,8].includes(i)){
							amount++;
							corners.push(i)
						}else{
							let tempAmnt = send[i].amount;
							if(tempAmnt){
								hasSet.push(i)
								amount += parseInt(tempAmnt)
							}else{
								hasDeg.push(i);
							}
						}
					}
				}
				//get amount width and Height 
				let amntWidth = Math.floor(handleRealDim.getDim('width') / interval);
				let amntHeight = Math.floor(handleRealDim.getDim('height') / interval);

				//check to added quantity
				for(let y = 3; y < 7; y+=3){
					//left top left
					let uneven = (y == 3) ? 2 : 4 ;
					if(!(hasSet.includes(uneven)) && corners.includes(y) && corners.includes(1)){
						if(hasDeg.includes(uneven)){
							send[uneven].amount = ((uneven) == 2) ? amntWidth : amntHeight;
						}else{
							send[uneven] = {amount: ((uneven) == 2) ? amntWidth : amntHeight}
						}
						
						(send[uneven].amount == 0) ? delete send[uneven] : amount += send[uneven].amount;

					}
					//right bottom
					let uneven2 = (y == 3) ? 5 : 7;
					if(!(hasSet.includes(uneven2)) && corners.includes(y) && corners.includes(8)){
						if(hasDeg.includes(uneven2)){
							send[uneven2].amount = (uneven2 == 7) ? amntWidth : amntHeight;
						}else{
							send[uneven2] = {amount: (uneven2 == 7) ? amntWidth : amntHeight }
						}

						(send[uneven2].amount == 0) ? delete send[uneven2] : amount += send[uneven2].amount;
					}
				  }
				
				  send.amount = amount;
			}
		}
		return send;
	}else{
		return false;
	}
}

//Saves canvas for undos and redos 
function getColorChangeObject(type){
	var optionType = type.attr("data-sign-optiontype");
	
	if (optionType == "background") {
		return [backgroundInnerShape, handleRealDim.getDim('background')];
	} else if (optionType == "bordercolor") {
		return [backgroundShape, handleRealDim.getDim('borderColor')];
	} else if (optionType == "symbolcolor") {
		let obj = canvas.getActiveObject();
		if (obj == null) return;
		return [obj,obj.fill];
	}else if (optionType == "shape-bg-color"){
		let obj = canvas.getActiveObject();
		if (obj == null) return;
		let send; 
		$.each(obj._objects, function(){
			if($(this)[0]["name"] == 'userShapeInner'){
				obj = $(this)[0];
				let fill = $(this)[0].fill;
				send = [obj, fill];
			}
		})
		return send;
	}else if(optionType == 'shape-border-color'){
		let obj = canvas.getActiveObject();
		if (obj == null) return;
		let send; 
		$.each(obj._objects, function(){
			if($(this)[0]["name"] != 'userShapeInner'){
				obj = $(this)[0];
				let fill = $(this)[0].fill;
				send = [obj, fill];
			}
		})
		return send;
	}

}

function applyConditions(){
	return new Promise((resolve, reject) =>{
        let orientationClass = new ManageOrientation();
		//check sizes
		let maxWidht = parseInt(currentSettings['max_width']);
		let minWidth = parseInt(currentSettings['min_width']);
		let maxHeight = parseInt(currentSettings['max_height']);
		let minHeight = parseInt(currentSettings['min_height']);

            if(orientationClass.shiftStatus()){
                let temp = maxWidht;
                maxWidht = maxHeight;
                maxHeight = temp;
                console.log('shifting status');
            }


		if(maxWidht < handleRealDim.getDim('width')){
           handleRealDim.updateWidth(maxWidht);
		}
		if(minWidth > handleRealDim.getDim('width')){
           handleRealDim.updateWidth(minWidth);
		}
		if(maxHeight < handleRealDim.getDim('height')){
           handleRealDim.updateHeight(maxHeight);
		}
		if(minHeight > handleRealDim.getDim('height')){
           handleRealDim.updateHeight(minHeight);
		}
		resolve();
	});
}

if (document.readyState === 'complete') {
	init();
	loadOther();
} else {
	window.addEventListener('load', function (){
			init();
			loadOther()
	});
}

////ALL events that are loaded after dome to function!!!-----------------------------------------------------------------------------------------
function loadOther (){
	
$( "#zoomrange" ).slider({
	range: "min",
	min: 50,
	max: 500,
	value: 100,
	slide: function( event, ui ) {
        onCanvasGUIHandler.handleZoom(ui.value);
		//onCanvasGUIHandler.userZoom(val);
	}
});


$( "#zoomrange-full" ).slider({
	range: "min",
	orientation: "vertical",
	min: 50,
	max: 500,
	value: 100,
	slide: function( event, ui ) {
        onCanvasGUIHandler.handleZoom(ui.value);
		//onCanvasGUIHandler.userZoom(val);
	}
});




/*$("#radius-range").slider({
	min: 0,
	max: 10,
	values: [0],
	slide: function( event, ui ) {
		let val = ui.value;
	}
});*/

//Materials navigation 
$(".material-parent").on("click", function(){
	//manProdAndMaterial($(this));
});

//hides material button on nomal product ib products
$('.t-signtype .sign-option').on('click', function(){
	//$('.toggle-material').addClass('dont-display');
});

function manProdAndMaterial(item){
	return;
	if(item.is('.material-parent')){
	   materialMenu.ongoing = true;
	   let display = getUpdatedMenu(item);
	   //display right buttons 
	   $.each($('.toggle-material'), function(){
			if(display.includes($(this).attr('data-connection-display'))){
				$(this).removeClass('dont-display').removeClass('active');
			}else{
				$(this).addClass('dont-display').removeClass('active');
			}
	   })
	   //display right actives
	   $.each($('.material-parent'), function(){
			if(display.includes($(this).attr('data-assoc'))){
				console.log($(this).attr('data-assoc'));
				$(this).addClass('active');
			}else{
				$(this).removeClass('active');
			}
	   });

	   //show content
	   //hides all before showing
		$(".multiple-material-container").removeClass('active-block');
		//show right one
		$(`.multiple-material-container[data-connection-display='${item.attr('data-assoc')}']`).addClass('active-block');
		//update title and toggle
		let id = item.attr('data-assoc');
		let toggle = $(`.toggle-material[data-connection-display="${id}"]`);
		$('.t-material .tool h4').text(toggle.find('.label').text());
		toggle.addClass("active");
	}
	//hide everything that is after the clicked group
	function getUpdatedMenu(item){
		let show = [];
		show.push(item.attr('data-assoc'));
		if(item.attr('data-assoc-parent')){
			show = [...show, ...getUpdatedMenu($(`.material-parent[data-assoc='${item.attr('data-assoc-parent')}']`))];
		}
		return show;
	}	
	//opens choices 
	$('.t-signtype').addClass("hidden");
	$('.t-material').removeClass("hidden");
	$('.toggle-signtype').removeClass("active");
	//$('.toggle-material').addClass("active");
}

/*$(".shape-setting-size").on("change", "input", function(e) {
	let activeObject = canvas.getActiveObject();
	if (activeObject == null) {
		return;
	}
	let newWidth = (e.target.dataset.signOption == "width") ? fabric.util.parseUnit(`${e.target.value}mm`) : false;
	let newHeight = (e.target.dataset.signOption == "height") ? fabric.util.parseUnit(`${e.target.value}mm`) : false;

	if(activeObject.name = "userFabricShape"){
		let inner, outer;

		if(newWidth){
			activeObject.scaleToWidth(newWidth)
		}else if(newHeight){
			activeObject.scaleToHeight(newHeight)
		}else{
			return;
		}
		canvas.requestRenderAll();
	}
	return;
	let objectCenter = activeObject.getCenterPoint(true,true);
	//let scaler = 1;
	if (e.target.dataset.signOption == "width") {
		//var currentWidth = (activeObject.width * activeObject.scaleX) / fabric.util.parseUnit("1mm");
		let newWidth = fabric.util.parseUnit(e.target.value+"mm");
		activeObject.scaleX = 1;
		activeObject.width = newWidth - activeObject.strokeWidth;
		//scaler = newWidth / currentWidth;
		//activeObject.scaleX = activeObject.scaleX*scaler*1;
	}else if (e.target.dataset.signOption == "height") {
		//var currentHeight = (activeObject.height * activeObject.scaleY) / fabric.util.parseUnit("1mm");
		let newHeight = fabric.util.parseUnit(e.target.value+"mm");
		activeObject.scaleY = 1;
		activeObject.height = newHeight - activeObject.strokeWidth;
		//scaler = newHeight / currentHeight;
		//activeObject.scaleY = scaler*activeObject.scaleX*1;
	}else{
		return;
	}
	activeObject.setPositionByOrigin(objectCenter, 'center', 'center');
	//scaleObjectAroundCenter(activeObject, activeObject.scaleX * scaler * 1);
	canvas.renderAll();
	updateSymbolSize();
	//runCompleteObjectOutsideDetection();
});
*/


$(".symbol-size-ui").on("click", "button", function(e) {
	var activeObject = canvas.getActiveObject();
	if (activeObject == null) {
		return;
	}

	var objectRatio = activeObject.width / activeObject.height;
	var backgroundRatio = backgroundShape.width / backgroundShape.height;

	var scaler = 1;
	if (objectRatio > backgroundRatio) {
		scaler = backgroundShape.width / activeObject.width;
	} else {
		scaler = backgroundShape.height / activeObject.height;
	}
	scaleObjectAroundCenter(activeObject, scaler * 1);
	canvas.requestRenderAll();
	updateSymbolSize();
	//runCompleteObjectOutsideDetection();
});

$(".layers-toggle").on("click",function() {
	$(".layers").toggle();
});

$(".layers").on("click", ".layer-action", function(e) {
	var action = $(this).data("action");
	var id = $(this).data("objid");

	var objects = canvas.getObjects();

	var obj = objects[id];

	if (action == "remove") {
		canvas.remove(obj);
		signSaveAndLoad.saveDesign();
	}

	canvas.requestRenderAll();
	updateGUI();
});

$(".toggle-inside-menu").on('click', function(e) {
	//closeInnerMenu();
	//closeCart();
});

$(".cart-mobile-button").on('click', function(e) {
	if ($(this).hasClass("active")) closeCart();
	else openCart();
});

$(".tools .tool-menu li").on('click', function(e) {
	var tool = $(this).data("minitool-toggle");
	$(this)
		.siblings()
		.removeClass("active");
	$(this).addClass("active");

	$(this)
		.parent()
		.parent()
		.find(".minitool")
		.each(function() {
			if ($(this).hasClass("tool-" + tool)) {
				$(this).addClass("active");
			} else {
				$(this).removeClass("active");
			}
		});
});

$('.toggle-fullscreen').on('click',function(){
	isMobile = window.innerWidth < 768;
	if(fullscreen.state == false){
		$('.toggle-fullscreen').addClass("active");
		$('#signbuilder-left-menu').hide("slide", {direction: "left"}, 300);
		$('.sign-info').hide("slide", {direction: "down"}, 300);
		$('.top-tool-bar').hide();
		$('#div_block-65-7').hide("slide", {direction: "up"}, 300);

		//set size to design area 
		if(isMobile){
			$('#content-holder, .signbuilder').css('height', 'calc(var(--vh, 1vh) * 100)');
			$('.top-bar-mobile').hide();
			$('.top-bar-mobile-2').hide();
			
		}else{
			//$('.design-area').css('padding-left', "0px");
			//$('.design-area').css('height', '100vh');
			//$('#content-holder').css('height', '100vh');
			$('.signbuilder').css({'height':'100vh', 'position': 'fixed', 'top': 0, 'bottom':0, 'left': 0, 'right' : 0});
		}
		fullscreen.state = true;
		//zoomCanvas();
		//masterSync({'updateBackgroundShape' : ''}, true);
		setTimeout(function(){updateCanvasSize(); 
							 onCanvasGUIHandler.centerCanvasView();
							 masterSync({'updateBackgroundShape' : ''}, true);
							 $('.fullscreen-tool-toggle').css('display', 'flex');}, 300);				 

	}else{
		$('.toggle-fullscreen').removeClass("active");
		$('#signbuilder-left-menu').show();
		$('.sign-info').show();
		$('.top-tool-bar').show();
		$('#div_block-65-7').show();
		fullscreen.state = false;
		//set size to design area 
		let height = $('#div_block-65-7').height();
		if(isMobile){
			//$('.design-area').css('height', `calc((var(--vh, 1vh) * 100) - ${height}px`);
			$('#content-holder').css('height', `calc((var(--vh, 1vh) * 100) - ${height}px`);
			$('.signbuilder').css('height', `calc((var(--vh, 1vh) * 100) - ${height}px`);
			$('.signbuilder-right-content').css('height', 'calc(100% - 120px)');
			$('.top-bar-mobile').show();
			$('.top-bar-mobile-2').show();
		}else{
			//$('.design-area').css('height', `calc(100vh - ${height}px`);
			//$('#content-holder').css('height', `calc(100vh - ${height}px`);
			//$('.signbuilder').css({'height': `calc(100vh - ${height}px`, 'position': 'relative'});
		}
		$('.fullscreen-tool-toggle').css('display', 'none');
		//zoomCanvas();
		setTimeout(() => {
					updateCanvasSize(); 
					onCanvasGUIHandler.centerCanvasView();
					masterSync({'updateBackgroundShape' : ''}, true);
					}, 100);
	}
});

$(".fullscreen-tool-toggle").on("click", function(){
	$('#signbuilder-left-menu').toggle();
	if($('#toolwrap').is('.dont-display')){
		onCanvasGUIHandler.menuCanvasSizeControl(true);
	}else{
		onCanvasGUIHandler.menuCanvasSizeControl(false);
	}
	
});

$(".option-font li").on('click', async function(e) {
	var font = $(this).data("font");

	if ($(this).hasClass("disabled")) return;

	if (canvas.getActiveObject() == null) return;

	var obj = canvas.getActiveObject();

	if (!obj instanceof fabric.IText) return;
	obj.styles = {};

	if (obj.fontStyle == "italic" && !selectedFontProperties.allowItalic)
		obj.fontStyle = "normal";

	if (obj.fontWeight == "bold" && !selectedFontProperties.allowBold)
		obj.fontWeight = "normal";

	//check that font is loaded before setting font 
    let fontSettings = false;
    try {
        fontSettings = await $.manageFonts.loadNewFont(font);
    } catch (error) {
        console.log("Couldn't load font:", error.message); // Assuming error object has a message property
    }
	
    if(fontSettings){
        obj.set({"fontFamily": fontSettings.family});
        obj.set({"fontWeight": fontSettings.weight});
        obj.set({"fontStyle": fontSettings.style});
        toolMenu.updateTextFormatButtons(obj);
    }

  canvas.requestRenderAll();
	$(".option-font").removeClass("active-block");
	activeDropdown = null;
});


$(".option-font-preview").on('click', function(e) {
	if (canvas.getActiveObject() == null) return;

	var obj = canvas.getActiveObject();
	if (!obj instanceof fabric.IText) return;
	var text = obj.text;

	$(".option-font .preview").text(
		text.replace(/(\r\n|\n|\r)/gm, " ").substring(0, 10)
	);
	
	if($(".option-font").hasClass("active-block")){
		activeDropdown = null;
	}else{
		activeDropdown = $(this).parent();
	}
	$(".option-font").toggleClass("active-block");
});




$(".skyltsdn-undo").on('click',function(e) {
	signSaveAndLoad.undo();
});

$(".skyltsdn-redo").on('click',function(e) {
	signSaveAndLoad.redo();
});



$(".category-read-more").on('click',function(e) {
	e.stopPropagation();
	e.preventDefault();

	//var category = $(this).data("category");
	//window.openCategoryDetails = category;
	//$(".material-details-wrapper").hide();
	//$(".material-details-wrapper[data-category=\""+category+"\"]").show();

	return false;
});


//function for drop-downbuttons
$('.dropdown-button').click(function(e) {
	//removes all active blocks 
	if($(e.target).hasClass('toggle-dropdown') || $(e.target).hasClass('material-icons')){
		$('.dropdown-content').removeClass('active-block');
	}
	var $dropdownContent = $(this).children('.dropdown-content');
	if (activeDropdown && activeDropdown.is($(this))) {
	  $dropdownContent.removeClass('active-block');
	  activeDropdown = null;
	} else {
	  if (activeDropdown) {
		activeDropdown.removeClass('active-block');
	  }
	  $dropdownContent.addClass('active-block');
	  activeDropdown = $(this);
	}
  });

  $document.click(function(event) {
	if(activeDropdown){
		if(activeDropdown.find($(event.target)).length != 1){
			activeDropdown.find('.active-block').removeClass('active-block');
		}
	}
  }); 


//disable scroll incement on number 
$document.on("wheel", "input[type=number]", function (e) {
    $(this).blur();
});


//END OF LOAD ON DOME 

};



function openShareModal(code, website_base_url) {
	if (code != null) {
		$(".share-window").html(
			"<h4>Dela din design med denna länken:</h4>" +
				'<input class="share-url-input" readonly type="text" value="'+website_base_url+'/skyltstudion/?base=' +
				code +
				'" />'
		);
		return;
	}

	var shareDom =
		'<div class="share-window"><div class="loading"><div class="spinner"></div><span>Sparar design....</span></div></div>';
	$.featherlight(shareDom);
}

function clearLocalData() {
	localStorage.removeItem("designCache");

	window.location.href = window.location.href.substring(
		0,
		window.location.href.indexOf("?")
	);
}

function scaleSvgBoundingBoxAndAdd(url) {
	$.ajax({
		url: url,
		success: function(data, textStatus, jqXHR) {
			var svgData = jqXHR.responseXML.firstElementChild.outerHTML;
			svgData = svgData.replace("svg:svg", "svg");
			$("#draw-svg-before-import").html(svgData);
			setTimeout(function() {
				var svg = document.getElementById("draw-svg-before-import")
					.firstElementChild;
				var bbox = svg.getBBox();
				svg.setAttribute(
					"viewBox",
					bbox.x + " " + bbox.y + " " + bbox.width + " " + bbox.height
				);
				svg.setAttribute("width", bbox.width);
				svg.setAttribute("height", bbox.height);
				var importSvgHtml = document.getElementById("draw-svg-before-import")
					.firstElementChild.outerHTML;

				var serializer = new XMLSerializer();
				var svgStr = serializer.serializeToString(
					document.getElementById("draw-svg-before-import").firstElementChild
				);
				addSymbol(svgStr, false, true);
			}, 10);
		}
	});
}



function deleteFromLocalFiles(index) {
	var dis = localStorage.getItem("designUploads");
	if (dis == null) {
		return;
	}

	var uploads = JSON.parse(dis);

	uploads.splice(index, 1);

	localStorage.setItem("designUploads", JSON.stringify(uploads));
}

});


