From 128168be58bec5a040eecd8c005109dfde323821 Mon Sep 17 00:00:00 2001
From: Sam Thorogood
Date: Thu, 16 Apr 2020 14:42:27 +1000
Subject: [PATCH] update readme, use font var
---
.npmignore | 4 +++-
README.md | 36 +++++++++++++++++++++++-------------
pwacompat.min.js | 26 +++++++++++++-------------
src/pwacompat.js | 35 +++++++++++++++++++++++++----------
test/index.html | 8 +++++++-
5 files changed, 71 insertions(+), 38 deletions(-)
diff --git a/.npmignore b/.npmignore
index 712bfa2..e2c638f 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,4 +1,6 @@
+.travis.yml
suite.*
testrunner.*
yarn.lock
-test/
\ No newline at end of file
+test/
+src/suite.*
diff --git a/README.md b/README.md
index b945a2b..5463567 100644
--- a/README.md
+++ b/README.md
@@ -1,19 +1,17 @@
[](https://travis-ci.org/GoogleChromeLabs/pwacompat)
PWACompat is a library that brings the [Web App Manifest](https://developers.google.com/web/fundamentals/web-app-manifest/) to non-compliant browsers for better [Progressive Web Apps](https://en.wikipedia.org/wiki/Progressive_Web_Apps).
-This includes creating splash screens for Mobile Safari, and supporting IE/Edge's Pinned Sites feature.
+This mostly means creating splash screens and icons for Mobile Safari, as well as supporting IE/Edge's Pinned Sites feature.
So, if you've created a `manifest.webmanifest` but want to have wide support everywhere else—through legacy HTML tags for icons and theming—look no further.
-Just include the minified script `pwacompat.min.js` (or [bundle/serve it yourself](https://npmjs.com/package/pwacompat)) in your page:
+We recommend including it from a CDN to get the latest version, or [bundling it yourself](https://npmjs.com/package/pwacompat):
```html
-
+
```
-And you're done! 🎉📄
+And you're done^! 🎉📄
For more on the Web App Manifest, read 📖 [how to add a Web App Manifest and mobile-proof your site](https://medium.com/dev-channel/how-to-add-a-web-app-manifest-and-mobile-proof-your-site-450e6e485638), watch 📹 [theming as part of The Standard](https://www.youtube.com/watch?v=5fEMTxpA6BA), or check out 📬 [the Web Fundamentals post on PWACompat](https://developers.google.com/web/updates/2018/07/pwacompat).
@@ -22,7 +20,7 @@ For more on the Web App Manifest, read 📖 [how to add a Web App Manifest and m
PWACompat takes your regular manifest and enhances other browsers
-# Best Practice & Caveats
+# ^Best Practice & Caveats
While PWACompat can generate most icons, meta tags etc that your PWA might need, it's best practice to include at least one ``.
This is standardized and older browsers, along with search engines, may use it from your page to display an icon.
@@ -35,23 +33,35 @@ For example:
```
-If you're looking for the best load performance, you can also defer loading PWACompat until after your site has loaded.
-This is the approach taken in [Emojityper](https://github.com/emojityper/emojityper/blob/master/src/loader.js#L8).
+You should also consider only loading PWACompat after your site is loaded, as adding your site to a homescreen is a pretty rare operation.
+This is the approach taken on [v8.dev](https://github.com/v8/v8.dev/pull/310/files) and [Emojityper](https://github.com/emojityper/emojityper/blob/master/src/loader.js#L8).
## iOS
PWACompat looks for a viewport tag which includes `viewport-fit=cover`, such as ``.
If this tag is detected, PWACompat will generate a meta tag that makes your PWA load in fullscreen mode—this is particularly useful for devices with a notch.
+You can customize the generated splash screen's font by using a CSS Variable.
+For example:
+
+```html
+
+```
+
+This is set directly as a [canvas font](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/font), so you must as a minimum include size _and_ family.
+The default value is "24px HelveticaNeue-CondensedBold".
+
+⚠️ PWACompat won't wait for your fonts to load, so if you're using custom fonts, be sure to only load the library after they're ready.
+
### Old Versions
Prior [to iOS 12.2](https://twitter.com/mhartington/status/1089293403089784832), Mobile Safari opens external sites in the regular browser, meaning that flows like Oauth won't complete correctly.
This [isn't a problem with PWACompat](https://github.com/GoogleChromeLabs/pwacompat/issues/15), but is an issue with PWAs on iOS generally.
-Prior to [iOS 11.3](https://medium.com/@firt/pwas-are-coming-to-ios-11-3-cupertino-we-have-a-problem-2ff49fd7d6ea), Mobile Safari would not respect the `start_url` paramater inside the manifest.
-If you want to emulate this behavior (and redirect the user to the start page), then you could detect `navigator.standalone` (indicating that your site is loaded in PWA mode) and set a flag in `window.sessionStorage`.
-If the flag is not yet set, then you should redirect to your site's start URL.
-
## Session Storage
PWACompat uses `window.sessionStorage` to cache your site's manifest (and on iOS, any updated icons and generated splash screens).
diff --git a/pwacompat.min.js b/pwacompat.min.js
index d73fe27..10dedc3 100644
--- a/pwacompat.min.js
+++ b/pwacompat.min.js
@@ -1,13 +1,13 @@
-function Q(n){var q=0;return function(){return q\'';var e=y([b,window.location]),g=n("manifest");if(g)try{var u=JSON.parse(g);H(u,e)}catch(v){console.warn("PWACompat error",v)}else{var t=new XMLHttpRequest;t.open("GET",b);t.withCredentials="use-credentials"===a.getAttribute("crossorigin");t.onload=function(){try{var v=JSON.parse(t.responseText);
-n("manifest",t.responseText);H(v,e)}catch(A){console.warn("PWACompat error",A)}};t.send(null)}}function y(a){for(var b={},e=0;e.6*f)m.fillText(c,I/-2,0),m.translate(0,24*1.2),h.splice(0,d),d=0}return function(){var J=m.canvas.toDataURL();g(l,J);return J}}function g(f,c){var l=document.createElement("link");l.setAttribute("rel","apple-touch-startup-image");l.setAttribute("media","(orientation: "+f+")");l.setAttribute("href",
-c);document.head.appendChild(l)}function u(f,c){var l=window.screen,h=e(l.availWidth,l.availHeight,"portrait",f),d=e(l.availHeight,l.availWidth,"landscape",f);window.setTimeout(function(){x.p=h();window.setTimeout(function(){x.l=d();c()},10)},10)}function t(f){function c(){--l||f()}var l=C.length+1;c();C.forEach(function(h){var d=new Image;d.crossOrigin="anonymous";d.onerror=c;d.onload=function(){d.onload=null;h.href=K(d,B,!0);x.i[d.src]=h.href;c()};d.src=h.href})}function v(){n("iOS",JSON.stringify(x))}
-function A(){var f=C.shift();if(f){var c=new Image;c.crossOrigin="anonymous";c.onerror=function(){return void A()};c.onload=function(){c.onload=null;u(c,function(){var l=a.background_color&&K(c,B);l?(f.href=l,x.i[c.src]=l,t(v)):v()})};c.src=f.href}else u(null,v)}var r=a.icons||[],p=r.filter(function(f){return(f.f||"").includes("maskable")});r.sort(function(f,c){return w(c)-w(f)});p.sort(function(f,c){return w(c)-w(f)});var C=(0w(f)))return c.rel="apple-touch-icon",E("link",c)}).filter(Boolean);p=document.head.querySelector('meta[name="viewport"]');var U=!!(p&&p.content||"").match(/\bviewport-fit\s*=\s*cover\b/),L=a.display;p=-1!==V.indexOf(L);k("mobile-web-app-capable",p);W(a.theme_color||"black",U);X&&(k("application-name",a.short_name),k("msapplication-tooltip",a.description),k("msapplication-starturl",b(a.start_url||".")),k("msapplication-navbutton-color",a.theme_color),(r=r[0])&&k("msapplication-TileImage",
-b(r.src)),k("msapplication-TileColor",a.background_color));document.head.querySelector('[name="theme-color"]')||k("theme-color",a.theme_color);if(D){var B=a.background_color||"#f8f9fa",T=M(B);(r=Y(a.related_applications))&&k("apple-itunes-app","app-id="+r);k("apple-mobile-web-app-capable",p);k("apple-mobile-web-app-title",a.short_name||a.name);if(p=n("iOS"))try{var G=JSON.parse(p);g("portrait",G.p);g("landscape",G.l);C.forEach(function(f){var c=G.i[f.href];c&&(f.href=c)});return}catch(f){}var x={i:{}};
-A()}else r={por:"portrait",lan:"landscape"}[String(a.orientation||"").substr(0,3)]||"",k("x5-orientation",r),k("screen-orientation",r),"fullscreen"===L?(k("x5-fullscreen","true"),k("full-screen","yes")):p&&(k("x5-page-mode","app"),k("browsermode","application"))}function Y(a){var b;(a||[]).filter(function(e){return"itunes"===e.platform}).forEach(function(e){e.id?b=e.id:(e=e.url.match(/id(\d+)/))&&(b=e[1])});return b}function W(a,b){if(D||Z){var e=M(a);if(D)k("apple-mobile-web-app-status-bar-style",
-b?"black-translucent":e?"black":"default");else{a:{try{var g=Windows.UI.ViewManagement.ApplicationView.getForCurrentView().titleBar;break a}catch(u){}g=void 0}if(b=g)b.foregroundColor=N(e?"black":"white"),b.backgroundColor=N(a)}}}function N(a){a=O(a);return{r:a[0],g:a[1],b:a[2],a:a[3]}}function O(a){var b=F();b.fillStyle=a;b.fillRect(0,0,1,1);return b.getImageData(0,0,1,1).data||[]}function M(a){a=O(a).map(function(b){b/=255;return.03928>b?b/12.92:Math.pow((b+.055)/1.055,2.4)});return 3\'';var b=y([a,window.location]),e=n("manifest");if(e)try{var g=JSON.parse(e);H(g,b)}catch(u){console.warn("PWACompat error",u)}else{var p=new XMLHttpRequest;p.open("GET",a);p.withCredentials="use-credentials"===A.getAttribute("crossorigin");p.onload=function(){try{var u=JSON.parse(p.responseText);
+n("manifest",p.responseText);H(u,b)}catch(v){console.warn("PWACompat error",v)}};p.send(null)}}function y(a){for(var b={},e=0;e.6*f)l.fillText(I,J/-2,0),l.translate(0,1.2*m),d.splice(0,c),c=0}return function(){var K=l.canvas.toDataURL();g(k,K);return K}}function g(f,c){var k=document.createElement("link");
+k.setAttribute("rel","apple-touch-startup-image");k.setAttribute("media","(orientation: "+f+")");k.setAttribute("href",c);document.head.appendChild(k)}function p(f,c){var k=window.screen,m=e(k.availWidth,k.availHeight,"portrait",f),d=e(k.availHeight,k.availWidth,"landscape",f);window.setTimeout(function(){x.p=m();window.setTimeout(function(){x.l=d();c()},10)},10)}function u(f){function c(){--k||f()}var k=C.length+1;c();C.forEach(function(m){var d=new Image;d.crossOrigin="anonymous";d.onerror=c;d.onload=
+function(){d.onload=null;m.href=L(d,B,!0);x.i[d.src]=m.href;c()};d.src=m.href})}function v(){n("iOS",JSON.stringify(x))}function M(){var f=C.shift();if(f){var c=new Image;c.crossOrigin="anonymous";c.onerror=function(){return void M()};c.onload=function(){c.onload=null;p(c,function(){var k=a.background_color&&L(c,B);k?(f.href=k,x.i[c.src]=k,u(v)):v()})};c.src=f.href}else p(null,v)}var t=a.icons||[],q=t.filter(function(f){return(f.h||"").includes("maskable")});t.sort(function(f,c){return w(c)-w(f)});
+q.sort(function(f,c){return w(c)-w(f)});var C=(0w(f)))return c.rel="apple-touch-icon",E("link",c)}).filter(Boolean);q=document.head.querySelector('meta[name="viewport"]');var W=!!(q&&q.content||"").match(/\bviewport-fit\s*=\s*cover\b/),N=a.display;q=-1!==X.indexOf(N);h("mobile-web-app-capable",q);Y(a.theme_color||"black",W);Z&&(h("application-name",a.short_name),h("msapplication-tooltip",a.description),
+h("msapplication-starturl",b(a.start_url||".")),h("msapplication-navbutton-color",a.theme_color),(t=t[0])&&h("msapplication-TileImage",b(t.src)),h("msapplication-TileColor",a.background_color));document.head.querySelector('[name="theme-color"]')||h("theme-color",a.theme_color);if(D){var B=a.background_color||"#f8f9fa",V=O(B);(t=aa(a.related_applications))&&h("apple-itunes-app","app-id="+t);h("apple-mobile-web-app-capable",q);h("apple-mobile-web-app-title",a.short_name||a.name);if(q=n("iOS"))try{var G=
+JSON.parse(q);g("portrait",G.p);g("landscape",G.l);C.forEach(function(f){var c=G.i[f.href];c&&(f.href=c)});return}catch(f){}var x={i:{}};M()}else t={por:"portrait",lan:"landscape"}[String(a.orientation||"").substr(0,3)]||"",h("x5-orientation",t),h("screen-orientation",t),"fullscreen"===N?(h("x5-fullscreen","true"),h("full-screen","yes")):q&&(h("x5-page-mode","app"),h("browsermode","application"))}function aa(a){var b;(a||[]).filter(function(e){return"itunes"===e.platform}).forEach(function(e){e.id?
+b=e.id:(e=e.url.match(/id(\d+)/))&&(b=e[1])});return b}function Y(a,b){if(D||ba){var e=O(a);if(D)h("apple-mobile-web-app-status-bar-style",b?"black-translucent":e?"black":"default");else{a:{try{var g=Windows.UI.ViewManagement.ApplicationView.getForCurrentView().titleBar;break a}catch(p){}g=void 0}if(b=g)b.foregroundColor=P(e?"black":"white"),b.backgroundColor=P(a)}}}function P(a){a=Q(a);return{r:a[0],g:a[1],b:a[2],a:a[3]}}function Q(a){var b=F();b.fillStyle=a;b.fillRect(0,0,1,1);return b.getImageData(0,
+0,1,1).data||[]}function O(a){a=Q(a).map(function(b){b/=255;return.03928>b?b/12.92:Math.pow((b+.055)/1.055,2.4)});return 3'`;
@@ -82,7 +85,7 @@ function unused() {
const data = /** @type {!Object} */ (JSON.parse(storedResponse));
process(data, hrefFactory);
} catch (err) {
- console.warn('PWACompat error', err)
+ console.warn('PWACompat error', err);
}
return;
}
@@ -100,7 +103,7 @@ function unused() {
store('manifest', xhr.responseText);
process(data, hrefFactory);
} catch (err) {
- console.warn('PWACompat error', err)
+ console.warn('PWACompat error', err);
}
};
xhr.send(null);
@@ -272,11 +275,18 @@ function unused() {
ctx.fillStyle = backgroundIsLight ? 'white' : 'black';
ctx.font = `${defaultSplashTextSize}px ${defaultFontName}`;
+ // Set the user-requested font; if it's invalid, the set will fail.
+ const s = window.getComputedStyle(manifestEl);
+ ctx.font = s.getPropertyValue('--pwacompat-splash-font'); // blank for old browsers
+
const title = manifest['name'] || manifest['short_name'] || document.title;
- const textWidth = ctx.measureText(title).width;
- if (textWidth < width * 0.8) {
+ const measure = ctx.measureText(title);
+ const textHeight = (measure.actualBoundingBoxAscent || defaultSplashTextSize);
+ ctx.translate(0, textHeight);
+
+ if (measure.width < width * 0.8) {
// short-circuit, just draw entire string
- ctx.fillText(title, textWidth / -2, 0);
+ ctx.fillText(title, measure.width / -2, 0);
} else {
// longer wrap case, draw once we have >0.7 width accumulated
const words = title.split(/\s+/g);
@@ -286,7 +296,7 @@ function unused() {
if (i === words.length || measureWidth > width * 0.6) {
// render accumulated words
ctx.fillText(cand, measureWidth / -2, 0);
- ctx.translate(0, (defaultSplashTextSize * 1.2));
+ ctx.translate(0, textHeight * 1.2);
words.splice(0, i);
i = 0;
}
@@ -295,6 +305,11 @@ function unused() {
return () => {
const data = ctx.canvas.toDataURL();
+ if (debug) {
+ const img = document.createElement('img');
+ img.src = data;
+ document.body.append(img);
+ }
appendSplash(orientation, data);
return data;
};
@@ -314,7 +329,7 @@ function unused() {
// fetch previous (session) iOS image updates
const rendered = store('iOS');
- if (rendered) {
+ if (!debug && rendered) {
try {
const prev = /** @type {!Object} */ (JSON.parse(rendered));
appendSplash('portrait', prev['p']);
diff --git a/test/index.html b/test/index.html
index 0660e33..d5f87fd 100644
--- a/test/index.html
+++ b/test/index.html
@@ -16,18 +16,24 @@
+
PWA Compat
-
+
This is a test of PWA Compat.
Try adding this site to your home screen on an iOS device to see correctly rendered icons and dynamic splash screens!
+ It uses the uncompiled file, for testing.