    importPackage(Packages.de.elo.ix.client);

    //@include lib_Class.js
    //@include lib_sol.common.Cache.js
    //@include lib_sol.common.Config.js
    //@include lib_sol.common.IxUtils.js

    importPackage(java.awt);
    importPackage(java.awt.event);
    importPackage(java.io);
    importPackage(java.lang);
    importPackage(java.util);
    importPackage(javax.imageio);
    importPackage(javax.swing);
    importPackage(javax.swing.filechooser);
    importPackage(com.github.sarxos.webcam);

    /**
     * This class provides functions to control webcams
     * e.g for capturing pictures
     *
     * @author JHR, ELO Digital Office GmbH
     * @version 1.0
     *
     * @elojc
     */
    sol.define("sol.common.jc.WebcamUtils", {

      singleton: true,

      userOptionSelectedIndex: "ELOjc.WebcamUtils.selectedIndex",

      dlgResult: {},

      /**
       * @private
       * Adjust Viewport of scrollPane
       * @param {javax.swing.JScrollPane} scrollPane
       * @param {java.awt.Dimension} webCamViewSize
       * @param {Object} cropImageDimension
       * @return {java.awt.Rectangle} Viewport Rectangle
       *
       */
      adjustViewport: function (scrollPane, webCamViewSize, cropImageDimension) {
        var me = this,
            w0, h0, w1, h1, vpRect, vp;

        w0 = webCamViewSize.getWidth();
        h0 = webCamViewSize.getHeight();
        w1 = cropImageDimension.width;
        h1 = cropImageDimension.height;
        vpRect = me.getViewportRectangle(w0, h0, w1, h1);
        vp = scrollPane.getViewport();
        vp.setViewPosition(new Point(0, 0));
        scrollPane.setBounds(10, 40, vpRect.width, vpRect.height);
        if (vpRect.height > vpRect.width) {
          scrollPane.setBounds(10, 40, vpRect.width, vpRect.height + 27);
          vp.setViewPosition(new Point(((w0 - scrollPane.getWidth()) / 2), 0));
        }
        if (vpRect.height < vpRect.width) {
          scrollPane.setBounds(10, 40, vpRect.width + 27, vpRect.height);
          vp.setViewPosition(new Point(0, ((h0 - scrollPane.getHeight() + 13) / 2)));
        }
        if (vpRect.height == vpRect.width) {
          scrollPane.setBounds(10, 40, vpRect.width, vpRect.height + 27);
          vp.setViewPosition(new Point(((w0 - scrollPane.getWidth()) / 2), 0));
        }
        return vpRect;
      },

       /**
       * @private
       * Get Viewport Rectangle
       * @param {Number} w0
       * @param {Number} h0
       * @param {Number} w1
       * @param {Number} h1
       * @return {java.awt.Rectangle} Viewport Rectangle
       */
      getViewportRectangle: function (w0, h0, w1, h1) {
        var x, y, w, h,
            scalex, scaley, rectangle;

        w0 = parseFloat(w0);
        h0 = parseFloat(h0);
        w1 = parseFloat(w1);
        h1 = parseFloat(h1);

        while ((h1 > h0) || (w1 > w0)) {
          h1 /= 2;
          w1 /= 2;
        }

        x = (w0 - w1) / 2;
        y = (h0 - h1) / 2;
        w = w1;
        h = h1;

        scalex = w1 / w0;
        scaley = h1 / h0;

        if (scalex < scaley) {
          x *= (scalex / 2);
          y = 0;
          h = h0;
          w = (w1 / h1 * h0);
        }

        if (scalex > scaley) {
          y *= (scaley / 2);
          x = 0;
          w = w0;
          h = (h1 / w1 * w0);
        }

        if (scalex == scaley) {
          y = 0;
          x = 0;
          w = w0;
          h = h0;
        }
        rectangle = new java.awt.Rectangle(x, y, w, h);
        return rectangle;
      },

      /**
       * Shows a webcam and get captured image
       */
      getImage: function () {
        var me = this,
            dlgWebcam, panel, scrollPane, okButton, cancelButton,
            webCamPanel, vp, acl, wc, webCamsIterator, wnames, i, maxIndex,
            webCam, webCams, selectedIndex, imageCaptured,
            cboWnames, vpRect, offsetX, offsetY, diff, cropImageDimension, webCamViewSize;
    // TODO aus config auslesen
        cropImageDimension = cropImageDimension || {
          width: "300",
          height: "300"
        };
    // TODO aus config auslesen
        wc = Webcam.getDefault();
        if (wc) {
          // get all webcams
          webCams = [];
          webCamsIterator = wc.getWebcams().iterator();
          while (webCamsIterator.hasNext()) {
            webCams.push(webCamsIterator.next());
          }
          // get webcam names
          wnames = [];
          for (i = 0; i < webCams.length; i++) {
            wnames.push(webCams[i].getName());
          }
          // build dialog
          if (webCams.length > 0) {
            selectedIndex = Number(archive.getUserOption(me.userOptionSelectedIndex, "5"));
            maxIndex = webCams.length - 1;
            if (selectedIndex > maxIndex) {
              selectedIndex = maxIndex;
              archive.setUserOption(me.userOptionSelectedIndex, maxIndex);
            }
            try {
              webCam = webCams[selectedIndex];
              webCam.close();
              // show image
              webCamViewSize = WebcamResolution.VGA.getSize();
              webCam.setViewSize(webCamViewSize);
              webCamPanel = new WebcamPanel(webCam);
              webCamPanel.setImageSizeDisplayed(true);
            } catch (e) {
            }
            panel = new JPanel();
            panel.add(webCamPanel);
            scrollPane = new JScrollPane (panel,
                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
            vpRect = me.adjustViewport(scrollPane, webCamViewSize, cropImageDimension);

            dlgWebcam = new JDialog(new Frame(), true);
            dlgWebcam.setResizable(false);
            dlgWebcam.setModal(true);
            dlgWebcam.setTitle(me.getText("sol.common.client.jc.webcamUtils.dialog.webcamTitle"));
            dlgWebcam.setLayout(null);

            dlgWebcam.add(scrollPane);

            acl = new JavaAdapter(ActionListener, {
              actionPerformed: function (ae) {
                if (ae.getSource() == okButton) {
                  // get image
                  imageCaptured = webCam.getImage();
                  vp = scrollPane.getViewport();
                  vpRect = vp.getViewRect();

                  diff = vpRect.x + vpRect.width - webCamViewSize.getWidth();
                  if (diff > 0) {
                    vpRect = new java.awt.Rectangle(vpRect.x, vpRect.y, vpRect.width - diff, vpRect.height);
                  }
                  diff = vpRect.y + vpRect.height - webCamViewSize.getHeight();
                  if (diff > 0) {
                    vpRect = new java.awt.Rectangle(vpRect.x, vpRect.y, vpRect.width, vpRect.height - diff);
                  }
                  // webcam release
                  webCam.close();
                  dlgWebcam.setModal(false);
                  dlgWebcam.dispose();
                  me.dlgResult = { result: "OK", capturedImage: imageCaptured, viewRect: vpRect };
                  return;
                }
                if (ae.getSource() == cancelButton) {
                  webCam.close();
                  dlgWebcam.setModal(false);
                  dlgWebcam.dispose();
                  me.dlgResult = { result: "Cancel" };
                  return;
                }
                if (ae.getSource() == cboWnames) {
                  try {
                    selectedIndex = cboWnames.getSelectedIndex();
                    webCam = webCams[selectedIndex];
                    archive.setUserOption(me.userOptionSelectedIndex, selectedIndex);
                    webCam.close();
                    // show image
                    webCamViewSize = WebcamResolution.VGA.getSize();
                    webCam.setViewSize(webCamViewSize);
                    webCamPanel = new WebcamPanel(webCam);
                    webCamPanel.setImageSizeDisplayed(true);
                    // update panel with selected webcam
                    panel.removeAll();
                    panel.add(webCamPanel);
                    vpRect = me.adjustViewport(scrollPane, webCamViewSize, cropImageDimension);
                  } catch (e) {
                    me.dlgResult = { result: "Exception", message: e.message };
                  }
                  return;
                }
              }
            });

            cboWnames = new JComboBox(wnames);
            cboWnames.setBounds (10, 10, scrollPane.getWidth(), 20);
            cboWnames.setSelectedIndex(selectedIndex);
            cboWnames.addActionListener(acl);
            dlgWebcam.add(cboWnames);

            offsetX = scrollPane.getX() + scrollPane.getWidth();
            offsetY = scrollPane.getY() + scrollPane.getHeight() + 10;

            okButton = new JButton (me.getText("sol.common.client.jc.webcamUtils.dialog.btnOk"));
            okButton.setBounds (offsetX - 210, offsetY, 100, 20);
            okButton.addActionListener(acl);
            dlgWebcam.add(okButton);

            cancelButton = new JButton (me.getText("sol.common.client.jc.webcamUtils.dialog.btnCancel"));
            cancelButton.setBounds (offsetX - 100, offsetY, 100, 20);
            cancelButton.addActionListener(acl);
            dlgWebcam.add(cancelButton);

            dlgWebcam.setSize(10 + scrollPane.getWidth() + 30, offsetY + 70);
            dlgWebcam.setVisible(true);
            return;
          }
        } else {
          workspace.showInfoBox("ELO", me.getText("sol.common.client.jc.webcamUtils.msg.noWebcam"));
          return;
        }

      },

      /**
       * @private
       * Save image as jpg file
       * @param {java.awt.image.BufferedImage} image image
       * @param {String} path path
       * @return {Boolean}
       */
      saveImageAsJpg: function (image, path) {
        var me = this,
            chooser, file, imageFilter, result;

        if (path == null) {
          path = System.getProperty("user.home");
        }
        file = new File(path.trim());
        chooser = new JFileChooser(path);
        chooser.setDialogType(JFileChooser.SAVE_DIALOG);
        imageFilter = new FileNameExtensionFilter("Image: jpg", "jpg");
        chooser.removeChoosableFileFilter(chooser.getAcceptAllFileFilter());
        chooser.setFileFilter(imageFilter);
        chooser.setDialogTitle(me.getText("sol.common.client.jc.webcamUtils.chooser.saveTitle"));
        chooser.setVisible(true);
        result = chooser.showSaveDialog(null);
        if (result == JFileChooser.APPROVE_OPTION) {
          path = chooser.getSelectedFile().toString();
          file = new File(path);
          if (imageFilter.accept(file)) {
            ImageIO.write(image, "JPG", file);
            workspace.showInfoBox("ELO", path + " " + me.getText("sol.common.client.jc.webcamUtils.msg.isSaved"));
          } else {
            path += ".jpg";
            file = new File(path);
            ImageIO.write(image, "JPG", file);
            workspace.showInfoBox("ELO", path + " " + me.getText("sol.common.client.jc.webcamUtils.msg.isSaved"));
          }
          chooser.setVisible(false);
          return true;
        }
        chooser.setVisible(false);
        return false;
      },

      /**
       * @private
       * Get cropped image
       * @param {java.awt.image.BufferedImage} image image
       * @param {Number} x
       * @param {Number} y
       * @param {Number} w
       * @param {Number} h
       * @return{java.awt.image.BufferedImage} cropped image
       */
      cropImage: function (image, x, y, w, h) {
        return image.getSubimage(x, y, w, h);
      },

      /**
       * @private
       * Convert image to base64
       * @param {java.awt.image.BufferedImage} image image
       * @param {String} type image type, e.g "JPG"
       * @return {String} converted Base64 String
       */
      convertImageToBase64String: function (image, type) {
        var imageString, bos, imageBytes, encoder;

        imageString = null;
        bos = new java.io.ByteArrayOutputStream();
        try {
          ImageIO.write(image, type, bos);
          imageBytes = bos.toByteArray();

          encoder = Base64.getEncoder();
          imageString = encoder.encodeToString(imageBytes);
          bos.close();
        } catch (e) {
        }
        return imageString;
      },

      /**
       * @private
       * Convert image to base64
       * @param {String} imageString base64 String
       * @return {java.awt.image.BufferedImage} image image
       */
      convertBase64StringToImage: function (imageString) {
        var image, bis, imageBytes, decoder;

        image = null;
        try {
          decoder = Base64.getDecoder();
          imageBytes = decoder.decode(imageString);
          bis = new ByteArrayInputStream(imageBytes);
          image = ImageIO.read(bis);
          bis.close();
        } catch (e) {
        }
        return image;
      },

      /**
       * @private
       * Show image
       * @param {java.awt.image.BufferedImage} image image
       */
      showImage: function (image) {
        var me = this,
            dlg, imagePanel, imageLabel;

        try {
          dlg = workspace.createGridDialog(me.getText("sol.common.client.jc.webcamUtils.dialog.imageTitle"), 1, 1);
          imagePanel = new JPanel();
          imageLabel = new JLabel(new ImageIcon(image));
          imagePanel.add(imageLabel);
          dlg.gridPanel.setGrowing([1], [1]);
          dlg.gridPanel.addComponent(1, 1, 1, 1, imagePanel);
          dlg.show();
        } catch (e) {
        }
      },

      /**
       * @private
       * show base64
       * @param {String} base64 base64
       */
      showBase64String: function (base64) {
        var me = this,
            dlg, ta;

        dlg = workspace.createGridDialog(me.getText("sol.common.client.jc.webcamUtils.dialog.base64Title"), 1, 1);
        dlg.gridPanel.setGrowing([1], [1]);
        ta = dlg.gridPanel.addTextArea(1, 1, 1, 1);
        ta.setText(base64);
        ta.setEditable(false);
        dlg.show();
      },

      /**
       * Helper function that returns localized text constants
       * @param {String} key Key of the text constants
       * @return {String} Localized text constant
       */
      getText: function (key) {
        return utils.getText("sol.common", key);
      }

    });

    // Receive Message from ELOwf Formular
    function eloReceiveBrowserMessage(msg, compName, browserComponent) {
      var respObject, browserComp, image, strBase64,
          dlgResult, capturedImage, viewRect,
          result;

      browserComp = browserComponent;
      if (msg.name == "sol.common.jc.WebcamUtils.getImage") {
        strBase64 = null;
        cropImageDimension = msg.data.cropImageDimension;
        sol.common.jc.WebcamUtils.getImage();
        dlgResult = sol.common.jc.WebcamUtils.dlgResult;
        if (dlgResult) {
          if (dlgResult.result == "OK") {
            capturedImage = dlgResult.capturedImage;
            viewRect = dlgResult.viewRect;
            image = sol.common.jc.WebcamUtils.cropImage(capturedImage, viewRect.x, viewRect.y, viewRect.width, viewRect.height);
            strBase64 = String(sol.common.jc.WebcamUtils.convertImageToBase64String(image, "jpg"));
          }
        } /*
        if (strBase64) {
        try {
            result = sol.common.IxUtils.execute("RF_sol_dev_service_UploadPhoto", {
              visitorObjId: msg.data.objId,
              base64Content: strBase64
            });
            if (result.objId) {
              workspace.gotoId(parseInt(result.objId));
            }
          } catch (e) {
            workspace.showAlertBox("ELO", sol.common.jc.WebcamUtils.getText("sol.common.client.jc.webcamUtils.error.saveImage") + " " + e.message);
          }

        } else {
          workspace.showInfoBox("ELO", sol.common.jc.WebcamUtils.getText("sol.common.client.jc.webcamUtils.msg.noImageCaptured"));
        }*/
        respObject = { response: strBase64 };
        browserComp.sendCustomResponse(msg, respObject);
      }
    }