code: function parse(node) {
      const cfg = Object.assign({}, copyXmlAttributes(node));
  
      for (let i = 0, l = node.childNodes.length; i < l; ++i) {
          const el = node.childNodes.item(i);
          if (el.nodeType === ELEMENT_NODE) {
              if (!el.namespaceURI) {
                  // attribute elements
                  if (!cfg.attributes) cfg.attributes = {};
                  cfg.attributes[el.localName] = el.childNodes.length ? parse(el) : copyXmlAttributes(el);
              } else if (el.namespaceURI === 'urn:flora:options') {
                  // flora specific elements
                  if (el.localName === 'dataSource') {
                      const dataSource = getDataSource(el);
                      if (!cfg.dataSources) cfg.dataSources = {};
                      if (cfg.dataSources[dataSource.name]) {
                          throw new Error(`Data source "${dataSource.name}" already defined`);
                      }
                      cfg.dataSources[dataSource.name] = dataSource.config;
                  }
                  if (el.localName === 'subFilter') {
                      if (!cfg.subFilters) cfg.subFilters = [];
                      cfg.subFilters.push(copyXmlAttributes(el));
                  }
              }
          } else if (el.nodeType === TEXT_NODE && el.textContent.trim().length > 0) {
              throw new ImplementationError(`Config contains unnecessary text: "${el.textContent.trim()}"`);
          }
      }
  
      return cfg;
  }
docs: Return DataSources, attributes and sub-resources as plain JS object.
  
  @param {Node} node
  @return {Object}
  @private