    /**
     * @private
     * Utility-Class that helps capture webcam images.
     *
     * It's needed to declare a text with 'WEBCAM_INIT' on the varname-field, to get the webcam container.
     * It's also needed to declare two buttons with 'JS_WEBCAM_SNAP' - (captures a preview).
     * And 'JS_WEBCAM_RESET' - (clears the freeze).
     */
    sol.define("sol.common.forms.Webcam", {

      /**
       * Initialize the webcam
       * @param {Object} config Configuration
       */
      initialize: function (config) {

        var me = this,
            webcamContainer = $var(config.varNameContainer),
            resetBtn = $var(config.varNameBtnReset),
            snapBtn = $var(config.varNameBtnSnap),
            sBrowser,
            sUsrAg = navigator.userAgent,
            objectElements,
            forceFlash = false;

        me.captured = false;
        me.varNameBtnSnap = config.varNameBtnSnap;
        me.varNameBtnReset = config.varNameBtnReset;
        me.varNameContainer = config.varNameContainer;
        me.webcamContainer = webcamContainer;

        sol.common.forms.Utils.initializeIxSession();

        if (resetBtn) {
          resetBtn.disabled = true;
        }

        if (snapBtn) {
          snapBtn.disabled = false;
        }

        if (sUsrAg.indexOf("Chrome/") > -1) {
          sBrowser = "Google Chrome";
        }

        // https?
        if (window.location.protocol == "http:" && sBrowser == "Google Chrome") {
          forceFlash = true;
        }

        if (webcamContainer) {

          Webcam.set({
            width: config.width,
            height: config.height,
            dest_width: config.dest_width,
            dest_height: config.dest_height,
            crop_width: config.crop_width,
            crop_height: config.crop_height,
            image_format: config.image_format,
            jpeg_quality: config.jpeg_quality,
            swfURL: config.swfURL,
            force_flash: forceFlash,
            fps: config.fps
          });

          var webcamConnected = undefined;
          Webcam.on("error", function (err) {
            webcamConnected = false;
          });

          Webcam.attach(config.varNameContainer);

          var container = document.getElementById(config.varNameContainer);
          var childNodes = container && Array.prototype.slice.call(container.childNodes);
          var children = childNodes.length === 2 && childNodes[1].children;
          var swfObject = (children && children.length > 0 && children[0].tagName === "OBJECT") && children[0];
          swfObject && childNodes[1].classList.add("sol-webcam");

          objectElements = webcamContainer.getElementsByTagName("object");
          if (objectElements && (objectElements.length > 0)) {
            objectElements[0].value = "";
          }
          me.intervalUntil(
            function () {
              return true;
            },
            function () { // until webcam is loaded or loading failed
              return (Webcam.loaded || typeof webcamConnected !== "undefined");
            },
            function () {
              function removeSwf() {
                if (!config.showIfNoCam) {
                  webcamContainer.style.display = "none";
                  $hide(config.varNameContainer);
                }
                console.log("No webcam found");

                $hide(config.varNameBtnSnap);
                $hide(config.varNameBtnReset);
                swfObject && swfObject.parentElement.removeChild(swfObject);
              }
              (typeof webcamConnected !== "undefined") &&
                removeSwf();
            },
            50,
            { name: "loading webcam library", logging: true }
          );
        }
      },
      /**
       * freeze camera, user get's a preview picture and swap the button sets.
       */
      snap: function () {
        var me = this;
        me.state = true;
        Webcam.freeze();
        me.toggle();
      },
      /**
       * Cancel preview freeze and return to live camera feed and swap buttons back.
       */
      reset: function () {
        var me = this;
        me.state = false;
        me.capture = false;
        Webcam.unfreeze();
        me.toggle();
      },

      toggle: function () {
        var me = this;
        $var(me.varNameBtnSnap).disabled = !$var(me.varNameBtnSnap).disabled;
        $var(me.varNameBtnReset).disabled = !$var(me.varNameBtnReset).disabled;
      },

      /**
       * Saves the snapshot in the folder that was selected for this workflow.
       * @param {Function} saveFunc Save Function
       */
      savePicture: function (saveFunc) {
        var me = this;

        if (me.state && !me.captured) {
          Webcam.snap(function (data_uri) {
            var image;

            ELOF.showLoadingDiv();
            image = data_uri.replace(/^data:image\/(jpeg|png|gif|bmp);base64,/, "");

            if (saveFunc(image)) {
              console.info("Successful saved");
              me.captured = true;
            } else {
              me.reset();
            }
          });
        }
      },
      //small wrapper around interval functions
      //This function is 100% decoupled from any ELO-specifics and can be used in pure Javascript environments
      /*
        f: function to perform work
        until: function to check if f should be run again or not
        after: function to run after until conditions were met
        intervalTime: time in ms (interval runs every x ms)
        params: {
          f: any            pass f any param you like
          f_message:String  console.log before f is called
          until:any   pass any any param you like
          until_message: String  console.log before until is called
          conditionmet_message:String  console.log if contition is met
          after:any   pass after any param you like
          afterafter_message:String   console.log after after was called
          logging: Boolean    enables logging if set to true
          maxtries: Integer     how often the f function will be executed before the interval is stopped
          name: String          name in logger
        }
        if the messages are defined as empty strings, no console-logging will be done.

        Of course the execution time of f and until is synchronized. f always runs before until.
        returns a function, which can later be used to stop the interval, if the until condition seems to never reach an end
      */
      intervalUntil: function (f, until, after, intervalTime, params) {
        var intervals = [], parms = params || {}, maxTries = params.maxtries - 1, executed = 0,
            allowF = true, allowUntil = false; //for synchronizing f and until
        function logIt(message, paramMsg) {
          parms.logging && paramMsg !== "" && console.log("Interval " + (parms.name ? "'" + parms.name + "'" : "") + ":" + (paramMsg || message));
        }

        intervals.push(
          setInterval(  //setInterval is a standard Javascript function
            function () {
              if (!allowF) {
                return;
              }
              logIt("performing work", parms.f_message);
              f && f(parms.f);
              executed++;
              allowF = false;
              allowUntil = true;
            }, intervalTime
          )
        );
        intervals.push(
          setInterval(
            function () {
              if (!allowUntil) {
                return;
              }
              logIt("until condition met?", parms.until_message);
              if (parms.maxtries && maxTries-- === 0 || until && until(parms.until)) {
                logIt(maxTries === 0 ? "Tried too many times:" + parms.maxTries : "until condition met!", maxTries === 0 ? parms.maxTriesMessage : parms.conditionmet_message);
                intervals.forEach(clearInterval);   //clearInterval is a standard Javascript function
                after && after(parms.after);
                logIt("after callback executed. Interval finished & closed after " + executed + " cycles!", parms.afterafter_message);
              }
              allowF = true;
              allowUntil = false;
            }, intervalTime
          )
        );
        return function () {
          intervals.forEach(clearInterval);
          logIt("closed manually after " + executed + " cycles!", parms.clearedManually);
        };
      },
    });