const _s = (ss, ...pp)=> ss.map((s,i)=> s + (pp[i]||'')).join('') , _a = iterable=> iterable.length > 1 ? iterable : iterable[0] , Q = (ss, ...pp)=> [...document.querySelectorAll( _s(ss, ...pp))] , T = document.createElement('template') , DOM = (ss, ...pp)=> ( T.innerHTML = _s(ss, ...pp), document.adoptNode(T.content).childNodes ) , $ = (ss, ...pp)=> _a( ~_s(ss, ...pp).indexOf('<') ? DOM(ss, ...pp) : Q(ss, ...pp) ) , notify = (html,attrs)=> $`body`.add( $`${html}` ) , bubbleOn = el=> e=> el.dispatchEvent( new ErrorEvent('Error',{bubbles:true, error:e}) ) , pulse = color=> (document.body.style.background = color) && setTimeout(o=> document.body.style.background = 'lightgrey', 1000) Node.prototype.add = function( ...els ) { this.append( ...els ) return _a( els ) } Node.prototype.$ = function(ss, ...pp) { return _a( ~_s(ss, ...pp).indexOf('<') ? this.add(...DOM(ss, ...pp)) : [...this.querySelectorAll( _s(ss, ...pp))] ) } Node.prototype.on = function(ss, ...pp) { let types = _s( [].concat(ss), ...pp ).split(/[,\s]/) return (...args)=> ( types.map( type=> this.addEventListener(type,...args)), this ) } class UI extends HTMLElement { static get is(){ return 'ui' + this.name.replace(/[A-Z]/g, L=> '-'+L.toLocaleLowerCase()) } constructor() { super() let tpl = $`template[${this.constructor.is}]` tpl && this.attachShadow({mode: 'open'}) .appendChild( tpl.content.cloneNode(true) ) } } class Toast extends UI { static get duration(){ return this._duration || 10000 } static set duration( v ){ this._duration = v } connectedCallback() { setTimeout( toast=> toast.remove(), this.getAttribute('duration') || Toast.duration, this ) } } class Data extends UI { static get observedAttributes(){ return ['bin','uint','utf8','base64','frame'] } connectedCallback() { // this.observer = new MutationObserver( // changes=> console.log(changes.filter(m=>~['bin','255','utf8','base64','object'].indexOf(m.attributeName)) ) // ) // .observe( this, { attributes: true } ) this.shadowRoot.append( ...Data.observedAttributes .map( type=> $`` .on`click`( e=> this.type = e.target.innerText ) ) ) // this.shadowRoot.$`button`.map( button=> button.onclick = e=> this.type = e.target.innerText ) this.shadowRoot.$`textarea`.on`change`( e=> this._value = CC[this.type+'2buffer'](e.target.value) ) } get type(){ return this.attributes[0].name } set type( v ){ if( this.type == v ) return; [...this.attributes].map( o=> o.ownerElement.removeAttributeNode(o) ) this.setAttribute( v, '' ) } get value(){ return this._value } set value( v ){ this._value = v; this.shadowRoot.$`textarea`.value = CC['buffer2'+this.type](v) } attributeChangedCallback(name, oldValue, newValue) { if( newValue == null ) return console.log(arguments) if( newValue != null && this._value && CC['buffer2'+this.type] ) this.shadowRoot.$`textarea`.value = CC['buffer2'+this.type]( this._value ) else if( this.type == 'frame' ) this.shadowRoot.$`iframe`.src = URL.createObjectURL(new File([$text.value],'file.jpg',{type: 'image/jpeg'}) ) } } ;[ Toast, Data ] .map( klass=> customElements.define(klass.is, klass) ) // document.body.addEventListener('Error', e=> pulse('red') && (notify(e.error.message).style.color = 'red') ) $`body`.on`Error`( e=> pulse('red') && notify( `${e.error.code||''} ${e.error.name}: ${e.error.message}` , 'error') )