/*!
 *  Monday Webforms for CoreMedia v1.9.3, (c) 2018 Monday Consulting GmbH
 *
 *  @see https://monday-webforms.com/
 */

/*global define, window, document, jQuery, exports, require */

;(function(factory) {
  "use strict";
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['jquery'], factory);
  } else if (typeof exports === 'object' && typeof require === 'function') {
    // Node/CommonJS
    factory(require('jquery'));
  } else {
    // Browser globals
    factory(jQuery);
  }
}(function($) {
  "use strict";

  // polyfill for customevent for IE10
  (function() {
    function CustomEvent(event, params) {
      params = params || {bubbles: false, cancelable: false, detail: undefined};
      var evt = document.createEvent('CustomEvent');
      evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
      return evt;
    }

    CustomEvent.prototype = window.Event.prototype;

    window.CustomEvent = CustomEvent;
  })();

  var EVENTS = {
      AJAX_FINISHED: "mwf-ajax-finished",
      AJAX_ERROR: "mwf-ajax-error",
      FILL_DROPDOWN: "mwf-fill-dropdown",
      FILL_SELECTION: "mwf-fill-selection",
      FILL_HIDDEN: "mwf-fill-hidden",
      SUGGESTION_SELECTED: "mwf-suggestion-selected",
      VALUE_CHANGED: "mwf-value-changed"
    },

    globalSettings = {};

  function triggerEvent(el, eventName, options) {
    var event;
    if (window.CustomEvent) {
      event = new CustomEvent(eventName, options);
    } else {
      event = document.createEvent('CustomEvent');
      event.initCustomEvent(eventName, true, true, options);
    }
    el.dispatchEvent(event);
  }

  function globalSettingsWithVariant(variant) {
    return variant === undefined ?
      globalSettings.def : $.extend(true, {}, globalSettings.def, globalSettings[variant]);
  }

  /**
   * Monday Webforms jQuery-Plugin
   * @constructor
   */
  function Webforms(elem, settings) {
    var
      plugin = this,
      $elem = $(elem),
      operators = {},
      formdata = {},
      filenames = {},

      uiState = {
        visible: {},
        hidden: {},
        writable: {},
        readonly: {},
        optional: {},
        mandatory: {},
        disabled: {},
        enabled: {}
      },

      stateMapping = {
        visible: 'hidden',
        hidden: 'visible',
        writable: 'readonly',
        readonly: 'writable',
        optional: 'mandatory',
        mandatory: 'optional',
        disabled: 'enabled',
        enabled: 'disabled'
      },

      $form = $elem.is('form') ? $elem : $elem.find("form"),
      formId = $form.dataWithDefault('mwf-id', $form.data('mwf-form')),
      targetUrl = $form.dataWithDefault('mwf-target', settings.url),
      statisticsUrl = settings.statisticsUrl;

    if (settings.formdata !== undefined) {
      formdata = settings.formdata;
    }

    if (!statisticsUrl || 0 === statisticsUrl.length) {
      statisticsUrl = null;
    }

    plugin.settings = settings;

    /* internal utility functions
     -----------------------------------------------------------------------------------------------------------  */

    function updateUI(fields, conditionId, cond) {
      var s, i, inverseState, isTrue, isFalse, state, input,

        conditionalFieldsState = {
          'visible': [], 'hidden': [],
          'writable': [], 'readonly': [],
          'optional': [], 'mandatory': [],
          'enabled': [], 'disabled': []
        };

      // clear UI state of this condition
      for (s in uiState) {
        if (uiState.hasOwnProperty(s)) {
          delete uiState[s][conditionId];
        }
      }

      for (i = 0; i < fields.length; i++) {
        input = fields[i].input;
        state = fields[i].state;

        if (cond) {
          conditionalFieldsState[state].push(input);
        }

        uiState[state][conditionId] = conditionalFieldsState[state];

        inverseState = stateMapping[state];

        isTrue = containsState(state, input);
        isFalse = containsState(inverseState, input);

        if (isTrue) {
          plugin.settings.operations[state]($form, fields[i]);
          if (plugin.settings.onConditionTrue !== undefined) {
            plugin.settings.onConditionTrue($form, fields[i], state);
          }
        } else if (isFalse || !isTrue) {
          plugin.settings.operations[inverseState]($form, fields[i]);
          if (plugin.settings.onConditionFalse !== undefined) {
            plugin.settings.onConditionFalse($form, fields[i], inverseState);
          }
        }
      }
    }

    function containsState(type, input) {
      var state = uiState[type], cond;
      for (cond in state) {
        if (state.hasOwnProperty(cond) && $.inArray(input, state[cond]) > -1) {
          return true;
        }
      }
      return false;
    }

    function valueChanged($elem) {
      if (!$elem.is(':file')) {
        formdata = serializeForm($form);
        $form.trigger("mwf-valuechange", $elem);
      }
    }

    function startsWith(formValue, value) {
      if (formValue == null || value == null) {
        return false;
      }
      return formValue.match(new RegExp("^" + value)) == value;
    }

    function serializeForm($form) {
      var o = {}, a = $form.serializeArray(), form = $form.get(0);

      $.each(a, function() {
        var input = form.querySelector('[name="' + this.name + '"]'),
          value = this.value || '',
          type = input.getAttribute('type'),
          key;

        if (type === 'radio' || type === 'checkbox' || input.nodeName.toLowerCase() === 'select') {

          if (!o.hasOwnProperty(this.name)) {
            o[this.name] = [];
          }

          if (startsWith(value, '{')) {
            try {
              key = JSON.parse(value).k;
            } catch (ex) {
              // do nothing
            }
          } else if (value === 'EMPTY_VALUE') {
            value = '';
          }

          o[this.name].push((key !== undefined) ? key : value);

        } else if (type === 'hidden') {

          if (startsWith(value, '{')) {
            try {
              key = JSON.parse(value).v;
            } catch (ex) {
              // do nothing
            }
          }

          o[this.name] = (key !== undefined) ? key : value;
        } else {
          o[this.name] = value;
        }
      });

      $.each(form.querySelectorAll('input[type="checkbox"]:not(:checked),input[type="radio"]:not(:checked)'), function() {
        var name = this.getAttribute('name');
        if (!o.hasOwnProperty(name)) {
          o[name] = '';
        }
      });

      return o;
    }

    function valueLabel(name, v) {
      var l = [];

      $.each($form.find('[name="' + name + '"]'), function() {
        var $input = $(this);

        if ($input.is(':radio') || $input.is(':checkbox')) {
          if ($.inArray($input.attr('data-mwf-value'), v) !== -1) {
            l.push($input.attr('data-mwf-label'));
          }
        } else if ($input.is('select')) {
          $.each($input.find('option'), function() {
            var $option = $(this);

            if ($.inArray($option.attr('data-mwf-value'), v) !== -1) {
              l.push($option.attr('data-mwf-label'));
            }
          });
        } else if ($input.is(':file')) {
          $.merge(l, filenames[name]);
        } else {
          l.push(v);
        }
      });

      return l;
    }

    /* API functions
     -----------------------------------------------------------------------------------------------------------  */

    function maxSelected(array, max) {
      var m = Number(max), l = (array !== null) ? array.length : 0;
      return (l < m);
    }

    function minSelected(array, min) {
      var m = Number(min), l = (array !== null) ? array.length : 0;
      return (l > m);
    }

    function isSelected(array, s) {
      return array != null && $.inArray(s, array) > -1;
    }

    function parseAge(format, str) {
      if (str == null) {
        return null;
      }
      var date = parseDate(format, str), today = new Date();
      return Math.floor((today - date) / (365.25 * 24 * 60 * 60 * 1000));
    }

    function parseDate(format, date) {
      if (date == null) {
        return null;
      }
      return $.format.date(date, format);
    }

    function isEmptyList(array) {
      var i, s;
      if (array != null) {
        for (i = 0; i < array.length; i++) {
          s = String(array[i]);
          if (s !== null && s !== '' && s !== 'EMPTY_VALUE') {
            return false;
          }
        }
      }
      return true;
    }

    function ajaxFillSelection($elem, attr) {
      var data = $.extend(formdata, attr.data);

      if ((attr.createEntry === undefined)) {
        attr.createEntry = (attr.type === 'checkbox') ? plugin.settings.createCheckBox : plugin.settings.createRadio;
      }

      if ((attr.onFillSelection === undefined)) {
        attr.onFillSelection = plugin.settings.onFillSelection;
      }

      $.ajax({
        url: attr.url,
        type: 'POST',
        data: data,
        traditional: true,
        cache: false,
        xhrFields: { withCredentials: true } // gfi added
      })
        .done(function(json) {
          var pre = attr.preselected.split(','), i, entry;

          $elem.find('[data-mwf-mutable]').remove();

          for (i in json) {
            if (json.hasOwnProperty(i)) {
              entry = json[i];
              $elem.append(attr.createEntry($form, attr.name, entry, $.inArray(entry.k, pre) !== -1));
            }
          }

          attr.onFillSelection($form, $elem);
          triggerEvent(document, EVENTS.FILL_SELECTION, {detail: {$form: $form, $elem: $elem}});
        })
        .fail(function(jqXHR, textStatus, errorThrown) {
          plugin.settings.onAjaxError(jqXHR, textStatus, errorThrown);
        });
    }

    function ajaxFillHidden($elem, attr) {
      var data = $.extend(formdata, attr.data);

      $.ajax({
        url: attr.url,
        type: 'POST',
        data: data,
        traditional: true,
        cache: false,
        xhrFields: { withCredentials: true } // gfi added
      })
        .done(function(json) {
          $elem.val(JSON.stringify(json));
          $elem.attr('data-mwf-value', json.v);
          $elem.trigger('change');

          triggerEvent(document, EVENTS.FILL_HIDDEN, {detail: {$form: $form, $elem: $elem}});
        })
        .fail(function(jqXHR, textStatus, errorThrown) {
          plugin.settings.onAjaxError(jqXHR, textStatus, errorThrown);
        });
    }

    function ajaxFillDropdown($elem, attr) {
      var data = $.extend(formdata, attr.data);

      if ((attr.createOption === undefined)) {
        attr.createOption = plugin.settings.createOption;
      }

      if ((attr.onFillDropdown === undefined)) {
        attr.onFillDropdown = plugin.settings.onFillDropdown;
      }

      $.ajax({
        url: attr.url,
        type: 'POST',
        data: data,
        traditional: true,
        cache: false,
        xhrFields: { withCredentials: true } // gfi added
      })
        .done(function(json) {
          var $option, i, entry, pre = attr.preselected.split(',');

          $elem.find('[data-mwf-mutable]').remove();

          for (i in json) {
            if (json.hasOwnProperty(i)) {
              entry = json[i];
              $option = attr.createOption($form, entry, $.inArray(entry.k, pre) !== -1);
              $elem.append($option);
            }
          }

          attr.onFillDropdown($form, $elem);

          triggerEvent(document, EVENTS.FILL_DROPDOWN, {detail: {$form: $form, $elem: $elem}});
        })
        .fail(function(jqXHR, textStatus, errorThrown) {
          plugin.settings.onAjaxError(jqXHR, textStatus, errorThrown);
        });
    }

    function ajaxAutocomplete($elem, attr) {
      var chars = 1;

      if (attr.params.chars !== undefined) {
        chars = attr.params.chars;
      }

      var onSelect = function(selection) {
        triggerEvent(document, EVENTS.SUGGESTION_SELECTED, {
          detail: {
            $form: $form,
            $elem: $elem,
            id: formId,
            selection: selection,
            params: attr.params
          }
        });
        if ($.isFunction(attr.onSelect)) {
          attr.onSelect(arguments);
        }
      };

      $elem.autocomplete({
        paramName: 'query',
        params: attr.data,
        serviceUrl: attr.url,
        maxHeight: 150,
        noCache: true,
        minChars: chars,
        type: 'POST',
        onSelect: onSelect,
        autoSelectFirst: true,
        sendFormData: true,

        transformResult: function(response) {
          response = JSON.parse(response);
          return {
            suggestions: $.map(response, function(entry, key) {
              return {value: entry.v, data: entry.k};
            })
          };
        }
      });
    }

    function ajaxSubmit(opt) {
      var $buttons = $form.find(':button').filter(':not(:disabled)'),
        data = {_parentUrl: window.location.href};

      if (plugin.settings.appendUrlVars) {
        $.extend(data, $.getUrlVars(window.location.search));
        delete data.view;
      }

      $.extend(data, $.getUrlVars(opt.query), opt.data, $form.serializeObject());

      if (opt.onSubmit !== undefined) {
        if (!opt.onSubmit($form, opt.url, opt.query)) {
          return;
        }
      }

      $buttons.prop('disabled', true);

      $.ajax({
        url: opt.url,
        type: 'POST',
        data: data,
        traditional: true,
        cache: false,
        xhrFields: { withCredentials: true } // gfi added
      })
        .done(function(data, textStatus, jqXHR) {

          if (jqXHR && jqXHR.getResponseHeader("webforms_redirect")) {
            if (opt.onRedirect !== undefined) {
              opt.onRedirect($form, data, textStatus, jqXHR);
            }
            var timeout = (jqXHR.getResponseHeader('webforms_redirect_delay') || 0) * 1000;
            setTimeout(function() {
              location.assign(jqXHR.getResponseHeader('webforms_redirect'));
            }, timeout);

            if (!timeout) {
              return;
            }
          }

          $elem = $elem.replaceWithPush(data);
          $form = $elem.is('form') ? $elem : $elem.find("form");

          if (opt.onSuccess !== undefined) {
            opt.onSuccess($form, data, textStatus, jqXHR);
          }

          triggerEvent(document, EVENTS.AJAX_FINISHED, {detail: {$dest: $elem}});
        })
        .fail(function(jqXHR, textStatus, errorThrown) {
          if (opt.onError !== undefined) {
            opt.onError($form, jqXHR, textStatus, errorThrown);
          }

          $buttons.prop('disabled', false);
          triggerEvent(document, EVENTS.AJAX_ERROR, {
            detail: {
              $dest: $elem,
              jqXHR: jqXHR,
              textStatus: textStatus,
              errorThrown: errorThrown
            }
          })
        });
    }

    function createCondition(conditionId, conjunction, conditions, fields) {
      var checkCondition = function() {
        var all = true, any = false, condTrue, i, formValue, cond;

        for (i = 0; i < conditions.length; i++) {
          cond = conditions[i];

          if (!cond) {
            continue;
          }

          formValue = formdata[cond[1]];

          if (formValue === undefined) {
            formValue = cond[4];
          }
          condTrue = operators[cond[2]](formValue, cond[3]);

          if (condTrue) {
            any = true;
          } else {
            all = false;
          }

          if (!conjunction && any) {
            break;
          }
        }

        updateUI(fields, conditionId, (all || (!conjunction && any)));
      };

      // register event listener
      $form.on('mwf-valuechange', function(e, input) {
        var cond, i;

        for (i = 0; i < conditions.length; i++) {
          cond = conditions[i];

          if (!cond) {
            continue;
          }

          if ($(input).attr("name") === conditions[i][1]) {
            checkCondition();
          }
        }
      });

      // check conditions initially
      checkCondition();

      if (plugin.settings.onConditionCreated !== undefined) {
        plugin.settings.onConditionCreated($form, conditionId, conjunction, conditions, fields);
      }
    }

    function createCalculatedValue(calcValueId, js, fields, formVariables, updateValue) {
      updateValue = (updateValue === undefined) ? plugin.settings.updateCalculatedValue : updateValue;

      var name,
        id = calcValueId,
        vars = formVariables,
        fct,
        calculate = function() {
          var state = $.extend({}, fields, formdata, vars),
            result = fct(state),
            $field = $form.find('[data-mwf-id="' + id + '"]');

          updateValue($form, id, result);
          $field.val(result).trigger('change');
        };

      try {
        eval('fct = function(s) {' + js + '}');
      } catch (ex) {
        $.logError(ex.message);
      }

      // register event listener
      $form.on('mwf-valuechange', function(e, input) {
        for (name in fields) {
          if (fields.hasOwnProperty(name) && $(input).attr('name') === name) {
            calculate();
            break;
          }
        }
      });
    }

    function recursiveEach(obj, callback, maxDeep) {
      var history = [], deep = -1, _recursiveEach;

      if (maxDeep === undefined) {
        maxDeep = Number.MAX_VALUE;
      }

      _recursiveEach = function(obj, callback) {
        var p, o, i, names = Object.getOwnPropertyNames(obj || {});

        deep++;

        for (i = 0; i < names.length; i++) {
          p = names[i];
          o = obj[p];

          if (typeof o === "object") {
            if (deep < maxDeep && $.inArray(o, history) === -1) {
              history.push(o);
              _recursiveEach(o, callback);
            }
          } else {
            if (callback.call(obj, obj, p, o) === false) {
              break;
            }
          }
        }

        deep--;
      };

      _recursiveEach(obj, callback);
    }

    /* condition operators
     -----------------------------------------------------------------------------------------------------------  */

    operators.contains = function(formValue, value) {
      if (formValue == null || value == null) {
        return false;
      }
      return formValue.indexOf(value) !== -1;
    };

    operators.containsnot = function(formValue, value) {
      if (formValue == null || value == null) {
        return false;
      }
      return formValue.indexOf(value) === -1;
    };

    operators.startswith = function(formValue, value) {
      return startsWith(formValue, value);
    };

    operators.endswith = function(formValue, value) {
      if (formValue == null || value == null) {
        return false;
      }
      return formValue.match(new RegExp(value + "$")) == value;
    };

    operators.equal = function(formValue, value) {
      if (formValue == null || value == null) {
        return formValue === value;
      }
      var f1 = parseFloat(formValue.replace(',', '.')),
        f2 = parseFloat(value.replace(',', '.'));

      return (!isNaN(f1) && !isNaN(f2) && f1 === f2) || (formValue === value);
    };

    operators.notequal = function(formValue, value) {
      if (formValue == null || value == null) {
        return formValue !== value;
      }
      var f1 = parseFloat(formValue.replace(',', '.')),
        f2 = parseFloat(value.replace(',', '.'));

      return (!isNaN(f1) && !isNaN(f2) && f1 !== f2) || (formValue !== value);
    };

    operators.empty = function(formValue, value) {
      return formValue == null || formValue === '';
    };

    operators.notempty = function(formValue, value) {
      return formValue != null && formValue !== '';
    };

    operators.emptylist = function(formValue, value) {
      return isEmptyList(formValue);
    };

    operators.notemptylist = function(formValue, value) {
      return !isEmptyList(formValue);
    };

    operators.selected = function(formValue, value) {
      if (formValue == null || value == null) {
        return false;
      }
      return $.inArray(value, formValue) !== -1;
    };

    operators.notselected = function(formValue, value) {
      if (formValue == null || value == null) {
        return false;
      }
      return $.inArray(value, formValue) === -1;
    };

    operators.minselected = function(formValue, value) {
      var l = (formValue == null) ? 0 : formValue.length;
      return l > Number(value);
    };

    operators.maxselected = function(formValue, value) {
      var l = (formValue == null) ? 0 : formValue.length;
      return l < Number(value);
    };

    operators.lessthan = function(formValue, value) {
      if (formValue == null || value == null) {
        return false;
      }
      var f1 = parseFloat(formValue.replace(',', '.')),
        f2 = parseFloat(value.replace(',', '.'));

      return (!isNaN(f1) && !isNaN(f2) && f1 < f2);
    };

    operators.greaterthan = function(formValue, value) {
      if (formValue == null || value == null) {
        return false;
      }
      var f1 = parseFloat(formValue.replace(',', '.')),
        f2 = parseFloat(value.replace(',', '.'));

      return (!isNaN(f1) && !isNaN(f2) && f1 > f2);
    };

    /* public methods
     -----------------------------------------------------------------------------------------------------------  */

    plugin.formSubmit = function(opt) {
      ajaxSubmit({
        url: targetUrl,
        id: formId,
        query: (opt.query !== undefined) ? opt.query : '',
        onSubmit: (opt.submit !== undefined) ? opt.submit : plugin.settings.onSubmit,
        onSuccess: (opt.success !== undefined) ? opt.success : plugin.settings.onSuccess,
        onError: (opt.error !== undefined) ? opt.error : plugin.settings.onAjaxError,
        onRedirect: (opt.redirect !== undefined) ? opt.redirect : plugin.settings.onRedirect
      });
    };

    plugin.gotoPage = function(opt) {
      ajaxSubmit({
        url: targetUrl,
        id: formId,
        query: opt.query.concat('&_target', opt.page, '=', opt.page),
        onSubmit: (opt.submit !== undefined) ? opt.submit : plugin.settings.onSubmit,
        onSuccess: (opt.success !== undefined) ? opt.success : plugin.settings.onSuccess,
        onError: (opt.error !== undefined) ? opt.error : plugin.settings.onAjaxError,
        onRedirect: (opt.redirect !== undefined) ? opt.redirect : plugin.settings.onRedirect
      });
    };

    plugin.init = function() {
      recursiveEach(settings, function(obj, property, value) {
        if (typeof value === 'string' && value.trim().substring(0, 8) === 'function') {
          try {
            eval('obj[property] =' + value);
          } catch (ex) {
            $.logError(ex.message);
          }
        }
      }, 1);

      $form.on('change', function(event) {
        valueChanged($(event.target));

        if (statisticsUrl !== null) {
          $.ajax({
            url: statisticsUrl,
            xhrFields: { withCredentials: true } // gfi added
          });
          statisticsUrl = null;
        }
      });

      if (targetUrl === undefined) {
        $.logError('No target URL specified.')
      }

      $.each($form.find('[data-mwf-submit]'), function() {
        var attr = $(this).data('mwf-submit'),
          query = (attr.query !== undefined) ? attr.query : '',
          onSuccess = (attr.success !== undefined) ? attr.success : plugin.settings.onSuccess,
          onRedirect = (attr.redirect !== undefined) ? attr.redirect : plugin.settings.onRedirect,
          onError = (attr.error !== undefined) ? attr.error : plugin.settings.onAjaxError,
          onSubmit = (attr.submit !== undefined) ? attr.submit : plugin.settings.onSubmit;

        switch (attr.type) {
          case 'next':
          case 'back':
          case 'goto':
            query = query.concat('&_target', attr.page, '=', attr.page);
            break;

          case 'cancel':
          case 'exit':
          case 'finish':
            query = query.concat('&_', attr.type, '=1');
            break;
        }

        $(this).on('click', function() {
          ajaxSubmit({
            url: targetUrl,
            id: formId,
            query: query,
            onSubmit: onSubmit,
            onSuccess: onSuccess,
            onError: onError,
            onRedirect: onRedirect
          });
        });
      });

      $.each($form.find('[data-mwf-bind]'), function() {
        var name = $(this).data('mwf-bind'),
          $elem = $(this);

        $form.on('mwf-valuechange', function(e, input) {
          var v, l;

          if ($(input).attr('name') === name) {
            v = formdata[name];
            l = valueLabel(name, v);

            plugin.settings.updateFormValue($form, $elem, name, l);
            triggerEvent(document, EVENTS.VALUE_CHANGED, {
              detail: {
                $form: $form,
                $elem: $elem,
                name: name,
                value: l
              }
            });
            return false;
          }
        });
      });

      $.each($form.find('[data-mwf-fileupload]'), function() {
        var $upload = $(this),
          attr = $(this).data('mwf-fileupload'),
          $dropzone = $upload.find('[data-mwf-dropzone]'),
          $files = $upload.find('[data-mwf-files]'),
          $fileInput = $upload.find('input:file'),
          multiple = $fileInput.attr('multiple') !== undefined,
          useXhr = (!multiple && $.support.xhrFileUpload) || $.support.xhrFormDataFileUpload,
          count = 0,
          pendingList = [],
          errorRows = [],
          enabled = true,
          $buttons = null;

        $upload.find('[data-mwf-file-upload-all]').on('click', function() {
          pendingList.forEach(function(data) {
            data.submit();
          });
          pendingList = [];
        });

        $upload.find('[data-mwf-file-delete-all]').on('click', function() {
          $files.find('[data-mwf-file-delete]').trigger('click');
          pendingList = [];
        });

        $upload.on('disable', function() {
          enabled = false;
          $upload.fileupload('disable');
          $upload.addClass('mwf-upload-disabled');
          $upload.find(':button').prop('disabled', true);
          $fileInput.prop('disabled', true);
        });

        $upload.on('enable', function() {
          enabled = true;
          $upload.fileupload('enable');
          $upload.removeClass('mwf-upload-disabled');
          $upload.find(':button').prop('disabled', false);
          $fileInput.prop('disabled', false);
        });

        $dropzone.on('dragover', function() {
          if (enabled) {
            $(this).addClass('mwf-upload-dragover');
          }
        });

        $dropzone.on('dragleave drop', function() {
          $(this).removeClass('mwf-upload-dragover');
        });

        $upload.fileupload({
          url: attr.url,
          paramName: attr.name,
          dataType: 'json',
          dropZone: $dropzone,
          fileInput: $fileInput,
          previewMaxWidth: attr.previewMaxWidth,
          previewMaxHeight: attr.previewMaxHeight,
          previewCrop: true,
          maxNumberOfFiles: 1,
          formData: {xhr: useXhr},

          add: function(e, data) {
            var $this = $upload;

            if (!enabled) {
              return false;
            }

            $.each(errorRows, function(index, $row) {
              $row.remove();
            });

            if (multiple || count === 0) {
              count++;
            } else {
              return false;
            }

            pendingList.push(data);

            data.process(function() {
              return $this.fileupload('process', data);
            }).always(function() {
              $.each(data.files, function(index, file) {
                var $row = plugin.settings.createUploadFileRow($form, attr, file);
                data.context = $row;

                $row.find('[data-mwf-file-upload]').data(data).on('click', function() {
                  pendingList.splice($.inArray(data, pendingList), 1);
                  data.submit();
                });

                $row.find('[data-mwf-file-delete]').data(data).on('click', function() {
                  $row.remove();
                  pendingList.splice($.inArray(data, pendingList), 1);
                  count--;
                });

                $row.find('[data-mwf-file-preview]').append(file.preview);

                $files.append($row);
              });

              if (attr.autoUpload === true) {
                data.submit();
              }
            });
          },

          submit: function(e, data) {
            if ($buttons === null) {
              data.xhrFields= { withCredentials: true }; // gfi added
              $buttons = $form.find(':button').filter(':not(:disabled)');
              $buttons.prop('disabled', true);
            }
          },

          progress: function(e, data) {
            var progress = parseInt(data.loaded / data.total * 100, 10);
            if (data.context) {
              data.context.each(function() {
                data.context.find('[data-mwf-file-progress]').attr('aria-valuenow', progress).css('display', 'block').children().first().css('width', progress + '%').text(progress + '%');
              });
            }
          },

          done: function(e, data) {
            var files = (typeof data.result === 'string') ? JSON.parse(data.result).files : data.result.files;

            $.each(files, function(index, file) {
              var $row = plugin.settings.createDownloadFileRow($form, attr, file);

              if (data.context !== undefined) {
                data.context.replaceWith($row)
              } else {
                $files.append($row);
              }

              if (file.error === undefined) {
                $row.find('[data-mwf-file-delete]').on('click', function() {
                  if (file.delete_url) {
                    $.ajax({
                      url: file.delete_url,
                      xhrFields: { withCredentials: true } // gfi added
                    });
                  }
                  $row.remove();
                  pendingList.splice($.inArray(data, pendingList), 1);

                  // update filenames
                  if (filenames.hasOwnProperty(attr.name)) {
                    filenames[attr.name].splice($.inArray(file.name, filenames[attr.name]), 1);
                    $form.trigger("mwf-valuechange", data.fileInput);
                  }

                  count--;
                });

                // update filenames
                if (!filenames.hasOwnProperty(attr.name)) {
                  filenames[attr.name] = [];
                }

                filenames[attr.name].push(file.name);
                $form.trigger("mwf-valuechange", data.fileInput);

              } else {
                $row.find('[data-mwf-file-delete]').on('click', function() {
                  $row.remove();
                  pendingList.splice($.inArray(data, pendingList), 1);
                });

                errorRows.push($row);
                count--;
              }
            });
          },

          fail: function(e, data) {
            $.each(data.files, function(index, file) {

              var $row = plugin.settings.createDownloadFileRow($form, attr, {
                name: file.name,
                size: file.size,
                error: attr.labels.error
              });

              $row.find('[data-mwf-file-delete]').data(data).on('click', function() {
                $row.remove();
                pendingList.splice($.inArray(data, pendingList), 1);
              });

              errorRows.push($row);
              count--;

              if (data.context !== undefined) {
                data.context.replaceWith($row)
              } else {
                $files.append($row);
              }
            });
          },

          always: function(e, data) {
            if ($buttons !== null && $upload.fileupload('active') === 1) {
              $buttons.prop('disabled', false);
              $buttons = null;
            }
          }
        });

        // preload existing files
        $.ajax({
          url: attr.url,
          dataType: "json",
          data: {_name: attr.name, _do: 'files'},
          xhrFields: { withCredentials: true } // gfi added
        }).done(function(result) {
          $upload.fileupload('option', 'done').call($upload, $.Event('done'), {result: result});
          count = result.files.length;
        });
      });

      $.each($form.find('[data-mwf-datasource]'), function() {
        var def = {
            params: {},
            data: {}
          },
          attr = $.extend(true, {}, def, $(this).data('mwf-datasource')),
          $elem = $(this);

        switch (attr.type) {
          case 'checkbox':
          case 'radio':
            ajaxFillSelection($elem, attr);

            $form.on('mwf-valuechange', function(e, input) {
              if ($.trim(attr.params.dependsOn)) {
                $.each(attr.params.dependsOn.split(','), function(i, v) {
                  if ($(input).attr('name') === v) {
                    ajaxFillSelection($elem, attr);
                    return false;
                  }
                });
              }
            });
            break;
          case 'selection':
            ajaxFillDropdown($elem, attr);

            $form.on('mwf-valuechange', function(e, input) {
              if ($.trim(attr.params.dependsOn)) {
                $.each(attr.params.dependsOn.split(','), function(i, v) {
                  if ($(input).attr('name') === v) {
                    ajaxFillDropdown($elem, attr);
                    return false;
                  }
                });
              }
            });
            break;
          case 'suggestion':
            ajaxAutocomplete($elem, attr);
            break;
          case 'hidden':
            ajaxFillHidden($elem, attr);

            $form.on('mwf-valuechange', function(e, input) {
              if ($.trim(attr.params.dependsOn)) {
                $.each(attr.params.dependsOn.split(','), function(i, v) {
                  if ($(input).attr('name') === v) {
                    ajaxFillHidden($elem, attr);
                    return false;
                  }
                });
              }
            });
            break;
          default:
            $.logError(attr.type === undefined ?
              'Please specifiy the datasource type (checkbox, radio, selection or suggestion).' :
              'Unknown datasource type "'.concat(attr.type, '".'));
        }
      });

      //gfi added (behebt Fehler bei dropdowns mit conditions die initial nicht ausgewertet waren):
      formdata = serializeForm($form);
      // gfi added end

      $.each(plugin.settings.conditions, function() {
        createCondition($(this).attr('id'), $(this).attr('conjunction'), $(this).attr('conditions'), $(this).attr('fields'));
      });

      $.each(plugin.settings.calculatedValues, function() {
        createCalculatedValue($(this).attr('id'), $(this).attr('js'), $(this).attr('fields'), $(this).attr('vars'));
      });

      plugin.settings.onInit($form);
    };

    plugin.init();
  }

  /* default plugin settings
   -----------------------------------------------------------------------------------------------------------  */

  globalSettings.def = {
    appendUrlVars: false,
    operations: {},
    conditions: [],
    calculatedValues: [],
    fileuploadOptions: {},
    onConditionTrue: undefined,
    onConditionFalse: undefined,
    statisticsUrl: null
  };

  globalSettings.def.operations['visible'] = function($form, field) {
    var $field = $form.find('[data-mwf-container="' + field.input + '"]');
    $field.removeAttr('disabled');
    $field.removeClass('mwf-hidden');
  };

  globalSettings.def.operations['hidden'] = function($form, field) {
    var $field = $form.find('[data-mwf-container="' + field.input + '"]');
    $field.attr("disabled", "true");
    $field.addClass('mwf-hidden');
  };

  globalSettings.def.operations['writable'] = function($form, field) {
    var $field = $form.find('[data-mwf-id="' + field.input + '"]');
    $field.removeAttr('readonly');
  };

  globalSettings.def.operations['readonly'] = function($form, field) {
    var $field = $form.find('[data-mwf-id="' + field.input + '"]');
    $field.val(field.value);
    $field.attr("readonly", "true");
  };

  globalSettings.def.operations['enabled'] = function($form, field) {
    var $field = $form.find('[data-mwf-id="' + field.input + '"]');
    $field.removeAttr('disabled');
    $field.trigger('enable');
  };

  globalSettings.def.operations['disabled'] = function($form, field) {
    var $field = $form.find('[data-mwf-id="' + field.input + '"]');
    $field.attr("disabled", "true");
    $field.trigger('disable');
  };

  globalSettings.def.operations['optional'] = function($form, field) {
    var $label = $form.find('label[for="' + field.input + '"]');
    $label.children('span.mwf-required').remove();
  };

  globalSettings.def.operations['mandatory'] = function($form, field) {
    var $label = $form.find('label[for="' + field.input + '"]'),
      $span = $('<span></span>').attr('class', 'mwf-required').text('*');
    $label.children('span.mwf-required').remove();
    $label.append($span);
  };

  globalSettings.def.createOption = function($form, entry, selected) {
    var $option = $('<option></option>')
      .attr('data-mwf-value', entry.k)
      .attr('data-mwf-label', entry.v)
      .attr('data-mwf-mutable', '')
      .attr('value', JSON.stringify(entry))
      .append(entry.v);

    if (selected) {
      $option.attr('selected', true);
    }
    return $option;
  };

  globalSettings.def.createRadio = function($form, name, entry, checked) {
    var $span, $input, $label;

    $span = $('<span></span>')
      .attr('class', 'mwf-option')
      .attr('data-mwf-mutable', '');

    $input = $('<input/>')
      .attr('type', 'radio')
      .attr('id', entry.i)
      .attr('data-mwf-id', entry.i)
      .attr('data-mwf-value', entry.k)
      .attr('data-mwf-label', entry.v)
      .attr('value', JSON.stringify(entry))
      .attr('name', name)
      .attr('class', 'mwf-radio');

    if (checked) {
      $input.attr('checked', 'checked');
    }

    $label = $('<label></label>')
      .attr('for', entry.i).append(entry.v);

    $span.append($input);
    $span.append(' ');
    $span.append($label);

    return $span;
  };

  globalSettings.def.createCheckBox = function($form, name, entry, checked) {
    var $span, $input, $label;

    $span = $('<span></span>')
      .attr('class', 'mwf-option')
      .attr('data-mwf-mutable', '');

    $input = $('<input/>')
      .attr('type', 'checkbox')
      .attr('id', entry.i)
      .attr('data-mwf-id', entry.i)
      .attr('data-mwf-value', entry.k)
      .attr('data-mwf-label', entry.v)
      .attr('value', JSON.stringify(entry))
      .attr('name', name)
      .attr('class', 'mwf-checkbox');

    if (checked) {
      $input.attr('checked', 'checked');
    }

    $label = $('<label></label>')
      .attr('for', entry.i).append(entry.v);

    $span.append($input);
    $span.append(' ');
    $span.append($label);

    return $span;
  };

  globalSettings.def.updateFormValue = function($form, $elem, name, v) {
    $elem.html($.isArray(v) ? v.join(', ') : v);
  };

  globalSettings.def.createUploadFileRow = function($form, attr, file) {

    var $row, $fileinfo, $meta, $preview, $ul, $name, $size, $buttons, $upload, $delete, $span, $progress, $error;

    $row = $('<div/>').attr('data-mwf-file', '').attr('class', 'mwf-upload-row');

    $fileinfo = $('<div/>').attr('class', 'mwf-upload-fileinfo');

    $preview = $('<div/>').attr('data-mwf-file-preview', '').attr('class', 'mwf-upload-preview');

    $meta = $('<div/>').attr('class', 'mwf-upload-metadata');

    $ul = $('<ul/>');

    $name = $('<li>').text(attr.labels.name + ': ' + file.name);

    $size = $('<li>').text(attr.labels.size + ': ' + $.formatSize(file.size));

    $buttons = $('<div/>').attr('class', ' mwf-upload-actions');

    $upload = $('<button>').attr('data-mwf-file-upload', '').attr('class', 'mwf-upload-upload').text(attr.labels.upload);

    $delete = $('<button>').attr('data-mwf-file-delete', '').attr('class', 'mwf-upload-delete').text(attr.labels.del);

    $span = $('<span>').css('width', '0%');

    $progress = $('<div/>').attr('data-mwf-file-progress', '').attr('class', 'mwf-upload-progressbar');

    $error = $('<div/>').attr('data-mwf-file-error', '').attr('class', 'mwf-upload-error');

    $buttons.append($delete);

    if (attr.autoUpload === false) {
      $buttons.append($upload);
    }

    $ul.append($name);
    $ul.append($size);
    $meta.append($ul);
    $fileinfo.append($preview);
    $fileinfo.append($meta);
    $progress.append($span);
    $row.append($fileinfo);
    $row.append($buttons);
    $row.append($error);
    $row.append($progress);

    return $row;
  };

  globalSettings.def.createDownloadFileRow = function($form, attr, file) {

    var $row, $fileinfo, $meta, $preview, $image, $ul, $name, $size, $buttons, $delete, $span, $error;

    $row = $('<div/>').attr('data-mwf-file', '').attr('class', 'mwf-upload-row');

    $fileinfo = $('<div/>').attr('class', 'mwf-upload-fileinfo');

    $preview = $('<div/>').attr('data-mwf-preview', '').attr('class', 'mwf-upload-preview');

    $meta = $('<div/>').attr('class', 'mwf-upload-metadata');

    $ul = $('<ul/>');

    $name = $('<li>').text(attr.labels.name + ': ' + file.name);

    $size = $('<li>').text(attr.labels.size + ': ' + $.formatSize(file.size));

    $buttons = $('<div/>').attr('class', ' mwf-upload-actions');

    $delete = $('<button>').attr('data-mwf-file-delete', '').attr('class', 'mwf-upload-delete').text(attr.labels.del);

    $buttons.append($delete);
    $ul.append($name);
    $ul.append($size);
    $meta.append($ul);

    if (file.thumbnail_url) {
      $image = $('<img/>').attr('src', file.thumbnail_url);
      $preview.append($image);
    }

    $fileinfo.append($preview);
    $fileinfo.append($meta);
    $row.append($fileinfo);
    $row.append($buttons);

    if (file.error) {
      $error = $('<div/>').attr('data-mwf-file-error', '').attr('class', 'mwf-upload-error');
      $span = $('<span>').text(file.error);
      $error.append($span);
      $row.append($error);
    }

    return $row;
  };

  globalSettings.def.updateCalculatedValue = function($form, id, value) {
    $form.find('[data-mwf-value="' + id + '"]').text(value).html();
  };

  globalSettings.def.onAjaxError = function(jqXHR, status, error) {
    $.logError(jqXHR.responseText);
  };

  globalSettings.def.onSubmit = function($form, url, query) {
    return true;
  };

  globalSettings.def.onConditionCreated = function($form, conditionId, conjunction, conditions, fields) {
    // do nothing
  };

  globalSettings.def.onFillDropdown = function($form, $elem) {
    // do nothing
  };

  globalSettings.def.onFillSelection = function($form, $elem) {
    // do nothing
  };

  globalSettings.def.onInit = function($form) {
    // do nothing
  };


  /* plugin related jQuery functions
   ---------------------------------------------------------------------------------------------------------------  */

  $.fn.webforms = function(opt) {
    var dataKey = 'webforms', $this = $(this), $form = $this, settings, id, s;

    if ($this.data('mwf-form') === undefined) {
      $form = $this.find('[data-mwf-form]');
    }

    if ($form.length === 0) {
      return;
    }

    id = $form.dataWithDefault('mwf-id', $form.data('mwf-form'));
    settings = $form.dataWithDefault('mwf-settings', {});

    s = {
      id: id,
      type: 'GET',
      appendUrlVars: false
    };

    $.extend(true, s, globalSettingsWithVariant(settings.variant), settings, opt);

    if ($form .is('form') || $form .find('form').length > 0) {
      return $form.each(function() {
        var instance = $(this).data(dataKey);

        if (instance && instance.dispose) {
          instance.dispose();
        }

        instance = new Webforms($(this), s);
        $(this).data(dataKey, instance);
      });
    } else {
      s.selector = $this;
      $.mwfAjaxReplace(s);
    }
  };

  $.fn.webforms.settings = function(variant) {
    if (variant === undefined) {
      return globalSettings.def;
    } else {
      if (globalSettings[variant] === undefined) {
        globalSettings[variant] = {};
      }
      return globalSettings[variant];
    }
  };

  /* deprecated: Use $.fn.webforms.settings instead. */
  $.fn.webforms.defaults = $.fn.webforms.settings;

  /* general jQuery utility functions
   ---------------------------------------------------------------------------------------------------------------  */

  $.fn.replaceWithPush = function(elem) {
    var $elem = $(elem);
    this.replaceWith($elem);
    return $elem;
  };

  $.fn.dataWithDefault = function(key, def) {
    var value = this.data(key);
    return (value !== undefined) ? value : def;
  };

  if (typeof $.fn.serializeObject === 'undefined') {
    $.fn.serializeObject = function() {
      var o = {}, a = this.serializeArray();
      $.each(a, function() {
        if (o[this.name] !== undefined) {
          if (!o[this.name].push) {
            o[this.name] = [o[this.name]];
          }
          o[this.name].push(this.value || '');
        } else {
          o[this.name] = this.value || '';
        }
      });
      return o;
    };
  }

  if (typeof $.logError === 'undefined') {
    $.extend({
      logError: function(msg) {
        try {
          if (msg !== undefined && typeof window.console !== 'undefined') {
            console.error(msg);
          }
        } catch (ex) {
        }
      }
    });
  }

  $.extend({
    mwfAjaxReplace: function(opt) {
      var $dest = $(opt.selector), data = {}, optinId, s;

      if ($dest.length > 0) {
        optinId = $.getOptinIdFromHash(opt.id);

        if (optinId !== null) {
          data._optinId = optinId;

          if (typeof history.replaceState !== 'undefined') {
            history.replaceState({}, document.title, window.location.pathname);
          } else {
            window.location.hash = '';
          }
          // gfi modified
          /*$('html, body').animate({
            scrollTop: $dest.offset().top
          });*/
        }

        s = {
          url: null,
          type: 'POST',
          appendUrlVars: false
        };

        // Create the final settings object
        $.extend(s, opt);

        if (s.appendUrlVars) {
          $.extend(data, $.getUrlVars(window.location.search));
          delete data.view;
        }

        $.extend(data, $.getUrlVars(s.query), opt.data);

        $.ajax({
          url: s.url,
          type: s.type,
          data: data,
          traditional: true,
          cache: false,
          xhrFields: { withCredentials: true } // gfi added
        })
          .done(function(data, textStatus, jqXHR) {
            if (jqXHR && jqXHR.getResponseHeader("webforms_redirect")) {
              location.assign(jqXHR.getResponseHeader("webforms_redirect"));
              return;
            }

            $dest = $dest.replaceWithPush(data);
            if (s.onSuccess !== undefined) {
              s.onSuccess(data, textStatus, jqXHR);
            }
            triggerEvent(document, EVENTS.AJAX_FINISHED, {detail: {$dest: $dest}});
          })
          .fail(function(jqXHR, textStatus, errorThrown) {
            if (s.onError !== undefined) {
              s.onError(jqXHR, textStatus, errorThrown);
            } else {
              $.logError(jqXHR.responseText);
            }

            triggerEvent(document, EVENTS.AJAX_ERROR, {
              detail: {
                $dest: $dest,
                jqXHR: jqXHR,
                textStatus: textStatus,
                errorThrown: errorThrown
              }
            });
          })
      }
    },

    getUrlVars: function(url) {
      var params = {}, i, param, pos, key, value, length;

      if (typeof url === "string" && url.length > 0) {
        if (url[0] === '?') {
          url = url.substring(1);
        }

        url = url.split('&');

        for (i = 0, length = url.length; i < length; i++) {
          if (!url[i] || 0 === url[i].length) {
            continue;
          }

          param = url[i];
          pos = param.indexOf('=');

          if (pos >= 0) {
            key = param.substr(0, pos);
            value = param.substr(pos + 1);
          } else {
            key = param;
            value = '';
          }

          value = decodeURIComponent(value);

          if (params[key] === undefined) {
            params[key] = value;
          } else if (params[key] instanceof Array) {
            params[key].push(value);
          } else {
            params[key] = [params[key], value];
          }
        }
      }

      return params;
    },

    getUrlVar: function(url, name) {
      return $.getUrlVars(url)[name];
    },

    getOptinIdFromHash: function(id) {
      // legacy hash = mwf:uuid
      // new hash    = mwf:formId:uuid
      var hash = location.hash.replace('#', ''),
        idx = hash.indexOf("mwf:");

      if (idx !== -1) {
        var elements = hash.split(":");
        if (elements.length === 2 || elements[1] === String(id)) {
          return hash.substring(idx + 4, hash.length);
        }
      }
      return null;
    },

    formatSize: function(bytes) {
      if (typeof bytes !== 'number') {
        return '';
      }
      if (bytes >= 1000000000) {
        return (bytes / 1000000000).toFixed(2) + ' GB';
      }
      if (bytes >= 1000000) {
        return (bytes / 1000000).toFixed(2) + ' MB';
      }
      return (bytes / 1000).toFixed(2) + ' KB';
    }
  });


  /* register event handler
   ---------------------------------------------------------------------------------------------------------------  */

  $(document).ready(function() {
    var $document = $(document);
    var $cmCsrfToken = $('[data-mwf-csrf-token]');
    if ($cmCsrfToken.length) {
      $.ajaxSetup({
        beforeSend: function (jqXHR) {
          jqXHR.setRequestHeader('X-CSRFToken', $cmCsrfToken.attr('data-mwf-csrf-token'));
        }
      });
    }

    $document.on(EVENTS.AJAX_FINISHED, function(event) {
      var $dest = event.originalEvent.detail.$dest;
      $dest.webforms( {});
    });

    $('[data-mwf-form]').each(function() {
      $(this).webforms({});
    });
  });
}));
