Created
March 5, 2018 10:10
-
-
Save fand/94ab6ec0b67bfb4de297c7492f352a95 to your computer and use it in GitHub Desktop.
remark-strip-html-but-iframe.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// iframe以外のHTML文字列を禁止する。 | |
// remark-strip-htmlを参考にした。 | |
const IS_ATTR_ALLOWED = { | |
src: true, | |
frameborder: true, | |
height: true, | |
width: true, | |
}; | |
/** | |
* HTML文字列から許可されていない属性を取り除く。 | |
* @param {string} html - HTML文字列 | |
* @returns {string} | |
*/ | |
function filterAttributes(html) { | |
const attrs = {}; | |
html.replace(/(\S+?)=["']?(\S*)["']?/gi, (_, key, value) => { | |
if (IS_ATTR_ALLOWED[key]) { | |
attrs[key] = value; | |
} | |
}); | |
const iframe = document.createElement('iframe'); | |
for (const k in attrs) { | |
iframe.setAttribute(k, attrs[k]); | |
} | |
return iframe.outerHTML; | |
} | |
/** | |
* トークンからiframe以外のHTML文字列を削除する。 | |
* iframeについては属性を制限する。 | |
* @param {{ type: string, value: string }} token | |
* @returns {{ type: string, value: string }} | |
*/ | |
function transformHtmlToken(token) { | |
let value = token.value.replace(/(\n|\r|\r\n)/g, ''); | |
if (value.match(/^\s*(<iframe\s+[^>]*>).*(<\/\s*iframe>)\s*$/i)) { | |
// <iframe></iframe> の中身を削除 | |
value = RegExp.$1 + RegExp.$2; | |
value = filterAttributes(value); | |
} else if (value.match(/^\s*(<iframe\s+[^>]*\/>)\s*$/i)) { | |
// <iframe/> は許す | |
value = RegExp.$1; | |
value = filterAttributes(value); | |
} else { | |
// iframe以外の場合は空文字列にする | |
value = ''; | |
} | |
return { type: 'html', value: value }; | |
} | |
/** | |
* 1つの要素を処理する | |
* @param {IToken} token | |
* @returns {IToken} | |
*/ | |
function processToken(token) { | |
const type = token && token.type; | |
let newToken = token; | |
if (type === 'html') { | |
newToken = transformHtmlToken(token); | |
} | |
if ('length' in token) { | |
newToken = processTokens(token); | |
} | |
if (token.children) { | |
newToken.children = processTokens(newToken.children); | |
} | |
return newToken; | |
} | |
/** | |
* 複数の要素を処理する。 | |
* HTML文字列を削除した時、前後の文字列や段落を結合するために必要。 | |
* @param {IToken[]} tokens | |
* @returns {IToken[]} | |
*/ | |
function processTokens(tokens) { | |
let result = []; | |
tokens.forEach(token => { | |
const newToken = processToken(token); | |
if (newToken && typeof newToken.length === 'number') { | |
result = result.concat(newToken.map(t => token)); | |
} else { | |
result.push(newToken); | |
} | |
}); | |
return clean(result); | |
} | |
/** | |
* 連続する要素の文字列を連結する | |
* @param {IToken[]} tokens | |
* @returns {IToken[]} | |
*/ | |
function clean(tokens) { | |
const result = []; | |
let prev = null; | |
tokens.forEach(token => { | |
if ( | |
prev && | |
typeof token.value !== 'undefined' && | |
token.type === prev.type | |
) { | |
prev.value += token.value; | |
} else { | |
result.push(token); | |
prev = token; | |
} | |
}); | |
return result; | |
} | |
module.exports = function() { | |
return processToken; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment