function DOMParser(options) {
    this.options = options || {locator: {}};
}
DOMParser.prototype.parseFromString = function (source, mimeType) {
    var options = this.options;
    var sax = new XMLReader();
    var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
    var errorHandler = options.errorHandler;
    var locator = options.locator;
    var defaultNSMap = options.xmlns || {};
    var entityMap = {'lt': '<', 'gt': '>', 'amp': '&', 'quot': '"', 'apos': "'"}
    if (locator) {
        domBuilder.setDocumentLocator(locator)
    }
    sax.errorHandler = buildErrorHandler(errorHandler, domBuilder, locator);
    sax.domBuilder = options.domBuilder || domBuilder;
    if (/\/x?html?$/.test(mimeType)) {
        entityMap.nbsp = '\xa0';
        entityMap.copy = '\xa9';
        defaultNSMap[''] = 'http://www.w3.org/1999/xhtml';
    }
    defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
    if (source) {
        sax.parse(source, defaultNSMap, entityMap);
    } else {
        sax.errorHandler.error("invalid document source");
    }
    return domBuilder.document;
}
function buildErrorHandler(errorImpl, domBuilder, locator) {
    if (!errorImpl) {
        if (domBuilder instanceof DOMHandler) {
            return domBuilder;
        }
        errorImpl = domBuilder;
    }
    var errorHandler = {}
    var isCallback = errorImpl instanceof Function;
    locator = locator || {}
    function build(key) {
        var fn = errorImpl[key];
        if (!fn && isCallback) {
            fn = errorImpl.length == 2 ? function (msg) {
                    errorImpl(key, msg)
                } : errorImpl;
        }
        errorHandler[key] = fn && function (msg) {
                fn('[xmldom ' + key + ']\t' + msg + _locator(locator));
            } || function () {
            };
    }
    build('warning');
    build('error');
    build('fatalError');
    return errorHandler;
}
//console.log('#\n\n\n\n\n\n\n####')
/**
 * +ContentHandler+ErrorHandler
 * +LexicalHandler+EntityResolver2
 * -DeclHandler-DTDHandler
 *
 * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
 * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
 * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
 */
function DOMHandler() {
    this.cdata = false;
}
function position(locator, node) {
    node.lineNumber = locator.lineNumber;
    node.columnNumber = locator.columnNumber;
}
/**
 * @see org.xml.sax.ContentHandler#startDocument
 * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
 */
DOMHandler.prototype = {
    startDocument: function () {
        this.document = new DOMImplementation().createDocument(null, null, null);
        if (this.locator) {
            this.document.documentURI = this.locator.systemId;
        }
    },
    startElement: function (namespaceURI, localName, qName, attrs) {
        var doc = this.document;
        var el = doc.createElementNS(namespaceURI, qName || localName);
        var len = attrs.length;
        appendElement(this, el);
        this.currentElement = el;
        this.locator && position(this.locator, el)
        for (var i = 0; i < len; i++) {
            var namespaceURI = attrs.getURI(i);
            var value = attrs.getValue(i);
            var qName = attrs.getQName(i);
            var attr = doc.createAttributeNS(namespaceURI, qName);
            if (attr.getOffset) {
                position(attr.getOffset(1), attr)
            }
            attr.value = attr.nodeValue = value;
            el.setAttributeNode(attr)
        }
    },
    endElement: function (namespaceURI, localName, qName) {
        var current = this.currentElement;
        var tagName = current.tagName;
        this.currentElement = current.parentNode;
    },
    startPrefixMapping: function (prefix, uri) {
    },
    endPrefixMapping: function (prefix) {
    },
    processingInstruction: function (target, data) {
        var ins = this.document.createProcessingInstruction(target, data);
        this.locator && position(this.locator, ins)
        appendElement(this, ins);
    },
    ignorableWhitespace: function (ch, start, length) {
    },
    characters: function (chars, start, length) {
        chars = _toString.apply(this, arguments)
        //console.log(chars)
        if (this.currentElement && chars) {
            if (this.cdata) {
                var charNode = this.document.createCDATASection(chars);
                this.currentElement.appendChild(charNode);
            } else {
                var charNode = this.document.createTextNode(chars);
                this.currentElement.appendChild(charNode);
            }
            this.locator && position(this.locator, charNode)
        }
    },
    skippedEntity: function (name) {
    },
    endDocument: function () {
        this.document.normalize();
    },
    setDocumentLocator: function (locator) {
        if (this.locator = locator) {// && !('lineNumber' in locator)){
            locator.lineNumber = 0;
        }
    },
    //LexicalHandler
    comment: function (chars, start, length) {
        chars = _toString.apply(this, arguments)
        var comm = this.document.createComment(chars);
        this.locator && position(this.locator, comm)
        appendElement(this, comm);
    },
    startCDATA: function () {
        //used in characters() methods
        this.cdata = true;
    },
    endCDATA: function () {
        this.cdata = false;
    },
    startDTD: function (name, publicId, systemId) {
        var impl = this.document.implementation;
        if (impl && impl.createDocumentType) {
            var dt = impl.createDocumentType(name, publicId, systemId);
            this.locator && position(this.locator, dt)
            appendElement(this, dt);
        }
    },
    /**
     * @see org.xml.sax.ErrorHandler
     * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
     */
    warning: function (error) {
        //console.warn('[xmldom warning]\t' + error, _locator(this.locator));
    },
    error: function (error) {
        //console.error('[xmldom error]\t' + error, _locator(this.locator));
    },
    fatalError: function (error) {
        //console.error('[xmldom fatalError]\t' + error, _locator(this.locator));
        throw error;
    }
}
function _locator(l) {
    if (l) {
        return '\n@' + (l.systemId || '') + '#[line:' + l.lineNumber + ',col:' + l.columnNumber + ']'
    }
}
function _toString(chars, start, length) {
    if (typeof chars == 'string') {
        return chars.substr(start, length)
    } else {//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
        if (chars.length >= start + length || start) {
            return new java.lang.String(chars, start, length) + '';
        }
        return chars;
    }
}
/*
 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
 * used method of org.xml.sax.ext.LexicalHandler:
 *  #comment(chars, start, length)
 *  #startCDATA()
 *  #endCDATA()
 *  #startDTD(name, publicId, systemId)
 *
 *
 * IGNORED method of org.xml.sax.ext.LexicalHandler:
 *  #endDTD()
 *  #startEntity(name)
 *  #endEntity(name)
 *
 *
 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
 * IGNORED method of org.xml.sax.ext.DeclHandler
 * 	#attributeDecl(eName, aName, type, mode, value)
 *  #elementDecl(name, model)
 *  #externalEntityDecl(name, publicId, systemId)
 *  #internalEntityDecl(name, value)
 * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
 * IGNORED method of org.xml.sax.EntityResolver2
 *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
 *  #resolveEntity(publicId, systemId)
 *  #getExternalSubset(name, baseURI)
 * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
 * IGNORED method of org.xml.sax.DTDHandler
 *  #notationDecl(name, publicId, systemId) {};
 *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
 */
"endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g, function (key) {
    DOMHandler.prototype[key] = function () {
        return null
    }
})
/* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */
function appendElement(hander, node) {
    if (!hander.currentElement) {
        hander.document.appendChild(node);
    } else {
        hander.currentElement.appendChild(node);
    }
}//appendChild and setAttributeNS are preformance key
if (typeof require == 'function') {
    var XMLReader = require('./sax').XMLReader;
    var DOMImplementation = exports.DOMImplementation = require('./dom').DOMImplementation;
    var XMLSerializer = exports.XMLSerializer = require('./dom').XMLSerializer;
    exports.DOMParser = DOMParser;
    var DOMParser = {
        DOMImplementation: DOMImplementation,
        XMLSerializer: XMLSerializer,
        DOMParser: DOMParser
    }
    module.exports = DOMParser
}