All files shadow-dom-element.js

100% Statements 88/88
100% Branches 26/26
100% Functions 11/11
100% Lines 88/88

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 894x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 4x 4x 4x 4x 4x 4x 4x 4x 3x 3x 3x 3x 3x 3x 4x 4x 4x 2x 2x 2x 2x 2x 2x 2x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x  
const attr = (el, name)=>el.getAttribute(name)|| el.sde?.getAttribute(name);
const TAG = 'shadow-dom-element';
export default class ShadowDomElement extends HTMLElement
{
    promise;
 
    constructor()
    {
        super();
        const tag = attr(this,'tag')
            , tagName = this.tagName.toLowerCase();
        if( tag && tagName !== tag  )
        {   const sde = this;
            this.templates = [...this.getElementsByTagName( 'template' )];
            this.templates.map( t=>t.remove() );
            customElements.define( tag, class ShadowDomElementDCE extends ShadowDomElement{
                async slotsInit()
                {   this.sde = sde;
                    await sde.promise;
                    return super.slotsInit();
                }
            } );
            if( this.childElementCount )
            {   const html = this.outerHTML.replace('<'+tagName, '<'+tag);
                this.innerHTML='';
                this.insertAdjacentHTML('afterend',html);
            }
        }else
        {   this.templates = [];
            this.promise = this.slotsInit();
        }
    }
    async fetch( url ){ return ( await fetch( url ) ).text() }
 
    applyTemplate( t )
    {
        const s = this.shadowRoot;
        s.appendChild( t.content.cloneNode( true ) );
        this.postTemplateCallback( s );
        return this;
    }
    postTemplateCallback(s)
    {
        s.querySelectorAll('slot[attribute]').forEach( a =>
        {   let f = attr(a,'for')
            ,   s = f ? a.getRootNode().querySelector('#'+f) : a.parentElement;
 
            s.setAttribute( attr( a, 'attribute' )
                ,   a.assignedElements().map( l=>attr( l, 'href')
                                             ||  attr( l, 'src')
                                             ||  l.innerText).join(''))
        });
    }
    async slotsInit()
    {
        const getText = async url => this.fetch( url );
        const onAttr = async( att, cb ) =>
        {
            await ( async a => ( a ? cb( a ) : 0 ) )( attr(this, att ) );
        };
        const embeddedTemplates = [ ...this.getElementsByTagName( 'template' )
                                  , ...( this.sde ? this.sde.templates : [] ) ];
        await onAttr(
            'srcset',
            id => ( this.innerHTML = `${ document.getElementById( id )?.innerHTML }` )
        );
        await onAttr( 'src', async url => ( this.innerHTML = await getText( url ) ) );
 
        this.attachShadow( { mode: 'open' } );
        const applyTemplate = t => this.applyTemplate( t );
        // @ts-ignore
        embeddedTemplates.forEach( applyTemplate );
 
        await onAttr( 'for', id => applyTemplate( document.getElementById( id ) ) );
        await onAttr( 'code', async url =>
        {
            const d = document.createElement( 'div' );
            d.innerHTML = await getText( url );
            const t = document.createElement( 'template' );
            d.childNodes.forEach( n => t.content.append( n ) );
            this.templates.push(t);
            applyTemplate( t );
        } );
        return this;
    }
}
 
window.customElements.define(TAG, ShadowDomElement);