html-janitor
Advanced tools
Comparing version 2.0.0 to 2.0.1
@@ -5,2 +5,8 @@ # HTML Janitor | ||
## 2.0.1 | ||
Adds the ability to configure HTML Janitor with validation functions that determine whether a given element should be allowed programatically. | ||
Thanks [Brad Vogel](https://github.com/bradvogel) for adding this functionality. | ||
## 2.0.0 | ||
@@ -7,0 +13,0 @@ |
{ | ||
"name": "html-janitor", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"main": "src/html-janitor.js", | ||
@@ -8,2 +8,6 @@ "scripts": { | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/guardian/html-janitor.git" | ||
}, | ||
"devDependencies": { | ||
@@ -10,0 +14,0 @@ "karma": "~0.10.2", |
@@ -26,6 +26,36 @@ # html-janitor | ||
#### Blacklisting and whitelisting attributes | ||
#### Blacklisting and whitelisting all attributes | ||
You can set an element to be `true` to allow all attributes on an element and `false` to remove all attributes. | ||
#### Using logic | ||
If you need to apply logic when determining whether to whitelist an element or an attribute, you can pass a function. | ||
Here's an example that removes all `<u>` elements that are empty. | ||
``` | ||
u: function(el){ | ||
// Remove empty underline tags. | ||
var shouldKeep = el.textContent !== ''; | ||
return shouldKeep; | ||
}, | ||
``` | ||
A function can also be used for attributes, only the attribute's value and the element are passed as the function arguments: | ||
``` | ||
img: { | ||
height: function(value){ | ||
// Only allow if height is less than 10. | ||
return parseInt(value) < 10; | ||
}, | ||
width: function(value, el){ | ||
// Only allow if height also specified. | ||
return el.hasAttribute('height'); | ||
} | ||
} | ||
``` | ||
## Distribution | ||
@@ -32,0 +62,0 @@ |
@@ -22,3 +22,3 @@ (function (root, factory) { | ||
.map(function(k) { return typeof tagDefinitions[k]; }) | ||
.every(function(type) { return type === 'object' || type === 'boolean'; }); | ||
.every(function(type) { return type === 'object' || type === 'boolean' || type === 'function'; }); | ||
@@ -58,5 +58,2 @@ if(!validConfigValues) { | ||
do { | ||
var nodeName = node.nodeName.toLowerCase(); | ||
var allowedAttrs = this.config.tags[nodeName]; | ||
// Ignore nodes that have already been sanitized | ||
@@ -97,4 +94,2 @@ if (node._sanitized) { | ||
var isInvalid = isInline && containsBlockElement; | ||
// Block elements should not be nested (e.g. <li><p>...); if | ||
@@ -108,5 +103,11 @@ // they are, we want to unwrap the inner block element. | ||
var nodeName = node.nodeName.toLowerCase(); | ||
var allowedAttrs = this.config.tags[nodeName]; | ||
var isInvalid = isInline && containsBlockElement; | ||
// Drop tag entirely according to the whitelist *and* if the markup | ||
// is invalid. | ||
if (this.config.tags[nodeName] === undefined || isInvalid || (!this.config.keepNestedBlockElements && isNestedBlockElement)) { | ||
if (isInvalid || shouldRejectNode(node, allowedAttrs) | ||
|| (!this.config.keepNestedBlockElements && isNestedBlockElement)) { | ||
// Do not keep the inner text of SCRIPT/STYLE elements. | ||
@@ -127,9 +128,4 @@ if (! (node.nodeName === 'SCRIPT' || node.nodeName === 'STYLE')) { | ||
var attr = node.attributes[a]; | ||
var attrName = attr.name.toLowerCase(); | ||
// Allow attribute? | ||
var allowedAttrValue = allowedAttrs[attrName] || allowedAttrs === true; | ||
var notInAttrList = ! allowedAttrValue; | ||
var valueNotAllowed = allowedAttrValue !== true && attr.value !== allowedAttrValue; | ||
if (notInAttrList || valueNotAllowed) { | ||
if (shouldRejectAttr(attr, allowedAttrs, node)) { | ||
node.removeAttribute(attr.name); | ||
@@ -155,4 +151,32 @@ // Shift the array to continue looping. | ||
function shouldRejectNode(node, allowedAttrs){ | ||
if (typeof allowedAttrs === 'undefined') { | ||
return true; | ||
} else if (typeof allowedAttrs === 'function'){ | ||
return !allowedAttrs(node); | ||
} | ||
return false; | ||
} | ||
function shouldRejectAttr(attr, allowedAttrs, node){ | ||
var attrName = attr.name.toLowerCase(); | ||
if (allowedAttrs === true){ | ||
return false; | ||
} else if (typeof allowedAttrs[attrName] === 'function'){ | ||
return !allowedAttrs[attrName](attr.value, node); | ||
} else if (typeof allowedAttrs[attrName] === 'undefined'){ | ||
return true; | ||
} else if (allowedAttrs[attrName] === false) { | ||
return true; | ||
} else if (typeof allowedAttrs[attrName] === 'string') { | ||
return (allowedAttrs[attrName] !== attr.value); | ||
} | ||
return false; | ||
} | ||
return HTMLJanitor; | ||
})); |
@@ -16,3 +16,2 @@ define([ 'html-janitor' ], function (HTMLJanitor) { | ||
sup: {}, | ||
u: {}, | ||
strike: {}, | ||
@@ -25,3 +24,18 @@ | ||
div: {}, | ||
figure: false | ||
figure: false, | ||
u: function(el){ | ||
// Remove empty underline tags. | ||
var shouldKeepEl = el.innerHTML !== ''; | ||
return shouldKeepEl; | ||
}, | ||
img: { | ||
height: function(value){ | ||
// Only allow if height is less than 10. | ||
return parseInt(value) < 10; | ||
}, | ||
width: function(value, el){ | ||
// Only allow if height also specified. | ||
return el.hasAttribute('height'); | ||
} | ||
} | ||
} | ||
@@ -153,3 +167,3 @@ | ||
el.setAttribute('title', 'test'); | ||
var outputEl = document.createElement('div'); | ||
@@ -166,3 +180,3 @@ outputEl.innerHTML = janitor.clean(el.outerHTML); | ||
expect(attributes.getNamedItem('data-test').value).toBe('true'); | ||
expect(attributes.getNamedItem('title').name).toBe('title'); | ||
@@ -182,2 +196,25 @@ expect(attributes.getNamedItem('title').value).toBe('test'); | ||
it('should handle functions as options', function () { | ||
var html = '<div><u>content</u></div>'; | ||
expect(janitor.clean(html)).toBe('<div><u>content</u></div>'); | ||
html = '<div><u></u></div>'; | ||
expect(janitor.clean(html)).toBe('<div></div>'); | ||
}); | ||
it('should handle functions as options for attributes', function () { | ||
var html = '<img height="11">'; | ||
expect(janitor.clean(html)).toBe('<img>'); | ||
html = '<img height="9">'; | ||
expect(janitor.clean(html)).toBe('<img height="9">'); | ||
}); | ||
it('should also handle functions for attributes that take an element', function () { | ||
var html = '<img width="1">'; | ||
expect(janitor.clean(html)).toBe('<img>'); | ||
html = '<img height="9" width="1">'; | ||
expect(janitor.clean(html)).toBe('<img height="9" width="1">'); | ||
}); | ||
}); | ||
@@ -184,0 +221,0 @@ |
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
32687
439
84
2