I am using the new cache manifest functionality from HTML5 to cache my web app so it will work offline. The content is cached automatically when the page is loaded with the following html element:
<html lang="en" manifest="offline.manifest">
This works fine. However, I want to give my users the option of whether they want the content cached offline. So, here is my question:
Is there any way to trigger that an application be cached at runtime, using JavaScript, and not have it automatically done when the page is loaded.
For example, something like this (using jquery):
----------------index.html--------------
<head>
<meta charset="utf-8" />
<script src="http://code.jquery.com/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="Main.js"></script>
</head>
<body>
<button id="cacheButton">Cache Page</button>
</body>
</html>
---------Main.js---------
$(document).ready(
function()
{
$('#cacheButton').click(onCacheButtonClick);
}
)
function onCacheButtonClick(event)
{
console.log("Setting Offline Manifest");
$('#htmlRoot').attr("manifest","offline.manifest");
}
-------------offline.manifest-------------
CACHE MANIFEST
#version .85
#root
index.html
scripts/main.js
#jquery assets
http://code.jquery.com/jquery-1.4.4.min.js
Basically, when the button is clicked, I dynamically set the manifest attribute of the html element. This works (in the sense the element is set), but it does not cause the browser to then cache the page.
Any suggestions?
You dynamically trigger caching by adding an iframe that points to an empty page containing the actual cache manifest.
offline.html:
<!DOCTYPE html>
<html manifest="offline.appcache">
<head>
<title></title>
</head>
<body>
</body>
</html>
Make sure to add index.html to the cache manifest.
Then just add something like:
<iframe src="offline.html" width="0" height="0">
to document.body dynamically to trigger caching.
After many weeks spent with offline caching, the answer is no, you either cache or don't cache, setting the cache attribute on the client side has no effect.
You could consider offering an alternate url for the caching version, be aware that the page is also implicitly cached as a "master entry".
I am at a loss to understand why you would want to offline cache jquery though, since it is likely to be served with very long expiry anyway.
You may wish to consider offline storage as an alternative. Store the text of the scripts and inject them into the DOM on load. If not cached fetch using Ajax and inject the response, as creating a script tag with the src won't load the script.
Depending on your application, it may be possible to use a modified version of #schibum's approach by breaking down your app into "mini" apps, then caching the sub-sections in an iframe. Consider this example:
index.html
<html manifest="indexx.manifest">
<head>
<script src="jquery-2.1.4.min.js"></script>
<script src="index.js"></script>
<title>Index</title>
</head>
<body>
<ul>
<li>One
<li>Two
<li>Three
</ul>
<iframe id="if" />
</body>
</html>
index.manifest
CACHE MANIFEST
# 3
index.html
jquery-2.1.4.min.js
index.js
index.js
$( document).ready(function() {
var pages = ['1.html','2.html','3.html'];
var LoadNext = function() {
alert(pages[0]);
page = pages.shift();
alert(page)
if ( page !== undefined ) {
console.log("Attempting to load " + page);
$('#if').attr('src', page)
} else {
console.log("All done");
}
};
$('#if').load(function() {
console.log("Finished loading");
LoadNext()
});
LoadNext();
});
1.html
<html manifest="1.manifest">
<head>
<title>One</title>
</head>
<body>
<img src="1.jpg" width="50%">
</body>
</html>
1.manifest
CACHE MANIFEST
# 2
1.html
1.jpg
{2,3}.{html,manifest} follow 1.{html,manifest}'s form.
As a result, each sub-page (1.html, 2.html, 3.html) have their own manifest, and are thus cached independently. index.html has its own (minimal) manifest, so caching that unconditionally is not nearly as network-heavy as caching the entire app. Then the javascript is responsible for pre-loading every page in the iframe so that it gets cached.
Load index.html, then go offline and see how the sub-pages work. too.
An obvious drawback is that any assets shared between pages (e.g. jQuery) must be redundantly cached.
One thing you must remember. Do not cache the manifest file itself. So all you need to do is refresh the page with a different version of the manifest file according for your user selection. You can dynamically generate the manifest file itself, any change to that file will cause a cache refreshment. Best practice to trigger re-caching is to change the version of the manifest file, something like: # ver1 - 01/01/2018 to # ver2 - 02/02/2018 will do the trick.
So you cannot change it in client side but you can do it server side.
Related
I have a page with iframe, to simplify this case lets assume that it looks like this:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<iframe src="http://example.com"></iframe>
</body>
</html>
Assume that the page is on the Internet and you enter it. It loads http://example.com inside the iframe.
I want to change this so that you couldn't see the http://example.com because you can open this adress only using my page not just putting http://example.com in second tab of webbrowser.
I see http://example.com in page source (right click -> page source)
I also see in the network in developer tools that the browser makes request to http://example.com. So I just can copy this address and open it in new webbrowser tab.
What should I do to make it impossible for you to know the address: http://example.com
The only way I can think of is if you screen scrape the page in your code and serve up the html yourself. So it would look like this:
Someone makes a request to your page
Before you serve the page in your back end code you grab the html on
example.com
Serve up the html that comes out of the back end request
This may not be formatted properly though because you are missing css or lose functionality because you are missing js but you just need to make sure you include that stuff.
An IFrame is client-side.
This means for the browser to render the IFrame, it must be visible to the client web-browser.
The only way to hide it is for you to develop a web proxy.
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<iframe src="grab_secret_site.php"></iframe>
</body>
</html>
Here, the code within grab_secret_site.php would make an HTTP request to http://example.com to grab the HTML. An example here. However, bear in mind that any resources won't load unless you rewrite them.
e.g.
<img src=icon.jpg />
Will point to icon.jpg on your site, not on example.com.
If you don't want the page to be loaded without the parent frame, you could create a session variable (e.g. PHP session) in the outer page, which is then checked within the IFrame. If it doesn't exist, redirect the user back to the outer page.
There is a header - X-Frame-Options that allows you to block opening page in iframe, but in this way I think It's impossible.
Given an url (e.g. localhost:8000), how can a script find what resources will a browser load (via HTTP requests)?
For example, let's suppose that the / route responds with:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
some content
<img src="/foo.png" alt="">
</body>
</html>
The resources which will be loaded are:
/css/style.css
foo.png
This is simple (just a dom iteration with via cheerio or so), but it's not so native I think it should.
An iteration in the response HTML will not work for the additional CSS #imports and background-image and so on.
What is the native way to get the list with the CSS, images and maybe other resources which are loaded by the browser?
Maybe is it possible via jsdom?
Like #adeneo suggested, the missing keywords were headless browser. I find it very simple via the zombie library. Below you can see a small example, however the documentation is a great resource.
// Dependencies
var Browser = require("zombie");
// Load localhost:9000
Browser.localhost("localhost", 9000);
// Load the page from localhost,
// including js, css, images and iframes
var browser = new Browser({
features: "scripts css img iframe"
});
// Open the page and list the resources
browser.visit("/", function(error) {
console.log(browser.resources.map(function (c) {
return c.request.url;
}));
});
I have the following jQuery CDN fallback on a test page. I am testing locally with chrome and IE. The CDN is not loading. If I use the http:// in the CDN it loads, but if I remove it and just use // This makes no sense to me. If the script doesn't load the conditional statement should load it locally, but it's not. If I replace the
document.write('<script src="/scripts/jquery-2.1.1.min.js"><\/script>')
with
document.write('undefined')
then I get the word undefined on the page after about 5 seconds. Am I not scaping properly? Here is my html page:
<!DOCTYPE html>
<html>
<head>
<title>jQuery</title>
</head>
<body>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<script>
(window.jQuery || document.write('<script src="/scripts/jquery-2.1.1.min.js">
<\/script>'));
</script>
<script>
$(document).ready(function() {
alert( "welcome" );
});
</script>
</body>
</html>
You may be loading the page using the file:// protocol. The exact meaning of the // protocol is "use the same protocol that I am using". If you are looking at file://C:/Users/Me/test.html, then your //ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js becomes the URI file://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js which is not a valid file path on your computer. If you want to test locally you need to either use the http:// protocol in the link or host the project on IIS or apache on your box.
There are some services (like FB like or AddThis) that provide a snippet of code.
It looks like
<div class="service-name" data-something="x"></div>
<script type="text/javascript" src="http://example.com/service-name.js"></script>
OK, cool, so normally you paste it to your HTML and it works. Not with Meteor.
Here's what I see:
<script> inside of template / body is not loading -- I don't see it in Resources, something in Meteor is actually preventing browser from recognizing it as a JS file
it works from <head>
Now here are the problems and questions:
I don't want loading it from <head> -- because of the speed
Even if I load it from there -- we have QA and PROD environments. They must load this script from different domains (like service-domain-qa.example vs. example.com)
And surprisingly you cannot use template helpers / variables in the <head>.
With traditional frameworks it's not a question at all - you can include scripts anywhere and they just load; you can use logic / variables in any part of you server templates.
So, how should I do this in Meteor?
Let me repeat:
I need some external scripts (hosted on 3rd party domain) to be loaded into my app page
Saving this script into my project's folder is not an option
Script path depends on the environment (we already have the settings system), so the place of the template that renders it should be passed some data from the code
I know the way to achieve this with dynamic script loading from my code (with LAB.js or whatever) on Template.created, but this is so much an overkill...
<script> tags in body or templates aren't executed by Meteor, they are parsed and then handled by Meteor's templating system. You can't expect a script tag in either of those to just work as it would with a normal HTML page.
The solution is to use Template events (where you could manually append the script tag to the body or something) or load it dynamically like you said. It's not overkill, it's how Meteor works - remember, there is no traditional HTML page or body, there's just the Meteor API, and the Meteor API specifies that in order to load and execute external scripts, you must use the appropriate API methods.
My solution is use packages. See https://github.com/meteor/meteor/tree/master/packages/spiderable for more details.
Package.describe({
summary: "External script"
});
Package.on_use(function (api) {
api.use(['templating'], 'client');
api.add_files('external_script.html', 'client');
});
<head><script type="text/javascript" src=""//mc.yandex.ru/metrika/watch.js""></script></head>
If you are using IronRouter you can load external scipt using this package:
https://github.com/DerMambo/wait-on-lib
Router.map( function () {
this.route('codeEditor',{
waitOn: IRLibLoader.load('https://some-external.com/javascript.js')
});
});
Why not use jQuery's getscript?
http://api.jquery.com/jquery.getscript/
You can add a callback function
You could use something like yepnope to load the script asynchronously. I use this to load leaflet as and when I need. I'm starting to move over to loading more scripts via yepnope, so that my application renders the bare minimum on initial page load. I place the yepnope stuff inside Template.created.
Using iframe and the public directory was a hack I used to get script code embedded. In this it was for google adwords code and I did this my main html template:
<iframe src="/gads.html?v={{{unique}}}" seamless width="160" height="600"
scrolling="no" frameborder="0" marginheight="0" marginwidth="0"
style="margin:0;padding:0;border:none;width:160px;height:600px"></iframe>
and then in the public directory put an gads.html file with my google adwords code, like this:
<html>
<head>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div>
<script type="text/javascript"><!--
google_ad_client = "ca-pub-54*********";
google_ad_slot = "66******";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</body>
</html>
that worked to get the code on the page, although it's far from ideal (for one thing, I think it violates google's terms of service).
I'm using METEOR 1.0. I have put all external SCRIPT tags inside a DIV element right before the tag in the layout template. Meteor recognizes them without any issue and they are loaded by browser.
I have built an iPhone webapp using PHP. The main (and only) page includes the apple-mobile-web-app-capable and apple-touch-fullscreen meta tags so that it can run fullscreen once added to the homescreen. However, it seems every time I launch the app from the homescreen, the cache version of the page is used instead of refreshing the page (and of course, I need the page to be updated on startup, and cannot use Ajax here - need to dynamically authenticate the user with SSO phpCAS). I did not use any manifest file and tried adding meta tags about cache without success. Does anybody know how to fix this?
Thanks
What meta tags have you tried?
<META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">
<META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE">
should tell Safari not to cache, but I have not tried them.
You could use javascript to load your "real" startup page from the cached page using any of the standard techniques to make the URL unique, such as adding a random number.
That might even work for the main startup page, but I doubt it. Worth a try, though.
I suggest the cached page load a new page only if it is on the springboard:
UNTESTED suggestion:
window.onload = function () {
if (navigator.standalone) {
document.location.href = 'http://your.com/whatever.php?randomjunk=1234') }
}
so if the page is in the browser it can give instructions on saving to the home screen and if it is run from the home screen it will load the real page.
I know when you include a manifest file the main page is cached automatically, but I didn't think it happened when just using ...web-app-capable. Have you tried using a cache manifest file and listing your page under "Network:" to explicitly exclude it from this method of caching? If it isn't this it must be to do with the header and the meta information there.