UNPKG

ava-pwar

Version:

Web component that generates markup from manifest

318 lines (316 loc) 9.69 kB
//@ts-check (function () { function define(custEl) { let tagName = custEl.is; if (customElements.get(tagName)) { console.warn('Already registered ' + tagName); return; } customElements.define(tagName, custEl); } const disabled = 'disabled'; function XtallatX(superClass) { return class extends superClass { constructor() { super(...arguments); this._evCount = {}; } static get observedAttributes() { return [disabled]; } get disabled() { return this._disabled; } set disabled(val) { this.attr(disabled, val, ''); } attr(name, val, trueVal) { const v = val ? 'set' : 'remove'; //verb this[v + 'Attribute'](name, trueVal || val); } to$(n) { const mod = n % 2; return (n - mod) / 2 + '-' + mod; } incAttr(name) { const ec = this._evCount; if (name in ec) { ec[name]++; } else { ec[name] = 0; } this.attr('data-' + name, this.to$(ec[name])); } attributeChangedCallback(name, oldVal, newVal) { switch (name) { case disabled: this._disabled = newVal !== null; break; } } de(name, detail) { const eventName = name + '-changed'; const newEvent = new CustomEvent(eventName, { detail: detail, bubbles: true, composed: false, }); this.dispatchEvent(newEvent); this.incAttr(eventName); return newEvent; } _upgradeProperties(props) { props.forEach(prop => { if (this.hasOwnProperty(prop)) { let value = this[prop]; delete this[prop]; this[prop] = value; } }); } }; } const href = 'href'; const service_url = 'service-url'; const fetch_in_progress = 'fetch-in-progress'; const fetch_complete = 'fetch-complete'; const title = 'title'; class CorsAnywhere extends XtallatX(HTMLElement) { constructor() { super(...arguments); this._serviceUrl = 'https://cors-anywhere.herokuapp.com/'; this._connected = false; } // _serviceUrl: string = 'https://crossorigin.me/'; /** @type {string} Url of service that gets preview. * */ get serviceUrl() { return this._serviceUrl; } set serviceUrl(val) { this.attr('service-url', val); } /** @type {string} Url to preview * */ get href() { return this._href; } set href(val) { this.attr('href', val); } get fetchInProgress() { return this._fetchInProgress; } set fetchInProgress(val) { this._fetchInProgress = val; this.attr(fetch_in_progress, val, ''); this.de(fetch_in_progress, { value: val }); } get fetchComplete() { return this._fetchComplete; } set fetchComplete(val) { this._fetchComplete = val; this.attr(fetch_complete, val, ''); this.de(fetch_complete, { value: val }); } get title() { return this._title; } set title(val) { this._title = val; this.attr(title, val); // this.de(title,{ // value: val // }) } static get observedAttributes() { return super.observedAttributes.concat([href, service_url,]); } attributeChangedCallback(name, oldValue, newValue) { super.attributeChangedCallback(name, oldValue, newValue); switch (name) { case 'href': this._href = newValue; // if(this._once) this.loadHref(); break; case 'service-url': this._serviceUrl = newValue; break; } this.onPropsChange(); } connectedCallback() { this._upgradeProperties(['disabled', href, 'serviceUrl']); this._connected = true; this.de('connected', { value: this.href }); this.onPropsChange(); } set abort(val) { if (this._controller) this._controller.abort(); } doFetch() { const url = this.calculateURL(); if (this._previousURL === url) { this.fetchComplete = false; this.fetchComplete = true; return; } this._previousURL = url; this.title = "Loading..."; this.fetchInProgress = true; this.fetchComplete = false; let init = null; if (AbortController) { this._controller = new AbortController(); init = this._controller.signal; } fetch(url, { signal: init, }).then(response => { this.fetchInProgress = false; this.processResponse(response); this.fetchComplete = true; }).catch(err => { if (err.name === 'AbortError') { console.log('Fetch aborted'); delete this._previousURL; } }); } calculateURL(upLevels = 0) { let href = this._href; if (upLevels) { const split = href.split('/'); if (upLevels === -1) { href = [split[0], split[1], split[2]].join('/'); } } return this._serviceUrl + href; } } /** * `ava-pwar` * Find and Process PWA Manifest url. * * @customElement * @polymer * @demo demo/index.html */ class AvaPwar extends CorsAnywhere { static get is() { return 'ava-pwar'; } onPropsChange() { if (!this._connected || !this._href || this.disabled || !this._serviceUrl) return; this.doFetch(); } processResponse(resp) { const debug = this.hasAttribute('debug'); resp.text().then(content => { if (debug) debugger; const parser = new DOMParser(); const htmlDoc = parser.parseFromString(content, "text/html"); const manifestLink = htmlDoc.querySelector('link[rel="manifest"]'); if (!manifestLink || !manifestLink.href) return; const path = location.href.split('/').slice(0, -1).join('/'); //const lastPath = manifestLink.href.split('/').pop(); let manifestURL = manifestLink.getAttribute('href'); const upLevels = manifestURL.startsWith('/') ? -1 : 0; manifestURL = this.calculateURL(upLevels) + manifestURL; fetch(manifestURL).then(resp => { resp.json().then(json => { json.url = this._href; this.manifest = json; }); }); }); } get manifest() { return this._manifest; } set manifest(val) { this._manifest = val; this.de('manifest', { value: val, }); } } define(AvaPwar); /** * `ava-pwar-simple` * Simple view of PWA Manifest * * @customElement * @polymer * @demo demo/index.html */ class AvaPwarSimple extends AvaPwar { static get is() { return 'ava-pwar-simple'; } set manifest(val) { super.manifest = val; this.render(); } render() { const input = this._manifest; const $ = this.$; if (!input.icons) return; let oneNineTwoIcon = input.icons.find(icon => (icon.sizes.indexOf('192') > -1)); if (!oneNineTwoIcon) oneNineTwoIcon = input.icons[input.icons.length - 1]; let imagePath = oneNineTwoIcon.src; if (imagePath.startsWith('/')) imagePath = imagePath.substring(1); const imgURL = imagePath.startsWith('http') ? imagePath : input.url + imagePath; const inverseColor = this.invertColor(input.background_color); this.innerHTML = /* html */ ` <div class="simple" style="background-color:${input.background_color};color:${inverseColor}"> <div class="iconLabel">Icon:</div> <div class="icon"><img height="192" width="192" src="${imgURL}"/></div> <div class="nameLabel">Name:</div> <div class="name">${$(input.name)}</div> <div class="shortNameLabel">Short Name:</div> <div class="shortName">${$(input.short_name)}</div> <a class="url" target="_blank" href="${input.url}">${$(input.url)}</a> </div> `; } $(str) { return str.replace(/(<([^>]+)>)/ig, ''); } invertColor(hex) { if (hex.indexOf('#') === 0) { hex = hex.slice(1); } // convert 3-digit hex to 6-digits. if (hex.length === 3) { hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } if (hex.length !== 6) { throw new Error('Invalid HEX color.'); } // invert color components var r = (255 - parseInt(hex.slice(0, 2), 16)).toString(16), g = (255 - parseInt(hex.slice(2, 4), 16)).toString(16), b = (255 - parseInt(hex.slice(4, 6), 16)).toString(16); // pad each with zeros and return return '#' + this.padZero(r) + this.padZero(g) + this.padZero(b); } padZero(str, len) { len = len || 2; var zeros = new Array(len).join('0'); return (zeros + str).slice(-len); } } define(AvaPwarSimple); })();