function xForm(form) {
  this.filters     = new Object();
  this.guid        = generateGUID();
  this.parent      = this;
  this.name        = form.getAttribute('name');
  if (null == this.name||
      0 == this.name.length) {
    this.name = "form";
  }
  this.form               = this;//совместимость с дочерними элементами
  this.element            = form;
  this.element.formObject = this;
  this.onSubmit           = form.getAttribute('onsubmit');
  this.element.onsubmit   = function() {
    logBuffer.reset();

    this.formObject.resetElements();
    var result = this.formObject.process(xFormParser.filters.POSTPROCESSOR, this.formObject);
    if (!this.ztsName) {
      var data = {type : 'hidden', name : 'zts:form' , value : this.formObject.name};
      this.ztsName = this.insertBefore(DOMUtl.createNode('input', data, data, document), this.firstChild);
    }
    logBuffer.log('result is ' + result);
    if (result) {
      try {
        eval(this.formObject.onSubmit);
      } catch (E) {
      }
    }
    return result;
  };

  this.element.onreset = function() {
    logBuffer.reset();

    logBuffer.log('reset triggered');
    this.formObject.resetElements();
    this.formObject.process(xFormParser.filters.RESET, this.formObject);
    return false;
  };
  this.resetElements();
}
xForm.prototype.setElementName = function(name) {
  if (!isUndefined(this.name)) {
    var ua = navigator.userAgent.toLowerCase();
    var isIE = (ua.indexOf("msie") != -1 &&
                ua.indexOf("opera") == -1 &&
               !navigator.appVersion.match(/MSIE 8/));

    if (this.inputType == "radio" && isIE) { //stupid IE fix
      var parent = this.element.parentNode;
      var sibling = this.element.nextSibling;
      var div = document.createElement("div");
      var checked = this.element.getAttributeNode('checked').value == "checked";

      div.innerHTML = this.element.outerHTML.replace(/name=\S+/i, "name=\"" + name + "\"" + (checked ? " checked='checked'" : ""));
      parent.removeChild(this.element);
      this.element = div.firstChild;
      this.element.xelement = this;
      this.element.xform = this.form;
      if (sibling == null) {
        parent.appendChild(this.element)
      } else {
        parent.insertBefore(this.element, sibling);
      }

      delete div, parent, sibling, checked;
    } else {
      var checked = this.element.getAttribute('checked');
      if (this.isCheckable() && checked) { // may be string
        var self = this;
        setTimeout(function() {
          self.element.checked = true;
          self.element.setAttribute('checked', 'checked');
        }, 10)
      }
      this.element.name = name;
      this.element.setAttribute('name', name);
    }
  }
};

xForm.prototype.generateGUID = function() {
  if (this.element.guid) {
    this.guid = this.element.guid;
  } else {
    this.guid = generateGUID();
    this.element.guid = this.guid;
  }
};

/* filter chain interface */
xForm.prototype.addFilter = function(filter, mode) {
  this.filters[filter.getName()] = {
    filter : filter,
    mode : mode
  };
};

xForm.prototype.resetElements = function() {
  this.elements    = {idx : {}, data: new Array()};
  this.subElements = new Array();
};

xForm.prototype.process = function(mode, node) {
  var result = true;
  var res = new Array();
  for (var i = 0; i < 31; i++) {
    if ((mode & (1 << i)) > 0) {
      res.push(xFormParser.filters.desc[(1 << i)]);
    }
  }
  logBuffer.log(sprintf('parsing with mode(s) "%s"', res.join('", "')));

  this.parse(node.getElement(), node);

  logBuffer.log('form parsed\nprocessing filter chain');

  for (var i in this.filters) {
    var currentFilter = this.filters[i].filter;
    if (0 == (mode & this.filters[i].mode)) {
      logBuffer.log(sprintf('&nbsp;&nbsp;skipping filter "%s" by mode', currentFilter.getName()));
      continue;
    }
    if (currentFilter.isEnabled()) {
      logBuffer.log(sprintf('&nbsp;&nbsp;applying filter "%s" (raw: %d)', currentFilter.getName(), this.filters[i].mode.toString(2)));
      result = currentFilter.process(node, logBuffer) && result;
    } else {
      logBuffer.log(sprintf('&nbsp;&nbsp;skipping disabled filter "%s"', currentFilter.getName()));
    }
  }
  var oldEL = logBuffer.getErrorLevel();
  var messages = logBuffer.getMessages();
  logBuffer.setErrorLevel(logBuffer.MSG_ERROR);
  if (messages.length == 0) {
    logBuffer.log('finished successfully');
  } else {
    logBuffer.log(sprintf('finished with errors...\n\nError log:\n%s', messages.join('\n')), logBuffer.MSG_ERROR);
  }
  logBuffer.setErrorLevel(oldEL);
  return result;
};

/* xFormElement interface */
xForm.prototype.isValidateable = function() {
  return true;
};

xForm.prototype.getDataType = function() {
  return 'group';
};

xForm.prototype.getGUID = function() {
  return this.guid;
};

xForm.prototype.getSubElements = function() {
  return this.subElements;
};
xForm.prototype.getCustomProperty = function(paramName, nameSpace) {
  var fullName = xFormParser.getFilterNS(nameSpace) + ':' + paramName;
  return this.element.getAttribute(fullName);
};
xForm.prototype.setCustomProperty = function(paramName, nameSpace, value) {
  this.element.setAttribute(
    xFormParser.getFilterNS(nameSpace) +
    ':' +
    paramName,
    value
  );
};
xForm.prototype.getValue = function() {
};
xForm.prototype.setValue = function(value) {
};
xForm.prototype.getElement = function() {
  return this.element;
};
xForm.prototype.getElements = function() {
  return this.elements.data;
};
xForm.prototype.addSubElement = function(element) {
  element.setParent(this);
  this.subElements.push(element);
};
xForm.prototype.addElement = function(element) {
  this.elements.data.push(element);
};
xForm.prototype.setParent = function(element) {
  this.parent = element;
};
xForm.prototype.getParent = function() {
  return this.parent;
};
xForm.prototype.getFullName = function() {
  return this.name;
};
xForm.prototype.reset = function() {
};

xForm.prototype.parse = function(root, ancentorElement) {
  for (var i = 0; i < root.childNodes.length; i++) {
    var node = root.childNodes.item(i);

    if (DOMUtl.hasAttributes(node) &&
        DOMUtl.getAttribute(node, xFormParser.getFilterNS('form') + ':parse') == 'yes') {

      var element = xFormElementFactory.createElement(ancentorElement, node);

      ancentorElement.addSubElement(element);
      this.addElement(element);

      this.parse(node, element);
    } else {
      this.parse(node, ancentorElement);
    }
  }
};