All files / src/custom-element http-request.js

100% Statements 60/60
94.44% Branches 17/18
100% Functions 20/20
100% Lines 47/47

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 89 9012x         3x               14x 62x 14x     14x 62x 60x 14x     4x       5x   34x 34x       38x 26x 12x 12x 1x 1x     11x 4x   7x 7x 7x   7x 7x 20x 7x   7x 7x 7x 15x 42x   7x 7x 7x 6x 6x 6x         75x 41x   41x 41x 33x   8x 8x               3x    
const     attr = (el, attr)=> el.getAttribute(attr);
 
export class HttpRequestElement extends HTMLElement
{
    static observedAttributes =
    [   'value' // populated from localStorage, if defined initially, sets the value in storage
    ,   'slice'
    ,   'url'
    ,   'method'
    ,   'header-accept'
    ];
 
    get requestHeaders()
    {   const ret = {};
        [...this.attributes].filter(a=>a.name.startsWith('header-')).map( a => ret[a.name.substring(7)] = a.value );
        return ret
    }
    get requestProps()
    {   const ret = {};
        [...this.attributes].filter(a=>!a.name.startsWith('header-'))
                            .filter(a=>!a.name.startsWith('slice')).map( a => ret[a.name] = a.value );
        return ret
    }
 
    disconnectedCallback(){ this.#destroy?.(); }
 
    connectedCallback()
    {
        setTimeout(()=>this.fetch(),0)
    }
    #inProgressUrl = ''
    #destroy = ()=>{}
 
    async fetch()
    {
        if( !this.closest('body') )
            return;
        const url = attr(this, 'url') || '';
        if( !url )
        {   this.#destroy?.();
            return this.value = {};
        }
 
        if( this.#inProgressUrl === url )
            return ;
 
        this.#inProgressUrl = url;
        const controller = new AbortController();
        this.#destroy = ()=> { controller.abort(this.localName+' disconnected'); this.#inProgressUrl = ''; }
 
        const request = { ...this.requestProps, headers: this.requestHeaders }
        ,       slice = { request }
        ,      update = () => this.dispatchEvent( new Event('change') );
        this.value = slice;
 
        update();
        const response = await fetch(url,{ ...this.requestProps, signal: controller.signal, headers: this.requestHeaders })
        ,      r = {headers: {}};
        [...response.headers].map( ([k,v]) => r.headers[k] = v );
        'ok,status,statusText,type,url,redirected'.split(',').map( k=> r[k] = response[k] )
 
        slice.response = r;
        update();
        if( r.headers['content-type']?.includes('json'))
            try
            {   slice.data = await response.json();
                update();
            }catch(_e){}
    }
 
    attributeChangedCallback(name, oldValue, newValue)
    {   if( name === 'url' )
        {   Eif( oldValue !== newValue)
            {
                oldValue && this.#destroy?.();
                if( newValue )
                    setTimeout(()=>this.fetch(),10)
                else
                {   this.value = {}
                    setTimeout(()=>this.dispatchEvent( new Event('change') ),10)
                }
            }
        }
    }
 
}
 
window.customElements.define( 'http-request', HttpRequestElement );
export default HttpRequestElement;