We want to avoid problems with non-supported browsers in our svelte/sapper application.
1. Problem: Detect Internet Explorer
I want to warn users that our application written in Sapper/Svelte is not compatible with Internet Explorer. It can be simple plain-text message or redirect to some error page.
What I want in case of message is to stop execution of any further code after displaying warning message.
If redirect will be chosen then stopping execution is not needed.
Now I have this code
<head>
...
<!-- IE10+ will switch to IE9 behaviour and will respect IE HTML conditional tags -->
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9">
...
</head>
<body>
<![if !IE]>
<div id='sapper'>%sapper.html%</div>
%sapper.scripts%
<![endif]>
<!--[if IE]>
<h1 style="color: #333; font-family: 'Arial'; font-size: 18px; padding: 24px 18px; text-align: center;">
We do not support Internet Explorer.
</h1>
<p style="font-size: 46px; text-align: center; padding: 12px 0;">:/</p>
<![endif]-->
</body>
in the template.html file. Will this be enough to detect all IE browsers (with old engine)?
2. Problem: Detect any other missing feature on run-time
I was thinking that detecting the IE may not be enough for proper browser compatibility detection. Is there some universal Svelte compatibility detection function that I can use?
I still want some last-resort block of code that if application will crush in runtime on SOME not supported feature (local storage, spread operator, service worker, ...) than i want to display message or redirect user to error page.
UPDATE: I used IE conditional tags with meta tag. If there will be requirement to better detect browser features I would implement it in form of tests that would be performed during app initialisation.
The rollup sapper template includes support for "legacy mode". It's not documented anywhere but it creates separate javascript files for older browsers using babel. If you are using this ES6 syntax shouldn't cause problems, if babel is configured appropriately. Your site should still function without a service worker. For other features, such as local storage, I would test for specific features as required and try to fail gracefully.
If you are curious here is the code Sapper uses to detect legacy browsers and load the appropriate js files:
(function() {
try {
eval("async function x(){}");
var main = "${main}"
} catch (e) {
main = "${legacy_main}"
};
var s = document.createElement("script");
try {
new Function("if(0)import('')")();
s.src = main;
s.type = "module";
s.crossOrigin = "use-credentials";
} catch (e) {
s.src = "${req.baseUrl}/client/shimport#${build_info.shimport}.js";
s.setAttribute("data-main", main);
}
document.head.appendChild(s);
}());
You're using conditional comment to detect IE which is not supported by IE 10 and 11. If you want to detect all IE version, you could execute the function below at the beginning of the app:
var ua = window.navigator.userAgent;
var msie = ua.indexOf("MSIE ");
if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) // If Internet Explorer
{
// If you want to redirect
window.location.href = "http://www.example.com";
}
For the features, I don't find API in Svelte to detect them all. I think you can use JavaScript to detect the support of the features when you need.
Related
Is it possible to configure the erb page to detect IE version and based on that select different compatibility tags, so it should work as:
if user IE=10 then
< meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9" / >
else
Please don't propose other solutions as I need exactly the logic I described.
We have an application which doesn't work in IE10 and all the solutions we tried don't work except and only with IE=EmulateIE9 it works.
Does the application work in IE11 or Microsoft Edge? If not, then you could force all Microsoft browsers to emulate IE9 using <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE9">.
If you need to target IE10 specifically, then you should use feature detection, since IE10+ no longer supports conditional comments and user agent sniffing can be unreliable.
Here's a code snippet which identifies the browser by checking for version-specific CSS properties. It's a simplified version of a solution that I found here. I've tested it in IE9, 10, 11 and Microsoft Edge:
<!DOCTYPE html>
<html>
<head>
<title>Browser Detection</title>
</head>
<body>
<h1>Browser Detection</h1>
<h2 id="message"></h2>
<script>
var message = document.getElementById('message');
var browser = "Less than IE10 (or not IE at all)";
if (document.body.style['msTouchAction'] != undefined) {
browser = "IE10";
}
if (document.body.style['msTextCombineHorizontal'] != undefined) {
browser = "IE11 or higher";
}
message.innerHTML = browser;
</script>
</body>
</html>
The above snippet may not work for your purposes because the Javascript gets executed in the body, after the meta tags in the head have already been parsed.
I suppose you could trigger a page redirect for IE10+ browsers and add an argument to the URL, which would prevent the feature detection Javascript from executing the next time (you don't want to create an infinite loop). A quick example:
<script>
if (!document.location.search) {
if (document.body.style['msTouchAction'] != undefined) {
// Browser is IE10 or higher
window.location = "http://www.yoururl.com/?ie9mode";
}
}
</script>
An even better solution would be to store the result of the browser detection script in a session variable, so that you wouldn't need to append anything to the URL.
I hope this helps!
I am building an app with extensive audio requirements, so I am using SoundJS for that, and I am compiling the app using phonegap.
I am using the mobile safe approach to build a soundJS app. It seems that there are different audio plugins, including a special Cordova audio plugin. So, I am not able to register any of these plugins on the compiled app. This is because registering any plugin requires to check if this plugin is supported or not. In case of the cordova, the method isSupported checks for the following:
if (s._capabilities != null || !(window.cordova || window.PhoneGap || window.phonegap) || !window.Media) { return;}
This means when the app is compiled, there is no global variable called window.cordova or phonegap and no global variable called window.media (I think this is the media plugin that needs to be installed to get soundjs to work, and I have added it to the config.xml that I'm using for phonegap build.
So the question is, how to investigate what is wrong, how to know if for example the media plugin is not installed properly (all from the javascript variables that we can use, as I am not able to use any other debugging), or is it the case that when I compile using phonegap build there is no variables for cordova or phonegap.. can we list all global variables to see which ones are used?
Edit
Thanks Jesse for drawing my attention to these points about phonegap, so I built a small app just to test the deviceready event, but for some reason it still doesn't work when compiled by phonegap build:
<!DOCTYPE html>
<html>
<head>
<title>Cordova Device Ready Example</title>
<script type="text/javascript" charset="utf-8" src="js/soundjs-NEXT.min.js"></script>
<script type="text/javascript" charset="utf-8" src="js/cordovaaudioplugin-NEXT.min.js"></script>
<script type="text/javascript" charset="utf-8">
// Call onDeviceReady when Cordova is loaded.
//
// At this point, the document has loaded but cordova-2.3.0.js has not.
// When Cordova is loaded and talking with the native device,
// it will call the event `deviceready`.
//
function onLoad() {
document.getElementById("doc_loaded").innerHTML="Document Loaded"
document.addEventListener("deviceready", onDeviceReady, false);
}
// Cordova is loaded and it is now safe to make calls Cordova methods
//
function onDeviceReady() {
// Now safe to use the Cordova API\
document.getElementById("device_loaded").innerHTML="Device Loaded"
if (window.cordova || window.PhoneGap || window.phonegap){
document.getElementById("phonegap_loaded").innerHTML="Phonegap Loaded"
}
if (window.Media){
document.getElementById("media_loaded").innerHTML="Media Loaded"
}
}
</script>
</head>
<body onload="onLoad()">
Hello Hello, testing phonegap deviceready
<div id="doc_loaded">Loading Doc</div>
<div id="device_loaded">Loading Device</div>
<div id="phonegap_loaded">Detecting Phonegap</div>
<div id="media_loaded">Detecting Media</div>
</body>
</html>
Can you please help me locate where can the problem be?
EDIT2
I figured out that the deviceready was not working because I didn't add cordova:
<script type="text/javascript" src="cordova.js"></script>
So, when I did, I was able to initialize the cordova audio plugin. However, I am still unable to play sound, despite using mobile safe approach:
(this code is hosted on arbsq.net/h6/)
<!DOCTYPE html>
<html>
<head>
<title>SoundJS: Mobile Safe</title>
<link href="css/shared.css" rel="stylesheet" type="text/css"/>
<link href="css/examples.css" rel="stylesheet" type="text/css"/>
<link href="css/soundjs.css" rel="stylesheet" type="text/css"/>
<script src="js/examples.js"></script>
</head>
<body onload="loading_doc()">
<header class="SoundJS">
<h1>Mobile Safe Play</h1>
<p>This example registers and plays a sound with SoundJS in a way that will
work on mobile devices.</p>
</header>
<div class="content" id="content" style="height: auto">
<p id="status">Hello World.</p>
</div>
<div id="error">
<h2>Sorry!</h2>
<p>SoundJS is not currently supported in your browser.</p>
<p>Please log a bug
with the device and browser you are using. Thank you.</p>
</div>
<script type="text/javascript" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8" src="js/soundjs-NEXT.min.js"></script>
<script type="text/javascript" charset="utf-8" src="js/cordovaaudioplugin-NEXT.min.js"></script>
<!-- We also provide hosted minified versions of all CreateJS libraries.
http://code.createjs.com -->
<script id="editable">
var displayMessage; // the HTML element we use to display messages to the user
this.myNameSpace = this.myNameSpace || {};
function loading_doc() {
if(( /(ipad|iphone|ipod|android|windows phone)/i.test(navigator.userAgent) )) {
document.addEventListener('deviceready', init, false);
} else {
init();
}
}
function init() {
// store this off because walking the DOM to get the reference is expensive
displayMessage = document.getElementById("status");
// if this is on mobile, sounds need to be played inside of a touch event
if (createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry || createjs.BrowserDetect.isWindowPhone) {
//document.addEventListener("click", handleTouch, false); // works on Android, does not work on iOS
displayMessage.addEventListener("click", handleTouch, false); // works on Android and iPad
displayMessage.innerHTML = "Touch to Start";
}
else {
handleTouch(null);
}
}
// launch the app inside of this scope
function handleTouch(event) {
displayMessage.removeEventListener("click", handleTouch, false);
// launch the app by creating it
var thisApp = new myNameSpace.MyApp();
}
// create a namespace for the application
// this is a function closure
(function () {
// the application
function MyApp() {
this.init();
}
MyApp.prototype = {
src: null, // the audio src we are trying to play
soundInstance: null, // the soundInstance returned by Sound when we create or play a src
displayStatus: null, // the HTML element we use to display messages to the user
loadProxy: null,
init: function () {
// store the DOM element so we do repeatedly pay the cost to look it up
this.displayStatus = document.getElementById("status");
// this does two things, it initializes the default plugins, and if that fails the if statement triggers and we display an error
// NOTE that WebAudioPlugin plays an empty sound when initialized, which activates web audio on iOS if played inside of a function with a touch event in its callstack
if (!createjs.Sound.initializeDefaultPlugins()) {
document.getElementById("error").style.display = "block";
document.getElementById("content").style.display = "none";
return;
}
// Create a single item to load.
var assetsPath = "audio/";
this.src = assetsPath + "M-GameBG.ogg";
this.displayStatus.innerHTML = "Waiting for load to complete."; // let the user know what's happening
// NOTE createjs.proxy is used to apply scope so we stay within the touch scope, allowing sound to play on mobile devices
this.loadProxy = createjs.proxy(this.handleLoad, this);
createjs.Sound.alternateExtensions = ["mp3"]; // add other extensions to try loading if the src file extension is not supported
createjs.Sound.addEventListener("fileload", this.loadProxy); // add event listener for when load is completed.
createjs.Sound.registerSound(this.src); // register sound, which preloads by default
return this;
},
// play a sound inside
handleLoad: function (event) {
this.soundInstance = createjs.Sound.play(event.src); // start playback and store the soundInstance we are currently playing
this.displayStatus.innerHTML = "Playing source: " + event.src; // let the user know what we are playing
createjs.Sound.removeEventListener("fileload", this.loadProxy); // we only load 1 sound, so remove the listener
}
}
// add MyApp to myNameSpace
myNameSpace.MyApp = MyApp;
}());
</script>
</body>
</html>
#hmghaly,
the general method for checking for the availability of Phonegap is to use the 'deviceready' event that Cordova/Phonegap provide. In addition, it is required that you wait until this event completes.
You will want to read #4 of this article FAQ:
Top Mistakes by Developers new to Cordova/Phonegap
I will quote the important part from the documentation (which your should read):
This is a very important event that every Cordova application should use.
Cordova consists of two code bases: native and JavaScript. While the native code is loading, a custom loading image is displayed.
However, JavaScript is only loaded once the DOM loads. This means your
web application could, potentially, call a Cordova JavaScript function
before it is loaded.
The Cordova deviceready event fires once Cordova has fully loaded. After the device has fired, you can safely make calls to Cordova function.
The documentation includes code examples that would relevant to your particular mobile device and platform.
Best of Luck
Whilst it is not a complete answer I am currently working through the exact same problem and it was breaking at the exact same point.
if (s._capabilities != null || !(window.cordova || window.PhoneGap ||
window.phonegap) || !window.Media) { return;}
After you have ensured cordova is installed the next big thing is to ensure you actually have the cordova-plugin-media installed. The !window.Media bit in the line above. Sounds easy but if you simply add the plugin and build without reading all the output you can come unstuck.
The media plugin requires cordova version > 5.0 . The problem is that cordova is pinned at version 4.1.1 - at least mine was despite repeated total removal of cordova - several times via npm and manual total deletion of all npm caches.
Cordova is hard wired internally to install a particular version unless you tell it not to.
So make sure you are using
cordova platform add android#5.X.X
as appropriate to your version not just a plain old
cordova platform add android (BAD)
which will install the pinned version
If you do the latter cordova will happily build with version 4.1.1 despite the cli command
cordova -v
reporting you are on later version - in my case 5.4.1
It will then hit the plugin step - decide the environment is not appropriate for your plugin - spits out a warning and merrily continues with the build - minus the media plugin. Everything else will seem to work - the app will run and unless you dig into it you won't notice you are on an old version of cordova.
Note: they have just released a new version which moves the pinned version forward - so if you update to the latest version - you should be fine.
New Cordova Version Released
If you are using SoundJS 0.6.2, then you don't have to include the MobileSafe code. Refer Official Doc
The problem I was facing from quite a long time was the local sound files were not loading successfully in iOS.
What I found:
Latest iOS uses WKWebView. It appears to treat local files as if they came from a remote server, even though they're in the app itself, and such requests are blocked. Reference Source
Finally after lot of debugging and logging,
the following solution worked for me:
Add the Corodova file plugin.
cordova plugin add cordova-plugin-file
Change the local file path to this:
cdvfile://localhost/bundle/www/you_folder_name/file_name.mp3
i create a javascript webapp based on Extjs 4.2. A lot of feature of this framework doesn't work correctly with IE8 (and i don't know the result with previous versions). I'm searching the more clean way to block the access of my web app using user agent like IE8 or lesser from displaying a message and avoid the login to the webapp.
Anytype of help is usefull
if( ! Ext.isIE6 || ! Ext.isIE7 ) {
yourapp.init();
} else {
//show the element that directs people to http://browsehappy.com/
}
I wouldn't block access, I would show a message that says features in this application may not function correctly. Denying access is bad. If you want easy IE8 detection checkout Conditionizr and the IE8 detect (I created it):
/*!
* IE8
* #cc_on Conditional Compilation to test the
* JavaScript versions
*/
conditionizr.add('ie8', [], function () {
var version = false;
/*#cc_on if (#_jscript_version > 5.7 && !/^(9|10)/.test(#_jscript_version))
version = true #*/
return version;
});
This gives you:
if (conditionizr.ie8) {
// stuff for ie8
}
conditionizr.on('ie8', function () {
// callbacks
});
Plus you can load polyfills/other assets. Perhaps you can load Ext.js for non-IE8 so that the app doesn't break, it just doesn't serve instead.
With Conditionizr you can ignore browsers too using !:
conditionizr.on('!ie8'[, callback]);
Just use one of IE's conditional statements within your markup:
<html>
...
<body>
<!--[if lte IE 8]>
<p>Notice: As you are using an old browser some features of this
web app may not work for you. Please update.</p>
<![endif]-->
...
</body>
</html>
Anything contained within the <!--[if lte IE 8]> block here targets any version of Internet Explorer less than or equal to IE8.
When I load http://maps.google.com/maps/api/js?sensor=false in a script tag, everything works fine for me in Chrome, Safari, Firefox, and IE9.
However, when I look in IE9 in compatibility mode (or, I'm told, in IE8) the map does not load and "'google' is undefined" is logged in the console.
Here's the relevant code, with the line that is triggering the error identified with a comment:
<html>
<head>
<title>Test Map</title>
<script type="application/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head>
<body>
<div id="map_canvas"></div>
<script type="text/javascript">
var lat=37.763154;
var lon=-122.457941;
var initialZoom=17;
var mapTypeId = 'Custom Map';
var mapStyle = [{featureType:"landscape", elementType:"all", stylers:[{hue:"#dae6c3"},{saturation:16},{lightness:-7}]},
{featureType:"road", elementType:"geometry", stylers:[{hue:"#ffffff"},{saturation:-100},{lightness:100}]}];
//**The error is tripped in this next line, again only in IE9 compatibility mode or IE8*
var styledMap = new google.maps.StyledMapType(mapStyle);
var mapType = new google.maps.ImageMapType({
tileSize: new google.maps.Size(256,256),
getTileUrl: function(coord,zoom) {
return "img/tiles/"+zoom+"/"+coord.x+"/"+coord.y+".png";
}
});
var map = new google.maps.Map(document.getElementById("map_canvas"),
{center:new google.maps.LatLng(lat,lon),
mapTypeId:google.maps.MapTypeId.ROADMAP,
zoom:initialZoom,
mapTypeControl:false});
map.overlayMapTypes.insertAt(0, mapType);
map.mapTypes.set(mapTypeId, styledMap);
map.setMapTypeId(mapTypeId);
</script>
</body>
</html>
So, for some reason, and only in IE9+compatibility-mode and IE8, the script tag specifying http://maps.google.com/maps/api/js?sensor=false isn't loading and/or executing before the subsequent embedded script in the body.
Are others able to replicate? How do I correct this problem?
The problem, apparently, is that IE 8 doesn't grok "application/javascript". I changed it to "text/javascript" in the <script> tag in the <head> section and now my code works. And, of course, if I change it back to "application/javascript", then it stops working.
One guess is that you're page works over https while the request from Google is http.
convert the Google request to https and the error will disappear.
this worked for me.
See : Possible solution
My google maps v3 site stopped working in IE 9 compatibility view mode, IE 8 and IE7.
Reason: Error in java-script using jQuery only caught when using the IE F12 Developer tools to examine the script. Here is the offending line. The error was missing single quotes from the token, class.
$('<tr>', { 'class': country }).appendTo(tableSelector).append(h1).append(h2);
At first I pursued a false lead, thinking it was the addition of a key=
http://maps.googleapis.com/maps/api/js?key=MY_KEY_FROM_API_CONSOLE&sensor=false
The lesson is: use tools, Firebug or IE tools or whatever, to examine your javascript for introduced issues.
Make sure that IE isn't in offline mode. Sounds like the browser isn't connecting to the internet.
IE is downloading then attempting to execute the JS on your local machine, while the other browsers are simply opening it as a text file. You can find the downloaded JS from IE in wherever stuff downloads to by default.
EDIT: In light of updates, see this Fiddle to see a sort-of working fix. http://jsfiddle.net/h6rc3/
How can I hide a div with javascript if the browser is firefox only?
To check Firefox browser
//Javascript
var FIREFOX = /Firefox/i.test(navigator.userAgent);
if (FIREFOX) {
document.getElementById("divId").style.display="none";
}
<!-- HTML-->
<div id="divId" />
Just check a FF-specific JavaScript property. E.g.
var FF = (document.getBoxObjectFor != null || window.mozInnerScreenX != null);
if (FF) {
document.getElementById("divId").style.display = 'none';
}
This is called feature detection which is preferred above useragent detection. Even the jQuery $.browser API (of which you'd have used if ($.browser.mozilla) for) recommends to avoid useragent detection.
“Is the browser Firefox” is almost always the wrong question. Sure, you can start grovelling through the User-Agent string, but it's so often misleading that it's not worth touching except as a very very last resort.
It's also a woolly question, as there are many browsers that are not Firefox, but are based around the same code so are effectively the same. Is SeaMonkey Firefox? Is Flock Firefox? Is Fennec Firefox? Is Iceweasel Firefox? Is Firebird (or Phoenix!) Firefox? Is Minefield Firefox?
The better route is to determine exactly why you want to treat Firefox differently, and feature-sniff for that one thing. For example, if you want to circumvent a bug in Gecko, you could try to trigger that bug and detect the wrong response from script.
If that's not possible for some reason, a general way to sniff for the Gecko renderer would be to check for the existence of a Mozilla-only property. For example:
if ('MozBinding' in document.body.style) {
document.getElementById('hellononfirefoxers').style.display= 'none';
}
edit: if you need to do the test in <head>, before the body or target div are in the document, you could do something like:
<style type="text/css">
html.firefox #somediv { display: none }
</style>
<script type="text/javascript">
if ('MozBinding' in document.documentElement.style) {
document.documentElement.className= 'firefox';
}
</script>
if(document.body.style.MozTransform!=undefined) //firefox only
function detectBrowser(){
....
}
hDiv = .... //getElementById or etc..
if (detectBrowser() === "firefox"){
hDiv.style.display = "none"
}
You might try Rafeal Lima's CSS Browser Selector script. It adds a few classes to the HTML element for OS, browser, js support, etc. You can then use these classes as hooks for further CSS and/or JS. You might write a CSS (or jQuery) selector like html.gecko div.hide-firefox once the script has run.