Create a gist now

Instantly share code, notes, and snippets.

@aaronk6 /README.md
Last active Feb 27, 2017

launchUri

Cross-browser implementation of navigator.msLaunchUri

Microsoft’s navigator.msLaunchUri method only works in Internet Explorer on Windows 8. Therefore I came up with a (nearly) cross-browser implementation that uses the native msLaunchUri when it’s available and falls back to adventurous hacks when running in other browsers.

Description

launchUri (uri, successCallback, noHandlerCallback, unknownCallback)

If a default protocol handler is available on the system that matches the URI, the successCallback is invoked, otherwise, the noHandlerCallback is called. This works in the following browsers:

  • Internet Explorer 8-11 (tested on Windows 7 and Windows 8)
  • Chrome (tested with v. 39 on OS X and Windows)
  • Firefox (tested with v. 34 on OS X and Windows)

In all other browsers, the URI will be launched but you cannot find out if it worked (the unknownCallback is invoked).

Example:

launchUri('x-my-app://example-string', function () {
    // SUCCESS - the protocol is registered and the user was asked to open
    // the URI in the appropriate application
    alert('Have fun with my app');
}, function () {
    // FAILURE - the protocol isn't registered
    alert('Y u no install my app?');
}, function () {
    // UNKNOWN - we don't know wether the protocol is registered or not
    alert('Hey, did my app launch? If not, please install it. kthxbye');
});

License

MIT

Credits

/*!
* Copyright © 2015 aaronk6
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
function launchUri (uri, successCallback, noHandlerCallback, unknownCallback) {
var res, parent, popup, iframe, timer, timeout, blurHandler, timeoutHandler, browser;
function callback (cb) {
if (typeof cb === 'function') cb();
}
function createHiddenIframe (parent) {
var iframe;
if (!parent) parent = document.body;
iframe = document.createElement('iframe');
iframe.style.display = 'none';
parent.appendChild(iframe);
return iframe;
}
function removeHiddenIframe(parent) {
if (!iframe) return;
if (!parent) parent = document.body;
parent.removeChild(iframe);
iframe = null;
}
browser = { isChrome: false, isFirefox: false, isIE: false };
if (window.chrome && !navigator.userAgent.match(/Opera|OPR\//)) {
browser.isChrome = true;
} else if (typeof InstallTrigger !== 'undefined') {
browser.isFirefox = true;
} else if ('ActiveXObject' in window) {
browser.isIE = true;
}
// Proprietary msLaunchUri method (IE 10+ on Windows 8+)
if (navigator.msLaunchUri) {
navigator.msLaunchUri(uri, successCallback, noHandlerCallback);
}
// Blur hack (Chrome)
else if (browser.isChrome) {
blurHandler = function () {
window.clearTimeout(timeout);
window.removeEventListener('blur', blurHandler);
callback(successCallback);
};
timeoutHandler = function () {
window.removeEventListener('blur', blurHandler);
callback(noHandlerCallback);
};
window.addEventListener('blur', blurHandler);
timeout = window.setTimeout(timeoutHandler, 500);
window.location.href = uri;
}
// Catch NS_ERROR_UNKNOWN_PROTOCOL exception (Firefox)
else if (browser.isFirefox) {
iframe = createHiddenIframe();
try {
// if we're still allowed to change the iframe's location, the protocol is registered
iframe.contentWindow.location.href = uri;
callback(successCallback);
} catch (e) {
if (e.name === 'NS_ERROR_UNKNOWN_PROTOCOL') {
callback(noHandlerCallback);
} else {
callback(unknownCallback);
}
} finally {
removeHiddenIframe();
}
}
// Open popup, change location, check wether we can access the location after the change (IE on Windows < 8)
else if (browser.isIE) {
popup = window.open('', 'launcher', 'width=0,height=0');
popup.location.href = uri;
try {
// Try to change the popup's location - if it fails, the protocol isn't registered
// and we'll end up in the `catch` block.
popup.location.href = 'about:blank';
callback(successCallback);
// The user will be shown a modal dialog to allow the external application. While
// this dialog is open, we cannot close the popup, so we try again and again until
// we succeed.
timer = window.setInterval(function () {
popup.close();
if (popup.closed) window.clearInterval(timer);
}, 500);
} catch (e) {
// Regain access to the popup in order to close it.
popup = window.open('about:blank', 'launcher');
popup.close();
callback(noHandlerCallback);
}
}
// No hack we can use, just open the URL in an hidden iframe and invoke `unknownCallback`
else {
iframe = createHiddenIframe();
iframe.contentWindow.location.href = uri;
window.setTimeout(function () {
removeHiddenIframe(parent);
callback(unknownCallback);
}, 500);
}
}
@ssirois
ssirois commented Jan 27, 2017

@aaronk6 Would you be so kind as to release this code under the GNU/GPL License? I would like to use this code inside a project related to Ring (a "Ring Me" HTML button).

Thank you very much for considering this demand. This would very much help the Ring community.

@aaronk6
Owner
aaronk6 commented Feb 25, 2017 edited

@ssirois Sorry for not getting back earlier to you. I must have missed the GitHub notification somehow, so thanks for pinging me on Twitter last night! I just put the code under the MIT license which will give you even more freedom than with GPL :-)

@ssirois
ssirois commented Feb 27, 2017

@aaronk6 Thank you for the follow up. Since the Expat License (a.k.a the MIT license) is compatible with the GNU/GPL License so all good on my side! 👍

This is really appreciated.

Thank you for your time and sorry for the Twitter harassment. 😉 I think Gist messages leave less footprint than issues&features comments inside a GitHub project, therefore the ping on Twitter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment