|  | @@ -53,19 +53,29 @@
 | 
	
		
			
				|  |  |        if ("barcode" in target) this.barcode = target["barcode"];
 | 
	
		
			
				|  |  |        if ("thumbnail" in target) this.thumbnail = target["thumbnail"];
 | 
	
		
			
				|  |  |        if ("on_stock" in target) this.on_stock = target["on_stock"];
 | 
	
		
			
				|  |  | +      try {
 | 
	
		
			
				|  |  | +        this.stock_count = Number(this.stock_count);
 | 
	
		
			
				|  |  | +      } catch {
 | 
	
		
			
				|  |  | +        this.stock_count = 0;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      try {
 | 
	
		
			
				|  |  | +        this.on_stock = Number(this.on_stock);
 | 
	
		
			
				|  |  | +      } catch {
 | 
	
		
			
				|  |  | +        this.on_stock = 0;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      get dump() {
 | 
	
		
			
				|  |  |        const dumped = {
 | 
	
		
			
				|  |  | -        "name": this.name,
 | 
	
		
			
				|  |  | -        "description": this.description,
 | 
	
		
			
				|  |  | -        "author": this.author,
 | 
	
		
			
				|  |  | -        "image": this.image,
 | 
	
		
			
				|  |  | -        "stock_count": this.stock_count,
 | 
	
		
			
				|  |  | -        "barcode": this.barcode,
 | 
	
		
			
				|  |  | -        "thumbnail": this.thumbnail
 | 
	
		
			
				|  |  | +        "name": new String(this.name),
 | 
	
		
			
				|  |  | +        "description": new String(this.description),
 | 
	
		
			
				|  |  | +        "author": new String(this.author),
 | 
	
		
			
				|  |  | +        "image": new String(this.image),
 | 
	
		
			
				|  |  | +        "barcode": new String(this.barcode),
 | 
	
		
			
				|  |  | +        "thumbnail": new String(this.thumbnail),
 | 
	
		
			
				|  |  | +        "stock_count": new String(this.stock_count)
 | 
	
		
			
				|  |  |        };
 | 
	
		
			
				|  |  |        if (this.on_stock !== null) {
 | 
	
		
			
				|  |  | -        dumped["on_stock"] = this.on_stock;
 | 
	
		
			
				|  |  | +        dumped["on_stock"] = new String(this.on_stock);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        return dumped;
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -412,11 +422,18 @@
 | 
	
		
			
				|  |  |    // application/scripts/request.js
 | 
	
		
			
				|  |  |    var request = class {
 | 
	
		
			
				|  |  |      get settings() {
 | 
	
		
			
				|  |  | -      return {
 | 
	
		
			
				|  |  | +      const settings = {
 | 
	
		
			
				|  |  |          "method": this.method,
 | 
	
		
			
				|  |  | -        "headers": this.headers,
 | 
	
		
			
				|  |  | -        "body": this.body
 | 
	
		
			
				|  |  | +        "headers": this.headers
 | 
	
		
			
				|  |  |        };
 | 
	
		
			
				|  |  | +      if (this.method === "GET" || this.method === "HEAD") {
 | 
	
		
			
				|  |  | +        return settings;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (this.body === null) {
 | 
	
		
			
				|  |  | +        return settings;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      settings.body = this.body;
 | 
	
		
			
				|  |  | +      return settings;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      get _apikey() {
 | 
	
		
			
				|  |  |        const manager = new login_manager();
 | 
	
	
		
			
				|  | @@ -432,16 +449,24 @@
 | 
	
		
			
				|  |  |        throw new TypeError("It must be overwrite.");
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      get headers() {
 | 
	
		
			
				|  |  | -      if (this.method === "GET") {
 | 
	
		
			
				|  |  | +      if (this.method === "GET" || this.method === "HEAD") {
 | 
	
		
			
				|  |  |          return {};
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | +      if (typeof this.data === "string") {
 | 
	
		
			
				|  |  | +        return {
 | 
	
		
			
				|  |  | +          "Content-Type": "text/plain"
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |        return {
 | 
	
		
			
				|  |  |          "Content-Type": "application/json"
 | 
	
		
			
				|  |  |        };
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      get body() {
 | 
	
		
			
				|  |  |        if (this.data === null) {
 | 
	
		
			
				|  |  | -        return "";
 | 
	
		
			
				|  |  | +        return null;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (typeof this.data === "string") {
 | 
	
		
			
				|  |  | +        return this.data;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        return JSON.stringify(this.data);
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -695,7 +720,7 @@
 | 
	
		
			
				|  |  |        title.appendChild(title_content);
 | 
	
		
			
				|  |  |        const form = document.createElement("form");
 | 
	
		
			
				|  |  |        center.appendChild(form);
 | 
	
		
			
				|  |  | -      form.addEventListener("click", () => {
 | 
	
		
			
				|  |  | +      form.addEventListener("change", () => {
 | 
	
		
			
				|  |  |          this._clear_results();
 | 
	
		
			
				|  |  |        });
 | 
	
		
			
				|  |  |        this.#form = document.createElement("div");
 | 
	
	
		
			
				|  | @@ -758,7 +783,10 @@
 | 
	
		
			
				|  |  |          throw new Error("Screen is not visible yet!");
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        this._append_child(container);
 | 
	
		
			
				|  |  | -      return () => {
 | 
	
		
			
				|  |  | +      return (target = null) => {
 | 
	
		
			
				|  |  | +        if (target !== null) {
 | 
	
		
			
				|  |  | +          input.value = target;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |          return input.value;
 | 
	
		
			
				|  |  |        };
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -816,16 +844,20 @@
 | 
	
		
			
				|  |  |        this.barcode = this._extract(target, "barcode");
 | 
	
		
			
				|  |  |        this.stock_count = this._extract(target, "stock_count");
 | 
	
		
			
				|  |  |        if (this.stock_count !== null) {
 | 
	
		
			
				|  |  | -        this.stock_count = Number(this.stock_count);
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +          this.stock_count = Number(this.stock_count);
 | 
	
		
			
				|  |  | +        } catch {
 | 
	
		
			
				|  |  | +          this.stock_count = 0;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      get dump() {
 | 
	
		
			
				|  |  |        return {
 | 
	
		
			
				|  |  | -        "name": this.name,
 | 
	
		
			
				|  |  | -        "description": this.description,
 | 
	
		
			
				|  |  | -        "author": this.author,
 | 
	
		
			
				|  |  | -        "barcode": this.barcode,
 | 
	
		
			
				|  |  | -        "stock_count": this.stock_count
 | 
	
		
			
				|  |  | +        "name": new String(this.name),
 | 
	
		
			
				|  |  | +        "description": new String(this.description),
 | 
	
		
			
				|  |  | +        "author": new String(this.author),
 | 
	
		
			
				|  |  | +        "barcode": new String(this.barcode),
 | 
	
		
			
				|  |  | +        "stock_count": new String(this.stock_count)
 | 
	
		
			
				|  |  |        };
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      _extract(dict, name) {
 | 
	
	
		
			
				|  | @@ -902,6 +934,9 @@
 | 
	
		
			
				|  |  |      #barcode;
 | 
	
		
			
				|  |  |      #stock_count;
 | 
	
		
			
				|  |  |      #image;
 | 
	
		
			
				|  |  | +    #loaded_image_type;
 | 
	
		
			
				|  |  | +    #loaded_image;
 | 
	
		
			
				|  |  | +    #image_preview;
 | 
	
		
			
				|  |  |      constructor(target) {
 | 
	
		
			
				|  |  |        super();
 | 
	
		
			
				|  |  |        this.#target = target;
 | 
	
	
		
			
				|  | @@ -913,6 +948,8 @@
 | 
	
		
			
				|  |  |        return "Product editor";
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      _build_form() {
 | 
	
		
			
				|  |  | +      this.#loaded_image = null;
 | 
	
		
			
				|  |  | +      this.#loaded_image_type = null;
 | 
	
		
			
				|  |  |        this.#name = this._create_input(
 | 
	
		
			
				|  |  |          "name",
 | 
	
		
			
				|  |  |          "Name:",
 | 
	
	
		
			
				|  | @@ -963,21 +1000,42 @@
 | 
	
		
			
				|  |  |            this.#image = input;
 | 
	
		
			
				|  |  |            input.type = "file";
 | 
	
		
			
				|  |  |            input.accept = "image/*";
 | 
	
		
			
				|  |  | +          input.addEventListener("change", () => {
 | 
	
		
			
				|  |  | +            this.#load_image_from_file();
 | 
	
		
			
				|  |  | +          });
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        );
 | 
	
		
			
				|  |  | +      this.#image_preview = document.createElement("img");
 | 
	
		
			
				|  |  | +      this.#image_preview.style.opacity = "1";
 | 
	
		
			
				|  |  | +      this.#image_preview.src = this.#target.image;
 | 
	
		
			
				|  |  | +      this._append_child(this.#image_preview);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get #ready_image() {
 | 
	
		
			
				|  |  | +      return this.#loaded_image;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    #update_image_preview() {
 | 
	
		
			
				|  |  | +      this.#image_preview.src = "data:" + this.#loaded_image_type + ";base64," + this.#loaded_image;
 | 
	
		
			
				|  |  | +      this.#image_preview.style.opacity = "1";
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    async #code_image() {
 | 
	
		
			
				|  |  | +    #reset_image() {
 | 
	
		
			
				|  |  | +      this.#loaded_image = null;
 | 
	
		
			
				|  |  | +      this.#loaded_image_type = null;
 | 
	
		
			
				|  |  | +      this.#image_preview.style.opacity = "0";
 | 
	
		
			
				|  |  | +      this.#image_preview.src = "";
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    async #load_image_from_file() {
 | 
	
		
			
				|  |  |        if (this.#image.files.length === 0) {
 | 
	
		
			
				|  |  | -        return null;
 | 
	
		
			
				|  |  | +        this.#reset_image();
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        const file = this.#image.files.item(0);
 | 
	
		
			
				|  |  |        const buffer = await file.arrayBuffer();
 | 
	
		
			
				|  |  | -      const list = new Uint8Array(buffer);
 | 
	
		
			
				|  |  | -      let content = new String();
 | 
	
		
			
				|  |  | -      list.forEach((code) => {
 | 
	
		
			
				|  |  | -        content += String.fromCharCode(code);
 | 
	
		
			
				|  |  | +      let as_string = new String();
 | 
	
		
			
				|  |  | +      new Uint8Array(buffer).forEach((letter) => {
 | 
	
		
			
				|  |  | +        as_string += String.fromCharCode(letter);
 | 
	
		
			
				|  |  |        });
 | 
	
		
			
				|  |  | -      return btoa(content);
 | 
	
		
			
				|  |  | +      this.#loaded_image = btoa(as_string);
 | 
	
		
			
				|  |  | +      this.#loaded_image_type = file.type;
 | 
	
		
			
				|  |  | +      this.#update_image_preview();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      async #submit() {
 | 
	
		
			
				|  |  |        const copy = this.#target.copy();
 | 
	
	
		
			
				|  | @@ -994,7 +1052,7 @@
 | 
	
		
			
				|  |  |        this.#target = copy;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      async #image_submit() {
 | 
	
		
			
				|  |  | -      const image = await this.#code_image();
 | 
	
		
			
				|  |  | +      const image = await this.#ready_image;
 | 
	
		
			
				|  |  |        if (image === null) {
 | 
	
		
			
				|  |  |          return;
 | 
	
		
			
				|  |  |        }
 | 
	
	
		
			
				|  | @@ -1511,6 +1569,7 @@
 | 
	
		
			
				|  |  |        image.classList.add("image");
 | 
	
		
			
				|  |  |        image.src = this.#target.thumbnail + this.#cache_bypass;
 | 
	
		
			
				|  |  |        image.alt = this.#target.name;
 | 
	
		
			
				|  |  | +      image.loading = "lazy";
 | 
	
		
			
				|  |  |        image.addEventListener("click", () => {
 | 
	
		
			
				|  |  |          new product_fullscreen(this.#target).show();
 | 
	
		
			
				|  |  |        });
 | 
	
	
		
			
				|  | @@ -1635,6 +1694,47 @@
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  // application/scripts/autocomplete_response.js
 | 
	
		
			
				|  |  | +  var autocomplete_response = class extends bool_response {
 | 
	
		
			
				|  |  | +    #found;
 | 
	
		
			
				|  |  | +    constructor(target) {
 | 
	
		
			
				|  |  | +      super(target);
 | 
	
		
			
				|  |  | +      this.#found = null;
 | 
	
		
			
				|  |  | +      if (this.result) {
 | 
	
		
			
				|  |  | +        this.#found = target["found"];
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get found() {
 | 
	
		
			
				|  |  | +      if (this.#found === null) {
 | 
	
		
			
				|  |  | +        throw new Error("Server response is not complete.");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      return this.#found;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // application/scripts/autocomplete_request.js
 | 
	
		
			
				|  |  | +  var autocomplete_request = class extends request {
 | 
	
		
			
				|  |  | +    #barcode;
 | 
	
		
			
				|  |  | +    constructor(barcode) {
 | 
	
		
			
				|  |  | +      super();
 | 
	
		
			
				|  |  | +      this.#barcode = barcode;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get _response() {
 | 
	
		
			
				|  |  | +      return autocomplete_response;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get data() {
 | 
	
		
			
				|  |  | +      return {
 | 
	
		
			
				|  |  | +        "apikey": this._apikey
 | 
	
		
			
				|  |  | +      };
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get method() {
 | 
	
		
			
				|  |  | +      return "POST";
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get url() {
 | 
	
		
			
				|  |  | +      return "/complete/barcode/" + this.#barcode;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    // application/scripts/product_adder.js
 | 
	
		
			
				|  |  |    var product_adder = class extends formscreen {
 | 
	
		
			
				|  |  |      #name;
 | 
	
	
		
			
				|  | @@ -1643,10 +1743,59 @@
 | 
	
		
			
				|  |  |      #barcode;
 | 
	
		
			
				|  |  |      #stock_count;
 | 
	
		
			
				|  |  |      #image;
 | 
	
		
			
				|  |  | +    #loaded_image_type;
 | 
	
		
			
				|  |  | +    #loaded_image;
 | 
	
		
			
				|  |  | +    #image_preview;
 | 
	
		
			
				|  |  |      get _name() {
 | 
	
		
			
				|  |  |        return "Add product";
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    async #autocomplete() {
 | 
	
		
			
				|  |  | +      const barcode = this.#barcode();
 | 
	
		
			
				|  |  | +      if (barcode.length === 0) {
 | 
	
		
			
				|  |  | +        this._info = "Fill barcode first.";
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      this._info = "Searching in the web...";
 | 
	
		
			
				|  |  | +      try {
 | 
	
		
			
				|  |  | +        const request2 = new autocomplete_request(barcode);
 | 
	
		
			
				|  |  | +        const response = await request2.connect();
 | 
	
		
			
				|  |  | +        if (!response.result) {
 | 
	
		
			
				|  |  | +          throw new Error(response.cause);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        const product2 = response.found;
 | 
	
		
			
				|  |  | +        this.#name(product2.title);
 | 
	
		
			
				|  |  | +        this.#description(product2.description);
 | 
	
		
			
				|  |  | +        this.#author(product2.author);
 | 
	
		
			
				|  |  | +        this.#barcode(product2.barcode);
 | 
	
		
			
				|  |  | +        this.#loaded_image = product2.image;
 | 
	
		
			
				|  |  | +        this.#loaded_image_type = product2.image_type;
 | 
	
		
			
				|  |  | +        this.#update_image_preview();
 | 
	
		
			
				|  |  | +        this._info = "Ready. Check results.";
 | 
	
		
			
				|  |  | +      } catch (error) {
 | 
	
		
			
				|  |  | +        this._error = new String(error);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    #update_image_preview() {
 | 
	
		
			
				|  |  | +      this.#image_preview.src = "data:" + this.#loaded_image_type + ";base64," + this.#loaded_image;
 | 
	
		
			
				|  |  | +      this.#image_preview.style.opacity = "1";
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get #autocomplete_button() {
 | 
	
		
			
				|  |  | +      const button = document.createElement("div");
 | 
	
		
			
				|  |  | +      button.classList.add("autocomplete-button");
 | 
	
		
			
				|  |  | +      button.classList.add("button");
 | 
	
		
			
				|  |  | +      const icon = document.createElement("span");
 | 
	
		
			
				|  |  | +      icon.classList.add("material-icons");
 | 
	
		
			
				|  |  | +      icon.innerText = "auto_fix_normal";
 | 
	
		
			
				|  |  | +      button.appendChild(icon);
 | 
	
		
			
				|  |  | +      const text = document.createElement("span");
 | 
	
		
			
				|  |  | +      text.classList.add("text");
 | 
	
		
			
				|  |  | +      text.innerText = "Autocomplete";
 | 
	
		
			
				|  |  | +      button.appendChild(text);
 | 
	
		
			
				|  |  | +      return button;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |      _build_form() {
 | 
	
		
			
				|  |  | +      this.#loaded_image = null;
 | 
	
		
			
				|  |  | +      this.#loaded_image_type = null;
 | 
	
		
			
				|  |  |        this.#name = this._create_input(
 | 
	
		
			
				|  |  |          "name",
 | 
	
		
			
				|  |  |          "Name:",
 | 
	
	
		
			
				|  | @@ -1686,21 +1835,45 @@
 | 
	
		
			
				|  |  |            this.#image = input;
 | 
	
		
			
				|  |  |            input.type = "file";
 | 
	
		
			
				|  |  |            input.accept = "image/*";
 | 
	
		
			
				|  |  | +          input.addEventListener("change", () => {
 | 
	
		
			
				|  |  | +            this.#load_image_from_file();
 | 
	
		
			
				|  |  | +          });
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        );
 | 
	
		
			
				|  |  | +      this.#image_preview = document.createElement("img");
 | 
	
		
			
				|  |  | +      this.#image_preview.style.opacity = "0";
 | 
	
		
			
				|  |  | +      this._append_child(this.#image_preview);
 | 
	
		
			
				|  |  | +      const autocomplete = this.#autocomplete_button;
 | 
	
		
			
				|  |  | +      this._append_child(autocomplete);
 | 
	
		
			
				|  |  | +      autocomplete.addEventListener("click", () => {
 | 
	
		
			
				|  |  | +        this.#autocomplete();
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    #reset_image() {
 | 
	
		
			
				|  |  | +      this.#loaded_image = null;
 | 
	
		
			
				|  |  | +      this.#loaded_image_type = null;
 | 
	
		
			
				|  |  | +      this.#image_preview.style.opacity = "0";
 | 
	
		
			
				|  |  | +      this.#image_preview.src = "";
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    async #code_image() {
 | 
	
		
			
				|  |  | +    async #load_image_from_file() {
 | 
	
		
			
				|  |  |        if (this.#image.files.length === 0) {
 | 
	
		
			
				|  |  | -        throw new Error("Upload image for product.");
 | 
	
		
			
				|  |  | +        this.#reset_image();
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        const file = this.#image.files.item(0);
 | 
	
		
			
				|  |  |        const buffer = await file.arrayBuffer();
 | 
	
		
			
				|  |  | -      const list = new Uint8Array(buffer);
 | 
	
		
			
				|  |  | -      let content = new String();
 | 
	
		
			
				|  |  | -      list.forEach((code) => {
 | 
	
		
			
				|  |  | -        content += String.fromCharCode(code);
 | 
	
		
			
				|  |  | +      let as_string = new String();
 | 
	
		
			
				|  |  | +      new Uint8Array(buffer).forEach((letter) => {
 | 
	
		
			
				|  |  | +        as_string += String.fromCharCode(letter);
 | 
	
		
			
				|  |  |        });
 | 
	
		
			
				|  |  | -      return btoa(content);
 | 
	
		
			
				|  |  | +      this.#loaded_image = btoa(as_string);
 | 
	
		
			
				|  |  | +      this.#loaded_image_type = file.type;
 | 
	
		
			
				|  |  | +      this.#update_image_preview();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get #ready_image() {
 | 
	
		
			
				|  |  | +      if (this.#loaded_image === null) {
 | 
	
		
			
				|  |  | +        throw new Error("Loady any image first.");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      return this.#loaded_image;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      async #submit() {
 | 
	
		
			
				|  |  |        const product2 = new product_base();
 | 
	
	
		
			
				|  | @@ -1709,8 +1882,7 @@
 | 
	
		
			
				|  |  |        product2.author = this.#author();
 | 
	
		
			
				|  |  |        product2.stock_count = this.#stock_count();
 | 
	
		
			
				|  |  |        product2.barcode = this.#barcode();
 | 
	
		
			
				|  |  | -      const image = await this.#code_image();
 | 
	
		
			
				|  |  | -      const request2 = new create_request(product2, image);
 | 
	
		
			
				|  |  | +      const request2 = new create_request(product2, this.#ready_image);
 | 
	
		
			
				|  |  |        const response = await request2.connect();
 | 
	
		
			
				|  |  |        if (!response.result) {
 | 
	
		
			
				|  |  |          throw new Error(response.cause);
 | 
	
	
		
			
				|  | @@ -1731,6 +1903,314 @@
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  // application/scripts/import_process_fail.js
 | 
	
		
			
				|  |  | +  var import_process_fail = class {
 | 
	
		
			
				|  |  | +    #product;
 | 
	
		
			
				|  |  | +    #error;
 | 
	
		
			
				|  |  | +    constructor(product2, error) {
 | 
	
		
			
				|  |  | +      this.#product = product2;
 | 
	
		
			
				|  |  | +      this.#error = error;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get error() {
 | 
	
		
			
				|  |  | +      return this.#error;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get product() {
 | 
	
		
			
				|  |  | +      return this.#product;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // application/scripts/database.js
 | 
	
		
			
				|  |  | +  var database = class {
 | 
	
		
			
				|  |  | +    #content;
 | 
	
		
			
				|  |  | +    #processed;
 | 
	
		
			
				|  |  | +    #on_skip;
 | 
	
		
			
				|  |  | +    constructor(content) {
 | 
	
		
			
				|  |  | +      this.#content = content;
 | 
	
		
			
				|  |  | +      this.#processed = /* @__PURE__ */ new Map();
 | 
	
		
			
				|  |  | +      this.#on_skip = null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    #append(target) {
 | 
	
		
			
				|  |  | +      if (this.#processed.has(target.barcode)) {
 | 
	
		
			
				|  |  | +        this.#processed.get(target.barcode).stock_count += 1;
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      this.#processed.set(target.barcode, target);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    #validate(target) {
 | 
	
		
			
				|  |  | +      if (!("id" in target)) {
 | 
	
		
			
				|  |  | +        throw new Error("One of item has no ID.");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (!("title" in target)) {
 | 
	
		
			
				|  |  | +        throw new Error("Product " + target.barcode + " has no title.");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (!("author" in target)) {
 | 
	
		
			
				|  |  | +        throw new Error("Product " + target.barcode + " has no author.");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    #convert(target) {
 | 
	
		
			
				|  |  | +      this.#validate(target);
 | 
	
		
			
				|  |  | +      const product2 = new product_base();
 | 
	
		
			
				|  |  | +      product2.name = target.title;
 | 
	
		
			
				|  |  | +      product2.description = "";
 | 
	
		
			
				|  |  | +      product2.author = target.author;
 | 
	
		
			
				|  |  | +      product2.stock_count = 1;
 | 
	
		
			
				|  |  | +      product2.barcode = target.id;
 | 
	
		
			
				|  |  | +      return product2;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    on_skip(target) {
 | 
	
		
			
				|  |  | +      this.#on_skip = target;
 | 
	
		
			
				|  |  | +      return this;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    process() {
 | 
	
		
			
				|  |  | +      this.#processed.clear();
 | 
	
		
			
				|  |  | +      if (!(this.#content instanceof Array)) {
 | 
	
		
			
				|  |  | +        throw new Error("Database woud be array of objects.");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      this.#content.forEach((count) => {
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +          const product2 = this.#convert(count);
 | 
	
		
			
				|  |  | +          this.#append(product2);
 | 
	
		
			
				|  |  | +        } catch (error) {
 | 
	
		
			
				|  |  | +          if (this.#on_skip === null) {
 | 
	
		
			
				|  |  | +            return;
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          try {
 | 
	
		
			
				|  |  | +            this.#on_skip(new import_process_fail(count, error));
 | 
	
		
			
				|  |  | +          } catch (fail) {
 | 
	
		
			
				|  |  | +            console.log(fail);
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +      return this;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    results() {
 | 
	
		
			
				|  |  | +      return Array.from(this.#processed.values());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // application/scripts/product_response.js
 | 
	
		
			
				|  |  | +  var product_response = class extends bool_response {
 | 
	
		
			
				|  |  | +    #product;
 | 
	
		
			
				|  |  | +    constructor(target) {
 | 
	
		
			
				|  |  | +      super(target);
 | 
	
		
			
				|  |  | +      this.#product = null;
 | 
	
		
			
				|  |  | +      if (this.result) {
 | 
	
		
			
				|  |  | +        if (!("product" in target)) {
 | 
	
		
			
				|  |  | +          throw new Error("Incomplete response with good status.");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        this.#product = new product(target.product);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get product() {
 | 
	
		
			
				|  |  | +      return this.#product;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // application/scripts/product_get_request.js
 | 
	
		
			
				|  |  | +  var product_get_request = class extends request {
 | 
	
		
			
				|  |  | +    #barcode;
 | 
	
		
			
				|  |  | +    constructor(barcode) {
 | 
	
		
			
				|  |  | +      super();
 | 
	
		
			
				|  |  | +      this.#barcode = barcode;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get _response() {
 | 
	
		
			
				|  |  | +      return product_response;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get data() {
 | 
	
		
			
				|  |  | +      return null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get method() {
 | 
	
		
			
				|  |  | +      return "GET";
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    get url() {
 | 
	
		
			
				|  |  | +      return "/product/get/barcode/" + new String(this.#barcode);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // application/scripts/import_loop.js
 | 
	
		
			
				|  |  | +  var import_loop = class {
 | 
	
		
			
				|  |  | +    #content;
 | 
	
		
			
				|  |  | +    #failed;
 | 
	
		
			
				|  |  | +    #on_autocomplete;
 | 
	
		
			
				|  |  | +    #on_create;
 | 
	
		
			
				|  |  | +    #on_single_fail;
 | 
	
		
			
				|  |  | +    #on_skip;
 | 
	
		
			
				|  |  | +    #on_single_success;
 | 
	
		
			
				|  |  | +    #finally;
 | 
	
		
			
				|  |  | +    on_autocomplete(target) {
 | 
	
		
			
				|  |  | +      this.#on_autocomplete = target;
 | 
	
		
			
				|  |  | +      return this;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    on_create(target) {
 | 
	
		
			
				|  |  | +      this.#on_create = target;
 | 
	
		
			
				|  |  | +      return this;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    on_single_fail(target) {
 | 
	
		
			
				|  |  | +      this.#on_single_fail = target;
 | 
	
		
			
				|  |  | +      return this;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    on_skip(target) {
 | 
	
		
			
				|  |  | +      this.#on_skip = target;
 | 
	
		
			
				|  |  | +      return this;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    on_single_success(target) {
 | 
	
		
			
				|  |  | +      this.#on_single_success = target;
 | 
	
		
			
				|  |  | +      return this;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    finally(target) {
 | 
	
		
			
				|  |  | +      this.#finally = target;
 | 
	
		
			
				|  |  | +      return this;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    constructor(dataset) {
 | 
	
		
			
				|  |  | +      this.#content = dataset;
 | 
	
		
			
				|  |  | +      this.#failed = new Array();
 | 
	
		
			
				|  |  | +      this.#on_autocomplete = null;
 | 
	
		
			
				|  |  | +      this.#on_create = null;
 | 
	
		
			
				|  |  | +      this.#on_single_fail = null;
 | 
	
		
			
				|  |  | +      this.#on_skip = null;
 | 
	
		
			
				|  |  | +      this.#on_single_success = null;
 | 
	
		
			
				|  |  | +      this.#finally = null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    async #autocomplete(target) {
 | 
	
		
			
				|  |  | +      if (this.#on_autocomplete !== null) {
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +          this.#on_autocomplete(target);
 | 
	
		
			
				|  |  | +        } catch (error) {
 | 
	
		
			
				|  |  | +          console.log(error);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      const request2 = new autocomplete_request(target.barcode);
 | 
	
		
			
				|  |  | +      const response = await request2.connect();
 | 
	
		
			
				|  |  | +      if (!response.result) {
 | 
	
		
			
				|  |  | +        throw new Error(response.cause);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      const found = response.found;
 | 
	
		
			
				|  |  | +      target.description = found.description;
 | 
	
		
			
				|  |  | +      if (found.image.length === 0) {
 | 
	
		
			
				|  |  | +        throw new Error("Image for " + target.barcode + " not found.");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      return new create_request(target, found.image);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    async process() {
 | 
	
		
			
				|  |  | +      for (const count of this.#content) {
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +          await this.#create(count);
 | 
	
		
			
				|  |  | +        } catch (error) {
 | 
	
		
			
				|  |  | +          console.log(error);
 | 
	
		
			
				|  |  | +          if (this.#on_single_fail !== null) {
 | 
	
		
			
				|  |  | +            try {
 | 
	
		
			
				|  |  | +              this.#on_single_fail(count);
 | 
	
		
			
				|  |  | +            } catch (error2) {
 | 
	
		
			
				|  |  | +              console.log(error2);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +          this.#failed.push(count);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (this.#finally !== null) {
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +          this.#finally(this.#failed);
 | 
	
		
			
				|  |  | +        } catch (error) {
 | 
	
		
			
				|  |  | +          console.log(error);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      return this;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    async #exists(target) {
 | 
	
		
			
				|  |  | +      const request2 = new product_get_request(target.barcode);
 | 
	
		
			
				|  |  | +      const response = await request2.connect();
 | 
	
		
			
				|  |  | +      return response.product !== null;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    async #create(target) {
 | 
	
		
			
				|  |  | +      if (await this.#exists(target)) {
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +          this.#on_skip(target);
 | 
	
		
			
				|  |  | +        } catch (error) {
 | 
	
		
			
				|  |  | +          console.log(error);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      const request2 = await this.#autocomplete(target);
 | 
	
		
			
				|  |  | +      if (this.on_create !== null) {
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +          this.#on_create(target);
 | 
	
		
			
				|  |  | +        } catch (error) {
 | 
	
		
			
				|  |  | +          console.log(error);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      const response = await request2.connect();
 | 
	
		
			
				|  |  | +      if (!response.result) {
 | 
	
		
			
				|  |  | +        throw new Error(response.cause);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      if (this.#on_single_success !== null) {
 | 
	
		
			
				|  |  | +        try {
 | 
	
		
			
				|  |  | +          this.#on_single_success(target);
 | 
	
		
			
				|  |  | +        } catch (error) {
 | 
	
		
			
				|  |  | +          console.log(error);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // application/scripts/import_products.js
 | 
	
		
			
				|  |  | +  var import_products = class extends formscreen {
 | 
	
		
			
				|  |  | +    #file;
 | 
	
		
			
				|  |  | +    #content;
 | 
	
		
			
				|  |  | +    get _name() {
 | 
	
		
			
				|  |  | +      return "Import products JSON";
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    _build_form() {
 | 
	
		
			
				|  |  | +      this._create_input("file", "Database:", "", (input) => {
 | 
	
		
			
				|  |  | +        this.#file = input;
 | 
	
		
			
				|  |  | +        input.type = "file";
 | 
	
		
			
				|  |  | +        input.accept = "application/json";
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    async #load_file() {
 | 
	
		
			
				|  |  | +      if (this.#file.files.length === 0) {
 | 
	
		
			
				|  |  | +        throw new Error("Select JSON products database first.");
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      const file = this.#file.files.item(0);
 | 
	
		
			
				|  |  | +      const text = await file.text();
 | 
	
		
			
				|  |  | +      return JSON.parse(text);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    async _process() {
 | 
	
		
			
				|  |  | +      try {
 | 
	
		
			
				|  |  | +        this._info = "Loading file...";
 | 
	
		
			
				|  |  | +        this.#content = await this.#load_file();
 | 
	
		
			
				|  |  | +        this._info = "Parsing file to dataset...";
 | 
	
		
			
				|  |  | +        const dataset = new database(this.#content).on_skip((fail) => {
 | 
	
		
			
				|  |  | +          this._info = "Skipping " + fail.product.barcode + "...";
 | 
	
		
			
				|  |  | +        }).process().results();
 | 
	
		
			
				|  |  | +        const loop = new import_loop(dataset).on_autocomplete((target) => {
 | 
	
		
			
				|  |  | +          this._info = "Searching for " + target.barcode + "...";
 | 
	
		
			
				|  |  | +        }).on_create((target) => {
 | 
	
		
			
				|  |  | +          this._info = "Creating " + target.barcode + "...";
 | 
	
		
			
				|  |  | +        }).on_single_fail((target) => {
 | 
	
		
			
				|  |  | +          this._info = "Can not add " + target.barcode + "...";
 | 
	
		
			
				|  |  | +        }).on_skip((target) => {
 | 
	
		
			
				|  |  | +          this._info = "Skipping " + target.barcode + "...";
 | 
	
		
			
				|  |  | +        }).on_single_success((target) => {
 | 
	
		
			
				|  |  | +          this._info = "Created " + target.barcode + " success.";
 | 
	
		
			
				|  |  | +        }).finally((broken) => {
 | 
	
		
			
				|  |  | +          searcher.reload();
 | 
	
		
			
				|  |  | +          if (broken.length === 0) {
 | 
	
		
			
				|  |  | +            this._success = "All items imported.";
 | 
	
		
			
				|  |  | +            setTimeout(() => {
 | 
	
		
			
				|  |  | +              this.hide();
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  | +          } else {
 | 
	
		
			
				|  |  | +            console.log(broken);
 | 
	
		
			
				|  |  | +            this._success = "Not all items imported...";
 | 
	
		
			
				|  |  | +          }
 | 
	
		
			
				|  |  | +        }).process();
 | 
	
		
			
				|  |  | +      } catch (error) {
 | 
	
		
			
				|  |  | +        console.log(error);
 | 
	
		
			
				|  |  | +        this._error = new String(error);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    // application/scripts/login_bar.js
 | 
	
		
			
				|  |  |    var login_bar = class {
 | 
	
		
			
				|  |  |      #manager;
 | 
	
	
		
			
				|  | @@ -1772,9 +2252,17 @@
 | 
	
		
			
				|  |  |        add_product_button.classList.add("add-product-button");
 | 
	
		
			
				|  |  |        add_product_button.classList.add("material-icons");
 | 
	
		
			
				|  |  |        target.appendChild(add_product_button);
 | 
	
		
			
				|  |  | +      const import_products_button = document.createElement("button");
 | 
	
		
			
				|  |  | +      import_products_button.innerText = "dataset_linked";
 | 
	
		
			
				|  |  | +      import_products_button.classList.add("material-icons");
 | 
	
		
			
				|  |  | +      import_products_button.classList.add("import-products-button");
 | 
	
		
			
				|  |  | +      target.appendChild(import_products_button);
 | 
	
		
			
				|  |  |        add_product_button.addEventListener("click", () => {
 | 
	
		
			
				|  |  |          new product_adder().show();
 | 
	
		
			
				|  |  |        });
 | 
	
		
			
				|  |  | +      import_products_button.addEventListener("click", () => {
 | 
	
		
			
				|  |  | +        new import_products().show();
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  |        logout_button.addEventListener("click", () => {
 | 
	
		
			
				|  |  |          this.#manager.logout();
 | 
	
		
			
				|  |  |          location.reload();
 |