Ejemplo n.º 1
0
        public async Task <List <string> > GetNetworkOnlyUrls(PwaOptions options, HttpContext context)
        {
            var result = new List <string>()
            {
                "/blog/canedit",
                "/page/canedit",
                "/pwa/topnav"
            };

            if (!context.User.Identity.IsAuthenticated)
            {
                //admin pages won't be in the menu anyway so just return empty list
                return(result);
            }

            var rootNode = await _siteMapTreeBuilder.GetTree();

            var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccesor.ActionContext);

            foreach (var navNode in rootNode.Flatten())
            {
                if (await WouldRenderNode(navNode))
                {
                    if (ShouldBeNetworkOnly(navNode))
                    {
                        var url = ResolveUrl(navNode, urlHelper);
                        result.Add(url);
                    }
                }
            }

            return(result);
        }
Ejemplo n.º 2
0
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            var option = new PwaOptions();

            Configuration.GetSection("pwa").Bind(option);

            services.AddMvc();
            services.AddProgressiveWebApp(option);
        }
Ejemplo n.º 3
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton(typeof(IHttpContextAccessor), typeof(HttpContextAccessor));
            services.AddMvc();
            PwaOptions options = new PwaOptions();

            options.OfflineRoute = "MyOfflinePage.html";

            services.AddProgressiveWebApp(options);
        }
        private async Task <List <string> > GetNetworkOnlyUrls(PwaOptions options, HttpContext context)
        {
            var result = new List <string>();

            foreach (var provider in _networkOnlyUrlProviders)
            {
                var list = await provider.GetNetworkOnlyUrls(options, context);

                result.AddRange(list);
            }


            return(result);
        }
Ejemplo n.º 5
0
        //private readonly IWorkboxCacheSuffixProvider _workboxCacheSuffixProvider;

        public Task AppendToServiceWorkerScript(StringBuilder sw, PwaOptions options, HttpContext context)
        {
            //no reason to clear the images cache if the service worker was updated
            //that would be extra bandwidth wasted
            //var cacheSuffix = await _workboxCacheSuffixProvider.GetWorkboxCacheSuffix();

            sw.Append("const cacheFirstMatcher = ({url, event}) => {");
            sw.Append("var re = new RegExp(/(.*)\\.(?:png|gif|jpg|jpeg)/);");
            sw.Append("var match = re.test(url.href);");

            //sw.Append("console.log(event);");
            //sw.Append("console.log(url);");
            sw.Append("if(match) {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('cacheFirstMatcher returning true for ' + url.href);");
            }

            sw.Append("return true; }");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('cacheFirstMatcher returning false for url ' + url.href);");
            }


            sw.Append("return false;");


            sw.Append("};");

            sw.Append("workbox.routing.registerRoute(");
            //sw.Append("/(.*)\\.(?:png|gif|jpg)/,");
            sw.Append("cacheFirstMatcher,");
            sw.Append("new workbox.strategies.CacheFirst({");
            //sw.Append("cacheName: 'images-cache-" + _options.CacheIdSuffix + "',");
            //sw.Append("plugins: [");
            //sw.Append("new workbox.expiration.Plugin({");
            //sw.Append("maxEntries: 2000,");
            //sw.Append("maxAgeSeconds: 30 * 24 * 60 * 60,"); // 30 Days
            //sw.Append("})");
            //sw.Append("]");
            sw.Append("})");
            sw.Append(");");


            return(Task.CompletedTask);
        }
Ejemplo n.º 6
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton(typeof(IHttpContextAccessor), typeof(HttpContextAccessor));
            services.AddMvc();
            var pwaoptions = new PwaOptions()
            {
                RoutesToPreCache = @"/lib/bootstrap/dist/css/bootstrap.css,
                        /lib/jquery/dist/jquery.js,
                        /lib/bootstrap/dist/js/bootstrap.js,
                        /css/site.css,
                        /js/site.js,
                        /images/banner1.svg,
                        /images/banner2.svg,
                        /images/banner3.svg,
                        /images/banner4.svg"
            };

            services.AddProgressiveWebApp(pwaoptions);
        }
 /// <summary>
 /// Adds Web App Manifest and Service Worker to the specified <see cref="IServiceCollection"/>.
 /// </summary>
 /// <param name="services">The service collection.</param>
 /// <param name="manifestFileName">The path to the Web App Manifest file relative to the wwwroot rolder.</param>
 /// <param name="options">Options for the service worker and Web App Manifest</param>
 public static IServiceCollection AddProgressiveWebApp(this IServiceCollection services, PwaOptions options, string manifestFileName = Constants.WebManifestFileName)
 {
     return(services.AddWebManifest(manifestFileName)
            .AddServiceWorker(options));
 }
        /// <summary>
        /// Adds ServiceWorker services to the specified <see cref="IServiceCollection"/>.
        /// </summary>
        public static IServiceCollection AddServiceWorker(this IServiceCollection services, PwaOptions options)
        {
            services.TryAddSingleton <IHttpContextAccessor, HttpContextAccessor>();
            services.AddTransient <ITagHelperComponent, ServiceWorkerTagHelperComponent>();
            services.AddTransient(factory => options);

            return(services);
        }
        public async Task AppendToServiceWorkerScript(StringBuilder sw, PwaOptions options, HttpContext context)
        {
            var cacheSuffix = await _workboxCacheSuffixProvider.GetWorkboxCacheSuffix();

            //if (context.User.Identity.IsAuthenticated)
            //{
            //    sw.Append("workbox.core.setCacheNameDetails({");
            //    sw.Append("prefix: 'web-app-auth',");
            //    sw.Append("suffix: '" + cacheSuffix + "',");
            //    sw.Append("precache: 'auth-user-precache',");
            //    sw.Append("runtime: 'auth-user-runtime-cache'");
            //    sw.Append("});");


            //}
            //else
            //{
            //    sw.Append("workbox.core.setCacheNameDetails({");
            //    sw.Append("prefix: 'web-app-anon',");
            //    sw.Append("suffix: '" + cacheSuffix + "',");
            //    sw.Append("precache: 'unauth-user-precache',");
            //    sw.Append("runtime: 'unauth-user-runtime-cache'");
            //    sw.Append("});");
            //}

            //no longer need separate cahce for auth vs anonymous
            sw.Append("workbox.core.setCacheNameDetails({");
            sw.Append("prefix: 'web-app',");
            sw.Append("suffix: '" + cacheSuffix + "',");
            sw.Append("precache: 'precache',");
            sw.Append("runtime: 'runtime-cache'");
            sw.Append("});");


            //https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.core

            //Force a service worker to become active, instead of waiting. This is normally used in conjunction with clientsClaim()
            sw.Append("workbox.core.skipWaiting();");
            sw.Append("workbox.core.clientsClaim();");


            //https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.core

            //Force a service worker to become active, instead of waiting. This is normally used in conjunction with clientsClaim()
            //sw.Append("workbox.skipWaiting();");
            //sw.Append("workbox.clientsClaim();");

            //https://github.com/GoogleChrome/workbox/issues/1407
            //cleanup old caches
            sw.Append("let currentCacheNames = Object.assign(");
            sw.Append("{ precacheTemp: workbox.core.cacheNames.precache + \"-temp\" },");
            sw.Append("workbox.core.cacheNames");
            sw.Append(");");

            sw.Append("self.addEventListener(\"activate\", function(event) {");

            sw.Append("event.waitUntil(");
            sw.Append("caches.keys().then(function(cacheNames) {");
            sw.Append("let validCacheSet = new Set(Object.values(currentCacheNames));");
            sw.Append("return Promise.all(");
            sw.Append("cacheNames");
            sw.Append(".filter(function(cacheName) {");
            sw.Append("return !validCacheSet.has(cacheName);");
            sw.Append("})");
            sw.Append(".map(function(cacheName) {");
            sw.Append("return caches.delete(cacheName);");
            sw.Append("})");
            sw.Append(");");

            sw.Append("})");
            sw.Append(");");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('activate event fired');");
            }



            sw.Append("});");

            //new in 4.0.x
            sw.Append("workbox.precaching.cleanupOutdatedCaches();");
        }
        public Task AppendToServiceWorkerScript(StringBuilder sw, PwaOptions options, HttpContext context)
        {
            sw.Append("workbox.googleAnalytics.initialize();");

            return(Task.CompletedTask);
        }
        public async Task AppendToServiceWorkerScript(StringBuilder sw, PwaOptions options, HttpContext context)
        {
            sw.Append("const networkOnlyMatchFunction = ({url, event}) => {");

            sw.Append("if(event && event.request && event.request.method === 'POST') {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('networkOnlyMatchFunction returning true for url ' + url.href + ' for POST');");
            }

            sw.Append("return true;");
            sw.Append("} ");

            sw.Append("if(url.href.indexOf('/pwanav/topnav') > -1) {");
            sw.Append("return true;");
            sw.Append("} ");

            sw.Append("if(url.href.indexOf('/pwa/getpublickey') > -1) {");
            sw.Append("return true;");
            sw.Append("} ");

            var urls = await GetNetworkOnlyUrls(options, context);

            sw.Append("var networkOnlyUrls = [");

            var comma = "";

            foreach (var url in urls)
            {
                if (string.IsNullOrEmpty(url))
                {
                    continue;
                }

                sw.Append(comma);
                sw.Append("\"" + url + "\"");

                comma = ",";
            }

            sw.Append("];");

            sw.Append("if(networkOnlyUrls.indexOf(url.href) > -1) {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('networkOnlyMatchFunction returning true for url ' + url.href);");
            }

            sw.Append("return true; }");


            //if (options.EnableServiceWorkerConsoleLog)
            //{
            //    sw.Append("console.log('networkOnlyMatchFunction returning false for url ' + url.href);");
            //}

            sw.Append("return false;");
            sw.Append("};");

            sw.Append("workbox.routing.registerRoute(");
            sw.Append("networkOnlyMatchFunction,");
            sw.Append("new workbox.strategies.NetworkOnly()");
            sw.Append(");");
        }
Ejemplo n.º 12
0
        public async Task AppendToServiceWorkerScript(StringBuilder sw, PwaOptions options, HttpContext context)
        {
            var items = new List <ServiceWorkerCacheItem>();

            foreach (var provider in _preCacheProviders)
            {
                var i = await provider.GetItems();

                items.AddRange(i);
            }

            if (items.Count == 0)
            {
                return;
            }

            //sw.Append("const precacheController = new workbox.precaching.PrecacheController();");
            //sw.Append("precacheController.addToCacheList([");

            var comma = "";

            sw.Append("workbox.precaching.precacheAndRoute([");
            foreach (var item in items)
            {
                sw.Append(comma);
                if (!string.IsNullOrEmpty(item.Revision))
                {
                    sw.Append("{");
                    sw.Append("\"url\": \"" + item.Url + "\",");
                    sw.Append("\"revision\": \"" + item.Revision + "\"");

                    sw.Append("}");
                }
                else
                {
                    sw.Append("'" + item.Url + "'");
                }

                comma = ",";
            }

            sw.Append("]");

            sw.Append(",{"); //begin options

            sw.Append("plugins: [");
            sw.Append("new workbox.broadcastUpdate.Plugin({");
            sw.Append("channelName: 'app-channel'");
            sw.Append("}),");
            sw.Append("]");

            sw.Append("}"); //end options


            sw.Append(");");


            //sw.Append("self.addEventListener('install', (event) => {");
            //sw.Append("console.log('install event for pre-cache controller');");
            //sw.Append("event.waitUntil(precacheController.install());");
            //sw.Append("});");

            //sw.Append("self.addEventListener('activate', (event) => {");
            //sw.Append("console.log('activate event for pre-cache controller');");
            ////sw.Append("event.waitUntil(precacheController.cleanup());");
            //sw.Append("});");

            //sw.Append("self.addEventListener('fetch', (event) => {");
            //sw.Append("console.log('fetch event for pre-cache controller for url ' + event.request.url);");
            //sw.Append("const cacheKey = precacheController.getCacheKeyForURL(event.request.url);");

            //sw.Append("event.waitUntil(");
            //sw.Append("caches.match(cacheKey).then(function(response) {");

            //sw.Append("console.log('response was');");
            //sw.Append("console.log(response);");
            //sw.Append("event.respondWith(response);");

            //sw.Append("});");

            //sw.Append(");");//wiatuntil



            //sw.Append("});");//end fetch
        }
Ejemplo n.º 13
0
        public Task AppendToServiceWorkerScript(StringBuilder sw, PwaOptions options, HttpContext context)
        {
            var offlineUrl = _offlinePageUrlProvider.GetOfflineUrl();

            sw.Append("workbox.routing.setCatchHandler(({event}) => {");

            sw.Append("console.log('catch handler invoked');");

            sw.Append("console.log(event);");

            sw.Append("if(event.request.url.indexOf('pwanav/topnav') > -1) {");
            sw.Append("return Response.error();");

            sw.Append("}");

            //sw.Append("const precacheCacheName = workbox.core.cacheNames.precache;");
            //sw.Append("caches.open(precacheCacheName).then(function(cache) {");
            //sw.Append("var key = workbox.precaching.getCacheKeyForURL(event.request.url);");

            //sw.Append("console.log(key);");

            //sw.Append("cache.match(event.request.url).then(function(response) {");

            //sw.Append("if(!response) {");

            //sw.Append("console.log('no response from cache in catch handler');");

            //sw.Append("switch (event.request.destination) {");
            //sw.Append("case 'document':");
            //sw.Append("return caches.match('" + offlineUrl + "');");
            //sw.Append("break;");

            //sw.Append("case 'image':");
            ////sw.Append("return caches.match(FALLBACK_IMAGE_URL);");
            //sw.Append("return new Response('<svg role=\"img\" aria-labelledby=\"offline-title\" viewBox=\"0 0 400 300\" xmlns=\"http://www.w3.org/2000/svg\"><title id=\"offline-title\">Offline</title><g fill=\"none\" fill-rule=\"evenodd\"><path fill=\"#D8D8D8\" d=\"M0 0h400v300H0z\"/><text fill=\"#9B9B9B\" font-family=\"Helvetica Neue,Arial,Helvetica,sans-serif\" font-size=\"72\" font-weight=\"bold\"><tspan x=\"93\" y=\"172\">offline</tspan></text></g></svg>', { headers: { 'Content-Type': 'image/svg+xml' } });");
            //sw.Append("break;");

            ////sw.Append("case 'font':");
            ////sw.Append("return caches.match(FALLBACK_FONT_URL);");
            ////sw.Append("break;");

            //sw.Append("default:");
            ////sw.Append("return caches.match('" + offlineUrl + "');");
            //sw.Append("return Response.error();");

            //sw.Append("}"); //end switch

            //sw.Append("} ");//end if !response

            //sw.Append("console.log('returning response from cache in catch handler');");
            //sw.Append("console.log(response);");

            //sw.Append("return response.clone();");
            //sw.Append("}).catch(function(err) {");

            //sw.Append("console.log('caught error in catch handler');");
            //sw.Append("console.log(err);");
            //sw.Append("return caches.match('" + offlineUrl + "');");

            //sw.Append("}) ");


            //sw.Append("}) "); //end cache open

            //sw.Append("console.log('something went wrong catch handler nothing returned');");



            //sw.Append("caches.match(event.request).then(function(response) {");
            //sw.Append("return response;");
            //sw.Append("}).catch(function() {");

            //sw.Append("return caches.match('" + offlineUrl + "');");

            //sw.Append("}) ");

            // The FALLBACK_URL entries must be added to the cache ahead of time, either via runtime
            // or precaching.
            // If they are precached, then call workbox.precaching.getCacheKeyForURL(FALLBACK_URL)
            // to get the correct cache key to pass in to caches.match().
            //
            // Use event, request, and url to figure out how to respond.
            // One approach would be to use request.destination, see
            // https://medium.com/dev-channel/service-worker-caching-strategies-based-on-request-types-57411dd7652c


            sw.Append("switch (event.request.destination) {");
            sw.Append("case 'document':");
            sw.Append("return caches.match('" + offlineUrl + "');");
            sw.Append("break;");

            sw.Append("case 'image':");
            //sw.Append("return caches.match(FALLBACK_IMAGE_URL);");
            sw.Append("return new Response('<svg role=\"img\" aria-labelledby=\"offline-title\" viewBox=\"0 0 400 300\" xmlns=\"http://www.w3.org/2000/svg\"><title id=\"offline-title\">Offline</title><g fill=\"none\" fill-rule=\"evenodd\"><path fill=\"#D8D8D8\" d=\"M0 0h400v300H0z\"/><text fill=\"#9B9B9B\" font-family=\"Helvetica Neue,Arial,Helvetica,sans-serif\" font-size=\"72\" font-weight=\"bold\"><tspan x=\"93\" y=\"172\">offline</tspan></text></g></svg>', { headers: { 'Content-Type': 'image/svg+xml' } });");
            sw.Append("break;");

            //sw.Append("case 'font':");
            //sw.Append("return caches.match(FALLBACK_FONT_URL);");
            //sw.Append("break;");

            sw.Append("default:");
            //sw.Append("return caches.match('" + offlineUrl + "');");
            sw.Append("return Response.error();");

            sw.Append("}");   //end switch

            sw.Append("});"); //end event

            return(Task.CompletedTask);
        }
Ejemplo n.º 14
0
        public async Task AppendToServiceWorkerScript(StringBuilder sw, PwaOptions options, HttpContext context, IUrlHelper urlHelper)
        {
            #region ClientMessageBus

            sw.Append("var idb;");
            sw.Append("if(self.indexedDB) { ");
            sw.Append("idb = self.indexedDB; ");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('IndexedDB is supported');");
            }
            sw.Append("} ");



            sw.Append("function sendMessage(msg) {");

            sw.Append("if ('BroadcastChannel' in self) {");

            sw.Append("const channel = new BroadcastChannel('app-channel');");
            sw.Append("channel.postMessage(msg);");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('sent message to client');");
            }

            sw.Append("} else {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('message not sent to client because BroadcastChannel not supported by the browser');");
            }
            sw.Append("} "); //endif broadcast channel
            sw.Append("} "); //end send message


            sw.Append("var messageListBuilder = function() {");
            sw.Append("var priv;");

            sw.Append("function build() {");

            sw.Append("if(priv) {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('returning ready function');");
            }

            sw.Append("return;");

            sw.Append("} else {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('building messagelist function');");
            }

            sw.Append("priv = {");

            sw.Append("addMessage : function(msg) {");

            sw.Append("if(idb) {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('add message');");
            }

            sw.Append("var request = idb.open('sw_DB', 1);");

            sw.Append("request.onsuccess = function(event) {");

            sw.Append("var db = event.target.result;");
            sw.Append("var transaction = db.transaction('clientmessages', 'readwrite');");

            sw.Append("transaction.onsuccess = function(event) {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('IndexDB transaction success');");
            }

            sw.Append("}; ");//endtransaction.success

            sw.Append("var msgStore = transaction.objectStore('clientmessages');");
            sw.Append("var db_op_req = msgStore.add(msg);");

            sw.Append("db_op_req.onsuccess = function(event) {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log(event);");
            }

            sw.Append("}; "); //end db_op_req onsucess

            sw.Append("}; "); //end request onsuccess

            sw.Append("request.onupgradeneeded = function(event) {");
            sw.Append("var db = event.target.result;");
            sw.Append("var store = db.createObjectStore('clientmessages', {keyPath:'id'});");
            sw.Append("}; ");// end onupgraded

            sw.Append("} else {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('failed to add message because IndexedDB not suppported in this browser');");
            }
            sw.Append("} ");

            sw.Append("},"); //end add message

            sw.Append("iterate: function(f) {");

            sw.Append("if(idb) {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('iterate message');");
            }
            sw.Append("var request = idb.open('sw_DB', 1);");

            sw.Append("request.onupgradeneeded = function(event) {");
            sw.Append("var db = event.target.result;");
            sw.Append("var store = db.createObjectStore('clientmessages', {keyPath : 'id'});");
            sw.Append("}; ");// end onupgraded


            sw.Append("request.onsuccess = function(event) {");

            sw.Append("var db = event.target.result; ");

            sw.Append("var transaction = db.transaction('clientmessages', 'readwrite');");
            sw.Append("transaction.onsuccess = function(event) {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('IndexDb transaction success');");
            }

            sw.Append("}; ");//endtransaction.success

            sw.Append("var msgStore = transaction.objectStore('clientmessages');");

            //get all messages and send to client
            sw.Append("msgStore.getAll().onsuccess = function(event) {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('get all messages');");
                sw.Append("console.log(event.target.result);");
            }

            sw.Append("var list = event.target.result;");

            sw.Append("list.forEach(function(msg) {");
            sw.Append("f(msg);");
            sw.Append("});");//end foreach

            sw.Append("msgStore.clear().onsuccess = function(event) {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('clear all messages');");
                sw.Append("console.log(event);");
            }

            sw.Append("}; "); //end clear

            sw.Append("}; "); //end getall

            sw.Append("}; "); //end request onsuccess

            sw.Append("} else {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('failed to send message because IndexedDB not suppported in this browser');");
            }

            sw.Append("} ");



            sw.Append("}");  //end iterate
            sw.Append("};"); // end  priv
            sw.Append("} "); //end if priv
            sw.Append("} "); //end build function

            sw.Append("if(priv) {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('returning ready priv');");
            }

            sw.Append("return priv;");
            sw.Append("}");

            sw.Append("build();");
            sw.Append("return priv;");
            sw.Append("}; "); //end function

            sw.Append("if(!self.messageList) {");

            sw.Append("self.messageList = messageListBuilder(); ");
            sw.Append("} ");



            sw.Append("self.addEventListener('message', function(event){");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('SW Received Message ' + new Date().toString());");
                sw.Append("console.log(event);");
            }


            sw.Append("if(event.data.type === 'page-ready') {");
            sw.Append("if(self.messageList) {");
            sw.Append("self.messageList.iterate(sendMessage);");

            //sw.Append("sendMessage({type:'hello' });");
            sw.Append("} ");


            sw.Append("}"); //endif windowready

            sw.Append("}); ");

            #endregion


            #region pushmanager

            sw.Append("if ('PushManager' in self) {");


            await _configurePushApiMethods.AppendToInitScript(sw, context, urlHelper);

            //sw.AppendLine("self.importScripts('/pwa/js/push-notifications-controller.js');");

            sw.Append("self.addEventListener('push', function (event) {");


            sw.Append("var json = event.data.json();");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log(json);");
            }

            sw.Append("const precacheCacheName = workbox.core.cacheNames.runtime;");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('pre-cache name is ' + precacheCacheName);");
            }


            // --- content add begin

            sw.Append("if(json.messageType === 'newcontent') {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('content add');");
            }

            sw.Append("caches.open(precacheCacheName).then(function(cache) {");

            sw.Append("fetch(json.data).then(function (response) {");
            sw.Append("cache.put(json.data, response.clone()).then(function () {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('added item to cache');");
            }

            sw.Append("});");
            sw.Append("});");


            sw.Append("});"); //end cache open
            sw.Append("}");
            // --- content add end


            // --- content update begin

            sw.Append("if(json.messageType === 'contentupdate') {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('content update');");
            }

            sw.Append("caches.open(precacheCacheName).then(function(cache) {");


            //sw.Append("var key = workbox.precaching.getCacheKeyForURL(json.data);");
            //if (options.EnableServiceWorkerConsoleLog)
            //{
            //    sw.Append("console.log('key is ' + key);");
            //}

            sw.Append("cache.delete(json.data).then(function(response) {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log(response);");
            }

            sw.Append("if(response === true) {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('deleted from cache');");
            }


            sw.Append("fetch(json.data).then(function (response) {");
            sw.Append("cache.put(json.data, response.clone()).then(function () {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('updated cache');");
            }

            sw.Append("let msg = { type:'cacheupdate', url: json.data, id: json.data }; ");

            sw.Append("if(self.messageList) {");
            sw.Append("self.messageList.addMessage(msg);");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('added new message ' + new Date().toString())");
            }


            sw.Append("} ");



            sw.Append("});");
            sw.Append("});");



            sw.Append("}");   //end if delete response === true

            sw.Append("});"); //end delete



            sw.Append("})"); //end cache open

            sw.Append("};"); //end if contentupdate

            /// --- content update end

            /// -- content delete

            sw.Append("if(json.messageType === 'contentdelete') {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('content delete');");
            }

            sw.Append("caches.open(precacheCacheName).then(function(cache) {");

            sw.Append("cache.delete(json.data).then(function(response) {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('removed item from cache');");
            }

            sw.Append("});"); //end delete

            sw.Append("});"); //end cache open
            sw.Append("}");

            /// --- content delete end



            sw.Append("if(json.messageType === 'Visible') {");
            sw.Append("event.waitUntil(self.registration.showNotification(json.title, json));");
            sw.Append("} else {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('non visible message');");
            }


            sw.Append("return;"); //cancel notification
            sw.Append("}");

            sw.Append("});"); //end push event



            sw.Append("self.addEventListener('pushsubscriptionchange', function (event) {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('push subscription change');");
            }

            sw.Append("const handlePushSubscriptionChangePromise = Promise.resolve();");

            sw.Append("if (event.oldSubscription) {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('old subscription');");
            }

            sw.Append("handlePushSubscriptionChangePromise = handlePushSubscriptionChangePromise.then(function () {");
            sw.Append("return PushNotificationsController.discardPushSubscription(event.oldSubscription);");
            sw.Append("});");
            sw.Append("}");

            sw.Append("if (event.newSubscription) {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('new subscription');");
            }

            sw.Append("handlePushSubscriptionChangePromise = handlePushSubscriptionChangePromise.then(function () {");
            sw.Append("return PushNotificationsController.storePushSubscription(event.newSubscription);");
            sw.Append("});");
            sw.Append("}");

            sw.Append("if (!event.newSubscription) {");

            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('not new subscription');");
            }

            sw.Append("handlePushSubscriptionChangePromise = handlePushSubscriptionChangePromise.then(function () {");
            sw.Append("return PushNotificationsController.retrievePublicKey().then(function (applicationServerPublicKey) {");
            sw.Append("return pushServiceWorkerRegistration.pushManager.subscribe({");
            sw.Append("userVisibleOnly: true,");
            sw.Append("applicationServerKey: applicationServerPublicKey");
            sw.Append("}).then(function (pushSubscription) {");
            sw.Append("return PushNotificationsController.storePushSubscription(pushSubscription);");
            sw.Append("});");
            sw.Append("});");
            sw.Append("});");
            sw.Append("}");


            sw.Append("event.waitUntil(handlePushSubscriptionChangePromise);");
            sw.Append("});");

            sw.Append("self.addEventListener('notificationclick', function (event) {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log(event);");
            }

            //sw.Append("event.notification.close();");
            sw.Append("});");

            sw.Append("} else {");
            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('PushManager NOT supported');");
            }

            sw.Append("} ");

            #endregion

            //return Task.CompletedTask;
        }
        public async Task AppendToServiceWorkerScript(StringBuilder sw, PwaOptions options, HttpContext context)
        {
            var cacheSuffix = await _workboxCacheSuffixProvider.GetWorkboxCacheSuffix();

            sw.Append("const networkFirstHandler = new workbox.strategies.NetworkFirst({");
            //sw.Append("cacheName: 'network-first-content-cache-" + cacheSuffix + "',");
            //sw.Append("plugins: [");
            //sw.Append("new workbox.expiration.Plugin({");
            //sw.Append(" maxEntries: 2000,");
            //sw.Append("maxAgeSeconds: 30 * 24 * 60 * 60,"); //30 days

            //sw.Append("})");
            //sw.Append("]");
            sw.Append("}); ");

            sw.Append("const networkFirstMatchFunction = ({url, event}) => {");


            sw.Append("if(url.href.indexOf(\"serviceworkerinit\") > -1) {");
            sw.Append("return false;");
            sw.Append("}");



            if (options.EnableServiceWorkerConsoleLog)
            {
                sw.Append("console.log('networkFirstMatchFunction returning true for ' + url.href);");
            }
            // Return true if the route should match, we are basically matching all requests except to the initserviceworker script
            sw.Append("return true;");
            sw.Append("};");


            sw.Append("workbox.routing.registerRoute(");
            sw.Append("networkFirstMatchFunction,");
            sw.Append("new workbox.strategies.NetworkFirst()");
            sw.Append(");");



            // var offlineUrl = _offlinePageUrlProvider.GetOfflineUrl();



            //sw.Append("workbox.routing.registerRoute(networkFirstMatchFunction, args => {");
            //sw.Append("return networkFirstHandler.handle(args).then(response => {");

            //sw.Append("if (!response) {");

            //sw.Append("const precacheCacheName = workbox.core.cacheNames.precache;");

            //sw.Append("caches.open(precacheCacheName).then(function(cache) {");

            //sw.Append("cache.match(event.request).then(function(response) {");
            //sw.Append("return response;");
            //sw.Append("}).catch(function() {");

            //sw.Append("return caches.match('" + offlineUrl + "');");

            //sw.Append("}) ");


            //sw.Append("})");//end cache open

            ////sw.Append("caches.match(event.request).then(function(response) {");
            ////sw.Append("return response;");
            ////sw.Append("}).catch(function() {");

            ////sw.Append("return caches.match('" + offlineUrl + "');");

            ////sw.Append("}) ");



            //sw.Append("}"); //end if not response

            ////sw.Append("console.log('network first returning response');");
            ////sw.Append("console.log(response.url);");

            //sw.Append("return response;");
            //sw.Append("});");
            //sw.Append("});");
        }