description and source-codefunction sanitizeHtml(html, options, _recursing) {
var result = '';
function Frame(tag, attribs) {
var that = this;
this.tag = tag;
this.attribs = attribs || {};
this.tagPosition = result.length;
this.text = ''; // Node inner text
this.updateParentNodeText = function() {
if (stack.length) {
var parentFrame = stack[stack.length - 1];
parentFrame.text += that.text;
}
};
}
if (!options) {
options = sanitizeHtml.defaults;
options.parser = htmlParserDefaults;
} else {
options = extend(sanitizeHtml.defaults, options);
if (options.parser) {
options.parser = extend(htmlParserDefaults, options.parser);
} else {
options.parser = htmlParserDefaults;
}
}
// Tags that contain something other than HTML, or where discarding
// the text when the tag is disallowed makes sense for other reasons.
// If we are not allowing these tags, we should drop their content too.
// For other tags you would drop the tag but keep its content.
var nonTextTagsArray = options.nonTextTags || [ 'script', 'style', 'textarea' ];
var allowedAttributesMap;
var allowedAttributesGlobMap;
if(options.allowedAttributes) {
allowedAttributesMap = {};
allowedAttributesGlobMap = {};
each(options.allowedAttributes, function(attributes, tag) {
allowedAttributesMap[tag] = [];
var globRegex = [];
attributes.forEach(function(name) {
if(name.indexOf('*') >= 0) {
globRegex.push(quoteRegexp(name).replace(/\\\*/g, '.*'));
} else {
allowedAttributesMap[tag].push(name);
}
});
allowedAttributesGlobMap[tag] = new RegExp('^(' + globRegex.join('|') + ')$');
});
}
var allowedClassesMap = {};
each(options.allowedClasses, function(classes, tag) {
// Implicitly allows the class attribute
if(allowedAttributesMap) {
if (!has(allowedAttributesMap, tag)) {
allowedAttributesMap[tag] = [];
}
allowedAttributesMap[tag].push('class');
}
allowedClassesMap[tag] = classes;
});
var transformTagsMap = {};
var transformTagsAll;
each(options.transformTags, function(transform, tag) {
var transFun;
if (typeof transform === 'function') {
transFun = transform;
} else if (typeof transform === "string") {
transFun = sanitizeHtml.simpleTransform(transform);
}
if (tag === '*') {
transformTagsAll = transFun;
} else {
transformTagsMap[tag] = transFun;
}
});
var depth = 0;
var stack = [];
var skipMap = {};
var transformMap = {};
var skipText = false;
var skipTextDepth = 0;
var parser = new htmlparser.Parser({
onopentag: function(name, attribs) {
if (skipText) {
skipTextDepth++;
return;
}
var frame = new Frame(name, attribs);
stack.push(frame);
var skip = false;
var hasText = frame.text ? true : false;
var transformedTag;
if (has(transformTagsMap, name)) {
transformedTag = transformTagsMap[name](name, attribs);
frame.attribs = attribs = transformedTag.attribs;
if (transformedTag.text !== undefined) {
frame.innerText = transformedTag.text;
}
if (name !== transformedTag.tagName) {
frame.name = name = transformedTag.tagName;
transformMap[depth] = transformedTag.tagName;
}
}
if (transformTagsAll) {
transformedTag = transformTagsAll(name, attribs);
frame.attribs = attribs = transformedTag.attribs;
if (name !== transformedTag.tagName) {
frame.name = name = transformedTag.tagName;
transformMap[depth] = transformedTag.tagName;
}
}
if (options.allowedTags && options.allowedTags.indexOf(name) === -1) {
skip = true;
if (nonTextTagsArray.indexOf(name) !== -1) {
skipText = true;
skipTextDepth = 1;
}
skipMap[depth] = true;
}
depth++;
if (skip) {
// We want the contents but not this tag ...