setAttributes = (structure, propsSource, oldPropsSource) => {
  if ( propsSource === void 0 ) propsSource = {};
  if ( oldPropsSource === void 0 ) oldPropsSource = {};

  var props = propsSource || {};
  var oldProps = oldPropsSource || {};

  if (!structure.html || !structure.html[0]) { return structure; }
  var element = structure.html[0];

  if (!(element instanceof Node && element.nodeType !== 3)) { return structure; }

  var toRemove = Object.keys(oldProps)
    .filter(key => typeof props[key] === 'undefined');

  var loop = function ( prop ) {
    if (props.hasOwnProperty(prop)) {
      // Skip if proprs are the same
      if (typeof oldProps !== 'undefined' && oldProps[prop] === props[prop]) { return; }

      if (prop === 'checked') {
        element.checked = props[prop];
      }

      // Need to remove falsy attribute
      if (!props[prop] && typeof props[prop] !== 'number' && typeof props[prop] !== 'string') {
        element.removeAttribute(prop);
        return;
      }

      // Handle Listeners
      if (props[prop] instanceof Listener) {
        if (typeof structure.$attrListeners[prop] !== 'undefined') { return; }
        structure.$attrListeners[prop] = props[prop];
        props[prop].applyDepth(structure.depth).init();

        if (prop.toLowerCase() === 'model' || prop.toLowerCase() === 'checked') {
          if (element.getAttribute('type') === 'radio') {
            element.addEventListener('input', (e) => {
              structure.$attrListeners[prop].updateValue(
                (e.target.checked && e.target.value)
                || e.target.checked
              );
            }, false);
            structure.$attrListeners[prop].onValueChange(value => {
              setAttributes(structure, {
                checked: element.value === value && Boolean(value),
              }, {});
            });
          } else
          if (element.getAttribute('type') === 'checkbox') {
            element.addEventListener('input', (e) => {
              structure.$attrListeners[prop].updateValue(
                Boolean(e.target.checked)
              );
            }, false);
            structure.$attrListeners[prop].onValueChange(value => {
              setAttributes(structure, {
                checked: Boolean(value),
              }, {});
            });
          } else {
            element.addEventListener('input', (e) => {
              structure.$attrListeners[prop].updateValue(e.target.value);
            }, false);
          }
        }

        if (!/(checkbox|radio)/.test(element.getAttribute('type'))) {
          structure.$attrListeners[prop].onValueChange(value => {
            setAttributes(structure, {
              [prop]: value,
            }, {});
          });
        }

        // structure.setProps(Object.assign(structure.data.props, {
        //   [prop]: props[prop].value,
        // }));
        props[prop] = structure.$attrListeners[prop].value;
        return;
      }

      if (prop === 'value' || prop === 'model') {
        element.value = props[prop];
      }

      if (typeof GLOBALS.CUSTOM_ATTRIBUTES[prop] !== 'undefined') {
        var ref = GLOBALS.CUSTOM_ATTRIBUTES[prop];
        var allowedTags = ref.allowedTags;

        if (!allowedTags || (
          allowedTags
            && allowedTags.length > 0
            && allowedTags.indexOf(element.localName) >= 0
        )) {
          if (typeof GLOBALS.CUSTOM_ATTRIBUTES[prop].caller === 'function') {
            GLOBALS.CUSTOM_ATTRIBUTES[prop].caller(element, props[prop]);
          }
          if (!GLOBALS.CUSTOM_ATTRIBUTES[prop].addToElement) { return; }
        }
      }


      if (prop.toLowerCase() === 'style') {
        if (typeof props[prop] === 'object') {
          setStyles(structure, props[prop], (oldProps && oldProps.style) || {});
          // props[prop] = structure.setStyles(props[prop], (oldProps && oldProps.style) || {});
        } else {
          element.style = props[prop];
        }
        return;
      }

      if (prop.toLowerCase() === 'class' || prop.toLowerCase() === 'classname') {
        element.setAttribute('class', parseClass(props[prop]));
        return;
      }

      if (prop.toLowerCase() === 'loadfocus') {
        element.addEventListener('mount', () => {
          element.focus();
        }, false);
        return;
      }

      if (prop.toLowerCase() === 'html') {
        element.innerHTML = props[prop];
        return;
      }

      // Handles events 'on<event>'
      if (prop.substring(0, 2).toLowerCase() === 'on' && typeof props[prop] === 'function') {
        var fn = props[prop];
        if (prop.substring(0, 8).toLowerCase() === 'onsubmit') {
          element[prop] = (e) => {
            if (props.prevent) {
              e.preventDefault();
            }

            var data = [];
            var inputs = e.target.elements || [];
            for (var input of inputs) {
              if ((input.name !== ''
                && (input.type !== 'radio' && input.type !== 'checkbox'))
                || input.checked) {
                var item = {
                  name: input.name,
                  el: input,
                  type: input.type,
                  default: input.defaultValue,
                  value: input.value,
                  set(val) {
                    if (structure && structure.el && structure.el.value) {
                      structure.el.value = val;
                    }
                  },
                  reset(val) {
                    if (structure && structure.el && structure.el.value) {
                      structure.el.value = val;
                      structure.el.defaultValue = val;
                    }
                  },
                };
                data.push(item);
                if (!data[item.name]) {
                  Object.defineProperty(data, item.name, {
                    value: item,
                  });
                }
              }
            }

            return fn(e, data);
          };
        } else {
          element[prop] = (e, ...args) => fn(e, ...args);
        }
        return;
      }

      element.setAttribute(prop, props[prop]);
    }
  };

  for (var prop in props) loop( prop );

  for (var i = 0; i < toRemove.length; i++) {
    element.removeAttribute(toRemove[i]);
  }

  structure.props = props;

  return structure;
}
@param {Structure} structure
@param {object} propsSource
@param {object} oldPropsSource