Solved

Cache Manifest Progress Bar

Posted on 2014-03-29
7
1,691 Views
Last Modified: 2014-04-25
Hi am starting to use the cache manifest file to make a simple offline webapp for the iapd.
I want to implement a progress bar for when the user first visits the page (online)...

I have created some javascript but the progress bar goes to 100% straight away...

Could someone look at what I have done and suggest any improvements:


<script type="text/javascript">
window.addEventListener('load', function(e) {

    if (window.applicationCache) {
        //alert ("ddd");
        window.applicationCache.addEventListener('updateready', function(e) {

            if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {

                // Browser downloaded a new app cache.
                // Swap it in and reload the page to get the new hotness.
                window.applicationCache.swapCache();
                if (confirm('A new version of this site is available. Load it?')) {
                    window.location.reload();
                }
            } else {
                // Manifest didn't changed. Nothing new to server.
            }
        }, false);
    }
}, false);





window.applicationCache.onprogress = function (e) {               
    var message = 'Downloading offline resources.. ';

    if (e.lengthComputable) {
        updateProgress(Math.round(e.loaded / e.total * 100));
    } else {
        alert(message);
    };
};







var cacheStatusValues = [];
cacheStatusValues[0] = 'uncached';
cacheStatusValues[1] = 'idle';
cacheStatusValues[2] = 'checking';
cacheStatusValues[3] = 'downloading';
cacheStatusValues[4] = 'updateready';
cacheStatusValues[5] = 'obsolete';

var cache = window.applicationCache;
cache.addEventListener('cached', logEvent, false);
cache.addEventListener('checking', logEvent, false);
cache.addEventListener('downloading', logEvent, false);
cache.addEventListener('error', logEvent, false);
cache.addEventListener('noupdate', logEvent, false);
cache.addEventListener('obsolete', logEvent, false);
cache.addEventListener('progress', logEvent, false);
cache.addEventListener('updateready', logEvent, false);

 



function updateProgress(p) {
    var progress;
    progress = $("#progressbar")
    .progressbar("option","value");
        $("#progressbar")
        .progressbar("option", "value", progress = p);
} 



function logEvent(e) {

    //console.log(event.loaded + " of " + event.total + " downloaded.");

    var online, status, type, message;
    online = (navigator.onLine) ? 'yes' : 'no';
    status = cacheStatusValues[cache.status];
    type = e.type;
    message = 'online: ' + online;
    message+= ', event: ' + type;
    message+= ', status: ' + status;
    if (type == 'error' && navigator.onLine) {
        message+= ' There was an unknown error, check your Cache Manifest.';
    }
    console.debug(message);
}












$(function() {
    $("#progressbar").progressbar({ value: 0 });
});


 
</script>

Open in new window

0
Comment
Question by:sjtinsley83
  • 4
  • 3
7 Comments
 
LVL 42

Accepted Solution

by:
Rob Jurd, EE MVE earned 500 total points
Comment Utility
i did this a while ago but I have just been going through the code to refresh myself.

To cut a long story short (I hope) I ended up using an iframe as is done with other offline apps such as Angry Birds... Load it up in Chrome and view source.

The paradox is that you have to load the page to know how much is loaded but by then you have 100% loaded! sounds dumb doesn't it!  So what you do is have a very basic "index.html" file and an iframe just inside the <body> tag:

<body>
<div id="tooltip"></div>
<iframe id="appcacheInstaller" style="display: none;" src="http://www.domain.com/ApplicationCacheInstaller.html"></iframe>

Open in new window


ApplicationCacheInstaller.html takes care of al the manifest stuff AND the progress of it loading:

<!DOCTYPE html>
<html manifest="tool.manifest">
	<head></head>
	<body style="background-color: #0f0; margin: 0px;">
    ApplicationCacheInstaller.html
    <pre id='status'></pre>
    <script type="text/javascript">
var appcache = window.applicationCache;
appcache.addEventListener('cached', logEvent, false);
appcache.addEventListener('checking', logEvent, false);
appcache.addEventListener('downloading', logEvent, false);
appcache.addEventListener('error', logEvent, false);
appcache.addEventListener('noupdate', logEvent, false);
appcache.addEventListener('obsolete', logEvent, false);
appcache.addEventListener('progress', logEvent, false);
appcache.addEventListener('updateready', logEvent, false);

if (window.applicationCache !== undefined && window.applicationCache.status == window.applicationCache.IDLE){
	changeButtonToInstalled();
}

function setStatus(message) {
    window.parent.document.getElementById("tooltip").innerHTML = message;
}

function changeButtonToInstalled() {
    //window.parent.document.getElementById("installButton").id = "offlineInstalled";
}

function logEvent(e) {
    switch (e.type) {
      case "progress":
        message = "Installing offline version...";
        if (e.lengthComputable) {
          message += Math.round(e.loaded / e.total * 100) + "%";
        }
        break;
      case "noupdate":
        message = "You're running the latest offline capable version.";
        changeButtonToInstalled();
        break;
      case "obsolete":
        message = "Offline mode has been uninstalled.";
        break;
      case "cached":
        message = "We're ready to play offline!";
        changeButtonToInstalled();
        break;
      case "checking":
        message = "Checking for a new version...";
        break;
      case "downloading":
        message = "Downloading...";
        break;
      case "updateready":
        message = "A new version is ready. Click to <a href='javascript:window.location.reload();' onclick='return confirm(\"This will restart your game. Continue?\");'>upgrade</a>.";
        window.parent.appCacheUpdateReady();
        break;
      case "error":
        switch (window.applicationCache.status) {
          case window.applicationCache.IDLE:
            message = "We're ready to play offline!";
            break;
          case window.applicationCache.UNCACHED:
            message = "Error loading offline version. Please <a href='javascript:window.location.reload();' onclick='return confirm(\"This will restart your game. Continue?\");'>try again</a>.";
            break;
          case window.applicationCache.CHECKING:
          case window.applicationCache.DOWNLOADING:
          case window.applicationCache.UPDATEREADY:
          case window.applicationCache.OBSOLETE:
          default:
            message = "Error " + window.applicationCache.status + " loading offline version. Please <a href='javascript:window.location.reload();' onclick='return confirm(\"This will restart your game. Continue?\");'>try again</a>.";
            break;
        }
        break;
      default:
        message = "[" + e.type + "]";
    }
    document.getElementById("status").innerHTML += e.type + ": " + message + "\n";
    setStatus(message);
}
    </script>
  </body>
</html>

Open in new window

0
 

Author Comment

by:sjtinsley83
Comment Utility
Hi Rob,

Thanks for the help. I have been playing with the code today and beginning to put something together.... But i'm at a head scratching point. Would you mind looking at my code:

cacher.html
<!DOCTYPE html>
<html lang="en" manifest="offline.manifest">

<head>
    
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="apple-mobile-web-app-capable" content="yes" /> <!-- Run in full-screen mode. -->
    <meta name="apple-mobile-web-app-status-bar-style" content="black" /> <!-- Make the status bar black with white text. -->
    <meta name="apple-mobile-web-app-title" content="Menu"> <!-- Customize home screen title. -->
    <title></title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <!-- Set viewport. -->
        
    <!-- STARTUP IMAGES & ICONS -->
    <link rel="apple-touch-icon" sizes="76x76" href="webapp/images/webapp/touch-icon-ipad.png">
    <link rel="apple-touch-icon" sizes="152x152" href="webapp/images/webapp/touch-icon-ipad-retina.png">
    <!-- iPad (portrait) SPLASHSCREEN-->
    <link href="webapp/apple-touch-startup-image-768x1004.png" media="(device-width: 768px) and (orientation: portrait)" rel="apple-touch-startup-image">
    <!-- iPad (landscape) SPLASHSCREEN-->
    <link href="webapp/apple-touch-startup-image-748x1024.png" media="(device-width: 768px) and (orientation: landscape)" rel="apple-touch-startup-image">
    <!-- iPad (Retina, portrait) SPLASHSCREEN-->
    <link href="webapp/apple-touch-startup-image-1536x2008.png" media="(device-width: 1536px) and (orientation: portrait) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">
    <!-- iPad (Retina, landscape) SPLASHSCREEN-->
    <link href="webapp/apple-touch-startup-image-2048x1496.png" media="(device-width: 1536px)  and (orientation: landscape) and (-webkit-device-pixel-ratio: 2)" rel="apple-touch-startup-image">

	<link rel="stylesheet" href="webapp/includes/jquery-ui-1.10.4.min.css" type="text/css" rel="stylesheet" />
	<script src="webapp/includes/jquery.min.js" type="text/javascript"></script>
	<script src="webapp/includes/jquery-ui-1.10.4.min.js" type="text/javascript"></script>
	<script src="webapp/includes/jquery.stayInWebApp.min.js" type="text/javascript"></script>
	<script src="webapp/includes/fastclick.js" type="text/javascript"></script>
	<script src="webapp/includes/scripts_v1.js"></script>
	<link href="webapp/includes/style.css" type="text/css" rel="stylesheet" >

</head>

<body>


<script type="text/javascript">
	$(document).ready(function(){  

        var progressLabel =  $( ".progress-label" );
        $("#progressbar").progressbar({
        	value: 0,

        	complete: function() {
            	progressLabel.text( "Complete!" );
          	}
        });


		$("#details").click(function(){
			var txt = $("#appcacheInstaller").is(':visible') ? 'Show Details' : 'Hide Details';
			$("#details").text(txt);
			$("#appcacheInstaller").toggle();
		});


	});

</script>









<div id="container">
	<div id="content">


		<h1>Offline Downloader</h1><br>

		<div id="tooltip"></div>
		<div id="progressbar" style="display:none;"><div class="progress-label">Loading...</div></div>
		<br><a href="javascript:void(0)" id="details" style="float: right;">Show Details</a><br>
				

		<iframe id="appcacheInstaller" frameborder="0" style="display:none; border:1px dotted #000; width:100%; -webkit-box-sizing: border-box;" src="ApplicationCacheInstaller.html"></iframe>

		


	</div>
</div>

<div id="footer">
	<a href='#' onclick='location.reload(true); return false;'>Refresh</a>
</div>


<div id="help" title="Help">
	<ul>
		<li>When downloading the webapp it is best to be on a fast wifi connection rather then mobile internet.</li>
		<li>If you see no activity for a few minutes you may need to reload your browser.</li>
	</ul>
</div>


</body>

</html>

Open in new window



ApplicationCacheInstaller.html
<!DOCTYPE html>
<html lang="en" manifest="offline.manifest">
	<head> 
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
	<body>



    <pre id='status'></pre>

    <script type="text/javascript">


      if (window.applicationCache !== undefined && window.applicationCache.status == window.applicationCache.IDLE){
      	alert("(201)");
        //cacheComplete();
      }


      function updateProgressBar(percentage) {
        window.parent.$( "#progressbar" ).show();
        var progress;
        progress = window.parent.$("#progressbar")
        .progressbar("option","value");
        window.parent.$("#progressbar").progressbar("option", "value", progress = percentage);
      } 

      function setStatus(message) {
          window.parent.document.getElementById("tooltip").innerHTML = message;
      }

      function cacheComplete() {
        window.parent.$( "#content" ).load( "launch.html" ).fadeIn('slow');
        //window.parent.document.getElementById("installButton").id = "offlineInstalled";
      }

      function logEvent(e) {
          switch (e.type) {

            case "progress":
              
              message = "<div class=\"alert-box notice\">Installing offline version...";
              if (e.lengthComputable) {
                percentage = Math.round(e.loaded / e.total * 100);
                message += percentage + "%";
                updateProgressBar(percentage);
              } else {
                alert ("Error (199)");
              }
              message += "<br><br>Please be patient as this is a LARGE download and can take sometime.</div>";
              break;
            case "noupdate":
              message = "<div class=\"alert-box success\">You're already running the latest offline webapp. Click to <a href='webapp/'>continue</a>.</div>";
              //cacheComplete();
              break;
            case "obsolete":
              message = "<div class=\"alert-box notice\">Offline mode has been uninstalled.</div>";
              break;
            case "cached":
              message = "<div class=\"alert-box success\">Webapp is ready to use offline. Click to <a href='webapp/'>continue</a>.</div>";
              //cacheComplete();
              break;
            case "checking":
              message = "<div class=\"alert-box notice\">Checking for a new version, please wait.</div>";
              break;
            case "downloading":
              message = "<div class=\"alert-box notice\">Please be patient, the webapp is downloading...</div>";
              break;
            case "updateready":
              message = "<div class=\"alert-box success\">The webapp has been downloaded. Click to <a href='webapp/'>continue</a>.</div>";
              //window.parent.appCacheUpdateReady();
              break;
            case "error":
              switch (window.applicationCache.status) {
                case window.applicationCache.IDLE:
              message = "<div class=\"alert-box notice\">Click to <a href='webapp/'>continue</a>.</div";
                  break;
                case window.applicationCache.UNCACHED:
                  message = "<div class=\"alert-box error\">Error (102) loading offline version. Please <a href='javascript:window.location.reload();' onclick='return confirm(\"This will restart your download. Continue?\");'>try again</a>.</div>";
                  break;
                case window.applicationCache.CHECKING:
                case window.applicationCache.DOWNLOADING:
                case window.applicationCache.UPDATEREADY:
                case window.applicationCache.OBSOLETE:
                default:
                  message = "<div class=\"alert-box error\">Error (103)" + window.applicationCache.status + " loading offline version. Please <a href='javascript:window.location.reload();' onclick='return confirm(\"This will restart your sync. Continue?\");'>try again</a>.</div>";
                  break;
              }
              break;
            default:
              message = "[" + e.type + "]";
          }
          document.getElementById("status").innerHTML += e.type + ": " + message + "\n";
          //console.log(message);
          setStatus(message);
      }


      var appcache = window.applicationCache;
      appcache.addEventListener('cached', logEvent, false);
      appcache.addEventListener('checking', logEvent, false);
      appcache.addEventListener('downloading', logEvent, false);
      appcache.addEventListener('error', logEvent, false);
      appcache.addEventListener('noupdate', logEvent, false);
      appcache.addEventListener('obsolete', logEvent, false);
      appcache.addEventListener('progress', logEvent, false);
      appcache.addEventListener('updateready', logEvent, false);


    </script>



  </body>
</html>

Open in new window



The problem i'm having is that the first time the page is launched as a webapp it doesnt run the progress bar.... I only get as far as CHECKING and DOWNLOADING but i dont get any PROGRESS.

cache problem
If i look at the debugger on safari is shows the data increasing so it seems to be caching.

No other caching status's get run so I have to close safari and reopen the webapp...

When I close the webapp and reload it, it runs the progress bar as expected. After that, when ever i update the cachemanifest file the progress car works fine.

The problem seems to Safari.

Any ideas??
0
 
LVL 42

Expert Comment

by:Rob Jurd, EE MVE
Comment Utility
It is strange but my first thought is you have a lot more going on in your cacher.html than I did.  Before you start modifying that page too much, put the iframe at the top of the body element.  If that makes no difference then remove all the javascript / jquery, script tags, link tags and see if that makes a difference.  If so, add them back one by one until you find the culprit.  My gut says there's just too much going on.  It works on subsequent times because the browser has already cached your cacher.html file.
0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 

Author Comment

by:sjtinsley83
Comment Utility
Hi Rob,

After a little more playing i discovered that is was my HTML tag at the top of the cacher.html page that was causing the problem.

When it was like this:
<html lang="en" manifest="offline.manifest">
it failed... But when it was:
<html>
it worked.

So im guessing I dont need the manifest file at the top of that page....

---------------

Next (and hopefully final) problem.

The webapp (fullscreen) is caching 100mb of videos as well as a a few other images and html etc...

When the webapp caches for the first time it gets to 99% and fails. It gives the user the option to retry and then it completes.

This only happens on iPad Safari and not Chrome.

It appears to happen when the cache totals to more then 25mb. But it still goes through all the files (as I can see a PROGRESS for each file.)

When you click try again is caches correctly and works happy from there on.

Any Ideas?
0
 
LVL 42

Expert Comment

by:Rob Jurd, EE MVE
Comment Utility
Oh of course... the manifest is referenced in the iframe...*smacks forehead*
As for your other Issue, have you opened the developer tools to see if there are any errors reported in the console? I still suspect part of the Dom isn't ready when the cache completes but we'd need to confirm that via the console in dev tools
0
 

Author Comment

by:sjtinsley83
Comment Utility
I've requested that this question be closed as follows:

Accepted answer: 0 points for sjtinsley83's comment #a40018518

for the following reason:

Hi Rob,
I had another page which was redirecting to the cacher.html page automatically (if the webapp was in full screen / running ios7 / running on ipad etc). I decided to put a link on that page instead, and it works perfectly.
Its running like a dream now!
Thanks for all the help.
Steve
0
 

Author Closing Comment

by:sjtinsley83
Comment Utility
Hi Rob,
I had another page which was redirecting to the cacher.html page automatically (if the webapp was in full screen / running ios7 / running on ipad etc). I decided to put a link on that page instead, and it works perfectly.
Its running like a dream now!
Thanks for all the help.
Steve
0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Introduction If you're like most people, you have occasionally made a typographical error when you're entering information into an online form.  And to your consternation, the browser remembers the error, and offers to autocomplete your future entr…
This article describes how to create custom column layout styles for Bootstrap. The article uses 5 columns to illustrate the concept, but the principle can be extended to any number of columns.
In this tutorial viewers will learn how to style elements, such a divs, with a "drop shadow" effect using the CSS box-shadow property Start with a normal styled element, such as a div.: In the element's style, type the box shadow property: "box-shad…
In this tutorial viewers will learn how to embed an audio file in a webpage using HTML5. Ensure your DOCTYPE declaration is set to HTML5: : The declaration should display (CODE) HTML5 is supported by the most recent versions of all major browsers…

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now