Skip to content

Instantly share code, notes, and snippets.

@fand
Created March 5, 2018 10:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fand/94ab6ec0b67bfb4de297c7492f352a95 to your computer and use it in GitHub Desktop.
Save fand/94ab6ec0b67bfb4de297c7492f352a95 to your computer and use it in GitHub Desktop.
remark-strip-html-but-iframe.js
// 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