var inverseXML = getInverseObj(require(“../maps/xml.json”)),

xmlReplacer = getInverseReplacer(inverseXML);

exports.XML = getInverse(inverseXML, xmlReplacer);

var inverseHTML = getInverseObj(require(“../maps/entities.json”)),

htmlReplacer = getInverseReplacer(inverseHTML);

exports.HTML = getInverse(inverseHTML, htmlReplacer);

function getInverseObj(obj){

return Object.keys(obj).sort().reduce(function(inverse, name){
        inverse[obj[name]] = "&" + name + ";";
        return inverse;
}, {});

}

function getInverseReplacer(inverse){

var single = [],
    multiple = [];

Object.keys(inverse).forEach(function(k){
        if(k.length === 1){
                single.push("\\" + k);
        } else {
                multiple.push(k);
        }
});

//TODO add ranges
multiple.unshift("[" + single.join("") + "]");

return new RegExp(multiple.join("|"), "g");

}

var re_nonASCII = /[^0-x7F]/g,

re_astralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;

function singleCharReplacer©{

return "&#x" + c.charCodeAt(0).toString(16).toUpperCase() + ";";

}

function astralReplacer©{

// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
var high = c.charCodeAt(0);
var low  = c.charCodeAt(1);
var codePoint = (high - 0xD800) * 0x400 + low - 0xDC00 + 0x10000;
return "&#x" + codePoint.toString(16).toUpperCase() + ";";

}

function getInverse(inverse, re){

function func(name){
        return inverse[name];
}

return function(data){
        return data
                        .replace(re, func)
                        .replace(re_astralSymbols, astralReplacer)
                        .replace(re_nonASCII, singleCharReplacer);
};

}

var re_xmlChars = getInverseReplacer(inverseXML);

function escapeXML(data){

return data
                .replace(re_xmlChars, singleCharReplacer)
                .replace(re_astralSymbols, astralReplacer)
                .replace(re_nonASCII, singleCharReplacer);

}

exports.escape = escapeXML;