docs: function (nodeData, options) {
  
      var makeNode = function (name, content, attributes, level, hasSubNodes) {
  
          var indentValue = options.indent !== undefined ? options.indent : '\t';
          var indent = options.prettyPrint ? '\n' + new Array(level).join(indentValue) : '';
          if (options.removeIllegalNameCharacters) {
              name = name.replace(elementReplace, '_');
          }
  
          var node = [indent, '<', name, (attributes || '')];
          if (content && content.length > 0) {
              node.push('>');
              node.push(content);
              if(hasSubNodes) node.push(indent);
              node.push('</');
              node.push(name);
              node.push('>');
          } else {
              node.push('/>');
          }
          return node.join('');
      };
  
      return (function fn(nodeData, nodeDescriptor, level) {
          var type = typeof nodeData;
          if ((Array.isArray) ? Array.isArray(nodeData) : nodeData instanceof Array) {
              type = 'array';
          } else if (nodeData instanceof Date) {
              type = 'date';
          }
  
          switch (type) {
              //if value is an array create child nodes from values
              case 'array':
                  var ret = [];
                  nodeData.map(function (v) {
                      ret.push(fn(v, 1, level + 1));
                      //entries that are values of an array are the only ones that can be special node descriptors
                  });
                  if(options.prettyPrint) ret.push('\n');
                  return ret.join('');
              case 'date':
                  // cast dates to ISO 8601 date (soap likes it)
                  return nodeData.toJSON ? nodeData.toJSON() : nodeData + '';
              case 'object':
                  if (nodeDescriptor === 1 && nodeData.name) {
                      var content = [], attributes = [], c = '';
  
                      if (nodeData.attrs) {
                          if (typeof nodeData.attrs !== 'object') {
                              // attrs is a string, etc. - just use it as an attribute
                              attributes.push(' ');
                              attributes.push(nodeData.attrs);
                          } else {
                              for (var key in nodeData.attrs) {
                                  var value = nodeData.attrs[key];
                                  attributes.push(' ');
                                  attributes.push(key);
                                  attributes.push('="');
                                  attributes.push(options.escape ? esc(value) : value);
                                  attributes.push('"');
                              }
                          }
                      }
  
                      //later attributes can be added here
                      if (typeof nodeData.value !== 'undefined') {
                          c = '' + nodeData.value;
                          content.push(options.escape ? esc(c) : c);
                      } else if (typeof nodeData.text !== 'undefined') {
                          c = '' + nodeData.text;
                          content.push(options.escape ? esc(c) : c);
                      }
  
                      if (nodeData.children) {
                          content.push(fn(nodeData.children, 0, level + 1));
                      }
  
                      return makeNode(nodeData.name, content.join(''), attributes.join(''), level, !!nodeData.children);
                  } else {
                      var nodes = [];
                      for (var name in nodeData) {
                          nodes.push(makeNode(name, fn(nodeData[name], 0, level + 1), null, level + 1));
                      }
                      if(options.prettyPrint && nodes.length > 0) nodes.push('\n');
                      return nodes.join('');
                  }
              /* falls through */
              case 'function':
                  return nodeData();
              default:
                  return options.escape ? esc(nodeData) : '' + nodeData;
          }
      }(nodeData, 0, 0));
  }