import { h } from '../ema.core.js';

import './chunk-de721f0a.js';
import { j as TranslateService, k as StringUtil, n as createCommonjsModule, p as commonjsGlobal, d as Util, b as PermissionsService, m as Css } from './chunk-f41c48db.js';

class EntityForm {
    constructor() {
        this.translateService = TranslateService.instance;
    }
    async setData(data) {
        this.data = data;
    }
    async setDefinitions(data) {
        this.definitions = data;
    }
    translate(str) {
        return this.translateService.get(str);
    }
    renderFormField(key) {
        let field = {
            "label": key,
            "tag": "input",
            "type": "text",
            "placeholder": "",
            "hidden": false,
            "disabled": false
        };
        let fieldData;
        let styles = {};
        if (this.definitions && this.definitions[key]) {
            Object.keys(this.definitions[key]).forEach((k, i) => {
                field[k] = this.definitions[key][k];
            });
        }
        field.label = this.translate(field.label);
        fieldData = this.parseField(key, field);
        if (field.hidden || field.type === 'hidden')
            styles.display = "none";
        if (field.tag === 'input' && (field.type === 'text' ||
            field.type === 'password' ||
            field.type === 'date' ||
            field.type === 'datetime' ||
            field.type === 'datetime-local')) {
            return (h("ema-form-field", { key: key, value: this.data[key], "config-json": JSON.stringify(field), "data-json": JSON.stringify(fieldData), styles: styles }));
        }
        if (field.tag === 'input' && field.type === 'checkbox') {
            if (typeof this.data[key] === 'boolean' || typeof this.data[key] === 'number') {
                return (h("div", { class: "form-group row", style: styles },
                    h("label", { class: "col-md-4 col-form-label", htmlFor: key }, StringUtil.uppercaseFirst(this.translate(field.label))),
                    h("div", { class: "col-md-8" },
                        h("div", { class: "form-check form-check-inline" },
                            h("label", { class: "form-check-label" },
                                h("input", { class: "form-check-input", name: key, type: field.type, disabled: field.disabled, checked: this.data[key], hidden: field.hidden, value: this.data[key] })))),
                    h("div", { class: "col-md-8 offset-md-4 messages" })));
            }
            if (fieldData && fieldData.length) {
                let arrayName = key + '[]';
                return (h("div", { class: "form-group row", style: styles },
                    h("label", { class: "col-md-4 col-form-label", htmlFor: key }, StringUtil.uppercaseFirst(this.translate(field.label))),
                    h("div", { class: "col-md-8" }, fieldData.map(function (item) {
                        return h("div", { class: "form-check form-check-inline" },
                            h("label", { class: "form-check-label" },
                                h("input", { class: "form-check-input", name: arrayName, type: field.type, disabled: field.disabled, hidden: field.hidden, checked: item.selected, value: item.value }),
                                item.text));
                    })),
                    h("div", { class: "col-md-8 offset-md-4 messages" })));
            }
        }
        if (field.tag === 'input' && field.type === 'radio') {
            if (fieldData && fieldData.length) {
                return (h("div", { class: "form-group row", style: styles },
                    h("label", { class: "col-md-4 col-form-label", htmlFor: key }, StringUtil.uppercaseFirst(this.translate(field.label))),
                    h("div", { class: "col-md-8" }, fieldData.map(function (item) {
                        return h("div", { class: "form-check" },
                            h("label", { class: "form-check-label" },
                                h("input", { class: "form-check-input", name: key, type: field.type, disabled: field.disabled, hidden: field.hidden, checked: item.selected, value: item.value }),
                                item.text));
                    })),
                    h("div", { class: "col-md-8 offset-md-4 messages" })));
            }
        }
        if (field.tag === 'textarea') {
            return (h("div", { class: "form-group row", style: styles },
                h("label", { htmlFor: key, class: "col-md-4 col-form-label" }, StringUtil.uppercaseFirst(this.translate(field.label))),
                h("div", { class: "col-md-8" },
                    h("textarea", { class: "form-control", name: key, placeholder: field.placeholder, disabled: field.disabled, hidden: field.hidden, value: this.data[key], cols: 40, rows: 5 })),
                h("div", { class: "col-md-8 offset-md-4 messages" })));
        }
        if (field.tag === 'select' && fieldData) {
            return (h("div", { class: "form-group row", style: styles },
                h("label", { htmlFor: key, class: "col-md-4 col-form-label" }, StringUtil.uppercaseFirst(this.translate(field.label))),
                h("div", { class: "col-md-8" },
                    h("select", { name: key, class: "form-control" }, fieldData.map(function (item) {
                        return h("option", { value: item.value, selected: item.selected }, item.text);
                    }))),
                h("div", { class: "col-md-8 offset-md-4 messages" })));
        }
    }
    parseField(key, field) {
        let fieldData;
        let fieldRelationship;
        if (this.entity.relationships) {
            Object.keys(this.entity.relationships).forEach((k, i) => {
                if (k.match('field_' + key)) {
                    fieldRelationship = this.entity.relationships[k];
                }
            });
        }
        if (fieldRelationship && fieldRelationship.data) {
            fieldData = fieldRelationship.data;
        }
        if (field.tag === 'select') {
            fieldData = this.parseFieldDataArray(key, fieldData);
        }
        if (field.tag === 'input' && field.type === 'checkbox') {
            fieldData = this.parseFieldDataArray(key, fieldData);
        }
        if (field.tag === 'input' && field.type === 'radio') {
            fieldData = this.parseFieldDataArray(key, fieldData);
        }
        return fieldData;
    }
    parseFieldDataArray(key, fieldData) {
        if (!fieldData && Array.isArray(this.data[key])) {
            fieldData = this.data[key];
        }
        else {
            if (fieldData) {
                fieldData.map((item) => {
                    if (this.data[key] == item.value) {
                        item.selected = true;
                    }
                });
            }
        }
        return fieldData;
    }
    render() {
        let entity = {};
        if (this.definitionsJson) {
            this.definitions = JSON.parse(this.definitionsJson);
        }
        if (this.dataJson) {
            this.data = JSON.parse(this.dataJson);
        }
        if (this.data) {
            entity = this.entity = this.data;
        }
        if (entity.attributes) {
            this.data = entity.attributes;
        }
        else {
            this.data = entity;
        }
        if (entity.type) {
            this.type = entity.type;
        }
        this.dataCopy = JSON.parse(JSON.stringify(entity));
        return (h("div", null, Object.keys(this.data).map((key) => {
            return this.renderFormField(key);
        })));
    }
    static get is() { return "ema-entity-form"; }
    static get properties() { return {
        "data": {
            "state": true
        },
        "dataJson": {
            "type": String,
            "attr": "data-json"
        },
        "definitionsJson": {
            "type": String,
            "attr": "definitions-json"
        },
        "language": {
            "type": String,
            "attr": "language"
        },
        "setData": {
            "method": true
        },
        "setDefinitions": {
            "method": true
        }
    }; }
}

var formToObj_1 = createCommonjsModule(function (module) {
function formToObj(form) {
  var fields = formToArr(form);

  fields.sort(function (a, b) {
    return a.name.localeCompare(b.name);
  });

  return fields.reduce(function(obj, field) {
    addProp(obj, field.name, field.value);
    return obj;
  }, {});

  function formToArr(form) {
    var inputs = form.querySelectorAll('input, textarea, select, [contenteditable=true]');
    var arr = [];

    for (var i = 0; i < inputs.length; ++i) {
      var input = inputs[i],
          name = input.name || input.getAttribute('data-name'),
          val = input.value;

      if (!name ||
        ((input.type === 'checkbox' || input.type === 'radio') && !input.checked)) {
        continue;
      }

      if (input.getAttribute('contenteditable') === 'true') {
        val = input.innerHTML;
      }

      arr.push({
        name: name,
        value: val
      });
    }

    return arr;
  }

  function addProp(o, prop, val) {
    var props = prop.split('.');
    var lastProp = props.length - 1;

    props.reduce(function (obj, prop, i) {
      return setProp(obj, prop, i === lastProp
        ? val
        : {});
    }, o);
  }

  function setProp(obj, name, val) {
    if (name.slice(-2) === '[]') {
      makeArr(obj, name).push(val);
    } else if (obj[name]) {
      return obj[name];
    } else if (name[name.length - 1] === ']') {
      var arr = makeArr(obj, name);

      if (arr.prevName === name) {
        return arr[arr.length - 1];
      }

      arr.push(val);
      arr.prevName = name;
    } else {
      obj[name] = val;
    }

    return val;
  }

  function makeArr(obj, name) {
    var arrName = name.replace(/\[\d*\]/, '');
    return (obj[arrName] || (obj[arrName] = []));
  }
}

if (module.exports) {
  module.exports = formToObj;
}
});

var validate = createCommonjsModule(function (module, exports) {
/*!
 * validate.js 0.12.0
 *
 * (c) 2013-2017 Nicklas Ansman, 2013 Wrapp
 * Validate.js may be freely distributed under the MIT license.
 * For all details and documentation:
 * http://validatejs.org/
 */

(function(exports, module, define) {

  // The main function that calls the validators specified by the constraints.
  // The options are the following:
  //   - format (string) - An option that controls how the returned value is formatted
  //     * flat - Returns a flat array of just the error messages
  //     * grouped - Returns the messages grouped by attribute (default)
  //     * detailed - Returns an array of the raw validation data
  //   - fullMessages (boolean) - If `true` (default) the attribute name is prepended to the error.
  //
  // Please note that the options are also passed to each validator.
  var validate = function(attributes, constraints, options) {
    options = v.extend({}, v.options, options);

    var results = v.runValidations(attributes, constraints, options)
      ;

    if (results.some(function(r) { return v.isPromise(r.error); })) {
      throw new Error("Use validate.async if you want support for promises");
    }
    return validate.processValidationResults(results, options);
  };

  var v = validate;

  // Copies over attributes from one or more sources to a single destination.
  // Very much similar to underscore's extend.
  // The first argument is the target object and the remaining arguments will be
  // used as sources.
  v.extend = function(obj) {
    [].slice.call(arguments, 1).forEach(function(source) {
      for (var attr in source) {
        obj[attr] = source[attr];
      }
    });
    return obj;
  };

  v.extend(validate, {
    // This is the version of the library as a semver.
    // The toString function will allow it to be coerced into a string
    version: {
      major: 0,
      minor: 12,
      patch: 0,
      metadata: null,
      toString: function() {
        var version = v.format("%{major}.%{minor}.%{patch}", v.version);
        if (!v.isEmpty(v.version.metadata)) {
          version += "+" + v.version.metadata;
        }
        return version;
      }
    },

    // Below is the dependencies that are used in validate.js

    // The constructor of the Promise implementation.
    // If you are using Q.js, RSVP or any other A+ compatible implementation
    // override this attribute to be the constructor of that promise.
    // Since jQuery promises aren't A+ compatible they won't work.
    Promise: typeof Promise !== "undefined" ? Promise : /* istanbul ignore next */ null,

    EMPTY_STRING_REGEXP: /^\s*$/,

    // Runs the validators specified by the constraints object.
    // Will return an array of the format:
    //     [{attribute: "<attribute name>", error: "<validation result>"}, ...]
    runValidations: function(attributes, constraints, options) {
      var results = []
        , attr
        , validatorName
        , value
        , validators
        , validator
        , validatorOptions
        , error;

      if (v.isDomElement(attributes) || v.isJqueryElement(attributes)) {
        attributes = v.collectFormValues(attributes);
      }

      // Loops through each constraints, finds the correct validator and run it.
      for (attr in constraints) {
        value = v.getDeepObjectValue(attributes, attr);
        // This allows the constraints for an attribute to be a function.
        // The function will be called with the value, attribute name, the complete dict of
        // attributes as well as the options and constraints passed in.
        // This is useful when you want to have different
        // validations depending on the attribute value.
        validators = v.result(constraints[attr], value, attributes, attr, options, constraints);

        for (validatorName in validators) {
          validator = v.validators[validatorName];

          if (!validator) {
            error = v.format("Unknown validator %{name}", {name: validatorName});
            throw new Error(error);
          }

          validatorOptions = validators[validatorName];
          // This allows the options to be a function. The function will be
          // called with the value, attribute name, the complete dict of
          // attributes as well as the options and constraints passed in.
          // This is useful when you want to have different
          // validations depending on the attribute value.
          validatorOptions = v.result(validatorOptions, value, attributes, attr, options, constraints);
          if (!validatorOptions) {
            continue;
          }
          results.push({
            attribute: attr,
            value: value,
            validator: validatorName,
            globalOptions: options,
            attributes: attributes,
            options: validatorOptions,
            error: validator.call(validator,
                value,
                validatorOptions,
                attr,
                attributes,
                options)
          });
        }
      }

      return results;
    },

    // Takes the output from runValidations and converts it to the correct
    // output format.
    processValidationResults: function(errors, options) {
      errors = v.pruneEmptyErrors(errors, options);
      errors = v.expandMultipleErrors(errors, options);
      errors = v.convertErrorMessages(errors, options);

      var format = options.format || "grouped";

      if (typeof v.formatters[format] === 'function') {
        errors = v.formatters[format](errors);
      } else {
        throw new Error(v.format("Unknown format %{format}", options));
      }

      return v.isEmpty(errors) ? undefined : errors;
    },

    // Runs the validations with support for promises.
    // This function will return a promise that is settled when all the
    // validation promises have been completed.
    // It can be called even if no validations returned a promise.
    async: function(attributes, constraints, options) {
      options = v.extend({}, v.async.options, options);

      var WrapErrors = options.wrapErrors || function(errors) {
        return errors;
      };

      // Removes unknown attributes
      if (options.cleanAttributes !== false) {
        attributes = v.cleanAttributes(attributes, constraints);
      }

      var results = v.runValidations(attributes, constraints, options);

      return new v.Promise(function(resolve, reject) {
        v.waitForResults(results).then(function() {
          var errors = v.processValidationResults(results, options);
          if (errors) {
            reject(new WrapErrors(errors, options, attributes, constraints));
          } else {
            resolve(attributes);
          }
        }, function(err) {
          reject(err);
        });
      });
    },

    single: function(value, constraints, options) {
      options = v.extend({}, v.single.options, options, {
        format: "flat",
        fullMessages: false
      });
      return v({single: value}, {single: constraints}, options);
    },

    // Returns a promise that is resolved when all promises in the results array
    // are settled. The promise returned from this function is always resolved,
    // never rejected.
    // This function modifies the input argument, it replaces the promises
    // with the value returned from the promise.
    waitForResults: function(results) {
      // Create a sequence of all the results starting with a resolved promise.
      return results.reduce(function(memo, result) {
        // If this result isn't a promise skip it in the sequence.
        if (!v.isPromise(result.error)) {
          return memo;
        }

        return memo.then(function() {
          return result.error.then(function(error) {
            result.error = error || null;
          });
        });
      }, new v.Promise(function(r) { r(); })); // A resolved promise
    },

    // If the given argument is a call: function the and: function return the value
    // otherwise just return the value. Additional arguments will be passed as
    // arguments to the function.
    // Example:
    // ```
    // result('foo') // 'foo'
    // result(Math.max, 1, 2) // 2
    // ```
    result: function(value) {
      var args = [].slice.call(arguments, 1);
      if (typeof value === 'function') {
        value = value.apply(null, args);
      }
      return value;
    },

    // Checks if the value is a number. This function does not consider NaN a
    // number like many other `isNumber` functions do.
    isNumber: function(value) {
      return typeof value === 'number' && !isNaN(value);
    },

    // Returns false if the object is not a function
    isFunction: function(value) {
      return typeof value === 'function';
    },

    // A simple check to verify that the value is an integer. Uses `isNumber`
    // and a simple modulo check.
    isInteger: function(value) {
      return v.isNumber(value) && value % 1 === 0;
    },

    // Checks if the value is a boolean
    isBoolean: function(value) {
      return typeof value === 'boolean';
    },

    // Uses the `Object` function to check if the given argument is an object.
    isObject: function(obj) {
      return obj === Object(obj);
    },

    // Simply checks if the object is an instance of a date
    isDate: function(obj) {
      return obj instanceof Date;
    },

    // Returns false if the object is `null` of `undefined`
    isDefined: function(obj) {
      return obj !== null && obj !== undefined;
    },

    // Checks if the given argument is a promise. Anything with a `then`
    // function is considered a promise.
    isPromise: function(p) {
      return !!p && v.isFunction(p.then);
    },

    isJqueryElement: function(o) {
      return o && v.isString(o.jquery);
    },

    isDomElement: function(o) {
      if (!o) {
        return false;
      }

      if (!o.querySelectorAll || !o.querySelector) {
        return false;
      }

      if (v.isObject(document) && o === document) {
        return true;
      }

      // http://stackoverflow.com/a/384380/699304
      /* istanbul ignore else */
      if (typeof HTMLElement === "object") {
        return o instanceof HTMLElement;
      } else {
        return o &&
          typeof o === "object" &&
          o !== null &&
          o.nodeType === 1 &&
          typeof o.nodeName === "string";
      }
    },

    isEmpty: function(value) {
      var attr;

      // Null and undefined are empty
      if (!v.isDefined(value)) {
        return true;
      }

      // functions are non empty
      if (v.isFunction(value)) {
        return false;
      }

      // Whitespace only strings are empty
      if (v.isString(value)) {
        return v.EMPTY_STRING_REGEXP.test(value);
      }

      // For arrays we use the length property
      if (v.isArray(value)) {
        return value.length === 0;
      }

      // Dates have no attributes but aren't empty
      if (v.isDate(value)) {
        return false;
      }

      // If we find at least one property we consider it non empty
      if (v.isObject(value)) {
        for (attr in value) {
          return false;
        }
        return true;
      }

      return false;
    },

    // Formats the specified strings with the given values like so:
    // ```
    // format("Foo: %{foo}", {foo: "bar"}) // "Foo bar"
    // ```
    // If you want to write %{...} without having it replaced simply
    // prefix it with % like this `Foo: %%{foo}` and it will be returned
    // as `"Foo: %{foo}"`
    format: v.extend(function(str, vals) {
      if (!v.isString(str)) {
        return str;
      }
      return str.replace(v.format.FORMAT_REGEXP, function(m0, m1, m2) {
        if (m1 === '%') {
          return "%{" + m2 + "}";
        } else {
          return String(vals[m2]);
        }
      });
    }, {
      // Finds %{key} style patterns in the given string
      FORMAT_REGEXP: /(%?)%\{([^\}]+)\}/g
    }),

    // "Prettifies" the given string.
    // Prettifying means replacing [.\_-] with spaces as well as splitting
    // camel case words.
    prettify: function(str) {
      if (v.isNumber(str)) {
        // If there are more than 2 decimals round it to two
        if ((str * 100) % 1 === 0) {
          return "" + str;
        } else {
          return parseFloat(Math.round(str * 100) / 100).toFixed(2);
        }
      }

      if (v.isArray(str)) {
        return str.map(function(s) { return v.prettify(s); }).join(", ");
      }

      if (v.isObject(str)) {
        return str.toString();
      }

      // Ensure the string is actually a string
      str = "" + str;

      return str
        // Splits keys separated by periods
        .replace(/([^\s])\.([^\s])/g, '$1 $2')
        // Removes backslashes
        .replace(/\\+/g, '')
        // Replaces - and - with space
        .replace(/[_-]/g, ' ')
        // Splits camel cased words
        .replace(/([a-z])([A-Z])/g, function(m0, m1, m2) {
          return "" + m1 + " " + m2.toLowerCase();
        })
        .toLowerCase();
    },

    stringifyValue: function(value, options) {
      var prettify = options && options.prettify || v.prettify;
      return prettify(value);
    },

    isString: function(value) {
      return typeof value === 'string';
    },

    isArray: function(value) {
      return {}.toString.call(value) === '[object Array]';
    },

    // Checks if the object is a hash, which is equivalent to an object that
    // is neither an array nor a function.
    isHash: function(value) {
      return v.isObject(value) && !v.isArray(value) && !v.isFunction(value);
    },

    contains: function(obj, value) {
      if (!v.isDefined(obj)) {
        return false;
      }
      if (v.isArray(obj)) {
        return obj.indexOf(value) !== -1;
      }
      return value in obj;
    },

    unique: function(array) {
      if (!v.isArray(array)) {
        return array;
      }
      return array.filter(function(el, index, array) {
        return array.indexOf(el) == index;
      });
    },

    forEachKeyInKeypath: function(object, keypath, callback) {
      if (!v.isString(keypath)) {
        return undefined;
      }

      var key = ""
        , i
        , escape = false;

      for (i = 0; i < keypath.length; ++i) {
        switch (keypath[i]) {
          case '.':
            if (escape) {
              escape = false;
              key += '.';
            } else {
              object = callback(object, key, false);
              key = "";
            }
            break;

          case '\\':
            if (escape) {
              escape = false;
              key += '\\';
            } else {
              escape = true;
            }
            break;

          default:
            escape = false;
            key += keypath[i];
            break;
        }
      }

      return callback(object, key, true);
    },

    getDeepObjectValue: function(obj, keypath) {
      if (!v.isObject(obj)) {
        return undefined;
      }

      return v.forEachKeyInKeypath(obj, keypath, function(obj, key) {
        if (v.isObject(obj)) {
          return obj[key];
        }
      });
    },

    // This returns an object with all the values of the form.
    // It uses the input name as key and the value as value
    // So for example this:
    // <input type="text" name="email" value="foo@bar.com" />
    // would return:
    // {email: "foo@bar.com"}
    collectFormValues: function(form, options) {
      var values = {}
        , i
        , j
        , input
        , inputs
        , option
        , value;

      if (v.isJqueryElement(form)) {
        form = form[0];
      }

      if (!form) {
        return values;
      }

      options = options || {};

      inputs = form.querySelectorAll("input[name], textarea[name]");
      for (i = 0; i < inputs.length; ++i) {
        input = inputs.item(i);

        if (v.isDefined(input.getAttribute("data-ignored"))) {
          continue;
        }

        name = input.name.replace(/\./g, "\\\\.");
        value = v.sanitizeFormValue(input.value, options);
        if (input.type === "number") {
          value = value ? +value : null;
        } else if (input.type === "checkbox") {
          if (input.attributes.value) {
            if (!input.checked) {
              value = values[name] || null;
            }
          } else {
            value = input.checked;
          }
        } else if (input.type === "radio") {
          if (!input.checked) {
            value = values[name] || null;
          }
        }
        values[name] = value;
      }

      inputs = form.querySelectorAll("select[name]");
      for (i = 0; i < inputs.length; ++i) {
        input = inputs.item(i);
        if (v.isDefined(input.getAttribute("data-ignored"))) {
          continue;
        }

        if (input.multiple) {
          value = [];
          for (j in input.options) {
            option = input.options[j];
             if (option && option.selected) {
              value.push(v.sanitizeFormValue(option.value, options));
            }
          }
        } else {
          var _val = typeof input.options[input.selectedIndex] !== 'undefined' ? input.options[input.selectedIndex].value : '';
          value = v.sanitizeFormValue(_val, options);
        }
        values[input.name] = value;
      }

      return values;
    },

    sanitizeFormValue: function(value, options) {
      if (options.trim && v.isString(value)) {
        value = value.trim();
      }

      if (options.nullify !== false && value === "") {
        return null;
      }
      return value;
    },

    capitalize: function(str) {
      if (!v.isString(str)) {
        return str;
      }
      return str[0].toUpperCase() + str.slice(1);
    },

    // Remove all errors who's error attribute is empty (null or undefined)
    pruneEmptyErrors: function(errors) {
      return errors.filter(function(error) {
        return !v.isEmpty(error.error);
      });
    },

    // In
    // [{error: ["err1", "err2"], ...}]
    // Out
    // [{error: "err1", ...}, {error: "err2", ...}]
    //
    // All attributes in an error with multiple messages are duplicated
    // when expanding the errors.
    expandMultipleErrors: function(errors) {
      var ret = [];
      errors.forEach(function(error) {
        // Removes errors without a message
        if (v.isArray(error.error)) {
          error.error.forEach(function(msg) {
            ret.push(v.extend({}, error, {error: msg}));
          });
        } else {
          ret.push(error);
        }
      });
      return ret;
    },

    // Converts the error mesages by prepending the attribute name unless the
    // message is prefixed by ^
    convertErrorMessages: function(errors, options) {
      options = options || {};

      var ret = []
        , prettify = options.prettify || v.prettify;
      errors.forEach(function(errorInfo) {
        var error = v.result(errorInfo.error,
            errorInfo.value,
            errorInfo.attribute,
            errorInfo.options,
            errorInfo.attributes,
            errorInfo.globalOptions);

        if (!v.isString(error)) {
          ret.push(errorInfo);
          return;
        }

        if (error[0] === '^') {
          error = error.slice(1);
        } else if (options.fullMessages !== false) {
          error = v.capitalize(prettify(errorInfo.attribute)) + " " + error;
        }
        error = error.replace(/\\\^/g, "^");
        error = v.format(error, {
          value: v.stringifyValue(errorInfo.value, options)
        });
        ret.push(v.extend({}, errorInfo, {error: error}));
      });
      return ret;
    },

    // In:
    // [{attribute: "<attributeName>", ...}]
    // Out:
    // {"<attributeName>": [{attribute: "<attributeName>", ...}]}
    groupErrorsByAttribute: function(errors) {
      var ret = {};
      errors.forEach(function(error) {
        var list = ret[error.attribute];
        if (list) {
          list.push(error);
        } else {
          ret[error.attribute] = [error];
        }
      });
      return ret;
    },

    // In:
    // [{error: "<message 1>", ...}, {error: "<message 2>", ...}]
    // Out:
    // ["<message 1>", "<message 2>"]
    flattenErrorsToArray: function(errors) {
      return errors
        .map(function(error) { return error.error; })
        .filter(function(value, index, self) {
          return self.indexOf(value) === index;
        });
    },

    cleanAttributes: function(attributes, whitelist) {
      function whitelistCreator(obj, key, last) {
        if (v.isObject(obj[key])) {
          return obj[key];
        }
        return (obj[key] = last ? true : {});
      }

      function buildObjectWhitelist(whitelist) {
        var ow = {}
          , attr;
        for (attr in whitelist) {
          if (!whitelist[attr]) {
            continue;
          }
          v.forEachKeyInKeypath(ow, attr, whitelistCreator);
        }
        return ow;
      }

      function cleanRecursive(attributes, whitelist) {
        if (!v.isObject(attributes)) {
          return attributes;
        }

        var ret = v.extend({}, attributes)
          , w
          , attribute;

        for (attribute in attributes) {
          w = whitelist[attribute];

          if (v.isObject(w)) {
            ret[attribute] = cleanRecursive(ret[attribute], w);
          } else if (!w) {
            delete ret[attribute];
          }
        }
        return ret;
      }

      if (!v.isObject(whitelist) || !v.isObject(attributes)) {
        return {};
      }

      whitelist = buildObjectWhitelist(whitelist);
      return cleanRecursive(attributes, whitelist);
    },

    exposeModule: function(validate, root, exports, module, define) {
      if (exports) {
        if (module && module.exports) {
          exports = module.exports = validate;
        }
        exports.validate = validate;
      } else {
        root.validate = validate;
        if (validate.isFunction(define) && define.amd) {
          define([], function () { return validate; });
        }
      }
    },

    warn: function(msg) {
      if (typeof console !== "undefined" && console.warn) {
        console.warn("[validate.js] " + msg);
      }
    },

    error: function(msg) {
      if (typeof console !== "undefined" && console.error) {
        console.error("[validate.js] " + msg);
      }
    }
  });

  validate.validators = {
    // Presence validates that the value isn't empty
    presence: function(value, options) {
      options = v.extend({}, this.options, options);
      if (options.allowEmpty !== false ? !v.isDefined(value) : v.isEmpty(value)) {
        return options.message || this.message || "can't be blank";
      }
    },
    length: function(value, options, attribute) {
      // Empty values are allowed
      if (!v.isDefined(value)) {
        return;
      }

      options = v.extend({}, this.options, options);

      var is = options.is
        , maximum = options.maximum
        , minimum = options.minimum
        , tokenizer = options.tokenizer || function(val) { return val; }
        , err
        , errors = [];

      value = tokenizer(value);
      var length = value.length;
      if(!v.isNumber(length)) {
        v.error(v.format("Attribute %{attr} has a non numeric value for `length`", {attr: attribute}));
        return options.message || this.notValid || "has an incorrect length";
      }

      // Is checks
      if (v.isNumber(is) && length !== is) {
        err = options.wrongLength ||
          this.wrongLength ||
          "is the wrong length (should be %{count} characters)";
        errors.push(v.format(err, {count: is}));
      }

      if (v.isNumber(minimum) && length < minimum) {
        err = options.tooShort ||
          this.tooShort ||
          "is too short (minimum is %{count} characters)";
        errors.push(v.format(err, {count: minimum}));
      }

      if (v.isNumber(maximum) && length > maximum) {
        err = options.tooLong ||
          this.tooLong ||
          "is too long (maximum is %{count} characters)";
        errors.push(v.format(err, {count: maximum}));
      }

      if (errors.length > 0) {
        return options.message || errors;
      }
    },
    numericality: function(value, options, attribute, attributes, globalOptions) {
      // Empty values are fine
      if (!v.isDefined(value)) {
        return;
      }

      options = v.extend({}, this.options, options);

      var errors = []
        , name
        , count
        , checks = {
            greaterThan:          function(v, c) { return v > c; },
            greaterThanOrEqualTo: function(v, c) { return v >= c; },
            equalTo:              function(v, c) { return v === c; },
            lessThan:             function(v, c) { return v < c; },
            lessThanOrEqualTo:    function(v, c) { return v <= c; },
            divisibleBy:          function(v, c) { return v % c === 0; }
          }
        , prettify = options.prettify ||
          (globalOptions && globalOptions.prettify) ||
          v.prettify;

      // Strict will check that it is a valid looking number
      if (v.isString(value) && options.strict) {
        var pattern = "^-?(0|[1-9]\\d*)";
        if (!options.onlyInteger) {
          pattern += "(\\.\\d+)?";
        }
        pattern += "$";

        if (!(new RegExp(pattern).test(value))) {
          return options.message ||
            options.notValid ||
            this.notValid ||
            this.message ||
            "must be a valid number";
        }
      }

      // Coerce the value to a number unless we're being strict.
      if (options.noStrings !== true && v.isString(value) && !v.isEmpty(value)) {
        value = +value;
      }

      // If it's not a number we shouldn't continue since it will compare it.
      if (!v.isNumber(value)) {
        return options.message ||
          options.notValid ||
          this.notValid ||
          this.message ||
          "is not a number";
      }

      // Same logic as above, sort of. Don't bother with comparisons if this
      // doesn't pass.
      if (options.onlyInteger && !v.isInteger(value)) {
        return options.message ||
          options.notInteger ||
          this.notInteger ||
          this.message ||
          "must be an integer";
      }

      for (name in checks) {
        count = options[name];
        if (v.isNumber(count) && !checks[name](value, count)) {
          // This picks the default message if specified
          // For example the greaterThan check uses the message from
          // this.notGreaterThan so we capitalize the name and prepend "not"
          var key = "not" + v.capitalize(name);
          var msg = options[key] ||
            this[key] ||
            this.message ||
            "must be %{type} %{count}";

          errors.push(v.format(msg, {
            count: count,
            type: prettify(name)
          }));
        }
      }

      if (options.odd && value % 2 !== 1) {
        errors.push(options.notOdd ||
            this.notOdd ||
            this.message ||
            "must be odd");
      }
      if (options.even && value % 2 !== 0) {
        errors.push(options.notEven ||
            this.notEven ||
            this.message ||
            "must be even");
      }

      if (errors.length) {
        return options.message || errors;
      }
    },
    datetime: v.extend(function(value, options) {
      if (!v.isFunction(this.parse) || !v.isFunction(this.format)) {
        throw new Error("Both the parse and format functions needs to be set to use the datetime/date validator");
      }

      // Empty values are fine
      if (!v.isDefined(value)) {
        return;
      }

      options = v.extend({}, this.options, options);

      var err
        , errors = []
        , earliest = options.earliest ? this.parse(options.earliest, options) : NaN
        , latest = options.latest ? this.parse(options.latest, options) : NaN;

      value = this.parse(value, options);

      // 86400000 is the number of milliseconds in a day, this is used to remove
      // the time from the date
      if (isNaN(value) || options.dateOnly && value % 86400000 !== 0) {
        err = options.notValid ||
          options.message ||
          this.notValid ||
          "must be a valid date";
        return v.format(err, {value: arguments[0]});
      }

      if (!isNaN(earliest) && value < earliest) {
        err = options.tooEarly ||
          options.message ||
          this.tooEarly ||
          "must be no earlier than %{date}";
        err = v.format(err, {
          value: this.format(value, options),
          date: this.format(earliest, options)
        });
        errors.push(err);
      }

      if (!isNaN(latest) && value > latest) {
        err = options.tooLate ||
          options.message ||
          this.tooLate ||
          "must be no later than %{date}";
        err = v.format(err, {
          date: this.format(latest, options),
          value: this.format(value, options)
        });
        errors.push(err);
      }

      if (errors.length) {
        return v.unique(errors);
      }
    }, {
      parse: null,
      format: null
    }),
    date: function(value, options) {
      options = v.extend({}, options, {dateOnly: true});
      return v.validators.datetime.call(v.validators.datetime, value, options);
    },
    format: function(value, options) {
      if (v.isString(options) || (options instanceof RegExp)) {
        options = {pattern: options};
      }

      options = v.extend({}, this.options, options);

      var message = options.message || this.message || "is invalid"
        , pattern = options.pattern
        , match;

      // Empty values are allowed
      if (!v.isDefined(value)) {
        return;
      }
      if (!v.isString(value)) {
        return message;
      }

      if (v.isString(pattern)) {
        pattern = new RegExp(options.pattern, options.flags);
      }
      match = pattern.exec(value);
      if (!match || match[0].length != value.length) {
        return message;
      }
    },
    inclusion: function(value, options) {
      // Empty values are fine
      if (!v.isDefined(value)) {
        return;
      }
      if (v.isArray(options)) {
        options = {within: options};
      }
      options = v.extend({}, this.options, options);
      if (v.contains(options.within, value)) {
        return;
      }
      var message = options.message ||
        this.message ||
        "^%{value} is not included in the list";
      return v.format(message, {value: value});
    },
    exclusion: function(value, options) {
      // Empty values are fine
      if (!v.isDefined(value)) {
        return;
      }
      if (v.isArray(options)) {
        options = {within: options};
      }
      options = v.extend({}, this.options, options);
      if (!v.contains(options.within, value)) {
        return;
      }
      var message = options.message || this.message || "^%{value} is restricted";
      return v.format(message, {value: value});
    },
    email: v.extend(function(value, options) {
      options = v.extend({}, this.options, options);
      var message = options.message || this.message || "is not a valid email";
      // Empty values are fine
      if (!v.isDefined(value)) {
        return;
      }
      if (!v.isString(value)) {
        return message;
      }
      if (!this.PATTERN.exec(value)) {
        return message;
      }
    }, {
      PATTERN: /^[a-z0-9\u007F-\uffff!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9\u007F-\uffff!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$/i
    }),
    equality: function(value, options, attribute, attributes, globalOptions) {
      if (!v.isDefined(value)) {
        return;
      }

      if (v.isString(options)) {
        options = {attribute: options};
      }
      options = v.extend({}, this.options, options);
      var message = options.message ||
        this.message ||
        "is not equal to %{attribute}";

      if (v.isEmpty(options.attribute) || !v.isString(options.attribute)) {
        throw new Error("The attribute must be a non empty string");
      }

      var otherValue = v.getDeepObjectValue(attributes, options.attribute)
        , comparator = options.comparator || function(v1, v2) {
          return v1 === v2;
        }
        , prettify = options.prettify ||
          (globalOptions && globalOptions.prettify) ||
          v.prettify;

      if (!comparator(value, otherValue, options, attribute, attributes)) {
        return v.format(message, {attribute: prettify(options.attribute)});
      }
    },

    // A URL validator that is used to validate URLs with the ability to
    // restrict schemes and some domains.
    url: function(value, options) {
      if (!v.isDefined(value)) {
        return;
      }

      options = v.extend({}, this.options, options);

      var message = options.message || this.message || "is not a valid url"
        , schemes = options.schemes || this.schemes || ['http', 'https']
        , allowLocal = options.allowLocal || this.allowLocal || false;

      if (!v.isString(value)) {
        return message;
      }

      // https://gist.github.com/dperini/729294
      var regex =
        "^" +
        // protocol identifier
        "(?:(?:" + schemes.join("|") + ")://)" +
        // user:pass authentication
        "(?:\\S+(?::\\S*)?@)?" +
        "(?:";

      var tld = "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))";

      if (allowLocal) {
        tld += "?";
      } else {
        regex +=
          // IP address exclusion
          // private & local networks
          "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
          "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
          "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})";
      }

      regex +=
          // IP address dotted notation octets
          // excludes loopback network 0.0.0.0
          // excludes reserved space >= 224.0.0.0
          // excludes network & broacast addresses
          // (first & last IP address of each class)
          "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
          "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
          "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
        "|" +
          // host name
          "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
          // domain name
          "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
          tld +
        ")" +
        // port number
        "(?::\\d{2,5})?" +
        // resource path
        "(?:[/?#]\\S*)?" +
      "$";

      var PATTERN = new RegExp(regex, 'i');
      if (!PATTERN.exec(value)) {
        return message;
      }
    }
  };

  validate.formatters = {
    detailed: function(errors) {return errors;},
    flat: v.flattenErrorsToArray,
    grouped: function(errors) {
      var attr;

      errors = v.groupErrorsByAttribute(errors);
      for (attr in errors) {
        errors[attr] = v.flattenErrorsToArray(errors[attr]);
      }
      return errors;
    },
    constraint: function(errors) {
      var attr;
      errors = v.groupErrorsByAttribute(errors);
      for (attr in errors) {
        errors[attr] = errors[attr].map(function(result) {
          return result.validator;
        }).sort();
      }
      return errors;
    }
  };

  validate.exposeModule(validate, this, exports, module, define);
}).call(commonjsGlobal,
        exports,
        module,
        null);
});

class Form {
    constructor() {
        this.formClass = 'form-horizontal col-12';
        this.resettable = false;
        this.submitLabel = 'SUBMIT';
        this.translateService = TranslateService.instance;
    }
    componentDidLoad() {
        this.postRender();
    }
    componentDidUpdate() {
        this.postRender();
    }
    postRender() {
        this.form = this.el.getElementsByTagName('form')[0];
        if (this.entity) {
            this.entityForm = this.el.getElementsByTagName('ema-entity-form')[0];
            this.entityForm.setData(this.entity);
            if (this.definitions) {
                this.entityForm.setDefinitions(this.definitions);
            }
        }
        this.initValidations();
    }
    async handleSubmit(e) {
        let errors;
        if (this.constraints) {
            errors = validate(this.form, this.constraints);
            this.showErrors(this.form, errors || {});
        }
        if (errors)
            this.logErrors(errors);
        if (!this.action) {
            e.preventDefault();
        }
        if (this.action && errors) {
            e.preventDefault();
        }
        if (!errors) {
            let obj = formToObj_1(this.form);
            if (this.submitHandler) {
                obj = await this.submitHandler(obj, this.dataType);
            }
            if (obj) {
                this.formSubmitHandler();
            }
            else {
                console.error('form submit failure');
            }
        }
    }
    formSubmitHandler() {
        this.formSubmit.emit({ form: this.form, data: formToObj_1(this.form) });
    }
    async setData(data) {
        if (!data.data) {
            await this.setEntity(data);
        }
        else {
            await this.setEntity(data.data);
        }
    }
    async setEntity(entity) {
        if (entity.meta && entity.meta.definitions) {
            await this.setDefinitions(entity.meta.definitions);
        }
        this.entity = entity;
    }
    async setDefinitions(definitions) {
        typeof definitions === 'string' ? this.definitions = JSON.parse(definitions) : this.definitions = definitions;
        let constraints = {};
        Object.keys(this.definitions).forEach((k, i) => {
            let field = this.definitions[k];
            if (field.constraints) {
                constraints[k] = field.constraints;
            }
        });
        this.setConstraints(constraints);
    }
    async setConstraints(constraints) {
        typeof constraints === 'string' ? this.constraints = JSON.parse(constraints) : this.constraints = constraints;
    }
    async setSubmitInterceptor(submitFn) {
        this.submitHandler = submitFn;
    }
    async setTranslateService(instance) {
        this.translateService = instance;
        this.update();
    }
    async update() {
        this.render();
    }
    initValidations() {
        setTimeout((arg) => {
            const self = this;
            let inputs = this.el.querySelectorAll("input, textarea, select");
            for (let i = 0; i < inputs.length; ++i) {
                inputs.item(i).addEventListener("change", function (ev) {
                    let errors = validate(self.form, self.constraints) || {};
                    self.showErrorsForInput(this, errors[this['getAttribute']('name')]);
                });
            }
            validate.extend(validate.validators.datetime, {
                parse: function (value, options) {
                    return +moment.utc(value);
                },
                format: function (value, options) {
                    var format = options.dateOnly ? "YYYY-MM-DD" : "YYYY-MM-DD hh:mm:ss";
                    return moment.utc(value).format(format);
                }
            });
        }, 20);
    }
    showErrors(form, errors) {
        Array.from(form.querySelectorAll("input[name], select[name]")).forEach((input) => {
            this.showErrorsForInput(input, errors && errors[input['getAttribute']('name')]);
        });
    }
    showErrorsForInput(input, errors) {
        let formGroup = this.closestParent(input.parentNode, "form-group");
        if (formGroup) {
            let messages = formGroup.querySelector(".messages");
            this.resetFormGroup(formGroup);
            if (errors) {
                formGroup.classList.add("has-error");
                Object.keys(errors).forEach((k, i) => {
                    this.addError(messages, errors[k]);
                });
            }
            else {
                formGroup.classList.add("has-success");
            }
        }
        else {
            if (errors) {
                this.logErrors(errors, input['getAttribute']('name'));
            }
        }
    }
    logErrors(errors, fieldName = undefined) {
        Object.keys(errors).forEach((k, i) => {
            if (fieldName) {
                console.error(fieldName + ': ' + errors[k]);
            }
            else {
                console.error(k + ': ' + errors[k]);
            }
        });
    }
    closestParent(child, className) {
        if (!child || child == document) {
            return null;
        }
        if (child.classList.contains(className)) {
            return child;
        }
        else {
            return this.closestParent(child.parentNode, className);
        }
    }
    resetFormGroup(formGroup) {
        formGroup.classList.remove("has-error");
        formGroup.classList.remove("has-success");
        Array.from(formGroup.querySelectorAll(".help-block.error")).forEach((el) => {
            el.parentNode.removeChild(el);
        });
    }
    addError(messages, error) {
        let block = document.createElement("p");
        block.classList.add("help-block");
        block.classList.add("error");
        block.innerText = this.translate(error);
        messages.appendChild(block);
    }
    translate(str) {
        return this.translateService.get(str);
    }
    render() {
        if (this.dataJson) {
            this.setData(JSON.parse(this.dataJson));
        }
        if (this.entityJson) {
            this.setEntity(JSON.parse(this.entityJson));
        }
        if (this.definitionsJson) {
            this.setDefinitions(JSON.parse(this.definitionsJson));
        }
        if (this.language && this.language !== this.translateService.language) {
            this.translateService.use(this.language);
        }
        return (h("form", { onSubmit: (e) => this.handleSubmit(e), class: this.formClass, role: 'form', action: this.action || 'javascript:;', method: this.method || 'get' },
            this.entity &&
                h("div", null,
                    h("ema-entity-form", null)),
            h("slot", null),
            this.entity &&
                h("div", { class: "form-group row" },
                    h("div", { class: "col-md-8 offset-md-4" },
                        h("button", { type: "submit", class: "btn btn-primary mr-2" }, this.translate(this.submitLabel)),
                        this.resettable &&
                            h("button", { type: "reset", class: "btn btn-primary" }, this.translate('RESET')),
                        this.dataType &&
                            h("ema-permission", { can: '["delete"]', subject: this.dataType },
                                h("button", { type: "button", class: "btn btn-danger mr-2 delete-btn" }, this.translate('DELETE'))),
                        this.dataType &&
                            h("ema-permission", { can: '["create"]', subject: this.dataType },
                                h("button", { type: "button", class: "btn btn-secondary mr-2 delete-create" }, this.translate('CREATE')))),
                    this.submitError &&
                        h("div", { class: "col-md-8 offset-md-4 messages help-block error" },
                            this.translate(this.submitError),
                            "."))));
    }
    static get is() { return "ema-form"; }
    static get properties() { return {
        "action": {
            "type": String,
            "attr": "action"
        },
        "dataJson": {
            "type": String,
            "attr": "data-json"
        },
        "dataType": {
            "type": String,
            "attr": "data-type"
        },
        "definitionsJson": {
            "type": String,
            "attr": "definitions-json"
        },
        "el": {
            "elementRef": true
        },
        "entity": {
            "state": true
        },
        "entityJson": {
            "type": String,
            "attr": "entity-json"
        },
        "formClass": {
            "type": String,
            "attr": "form-class"
        },
        "language": {
            "type": String,
            "attr": "language"
        },
        "method": {
            "type": String,
            "attr": "method"
        },
        "resettable": {
            "type": Boolean,
            "attr": "resettable"
        },
        "setConstraints": {
            "method": true
        },
        "setData": {
            "method": true
        },
        "setDefinitions": {
            "method": true
        },
        "setEntity": {
            "method": true
        },
        "setSubmitInterceptor": {
            "method": true
        },
        "setTranslateService": {
            "method": true
        },
        "submitError": {
            "type": String,
            "attr": "submit-error"
        },
        "submitLabel": {
            "type": String,
            "attr": "submit-label"
        },
        "update": {
            "method": true
        }
    }; }
    static get events() { return [{
            "name": "formSubmit",
            "method": "formSubmit",
            "bubbles": true,
            "cancelable": true,
            "composed": true
        }]; }
    static get style() { return ":root{--ema-color:#343a40;--ema-primary:#007bff;--ema-secondary:#6c757d;--ema-success:#28a745;--ema-info:#17a2b8;--ema-warning:#ffc107;--ema-danger:#dc3545;--ema-light:#f8f9fa;--ema-dark:#343a40;--ema-background:#fff;--ema-background-diap:rgba(29,29,29,0.97);--ema-border-color:#c5c9cd;--ema-border:1px solid #c5c9cd;--ema-border-radius:3px 3px 3px 3px;--ema-box-shadow:1px 1px 10px 1px #ccc;--ema-margin:0.25rem;--ema-margin-sm:0.5rem;--ema-margin-md:1rem;--ema-margin-lg:1.5rem;--ema-margin-xl:2rem;--ema-padding:0.25rem;--ema-padding-sm:0.5rem;--ema-padding-md:1rem;--ema-padding-lg:1.5rem;--ema-padding-xl:2rem;--ema-gray-100:#f8f9fa;--ema-gray-200:#e9ecef;--ema-gray-300:#dee2e6;--ema-gray-400:#ced4da;--ema-gray-500:#adb5bd;--ema-gray-600:#868e96;--ema-gray-700:#495057;--ema-gray-800:#343a40;--ema-gray-900:#212529}ema-form .help-block.error{color:var(--ema-danger,#dc3545);margin-top:var(--ema-padding,.25rem);margin-bottom:var(--ema-padding,.25rem)}"; }
}

class FormField {
    constructor() {
        this.data = {};
        this.config = {
            "label": "",
            "tag": "input",
            "type": "text",
            "placeholder": "",
            "hidden": false,
            "disabled": false
        };
        this.key = '';
        this.tag = 'input';
        this.type = 'text';
        this.translateService = TranslateService.instance;
    }
    componentDidLoad() {
        this.postRender();
    }
    componentDidUpdate() {
        this.postRender();
    }
    postRender() {
        if (this.config.hidden || this.config.type === 'hidden')
            this.el.style.display = "none";
        this.change.emit({ value: this.currentValue, el: this.el });
    }
    async setData(data) {
        this.data = data;
    }
    async getValue() {
        return this.currentValue;
    }
    translate(str) {
        return this.translateService.get(str);
    }
    render() {
        let field;
        let fieldData;
        if (this.dataJson) {
            this.data = Object.assign({}, this.data, JSON.parse(this.dataJson));
            fieldData = this.data;
        }
        if (this.configJson) {
            this.config = Object.assign({}, this.config, JSON.parse(this.configJson));
            field = this.config;
        }
        if (this.value) {
            this.currentValue = Util.tryParseJSON(this.value) || this.value;
        }
        return (h("div", null, field.tag === 'input' && (field.type === 'text' ||
            field.type === 'password' ||
            field.type === 'date' ||
            field.type === 'datetime' ||
            field.type === 'datetime-local') &&
            h("div", { class: "form-group row", style: this.styles },
                h("label", { class: "col-md-4 col-form-label", htmlFor: this.key }, StringUtil.uppercaseFirst(this.translate(field.label))),
                h("div", { class: "col-md-8" },
                    h("input", { class: "form-control", name: this.key, placeholder: field.placeholder, disabled: field.disabled, hidden: field.hidden, type: field.type, value: this.currentValue })),
                h("div", { class: "col-md-8 offset-md-4 messages" }))));
    }
    static get is() { return "ema-form-field"; }
    static get properties() { return {
        "config": {
            "state": true
        },
        "configJson": {
            "type": String,
            "attr": "config-json"
        },
        "currentValue": {
            "state": true
        },
        "data": {
            "state": true
        },
        "dataJson": {
            "type": String,
            "attr": "data-json"
        },
        "el": {
            "elementRef": true
        },
        "getValue": {
            "method": true
        },
        "key": {
            "type": String,
            "attr": "key"
        },
        "setData": {
            "method": true
        },
        "styles": {
            "type": "Any",
            "attr": "styles"
        },
        "tag": {
            "type": String,
            "attr": "tag"
        },
        "type": {
            "type": String,
            "attr": "type"
        },
        "value": {
            "type": "Any",
            "attr": "value"
        }
    }; }
    static get events() { return [{
            "name": "change",
            "method": "change",
            "bubbles": true,
            "cancelable": true,
            "composed": true
        }]; }
}

/*
 *
 * Copryright 2018, Craig Condon
 * Licensed under MIT
 *
 * Filter JavaScript objects with mongodb queries
 */

/**
 */

function isFunction(value) {
  return typeof value === 'function';
}

/**
 */

function isArray(value) {
  return Object.prototype.toString.call(value) === '[object Array]';
}

/**
 */

function comparable(value) {
  if (value instanceof Date) {
    return value.getTime();
  } else if (isArray(value)) {
    return value.map(comparable);
  } else if (value && typeof value.toJSON === 'function') {
    return value.toJSON();
  } else {
    return value;
  }
}

function get(obj, key) {
  return isFunction(obj.get) ? obj.get(key) : obj[key];
}

/**
 */

function or(validator) {
  return function(a, b) {
    if (!isArray(b) || !b.length) {
      return validator(a, b);
    }
    for (var i = 0, n = b.length; i < n; i++) {
      if (validator(a, get(b,i))) return true;
    }
    return false;
  }
}

/**
 */

function and(validator) {
  return function(a, b) {
    if (!isArray(b) || !b.length) {
      return validator(a, b);
    }
    for (var i = 0, n = b.length; i < n; i++) {
      if (!validator(a, get(b, i))) return false;
    }
    return true;
  };
}

function validate$1(validator, b, k, o) {
  return validator.v(validator.a, b, k, o);
}

var OPERATORS = {

  /**
   */

  $eq: or(function(a, b) {
    return a(b);
  }),

  /**
   */

  $ne: and(function(a, b) {
    return !a(b);
  }),

  /**
   */

  $gt: or(function(a, b) {
    return compare(comparable(b), a) > 0;
  }),

  /**
   */

  $gte: or(function(a, b) {
    return compare(comparable(b), a) >= 0;
  }),

  /**
   */

  $lt: or(function(a, b) {
    return compare(comparable(b), a) < 0;
  }),

  /**
   */

  $lte: or(function(a, b) {
    return compare(comparable(b), a) <= 0;
  }),

  /**
   */

  $mod: or(function(a, b) {
    return b % a[0] == a[1];
  }),

  /**
   */

  $in: function(a, b) {

    if (b instanceof Array) {
      for (var i = b.length; i--;) {
        if (~a.indexOf(comparable(get(b, i)))) {
          return true;
        }
      }
    } else {
      var comparableB = comparable(b);
      if (comparableB === b && typeof b === 'object') {
        for (var i = a.length; i--;) {
          if (String(a[i]) === String(b) && String(b) !== '[object Object]') {
            return true;
          }
        }
      }

      /*
        Handles documents that are undefined, whilst also
        having a 'null' element in the parameters to $in.
      */
      if (typeof comparableB == 'undefined') {
        for (var i = a.length; i--;) {
          if (a[i] == null) {
            return true;
          }
        }
      }

      /*
        Handles the case of {'field': {$in: [/regexp1/, /regexp2/, ...]}}
      */
      for (var i = a.length; i--;) {
        var validator = createRootValidator(get(a, i), undefined);
        var result = validate$1(validator, b, i, a);
        if ((result) && (String(result) !== '[object Object]') && (String(b) !== '[object Object]')) {
          return true;
        }
      }

      return !!~a.indexOf(comparableB);
    }

    return false;
  },

  /**
   */

  $nin: function(a, b, k, o) {
    return !OPERATORS.$in(a, b, k, o);
  },

  /**
   */

  $not: function(a, b, k, o) {
    return !validate$1(a, b, k, o);
  },

  /**
   */

  $type: function(a, b) {
    return b != void 0 ? b instanceof a || b.constructor == a : false;
    },

  /**
   */

  $all: function(a, b, k, o) {
    return OPERATORS.$and(a, b, k, o);
  },

  /**
   */

  $size: function(a, b) {
    return b ? a === b.length : false;
  },

  /**
   */

  $or: function(a, b, k, o) {
    for (var i = 0, n = a.length; i < n; i++) if (validate$1(get(a, i), b, k, o)) return true;
    return false;
  },

  /**
   */

  $nor: function(a, b, k, o) {
    return !OPERATORS.$or(a, b, k, o);
  },

  /**
   */

  $and: function(a, b, k, o) {
    for (var i = 0, n = a.length; i < n; i++) {
      if (!validate$1(get(a, i), b, k, o)) {
        return false;
      }
    }
    return true;
  },

  /**
   */

  $regex: or(function(a, b) {
    return typeof b === 'string' && a.test(b);
  }),

  /**
   */

  $where: function(a, b, k, o) {
    return a.call(b, b, k, o);
  },

  /**
   */

  $elemMatch: function(a, b, k, o) {
    if (isArray(b)) {
      return !!~search(b, a);
    }
    return validate$1(a, b, k, o);
  },

  /**
   */

  $exists: function(a, b, k, o) {
    return o.hasOwnProperty(k) === a;
  }
};

/**
 */

var prepare = {

  /**
   */

  $eq: function(a) {

    if (a instanceof RegExp) {
      return function(b) {
        return typeof b === 'string' && a.test(b);
      };
    } else if (a instanceof Function) {
      return a;
    } else if (isArray(a) && !a.length) {
      // Special case of a == []
      return function(b) {
        return (isArray(b) && !b.length);
      };
    } else if (a === null){
      return function(b){
        //will match both null and undefined
        return b == null;
      }
    }

    return function(b) {
      return compare(comparable(b), comparable(a)) === 0;
    };
  },

  /**
   */

  $ne: function(a) {
    return prepare.$eq(a);
  },

  /**
   */

  $and: function(a) {
    return a.map(parse);
  },

  /**
   */

  $all: function(a) {
    return prepare.$and(a);
  },

  /**
   */

  $or: function(a) {
    return a.map(parse);
  },

  /**
   */

  $nor: function(a) {
    return a.map(parse);
  },

  /**
   */

  $not: function(a) {
    return parse(a);
  },

  /**
   */

  $regex: function(a, query) {
    return new RegExp(a, query.$options);
  },

  /**
   */

  $where: function(a) {
    return typeof a === 'string' ? new Function('obj', 'return ' + a) : a;
  },

  /**
   */

  $elemMatch: function(a) {
    return parse(a);
  },

  /**
   */

  $exists: function(a) {
    return !!a;
  }
};

/**
 */

function search(array, validator) {

  for (var i = 0; i < array.length; i++) {
    var result = get(array, i);
    if (validate$1(validator, get(array, i))) {
      return i;
    }
  }

  return -1;
}

/**
 */

function createValidator(a, validate) {
  return { a: a, v: validate };
}

/**
 */

function nestedValidator(a, b) {
  var values  = [];
  findValues(b, a.k, 0, b, values);

  if (values.length === 1) {
    var first = values[0];
    return validate$1(a.nv, first[0], first[1], first[2]);
  }

  // If the query contains $ne, need to test all elements ANDed together
  var inclusive = a && a.q && typeof a.q.$ne !== 'undefined';
  var allValid = inclusive;
  for (var i = 0; i < values.length; i++) {
    var result = values[i];
    var isValid = validate$1(a.nv, result[0], result[1], result[2]);
    if (inclusive) {
      allValid &= isValid;
    } else {
      allValid |= isValid;
    }
  }
  return allValid;
}

/**
 */

function findValues(current, keypath, index, object, values) {

  if (index === keypath.length || current == void 0) {

    values.push([current, keypath[index - 1], object]);
    return;
  }

  var k = get(keypath, index);

  // ensure that if current is an array, that the current key
  // is NOT an array index. This sort of thing needs to work:
  // sift({'foo.0':42}, [{foo: [42]}]);
  if (isArray(current) && isNaN(Number(k))) {
    for (var i = 0, n = current.length; i < n; i++) {
      findValues(get(current, i), keypath, index, current, values);
    }
  } else {
    findValues(get(current, k), keypath, index + 1, current, values);
  }
}

/**
 */

function createNestedValidator(keypath, a, q) {
  return { a: { k: keypath, nv: a, q: q }, v: nestedValidator };
}

/**
 * flatten the query
 */

function isVanillaObject(value) {
  return value && value.constructor === Object;
}

function parse(query) {
  query = comparable(query);

  if (!query || !isVanillaObject(query)) { // cross browser support
    query = { $eq: query };
  }

  var validators = [];

  for (var key in query) {
    var a = query[key];

    if (key === '$options') {
      continue;
    }

    if (OPERATORS[key]) {
      if (prepare[key]) a = prepare[key](a, query);
      validators.push(createValidator(comparable(a), OPERATORS[key]));
    } else {

      if (key.charCodeAt(0) === 36) {
        throw new Error('Unknown operation ' + key);
      }
      validators.push(createNestedValidator(key.split('.'), parse(a), a));
    }
  }

  return validators.length === 1 ? validators[0] : createValidator(validators, OPERATORS.$and);
}

/**
 */

function createRootValidator(query, getter) {
  var validator = parse(query);
  if (getter) {
    validator = {
      a: validator,
      v: function(a, b, k, o) {
        return validate$1(a, getter(b), k, o);
      }
    };
  }
  return validator;
}

/**
 */

function sift(query, array, getter) {

  if (isFunction(array)) {
    getter = array;
    array  = void 0;
  }

  var validator = createRootValidator(query, getter);

  function filter(b, k, o) {
    return validate$1(validator, b, k, o);
  }

  if (array) {
    return array.filter(filter);
  }

  return filter;
}

/**
 */

function compare(a, b) {
  if(a===b) return 0;
  if(typeof a === typeof b) {
    if (a > b) {
      return 1;
    }
    if (a < b) {
      return -1;
    }
  }
}

function ForbiddenError(message) {
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

  Error.call(this);
  this.constructor = ForbiddenError;
  this.subject = options.subject;
  this.subjectName = options.subjectName;
  this.action = options.action;
  this.field = options.field;
  this.message = message || 'Cannot execute "' + this.action + '" on "' + this.subjectName + '"';

  if (typeof Error.captureStackTrace === 'function') {
    this.name = this.constructor.name;
    Error.captureStackTrace(this, this.constructor);
  } else {
    this.stack = new Error(this.message).stack;
  }
}

ForbiddenError.prototype = Object.create(Error.prototype);

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  return typeof obj;
} : function (obj) {
  return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};

var classCallCheck = function (instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
};

var createClass = function () {
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }

  return function (Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
}();

function wrapArray(value) {
  return Array.isArray(value) ? value : [value];
}

function getSubjectName(subject) {
  if (!subject || typeof subject === 'string') {
    return subject;
  }

  var Type = (typeof subject === 'undefined' ? 'undefined' : _typeof(subject)) === 'object' ? subject.constructor : subject;

  return Type.modelName || Type.name;
}

var Rule = function () {
  function Rule(params) {
    classCallCheck(this, Rule);

    this.actions = params.actions || params.action;
    this.subject = params.subject;
    this.fields = !params.fields || params.fields.length === 0 ? undefined : wrapArray(params.fields);
    this.inverted = !!params.inverted;
    this.conditions = params.conditions;
    this._matches = this.conditions ? sift(this.conditions) : undefined;
    this.reason = params.reason;
  }

  Rule.prototype.matches = function matches(object) {
    if (!this._matches) {
      return true;
    }

    if (typeof object === 'string') {
      return !this.inverted;
    }

    return this._matches(object);
  };

  Rule.prototype.isRelevantFor = function isRelevantFor(object, field) {
    if (!this.fields) {
      return true;
    }

    if (!field) {
      return !this.inverted;
    }

    return this.fields.indexOf(field) !== -1;
  };

  return Rule;
}();

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

var DEFAULT_ALIASES = {
  manage: ['create', 'read', 'update', 'delete']
};
var PRIVATE_FIELD = typeof Symbol !== 'undefined' ? Symbol('private') : '__' + Date.now();

var Ability = function () {
  Ability.addAlias = function addAlias(alias, actions) {
    if (alias === actions || Array.isArray(actions) && actions.indexOf(alias) !== -1) {
      throw new Error('Attempt to alias action to itself: ' + alias + ' -> ' + actions.toString());
    }

    DEFAULT_ALIASES[alias] = actions;
    return this;
  };

  function Ability(rules) {
    var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
        _ref$RuleType = _ref.RuleType,
        RuleType = _ref$RuleType === undefined ? Rule : _ref$RuleType,
        _ref$subjectName = _ref.subjectName,
        subjectName = _ref$subjectName === undefined ? getSubjectName : _ref$subjectName;

    classCallCheck(this, Ability);

    this[PRIVATE_FIELD] = {
      RuleType: RuleType,
      subjectName: subjectName,
      originalRules: rules || [],
      rules: {},
      events: {},
      aliases: clone(DEFAULT_ALIASES)
    };
    this.update(rules);
  }

  Ability.prototype.update = function update(rules) {
    if (Array.isArray(rules)) {
      var payload = { rules: rules, ability: this };

      this.emit('update', payload);
      this[PRIVATE_FIELD].originalRules = Object.freeze(rules.slice(0));
      this[PRIVATE_FIELD].rules = this.buildIndexFor(this.rules);
      this.emit('updated', payload);
    }

    return this;
  };

  Ability.prototype.buildIndexFor = function buildIndexFor(rules) {
    var indexedRules = {};
    var RuleType = this[PRIVATE_FIELD].RuleType;


    for (var i = 0; i < rules.length; i++) {
      var rule = new RuleType(rules[i]);
      var actions = this.expandActions(rule.actions);
      var subjects = wrapArray(rule.subject);

      for (var k = 0; k < subjects.length; k++) {
        var subject = subjects[k];
        indexedRules[subject] = indexedRules[subject] || {};

        for (var j = 0; j < actions.length; j++) {
          var action = actions[j];
          indexedRules[subject][action] = indexedRules[subject][action] || [];
          indexedRules[subject][action].unshift(rule);
        }
      }
    }

    return indexedRules;
  };

  Ability.prototype.expandActions = function expandActions(rawActions) {
    var aliases = this[PRIVATE_FIELD].aliases;

    var actions = wrapArray(rawActions);
    var i = 0;

    while (i < actions.length) {
      var action = actions[i++];

      if (aliases.hasOwnProperty(action)) {
        actions = actions.concat(aliases[action]);
      }
    }

    return actions;
  };

  Ability.prototype.can = function can(action, subject, field) {
    var rule = this.relevantRuleFor(action, subject, field);

    return !!rule && !rule.inverted;
  };

  Ability.prototype.relevantRuleFor = function relevantRuleFor(action, subject, field) {
    var rules = this.rulesFor(action, subject, field);

    for (var i = 0; i < rules.length; i++) {
      if (rules[i].matches(subject)) {
        return rules[i];
      }
    }

    return null;
  };

  Ability.prototype.possibleRulesFor = function possibleRulesFor(action, subject) {
    var subjectName = this[PRIVATE_FIELD].subjectName(subject);
    var rules = this[PRIVATE_FIELD].rules;

    var specificRules = rules.hasOwnProperty(subjectName) ? rules[subjectName][action] : null;
    var generalRules = rules.hasOwnProperty('all') ? rules.all[action] : null;

    return (specificRules || []).concat(generalRules || []);
  };

  Ability.prototype.rulesFor = function rulesFor(action, subject, field) {
    return this.possibleRulesFor(action, subject).filter(function (rule) {
      return rule.isRelevantFor(subject, field);
    });
  };

  Ability.prototype.cannot = function cannot() {
    return !this.can.apply(this, arguments);
  };

  Ability.prototype.throwUnlessCan = function throwUnlessCan() {
    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    var rule = this.relevantRuleFor.apply(this, args);

    if (!rule || rule.inverted) {
      var action = args[0],
          subject = args[1],
          field = args[2];

      var subjectName = this[PRIVATE_FIELD].subjectName(subject);

      throw new ForbiddenError(rule ? rule.reason : null, {
        action: action,
        subjectName: subjectName,
        subject: subject,
        field: field
      });
    }
  };

  Ability.prototype.on = function on(event, handler) {
    var events = this[PRIVATE_FIELD].events;

    var isAttached = true;

    if (!events[event]) {
      events[event] = [];
    }

    events[event].push(handler);

    return function () {
      if (isAttached) {
        var index = events[event].indexOf(handler);
        events[event].splice(index, 1);
        isAttached = false;
      }
    };
  };

  Ability.prototype.emit = function emit(event, payload) {
    var handlers = this[PRIVATE_FIELD].events[event];

    if (handlers) {
      handlers.forEach(function (handler) {
        return handler(payload);
      });
    }
  };

  createClass(Ability, [{
    key: 'rules',
    get: function get$$1() {
      return this[PRIVATE_FIELD].originalRules;
    }
  }]);
  return Ability;
}();

function isStringOrNonEmptyArray(value) {
  return ![].concat(value).some(function (item) {
    return typeof item !== 'string';
  });
}

function isObject(value) {
  return value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object';
}

var RuleBuilder = function () {
  function RuleBuilder(rule) {
    classCallCheck(this, RuleBuilder);

    this.rule = rule;
  }

  RuleBuilder.prototype.because = function because(reason) {
    this.rule.reason = reason;
    return this;
  };

  return RuleBuilder;
}();

var AbilityBuilder = function () {
  AbilityBuilder.define = function define(params, dsl) {
    var options = typeof params === 'function' ? {} : params;
    var define = params === options ? dsl : params;
    var builder = new this(options);
    var result = define(builder.can.bind(builder), builder.cannot.bind(builder));
    var buildAbility = function buildAbility() {
      return new Ability(builder.rules, options);
    };

    return result && typeof result.then === 'function' ? result.then(buildAbility) : buildAbility();
  };

  AbilityBuilder.extract = function extract() {
    var builder = new this();

    return {
      can: builder.can.bind(builder),
      cannot: builder.cannot.bind(builder),
      rules: builder.rules
    };
  };

  function AbilityBuilder() {
    var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
        _ref$subjectName = _ref.subjectName,
        subjectName = _ref$subjectName === undefined ? getSubjectName : _ref$subjectName;

    classCallCheck(this, AbilityBuilder);

    this.rules = [];
    this.subjectName = subjectName;
  }

  AbilityBuilder.prototype.can = function can(actions, subject, conditionsOrFields, conditions) {
    if (!isStringOrNonEmptyArray(actions)) {
      throw new TypeError('AbilityBuilder#can expects the first parameter to be an action or array of actions');
    }

    var subjectName = [].concat(subject).map(this.subjectName);

    if (!isStringOrNonEmptyArray(subjectName)) {
      throw new TypeError('AbilityBuilder#can expects the second argument to be a subject name/type or an array of subject names/types');
    }

    var rule = { actions: actions, subject: subjectName };

    if (Array.isArray(conditionsOrFields) || typeof conditionsOrFields === 'string') {
      rule.fields = conditionsOrFields;
    }

    if (isObject(conditions) || !rule.fields && isObject(conditionsOrFields)) {
      rule.conditions = conditions || conditionsOrFields;
    }

    this.rules.push(rule);

    return new RuleBuilder(rule);
  };

  AbilityBuilder.prototype.cannot = function cannot() {
    var builder = this.can.apply(this, arguments);
    builder.rule.inverted = true;

    return builder;
  };

  return AbilityBuilder;
}();

class Permission {
    constructor() {
        this.roles = ['ANONYMOUS'];
        this.rules = [];
        this.can = '["read"]';
        this.subject = 'all';
        this.permissions = PermissionsService.instance;
        this.rules = this.permissions.rules;
        addEventListener('permissionsRulesChange', (event) => {
            if (!this.isolate) {
                this.render();
            }
        });
    }
    componentDidLoad() {
        this.displayDefault = Css.getStyle('display', this.el);
        this.parsePermissions();
    }
    componentDidUpdate() {
        this.parsePermissions();
    }
    componentDidUnload() {
        console.log('PermissionComponent removed from the DOM');
    }
    async setPermissionService(instance) {
        this.permissions = instance;
        this.rules = [...this.permissions.rules];
    }
    async getRules() {
        return this.rules;
    }
    async setRules(rules, replace = true) {
        if (replace === true) {
            this.rules = [...rules];
        }
        else {
            this.rules = [...this.rules, ...rules];
        }
    }
    async update() {
        this.render();
    }
    parsePermissions() {
        if (!this.ability) {
            return;
        }
        this.el.style.display = this.displayDefault;
        this.canArray.forEach((action) => {
            if (!this.ability.can(action, this.subject)) {
                this.el.style.display = 'none';
            }
        });
    }
    onPermissionsRulesChange() {
        if (!this.isolate) {
            this.render();
        }
    }
    render() {
        if (this.rulesJson) {
            this.rules = JSON.parse(this.rulesJson);
        }
        if (this.rules.length) {
            this.rules = [...this.permissions.rules];
            this.ability = new Ability(this.rules);
        }
        if (this.can) {
            this.canArray = JSON.parse(this.can);
        }
        if (this.cannot) {
            this.cannotArray = JSON.parse(this.cannot);
        }
        return (h("span", null,
            h("slot", null)));
    }
    static get is() { return "ema-permission"; }
    static get properties() { return {
        "can": {
            "type": String,
            "attr": "can"
        },
        "cannot": {
            "type": String,
            "attr": "cannot"
        },
        "conditions": {
            "type": String,
            "attr": "conditions"
        },
        "el": {
            "elementRef": true
        },
        "getRules": {
            "method": true
        },
        "isolate": {
            "type": Boolean,
            "attr": "isolate"
        },
        "rules": {
            "state": true
        },
        "rulesJson": {
            "type": String,
            "attr": "rules-json"
        },
        "setPermissionService": {
            "method": true
        },
        "setRules": {
            "method": true
        },
        "subject": {
            "type": String,
            "attr": "subject"
        },
        "update": {
            "method": true
        }
    }; }
}

export { EntityForm as EmaEntityForm, Form as EmaForm, FormField as EmaFormField, Permission as EmaPermission };
