cloudcmd/modules/html5-polyfills/device-motion-polyfill.js
2015-06-05 09:08:56 -04:00

253 lines
13 KiB
JavaScript

/**
* DeviceMotion and DeviceOrientation polyfill
* by Remy Sharp / leftlogic.com
* MIT http://rem.mit-license.org
*
* Usage: used for testing motion events, include
* script in head of test document and allow popup
* to open to control device orientation.
*/
(!document.DeviceOrientationEvent || !document.DeviceMotionEvent) && (function () {
// thankfully we don't have to do anything, because the event only fires on the window object
var polyfill = {
motion: !document.DeviceMotionEvent,
orientation: !document.DeviceOrientationEvent
};
if (polyfill.orientation) window.DeviceOrientationEvent = function () {};
if (polyfill.motion) window.DeviceMotionEvent = function () {};
// images - yes I do like to be self contained don't I?! :)
var imageSrc = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFsAAACpCAYAAABEbEGLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxODkwMDQ2NTNDNEYxMUUxQTYyOTk2M0QwMjU4OThGOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxODkwMDQ2NjNDNEYxMUUxQTYyOTk2M0QwMjU4OThGOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4OTAwNDYzM0M0RjExRTFBNjI5OTYzRDAyNTg5OEY4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4OTAwNDY0M0M0RjExRTFBNjI5OTYzRDAyNTg5OEY4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+jKamsAAAAtNJREFUeNrs2t1t01AYgGG3dACPkA3qEcIlV5QJSCdAnaAwAWKCsgFs0HQChwmaS+6SThCOKwuigtP4J+ln9LzSRyWaRMmTE8fHcJLt3yRNkeY8U9VDmkWa+ZAPOktTptmYf84qzU29GDtXQG49111XM7xuUy3QfF/oa2DHAbeijwReABp8vjVh+zI8zFw4fBxv7q3qF1jdp1s7Qx2ut9UfZ0Gh+26BJ313dANXRDuvXg38xuf1NjrKoeTxMBKlq/rCzlCt01zWP0N0GujjtjzQ4y4iYS+DPJd8ZI/bCTtKHw7wmLNIJwBngbCn9QZgOcDZSF4/XqgzrUjY26ds0//xZPs0E2zYgg1bsGHDFuwRt2sHOcfTqSJruPi1C/s1t07dZg2XGxxGHLNhCzZswYYNW7BhCzZs2IINW7BhCzZs2IINW7BhwxZs2IINW7BhwxZs2IING7ZgwxZs2IING7ZgwxZs2LAFG7Zgw4Yt2LAFG7Zgw4Yt2LAFGzZswYYt2LAFGzZswYYt2LBhCzZswYYt2LBhCzZswYYNW7BhCzZs2IINW7BhCzZs2IINW7BhwxZs2IIdvbMdv7vG06lJF+yP3BxGYAs2bGcj8VukmadZb/1dkWaaJh/Li6hO8TaB57YGbSofwWvYjAH7psWiqd6QVWTsyMfsr2kuW9x+3vL2viDrlmmuOtzve/0mwW7RlydfhG36BLv9Cu3zqVjCbgf2kve3qRl5OezjtY6KPXnh+x/sMPIj4POa9oSOhr3YfnLRdlv3PV7YTfSdcBnwCX7uAF0E3apfbD/JWdAnOWsJvRrLp7TM4l6Meu4S6kXgi1C/V/XJk5VRRj1tqq953GV/X89+X/+MuhN+1/TLqIeTMU759BP5quEUZZqp7+WCN2l+7nNjK3zAFb3vt3sJr9X0/l9kM+g7Z1WfMT27az1puQ2uVvu5Q/JjD9mff/Hfq18CDADFyFpnRW9JPwAAAABJRU5ErkJggg==';
var imageBackSrc = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFsAAACpCAIAAADLDtbcAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNS4xIE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo3OTFCRTMwQTNDNjMxMUUxQTYyOTk2M0QwMjU4OThGOCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo3OTFCRTMwQjNDNjMxMUUxQTYyOTk2M0QwMjU4OThGOCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4OTAwNDZCM0M0RjExRTFBNjI5OTYzRDAyNTg5OEY4IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4OTAwNDZDM0M0RjExRTFBNjI5OTYzRDAyNTg5OEY4Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+zWf2VgAABt9JREFUeNrsnU1P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';
var id = (+new Date).toString(32),
body = document.documentElement, // yep - cheatin' Yud'up! :)
height = 320;
// if the url hash doesn't contain tiltremote (our key) then fireup the popup, else we are the popup
if (window.location.hash.indexOf('tiltremote') === -1) {
initServer();
} else {
initRemote();
}
function initServer() {
// old way didn't work. Shame, but I like the new way too :)
// var remote = window.open('data:text/html,<script src="' + src + '?tiltremote"></script>', 'Tilt', 'width=300,height=' + height);
var remote = window.open(window.location.toString() + '#tiltremote', 'Tilt', 'width=300,height=' + height);
if (!remote) {
alert('The remote control for the orientation event uses a popup')
}
// TODO add remote-tilt.com support
}
function initRemote() {
var TO_RADIANS = Math.PI / 180;
orientation = {
alpha: 180,
beta: 0,
gamma: 0
},
accelerationIncludingGravity = { x: 0, y: 0, z: -9.81 },
guid = (+new Date).toString(32);
function update(fromInput) {
var preview = document.getElementById('preview');
preview.style.webkitTransform = 'rotateY('+ gamma.value + 'deg) rotate3d(1,0,0, '+ (beta.value*-1) + 'deg)';
preview.parentNode.style.webkitTransform = 'rotate(' + (180-orientation.alpha) + 'deg)';
if (!fromInput) {
for (var key in orientation) {
var el = document.getElementById(key);
oninput.call(window, { target: el });
}
}
fireEvent();
}
function fireDeviceOrienationEvent() {
var event = document.createEvent('HTMLEvents');
event.initEvent('deviceorientation', true, true);
event.eventName = 'deviceorientation';
event.alpha = orientation.alpha;
event.beta = orientation.beta;
event.gamma = orientation.gamma;
window.opener.dispatchEvent(event);
}
function fireDeviceMotionEvent() {
var event = document.createEvent('HTMLEvents');
event.initEvent('devicemotion', true, true);
event.eventName = 'devicemotion';
event.accelerationIncludingGravity = {};
event.accelerationIncludingGravity.x = accelerationIncludingGravity.x;
event.accelerationIncludingGravity.y = accelerationIncludingGravity.y;
event.accelerationIncludingGravity.z = accelerationIncludingGravity.z;
window.opener.dispatchEvent(event);
}
function fireEvent() {
if (polyfill.orientation) fireDeviceOrienationEvent();
if (polyfill.motion) fireDeviceMotionEvent();
}
// hides the old body - but doesn't prevent the JavaScript from running.
// just a clean up thing - lame, but tidier
setTimeout(function () {
var bodies = document.getElementsByTagName('body'),
body = bodies[1];
if (bodies.length == 2) {
if (body.id == guid) body = bodies[0];
}
document.documentElement.removeChild(body);
});
body.innerHTML = ['<head><title>Motion Emulator</title>',
'<style>',
'html { height: ' + height + 'px; }',
'body { margin: 10px; font-family: sans-serif; overflow: hidden; }',
'#pov { height: 170px; position: relative; -webkit-perspective: 500; perspective: 500; cursor: move; }',
'#controls { position: relative; z-index: 1; }',
'#preview { display: block; margin: 20px auto; max-width: 100%; width: 100px; height: 170px; }',
'#preview div { width: 100%; height: 170px; position: absolute; top: 0; left: 0; -webkit-backface-visibility: hidden; }',
'#front { background: url(' + imageSrc + ') no-repeat center; }',
'#back { background: url(' + imageBackSrc + ') no-repeat center; -webkit-transform: rotateY(180deg); }',
'label { display: block; clear: both; }',
'label input[type=range] { display:inline-block; float: right; }',
'#buttons { margin-top: 2px; }',
'#south { position: absolute; bottom: 0; width: 100%; left: 0; text-align: center; color: #aaa; }',
'</style>',
'</head>',
'<body id="' + guid + '">',
'<div id=controls>',
'<label for=gamma>Left - Right <input min=-180 max=180 id=gamma value=0 type=range step=0.001> <output id=og>0</output></label>',
'<label for=beta>Back - Front <input min=-180 max=180 id=beta value=0 type=range step=0.001> <output id=ob>0</output></label>',
'<label for=alpha>Rotate <input min=0 max=360 id=alpha value=180 type=range step=0.001> <output id=oa>180</output></label>',
'<label for=wobble>Emulate slight shake<input type=checkbox id=wobble></label>',
'<div id=buttons><button id=flat>Flat on it\'s back</button> ',
'</div>',
'</div>', // end of controls
'<div id=pov>',
'<div id=preview>',
'<div id=front></div>',
'<div id=back></div>',
'</div>',
'</div>',
'<span id=south>south</span>',
'</body>'
].join('');
function oninput(event) {
var target = event.target;
if (target.nodeName == 'INPUT') {
var value = target.value * 1;
if (target.id == 'beta') {
// parseFloat + toFixed avoids the massive 0.00000000 and infinitely small numbers
accelerationIncludingGravity.z = parseFloat( (Math.sin( (TO_RADIANS * (value - 90))) * 9.81).toFixed(10) );
accelerationIncludingGravity.y = parseFloat((Math.sin( (TO_RADIANS * (value - 180))) * 9.81).toFixed(10));
value = parseFloat((Math.sin(value * TO_RADIANS) * 90).toFixed(10));
} else if (target.id == 'gamma') {
accelerationIncludingGravity.x = parseFloat( (Math.sin( (TO_RADIANS * (value - 180))) * -9.81).toFixed(10) );
}
document.getElementById('o' + target.id.substring(0, 1)).value = parseFloat(value.toFixed(2));
orientation[target.id] = value;
if (this !== window) update(true); // i.e. if not called manually
}
}
body.addEventListener('input', oninput, false);
var down = false,
last = { x: null, y: null },
pov = document.getElementById('pov'),
alpha = document.getElementById('alpha'),
beta = document.getElementById('beta'),
gamma = document.getElementById('gamma');
pov.addEventListener('mousedown', function (event) {
down = true;
last.x = event.pageX;
last.y = event.pageY;
event.preventDefault();
}, false);
body.addEventListener('mousemove', function (event) {
if (down) {
var dx = (last.x - event.pageX)// * 0.1,
dy = (last.y - event.pageY); // * 0.1;
last.x = event.pageX;
last.y = event.pageY;
gamma.value -= dx;
beta.value -= dy;
update();
}
}, false);
body.addEventListener('mouseup', function (event) {
down = false;
}, false);
body.addEventListener('click', function (event) {
var target = event.target;
if (target.nodeName == 'BUTTON') {
if (target.id == 'flat') {
alpha.value = 180;
beta.value = 0;
gamma.value = 0;
}
update();
} else if (target.id == 'wobble') {
if (target.checked) startShake();
else stopShake();
}
}, false);
function startShake() {
shake = setInterval(function () {
alpha.value = parseFloat(alpha.value) + (Math.random() * (Math.random() < 0.5 ? 1 : -1) * 0.05);
beta.value = parseFloat(beta.value) + (Math.random() * (Math.random() < 0.5 ? 1 : -1) * 0.05);
gamma.value = parseFloat(gamma.value) + (Math.random() * (Math.random() < 0.5 ? 1 : -1) * 0.05);
update();
}, 100);
}
function stopShake() {
clearInterval(shake);
}
update();
}
})();