// similar to _.uniq const uniq = l => l.filter((x, i, a) => a.indexOf(x) === i) /** * SVG void elements that cannot be auto-closed and shouldn't contain child nodes. * @const {Array} */ export const VOID_SVG_TAGS_LIST = [ 'circle', 'ellipse', 'line', 'path', 'polygon', 'polyline', 'rect', 'stop', 'use' ] /** * List of html elements where the value attribute is allowed * @type {Array} */ export const HTML_ELEMENTS_HAVING_VALUE_ATTRIBUTE_LIST = [ 'button', 'data', 'input', 'select', 'li', 'meter', 'option', 'output', 'progress', 'textarea', 'param' ] /** * List of all the available svg tags * @const {Array} * @see {@link https://github.com/wooorm/svg-tag-names} */ export const SVG_TAGS_LIST = uniq([ 'a', 'altGlyph', 'altGlyphDef', 'altGlyphItem', 'animate', 'animateColor', 'animateMotion', 'animateTransform', 'animation', 'audio', 'canvas', 'clipPath', 'color-profile', 'cursor', 'defs', 'desc', 'discard', 'feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feDropShadow', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feImage', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence', 'filter', 'font', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignObject', 'g', 'glyph', 'glyphRef', 'handler', 'hatch', 'hatchpath', 'hkern', 'iframe', 'image', 'linearGradient', 'listener', 'marker', 'mask', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'metadata', 'missing-glyph', 'mpath', 'pattern', 'prefetch', 'radialGradient', 'script', 'set', 'solidColor', 'solidcolor', 'style', 'svg', 'switch', 'symbol', 'tbreak', 'text', 'textArea', 'textPath', 'title', 'tref', 'tspan', 'unknown', 'video', 'view', 'vkern' ].concat(VOID_SVG_TAGS_LIST)).sort() /** * HTML void elements that cannot be auto-closed and shouldn't contain child nodes. * @type {Array} * @see {@link http://www.w3.org/TR/html-markup/syntax.html#syntax-elements} * @see {@link http://www.w3.org/TR/html5/syntax.html#void-elements} */ export const VOID_HTML_TAGS_LIST = [ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr' ] /** * List of all the html tags * @const {Array} * @see {@link https://github.com/sindresorhus/html-tags} */ export const HTML_TAGS_LIST = uniq([ 'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'blockquote', 'body', 'canvas', 'caption', 'cite', 'code', 'colgroup', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'html', 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'main', 'map', 'mark', 'math', 'menu', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'p', 'picture', 'pre', 'q', 'rb', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'script', 'section', 'select', 'slot', 'small', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'svg', 'table', 'tbody', 'td', 'template', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'u', 'ul', 'var', 'video' ] .concat(VOID_HTML_TAGS_LIST) .concat(HTML_ELEMENTS_HAVING_VALUE_ATTRIBUTE_LIST) ).sort() /** * List of all boolean HTML attributes * @const {RegExp} * @see {@link https://www.w3.org/TR/html5/infrastructure.html#sec-boolean-attributes} */ export const BOOLEAN_ATTRIBUTES_LIST = [ 'disabled', 'visible', 'checked', 'readonly', 'required', 'allowfullscreen', 'autofocus', 'autoplay', 'compact', 'controls', 'default', 'formnovalidate', 'hidden', 'ismap', 'itemscope', 'loop', 'multiple', 'muted', 'noresize', 'noshade', 'novalidate', 'nowrap', 'open', 'reversed', 'seamless', 'selected', 'sortable', 'truespeed', 'typemustmatch' ] /** * Join a list of items with the pipe symbol (usefull for regex list concatenation) * @private * @param {Array} list - list of strings * @returns {string} the list received joined with pipes */ function joinWithPipe(list) { return list.join('|') } /** * Convert list of strings to regex in order to test against it ignoring the cases * @private * @param {...Array} lists - array of strings * @returns {RegExp} regex that will match all the strings in the array received ignoring the cases */ function listsToRegex(...lists) { return new RegExp(`^/?(?:${joinWithPipe(lists.map(joinWithPipe))})$`, 'i') } /** * Regex matching all the html tags ignoring the cases * @const {RegExp} */ export const HTML_TAGS_RE = listsToRegex(HTML_TAGS_LIST) /** * Regex matching all the svg tags ignoring the cases * @const {RegExp} */ export const SVG_TAGS_RE = listsToRegex(SVG_TAGS_LIST) /** * Regex matching all the void html tags ignoring the cases * @const {RegExp} */ export const VOID_HTML_TAGS_RE = listsToRegex(VOID_HTML_TAGS_LIST) /** * Regex matching all the void svg tags ignoring the cases * @const {RegExp} */ export const VOID_SVG_TAGS_RE = listsToRegex(VOID_SVG_TAGS_LIST) /** * Regex matching all the html tags where the value tag is allowed * @const {RegExp} */ export const HTML_ELEMENTS_HAVING_VALUE_ATTRIBUTE_RE = listsToRegex(HTML_ELEMENTS_HAVING_VALUE_ATTRIBUTE_LIST) /** * Regex matching all the boolean attributes * @const {RegExp} */ export const BOOLEAN_ATTRIBUTES_RE = listsToRegex(BOOLEAN_ATTRIBUTES_LIST) /** * True if it's a self closing tag * @param {string} tag - test tag * @returns {boolean} true if void * @example * isVoid('meta') // true * isVoid('circle') // true * isVoid('IMG') // true * isVoid('div') // false * isVoid('mask') // false */ export function isVoid(tag) { return [ VOID_HTML_TAGS_RE, VOID_SVG_TAGS_RE ].some(r => r.test(tag)) } /** * True if it's a known HTML tag * @param {string} tag - test tag * @returns {boolean} true if html tag * @example * isHtml('img') // true * isHtml('IMG') // true * isHtml('Img') // true * isHtml('path') // false */ export function isHtml(tag) { return HTML_TAGS_RE.test(tag) } /** * True if it's a known SVG tag * @param {string} tag - test tag * @returns {boolean} true if svg tag * @example * isSvg('g') // true * isSvg('radialGradient') // true * isSvg('radialgradient') // true * isSvg('div') // false */ export function isSvg(tag) { return SVG_TAGS_RE.test(tag) } /** * True if it's not SVG nor a HTML known tag * @param {string} tag - test tag * @returns {boolean} true if custom element * @example * isCustom('my-component') // true * isCustom('div') // false */ export function isCustom(tag) { return [ HTML_TAGS_RE, SVG_TAGS_RE ].every(l => !l.test(tag)) } /** * True if the value attribute is allowed on this tag * @param {string} tag - test tag * @returns {boolean} true if the value attribute is allowed * @example * hasValueAttribute('input') // true * hasValueAttribute('div') // false */ export function hasValueAttribute(tag) { return HTML_ELEMENTS_HAVING_VALUE_ATTRIBUTE_RE.test(tag) } /** * True if it's a boolean attribute * @param {string} attribute - test attribute * @returns {boolean} true if the attribute is a boolean type * @example * isBoolAttribute('selected') // true * isBoolAttribute('class') // false */ export function isBoolAttribute(attribute) { return BOOLEAN_ATTRIBUTES_RE.test(attribute) }