webvr-polyfill
Advanced tools
Comparing version 0.9.41 to 0.10.0
{ | ||
"name": "webvr-polyfill", | ||
"version": "0.9.41", | ||
"version": "0.10.0", | ||
"homepage": "https://github.com/googlevr/webvr-polyfill", | ||
@@ -12,10 +12,19 @@ "authors": [ | ||
"devDependencies": { | ||
"babel-core": "^6.24.1", | ||
"babel-plugin-external-helpers": "^6.22.0", | ||
"babel-preset-env": "^1.6.1", | ||
"chai": "^3.5.0", | ||
"jsdom": "^9.12.0", | ||
"localStorage": "^1.0.3", | ||
"mocha": "^3.2.0", | ||
"semver": "^5.3.0", | ||
"webpack": "^2.6.1", | ||
"webpack-dev-server": "2.7.1" | ||
"rollup": "^0.52.1", | ||
"rollup-plugin-babel": "^3.0.2", | ||
"rollup-plugin-cleanup": "^2.0.0", | ||
"rollup-plugin-commonjs": "^8.2.6", | ||
"rollup-plugin-json": "^2.3.0", | ||
"rollup-plugin-node-resolve": "^3.0.0", | ||
"rollup-plugin-uglify": "^2.0.1", | ||
"semver": "^5.3.0" | ||
}, | ||
"main": "src/node-entry", | ||
"main": "build/webvr-polyfill.js", | ||
"keywords": [ | ||
@@ -27,6 +36,10 @@ "vr", | ||
"scripts": { | ||
"start": "npm run watch", | ||
"watch": "webpack-dev-server", | ||
"build": "webpack", | ||
"test": "mocha" | ||
"build": "rollup -c", | ||
"build-min": "rollup -c rollup.config.min.js", | ||
"build-all": "npm run build && npm run build-min", | ||
"watch": "rollup -c -w", | ||
"test": "mocha -r test/init.js --compilers js:babel-core/register test/*.test.js", | ||
"preversion": "npm test", | ||
"version": "npm run build-all && git add build/*", | ||
"postversion": "git push && git push --tags && npm publish" | ||
}, | ||
@@ -36,3 +49,6 @@ "repository": "googlevr/webvr-polyfill", | ||
"url": "https://github.com/googlevr/webvr-polyfill/issues" | ||
}, | ||
"dependencies": { | ||
"cardboard-vr-display": "1.0.2" | ||
} | ||
} | ||
} |
191
README.md
@@ -6,138 +6,111 @@ # WebVR Polyfill | ||
A JavaScript implementation of the [WebVR spec][spec]. This project ensures | ||
your WebVR content works on any platform, whether or not the browser/device has | ||
native WebVR support, or when there are inconsistencies in implementation. | ||
A JavaScript implementation of the [WebVR spec][spec]. This project lets you use | ||
WebVR today, without requiring a [special][moz] [browser][cr] build. It also | ||
lets you view the same content without requiring a virtual reality viewer. | ||
Take a look at [basic WebVR samples][samples] that use this polyfill. | ||
[moz]: http://mozvr.com/ | ||
[cr]: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ | ||
[samples]: https://webvr.info/samples/ | ||
[spec]: https://mozvr.github.io/webvr-spec/ | ||
## Installing | ||
## Implementation | ||
### Script | ||
The polyfill decides which VRDisplays to provide, depending on the configuration | ||
of your browser. Mobile devices provide the `CardboardVRDisplay`. Desktop devices | ||
use the `MouseKeyboardVRDisplay`. | ||
Download the build at [dist/webvr-polyfill.js](dist/webvr-polyfill.js) and include it as a script tag, | ||
or use a CDN. You can also use the minified file in the same location as `webvr-polyfill.min.js`. | ||
`CardboardVRDisplay` uses DeviceMotionEvents to implement a complementary | ||
filter which does [sensor fusion and pose prediction][fusion] to provide | ||
orientation tracking. It can also render in stereo mode, and includes mesh-based | ||
lens distortion. This display also includes user interface elements in VR mode | ||
to make the VR experience more intuitive, including: | ||
```html | ||
<script src='webvr-polyfill.js'></script> | ||
<!-- or use a link to a CDN --> | ||
<script src='https://cdn.jsdelivr.net/npm/webvr-polyfill@latest/build/webvr-polyfill.js'></script> | ||
``` | ||
- A gear icon to select your VR viewer. | ||
- A back button to exit VR mode. | ||
- An interstitial which only appears in portrait orientation, requesting you switch | ||
into landscape orientation (if [orientation lock][ol] is not available). | ||
### npm | ||
`MouseKeyboardVRDisplay` uses mouse events to allow you to do the equivalent of | ||
mouselook. It also uses keyboard arrows keys to look around the scene | ||
with the keyboard. | ||
If you're using a build tool like [browserify] or [webpack], install it via [npm]. | ||
[fusion]: http://smus.com/sensor-fusion-prediction-webvr/ | ||
[ol]: https://www.w3.org/TR/screen-orientation/ | ||
``` | ||
$ npm install --save webvr-polyfill | ||
``` | ||
## Using | ||
## Configuration | ||
Instructions for using versions `>=0.10.0`. For `<=0.9.x` versions, see [0.9.40 tag](https://github.com/googlevr/webvr-polyfill/tree/v0.9.40). | ||
The polyfill can be configured and debugged with various options. The following | ||
are supported: | ||
The webvr-polyfill exposes a single constructor, `WebVRPolyfill` that takes an | ||
object for configuration. See full configuration options at [src/config.js](src/config.js). | ||
```javascript | ||
WebVRConfig = { | ||
// Flag to disabled the UI in VR Mode. | ||
CARDBOARD_UI_DISABLED: false, // Default: false | ||
Be sure to instantiate the polyfill before calling any of your VR code! The | ||
polyfill needs to patch the API if it does not exist so your content code can | ||
assume that the WebVR API will just work. | ||
// Forces availability of VR mode, even for non-mobile devices. | ||
FORCE_ENABLE_VR: true, // Default: false. | ||
If using script tags, a `WebVRPolyfill` global constructor will exist. | ||
// Complementary filter coefficient. 0 for accelerometer, 1 for gyro. | ||
K_FILTER: 0.98, // Default: 0.98. | ||
```js | ||
var polyfill = new WebVRPolyfill(); | ||
``` | ||
// Flag to disable the instructions to rotate your device. | ||
ROTATE_INSTRUCTIONS_DISABLED: false, // Default: false. | ||
In a modular ES6 world, import and instantiate the constructor similarly. | ||
// How far into the future to predict during fast motion (in seconds). | ||
PREDICTION_TIME_S: 0.040, // Default: 0.040. | ||
```js | ||
import WebVRPolyfill from 'webvr-polyfill'; | ||
const polyfill = WebVRPolyfill(); | ||
``` | ||
// Flag to disable touch panner. In case you have your own touch controls. | ||
TOUCH_PANNER_DISABLED: false, // Default: true. | ||
## Goals | ||
// Enable yaw panning only, disabling roll and pitch. This can be useful | ||
// for panoramas with nothing interesting above or below. | ||
YAW_ONLY: true, // Default: false. | ||
The polyfill's goal is to provide a library so that developers can create | ||
content targeting the WebVR API without worrying about what browsers and devices | ||
their users have in a world of growing, [but fragmented](caniuse) support. | ||
// To disable keyboard and mouse controls, if you want to use your own | ||
// implementation. | ||
MOUSE_KEYBOARD_CONTROLS_DISABLED: true, // Default: false. | ||
The three main components of the polyfill are: | ||
// Prevent the polyfill from initializing immediately. Requires the app | ||
// to call InitializeWebVRPolyfill() before it can be used. | ||
DEFER_INITIALIZATION: true, // Default: false. | ||
* Injects a [WebVR 1.1](spec) JavaScript implementation if one does not exist | ||
* Patches browsers that have an incomplete or inconsistent implementation of the API | ||
* Provide a synthesized [CardboardVRDisplay] on mobile when WebVR is not supported, or if it does have native support but no native VRDisplays and `PROVIDE_MOBILE_VRDISPLAY` is true (default). | ||
// Enable the deprecated version of the API (navigator.getVRDevices). | ||
ENABLE_DEPRECATED_API: true, // Default: false. | ||
## Performance | ||
// Scales the recommended buffer size reported by WebVR, which can improve | ||
// performance. | ||
BUFFER_SCALE: 0.5, // Default: 0.5. | ||
Performance is critical for VR. If you find your application is too sluggish, | ||
consider tweaking some of the above parameters. In particular, keeping | ||
`BUFFER_SCALE` at 0.5 (the default) will likely help a lot. | ||
// Allow VRDisplay.submitFrame to change gl bindings, which is more | ||
// efficient if the application code will re-bind its resources on the | ||
// next frame anyway. This has been seen to cause rendering glitches with | ||
// THREE.js. | ||
// Dirty bindings include: gl.FRAMEBUFFER_BINDING, gl.CURRENT_PROGRAM, | ||
// gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING, | ||
// and gl.TEXTURE_BINDING_2D for texture unit 0. | ||
DIRTY_SUBMIT_FRAME_BINDINGS: true, // Default: false. | ||
## Developing | ||
// When set to true, this will cause a polyfilled VRDisplay to always be | ||
// appended to the list returned by navigator.getVRDisplays(), even if that | ||
// list includes a native VRDisplay. | ||
ALWAYS_APPEND_POLYFILL_DISPLAY: false, | ||
If you're interested in developing and contributing on the polyfill itself, you'll need to | ||
have [npm] installed and familiarize yourself with some commands below. For full list | ||
of commands available, see `package.json` scripts. | ||
// There are versions of Chrome (M58-M60?) where the native WebVR API exists, | ||
// and instead of returning 0 VR displays when none are detected, | ||
// `navigator.getVRDisplays()`'s promise never resolves. This results | ||
// in the polyfill hanging and not being able to provide fallback | ||
// displays, so set a timeout in milliseconds to stop waiting for a response | ||
// and just use polyfilled displays. | ||
// https://bugs.chromium.org/p/chromium/issues/detail?id=727969 | ||
GET_VR_DISPLAYS_TIMEOUT: 1000, | ||
} | ||
``` | ||
$ git clone git@github.com:googlevr/webvr-polyfill.git | ||
$ cd webvr-polyfill/ | ||
## Performance | ||
# Install dependencies | ||
$ npm install | ||
Performance is critical for VR. If you find your application is too sluggish, | ||
consider tweaking some of the above parameters. In particular, keeping | ||
`BUFFER_SCALE` at 0.5 (the default) will likely help a lot. | ||
# Build uncompressed JS file | ||
$ npm run build | ||
## WebVR 1.1 Shim | ||
# Run tests | ||
$ npm test | ||
The polyfill exposes a helper method `WebVRPolyfill.InstallWebVRSpecShim` which | ||
installs a shim that updates a WebVR 1.0 spec implementation to WebVR 1.1. | ||
# Watch src/* directory and auto-rebuild on changes | ||
$ npm watch | ||
``` | ||
## Development | ||
### Testing | ||
If you'd like to contribute to the `webvr-poyfill` library, check out | ||
the repository and install | ||
[Node](https://nodejs.org/en/download/package-manager/) and the dependencies: | ||
Right now there are some unit tests in the configuration and logic for how things get polyfilled. | ||
Be sure to run tests before submitting any PRs, and bonus points for having new tests! | ||
```bash | ||
git clone https://github.com/googlevr/webvr-polyfill | ||
cd webvr-polyfill | ||
npm install | ||
``` | ||
$ npm test | ||
``` | ||
### Development Commands | ||
Due to the nature of the polyfill, be also sure to test the examples with your changes where appropriate. | ||
* `npm install`: installs the dependencies. | ||
* `npm start`: auto-builds the module whenever any source changes and serves the example | ||
content on `http://0.0.0.0:8080/`. | ||
* `npm run build`: builds the module. | ||
### Releasing a new version | ||
For maintainers only, to cut a new release for npm, use the [npm version] command. The `preversion`, `version` and `postversion` npm scripts will run tests, build, add built files and tag to git, push to github, and publish the new npm version. | ||
`npm version <semverstring>` | ||
## License | ||
@@ -148,16 +121,8 @@ | ||
## Thanks | ||
- [Brandon Jones][bj] and [Vladimir Vukicevic][vv] for their work on the [WebVR | ||
spec][spec]. | ||
- [Ricardo Cabello][doob] for THREE.js. | ||
- [Diego Marcos][dm] for VREffect and VRControls. | ||
- [Dmitriy Kovalev][dk] for help with lens distortion correction. | ||
[dk]: https://github.com/dmitriykovalev/ | ||
[bj]: https://twitter.com/tojiro | ||
[vv]: https://twitter.com/vvuk | ||
[spec]: https://mozvr.github.io/webvr-spec/ | ||
[dm]: https://twitter.com/dmarcos | ||
[doob]: https://twitter.com/mrdoob | ||
[samples]: https://webvr.info/samples/ | ||
[npm]: https://www.npmjs.com | ||
[browserify]: http://browserify.org/ | ||
[webpack]: https://webpack.github.io/ | ||
[caniuse]: https://caniuse.com/#search=webvr | ||
[spec]: https://immersive-web.github.io/webvr/spec/1.1 | ||
[CardboardVRDisplay]: https://github.com/googlevr/cardboard-vr-display |
428
src/util.js
@@ -18,9 +18,2 @@ /* | ||
Util.MIN_TIMESTEP = 0.001; | ||
Util.MAX_TIMESTEP = 1; | ||
Util.base64 = function(mimeType, base64) { | ||
return 'data:' + mimeType + ';base64,' + base64; | ||
}; | ||
Util.clamp = function(value, min, max) { | ||
@@ -30,6 +23,2 @@ return Math.min(Math.max(min, value), max); | ||
Util.lerp = function(a, b, t) { | ||
return a + ((b - a) * t); | ||
}; | ||
/** | ||
@@ -54,171 +43,11 @@ * Light polyfill for `Promise.race`. Returns | ||
Util.isIOS = (function() { | ||
var isIOS = /iPad|iPhone|iPod/.test(navigator.platform); | ||
return function() { | ||
return isIOS; | ||
}; | ||
})(); | ||
Util.isWebViewAndroid = (function() { | ||
var isWebViewAndroid = navigator.userAgent.indexOf('Version') !== -1 && | ||
navigator.userAgent.indexOf('Android') !== -1 && | ||
navigator.userAgent.indexOf('Chrome') !== -1; | ||
return function() { | ||
return isWebViewAndroid; | ||
}; | ||
})(); | ||
Util.isSafari = (function() { | ||
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); | ||
return function() { | ||
return isSafari; | ||
}; | ||
})(); | ||
Util.isFirefoxAndroid = (function() { | ||
var isFirefoxAndroid = navigator.userAgent.indexOf('Firefox') !== -1 && | ||
navigator.userAgent.indexOf('Android') !== -1; | ||
return function() { | ||
return isFirefoxAndroid; | ||
}; | ||
})(); | ||
Util.isR7 = (function() { | ||
var isR7 = navigator.userAgent.indexOf('R7 Build') !== -1; | ||
return function() { | ||
return isR7; | ||
}; | ||
})(); | ||
Util.isLandscapeMode = function() { | ||
var rtn = (window.orientation == 90 || window.orientation == -90); | ||
return Util.isR7() ? !rtn : rtn; | ||
Util.isIOS = function() { | ||
return /iPad|iPhone|iPod/.test(navigator.platform); | ||
}; | ||
// Helper method to validate the time steps of sensor timestamps. | ||
Util.isTimestampDeltaValid = function(timestampDeltaS) { | ||
if (isNaN(timestampDeltaS)) { | ||
return false; | ||
} | ||
if (timestampDeltaS <= Util.MIN_TIMESTEP) { | ||
return false; | ||
} | ||
if (timestampDeltaS > Util.MAX_TIMESTEP) { | ||
return false; | ||
} | ||
return true; | ||
Util.isMobile = function() { | ||
return /Android/i.test(navigator.userAgent) || | ||
/iPhone|iPad|iPod/i.test(navigator.userAgent); | ||
}; | ||
Util.getScreenWidth = function() { | ||
return Math.max(window.screen.width, window.screen.height) * | ||
window.devicePixelRatio; | ||
}; | ||
Util.getScreenHeight = function() { | ||
return Math.min(window.screen.width, window.screen.height) * | ||
window.devicePixelRatio; | ||
}; | ||
Util.requestFullscreen = function(element) { | ||
if (Util.isWebViewAndroid()) { | ||
return false; | ||
} | ||
if (element.requestFullscreen) { | ||
element.requestFullscreen(); | ||
} else if (element.webkitRequestFullscreen) { | ||
element.webkitRequestFullscreen(); | ||
} else if (element.mozRequestFullScreen) { | ||
element.mozRequestFullScreen(); | ||
} else if (element.msRequestFullscreen) { | ||
element.msRequestFullscreen(); | ||
} else { | ||
return false; | ||
} | ||
return true; | ||
}; | ||
Util.exitFullscreen = function() { | ||
if (document.exitFullscreen) { | ||
document.exitFullscreen(); | ||
} else if (document.webkitExitFullscreen) { | ||
document.webkitExitFullscreen(); | ||
} else if (document.mozCancelFullScreen) { | ||
document.mozCancelFullScreen(); | ||
} else if (document.msExitFullscreen) { | ||
document.msExitFullscreen(); | ||
} else { | ||
return false; | ||
} | ||
return true; | ||
}; | ||
Util.getFullscreenElement = function() { | ||
return document.fullscreenElement || | ||
document.webkitFullscreenElement || | ||
document.mozFullScreenElement || | ||
document.msFullscreenElement; | ||
}; | ||
Util.linkProgram = function(gl, vertexSource, fragmentSource, attribLocationMap) { | ||
// No error checking for brevity. | ||
var vertexShader = gl.createShader(gl.VERTEX_SHADER); | ||
gl.shaderSource(vertexShader, vertexSource); | ||
gl.compileShader(vertexShader); | ||
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); | ||
gl.shaderSource(fragmentShader, fragmentSource); | ||
gl.compileShader(fragmentShader); | ||
var program = gl.createProgram(); | ||
gl.attachShader(program, vertexShader); | ||
gl.attachShader(program, fragmentShader); | ||
for (var attribName in attribLocationMap) | ||
gl.bindAttribLocation(program, attribLocationMap[attribName], attribName); | ||
gl.linkProgram(program); | ||
gl.deleteShader(vertexShader); | ||
gl.deleteShader(fragmentShader); | ||
return program; | ||
}; | ||
Util.getProgramUniforms = function(gl, program) { | ||
var uniforms = {}; | ||
var uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); | ||
var uniformName = ''; | ||
for (var i = 0; i < uniformCount; i++) { | ||
var uniformInfo = gl.getActiveUniform(program, i); | ||
uniformName = uniformInfo.name.replace('[0]', ''); | ||
uniforms[uniformName] = gl.getUniformLocation(program, uniformName); | ||
} | ||
return uniforms; | ||
}; | ||
Util.orthoMatrix = function (out, left, right, bottom, top, near, far) { | ||
var lr = 1 / (left - right), | ||
bt = 1 / (bottom - top), | ||
nf = 1 / (near - far); | ||
out[0] = -2 * lr; | ||
out[1] = 0; | ||
out[2] = 0; | ||
out[3] = 0; | ||
out[4] = 0; | ||
out[5] = -2 * bt; | ||
out[6] = 0; | ||
out[7] = 0; | ||
out[8] = 0; | ||
out[9] = 0; | ||
out[10] = 2 * nf; | ||
out[11] = 0; | ||
out[12] = (left + right) * lr; | ||
out[13] = (top + bottom) * bt; | ||
out[14] = (far + near) * nf; | ||
out[15] = 1; | ||
return out; | ||
}; | ||
Util.copyArray = function (source, dest) { | ||
@@ -230,8 +59,2 @@ for (var i = 0, n = source.length; i < n; i++) { | ||
Util.isMobile = function() { | ||
var check = false; | ||
(function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); | ||
return check; | ||
}; | ||
Util.extend = function(dest, src) { | ||
@@ -247,240 +70,9 @@ for (var key in src) { | ||
Util.safariCssSizeWorkaround = function(canvas) { | ||
// TODO(smus): Remove this workaround when Safari for iOS is fixed. | ||
// iOS only workaround (for https://bugs.webkit.org/show_bug.cgi?id=152556). | ||
// | ||
// "To the last I grapple with thee; | ||
// from hell's heart I stab at thee; | ||
// for hate's sake I spit my last breath at thee." | ||
// -- Moby Dick, by Herman Melville | ||
if (Util.isIOS()) { | ||
var width = canvas.style.width; | ||
var height = canvas.style.height; | ||
canvas.style.width = (parseInt(width) + 1) + 'px'; | ||
canvas.style.height = (parseInt(height)) + 'px'; | ||
setTimeout(function() { | ||
canvas.style.width = width; | ||
canvas.style.height = height; | ||
}, 100); | ||
} | ||
// Debug only. | ||
window.Util = Util; | ||
window.canvas = canvas; | ||
Util.isFullScreenAvailable = function() { | ||
return (document.fullscreenEnabled || | ||
document.mozFullScreenEnabled || | ||
document.webkitFullscreenEnabled || | ||
false); | ||
}; | ||
Util.isDebug = function() { | ||
return Util.getQueryParameter('debug'); | ||
}; | ||
Util.getQueryParameter = function(name) { | ||
var name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); | ||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), | ||
results = regex.exec(location.search); | ||
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); | ||
}; | ||
Util.frameDataFromPose = (function() { | ||
var piOver180 = Math.PI / 180.0; | ||
var rad45 = Math.PI * 0.25; | ||
// Borrowed from glMatrix. | ||
function mat4_perspectiveFromFieldOfView(out, fov, near, far) { | ||
var upTan = Math.tan(fov ? (fov.upDegrees * piOver180) : rad45), | ||
downTan = Math.tan(fov ? (fov.downDegrees * piOver180) : rad45), | ||
leftTan = Math.tan(fov ? (fov.leftDegrees * piOver180) : rad45), | ||
rightTan = Math.tan(fov ? (fov.rightDegrees * piOver180) : rad45), | ||
xScale = 2.0 / (leftTan + rightTan), | ||
yScale = 2.0 / (upTan + downTan); | ||
out[0] = xScale; | ||
out[1] = 0.0; | ||
out[2] = 0.0; | ||
out[3] = 0.0; | ||
out[4] = 0.0; | ||
out[5] = yScale; | ||
out[6] = 0.0; | ||
out[7] = 0.0; | ||
out[8] = -((leftTan - rightTan) * xScale * 0.5); | ||
out[9] = ((upTan - downTan) * yScale * 0.5); | ||
out[10] = far / (near - far); | ||
out[11] = -1.0; | ||
out[12] = 0.0; | ||
out[13] = 0.0; | ||
out[14] = (far * near) / (near - far); | ||
out[15] = 0.0; | ||
return out; | ||
} | ||
function mat4_fromRotationTranslation(out, q, v) { | ||
// Quaternion math | ||
var x = q[0], y = q[1], z = q[2], w = q[3], | ||
x2 = x + x, | ||
y2 = y + y, | ||
z2 = z + z, | ||
xx = x * x2, | ||
xy = x * y2, | ||
xz = x * z2, | ||
yy = y * y2, | ||
yz = y * z2, | ||
zz = z * z2, | ||
wx = w * x2, | ||
wy = w * y2, | ||
wz = w * z2; | ||
out[0] = 1 - (yy + zz); | ||
out[1] = xy + wz; | ||
out[2] = xz - wy; | ||
out[3] = 0; | ||
out[4] = xy - wz; | ||
out[5] = 1 - (xx + zz); | ||
out[6] = yz + wx; | ||
out[7] = 0; | ||
out[8] = xz + wy; | ||
out[9] = yz - wx; | ||
out[10] = 1 - (xx + yy); | ||
out[11] = 0; | ||
out[12] = v[0]; | ||
out[13] = v[1]; | ||
out[14] = v[2]; | ||
out[15] = 1; | ||
return out; | ||
}; | ||
function mat4_translate(out, a, v) { | ||
var x = v[0], y = v[1], z = v[2], | ||
a00, a01, a02, a03, | ||
a10, a11, a12, a13, | ||
a20, a21, a22, a23; | ||
if (a === out) { | ||
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12]; | ||
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13]; | ||
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14]; | ||
out[15] = a[3] * x + a[7] * y + a[11] * z + a[15]; | ||
} else { | ||
a00 = a[0]; a01 = a[1]; a02 = a[2]; a03 = a[3]; | ||
a10 = a[4]; a11 = a[5]; a12 = a[6]; a13 = a[7]; | ||
a20 = a[8]; a21 = a[9]; a22 = a[10]; a23 = a[11]; | ||
out[0] = a00; out[1] = a01; out[2] = a02; out[3] = a03; | ||
out[4] = a10; out[5] = a11; out[6] = a12; out[7] = a13; | ||
out[8] = a20; out[9] = a21; out[10] = a22; out[11] = a23; | ||
out[12] = a00 * x + a10 * y + a20 * z + a[12]; | ||
out[13] = a01 * x + a11 * y + a21 * z + a[13]; | ||
out[14] = a02 * x + a12 * y + a22 * z + a[14]; | ||
out[15] = a03 * x + a13 * y + a23 * z + a[15]; | ||
} | ||
return out; | ||
}; | ||
function mat4_invert(out, a) { | ||
var a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3], | ||
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7], | ||
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11], | ||
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15], | ||
b00 = a00 * a11 - a01 * a10, | ||
b01 = a00 * a12 - a02 * a10, | ||
b02 = a00 * a13 - a03 * a10, | ||
b03 = a01 * a12 - a02 * a11, | ||
b04 = a01 * a13 - a03 * a11, | ||
b05 = a02 * a13 - a03 * a12, | ||
b06 = a20 * a31 - a21 * a30, | ||
b07 = a20 * a32 - a22 * a30, | ||
b08 = a20 * a33 - a23 * a30, | ||
b09 = a21 * a32 - a22 * a31, | ||
b10 = a21 * a33 - a23 * a31, | ||
b11 = a22 * a33 - a23 * a32, | ||
// Calculate the determinant | ||
det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; | ||
if (!det) { | ||
return null; | ||
} | ||
det = 1.0 / det; | ||
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; | ||
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; | ||
out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; | ||
out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; | ||
out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; | ||
out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; | ||
out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; | ||
out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; | ||
out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; | ||
out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; | ||
out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; | ||
out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; | ||
out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; | ||
out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; | ||
out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; | ||
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; | ||
return out; | ||
}; | ||
var defaultOrientation = new Float32Array([0, 0, 0, 1]); | ||
var defaultPosition = new Float32Array([0, 0, 0]); | ||
function updateEyeMatrices(projection, view, pose, parameters, vrDisplay) { | ||
mat4_perspectiveFromFieldOfView(projection, parameters ? parameters.fieldOfView : null, vrDisplay.depthNear, vrDisplay.depthFar); | ||
var orientation = pose.orientation || defaultOrientation; | ||
var position = pose.position || defaultPosition; | ||
mat4_fromRotationTranslation(view, orientation, position); | ||
if (parameters) | ||
mat4_translate(view, view, parameters.offset); | ||
mat4_invert(view, view); | ||
} | ||
return function(frameData, pose, vrDisplay) { | ||
if (!frameData || !pose) | ||
return false; | ||
frameData.pose = pose; | ||
frameData.timestamp = pose.timestamp; | ||
updateEyeMatrices( | ||
frameData.leftProjectionMatrix, frameData.leftViewMatrix, | ||
pose, vrDisplay.getEyeParameters("left"), vrDisplay); | ||
updateEyeMatrices( | ||
frameData.rightProjectionMatrix, frameData.rightViewMatrix, | ||
pose, vrDisplay.getEyeParameters("right"), vrDisplay); | ||
return true; | ||
}; | ||
})(); | ||
Util.isInsideCrossDomainIFrame = function() { | ||
var isFramed = (window.self !== window.top); | ||
var refDomain = Util.getDomainFromUrl(document.referrer); | ||
var thisDomain = Util.getDomainFromUrl(window.location.href); | ||
return isFramed && (refDomain !== thisDomain); | ||
}; | ||
// From http://stackoverflow.com/a/23945027. | ||
Util.getDomainFromUrl = function(url) { | ||
var domain; | ||
// Find & remove protocol (http, ftp, etc.) and get domain. | ||
if (url.indexOf("://") > -1) { | ||
domain = url.split('/')[2]; | ||
} | ||
else { | ||
domain = url.split('/')[0]; | ||
} | ||
//find & remove port number | ||
domain = domain.split(':')[0]; | ||
return domain; | ||
} | ||
module.exports = Util; |
@@ -17,102 +17,75 @@ /* | ||
var Util = require('./util.js'); | ||
var CardboardVRDisplay = require('./cardboard-vr-display.js'); | ||
var MouseKeyboardVRDisplay = require('./mouse-keyboard-vr-display.js'); | ||
// Uncomment to add positional tracking via webcam. | ||
//var WebcamPositionSensorVRDevice = require('./webcam-position-sensor-vr-device.js'); | ||
var VRDisplay = require('./base.js').VRDisplay; | ||
var VRFrameData = require('./base.js').VRFrameData; | ||
var HMDVRDevice = require('./base.js').HMDVRDevice; | ||
var PositionSensorVRDevice = require('./base.js').PositionSensorVRDevice; | ||
var VRDisplayHMDDevice = require('./display-wrappers.js').VRDisplayHMDDevice; | ||
var VRDisplayPositionSensorDevice = require('./display-wrappers.js').VRDisplayPositionSensorDevice; | ||
var CardboardVRDisplay = require('cardboard-vr-display'); | ||
var VRDisplay = require('cardboard-vr-display/src/base.js').VRDisplay; | ||
var VRFrameData = require('cardboard-vr-display/src/base.js').VRFrameData; | ||
var version = require('../package.json').version; | ||
var DefaultConfig = require('./config'); | ||
function WebVRPolyfill() { | ||
this.displays = []; | ||
this.devices = []; // For deprecated objects | ||
this.devicesPopulated = false; | ||
this.nativeWebVRAvailable = this.isWebVRAvailable(); | ||
this.nativeLegacyWebVRAvailable = this.isDeprecatedWebVRAvailable(); | ||
this.nativeGetVRDisplaysFunc = this.nativeWebVRAvailable ? | ||
navigator.getVRDisplays : | ||
null; | ||
function WebVRPolyfill(config) { | ||
this.config = Util.extend(Util.extend({}, DefaultConfig), config); | ||
this.polyfillDisplays = []; | ||
this.enabled = false; | ||
if (!this.nativeLegacyWebVRAvailable && !this.nativeWebVRAvailable) { | ||
this.enablePolyfill(); | ||
if (window.WebVRConfig.ENABLE_DEPRECATED_API) { | ||
this.enableDeprecatedPolyfill(); | ||
} | ||
// Must handle this in constructor before we start | ||
// destructively polyfilling `navigator` | ||
this.hasNative = 'getVRDisplays' in navigator; | ||
// Store initial references to native constructors | ||
// and functions | ||
this.native = {}; | ||
this.native.getVRDisplays = navigator.getVRDisplays; | ||
this.native.VRFrameData = window.VRFrameData; | ||
this.native.VRDisplay = window.VRDisplay; | ||
// If we don't have native 1.1 support, or if we want to provide | ||
// a CardboardVRDisplay in the event of native support with no displays, | ||
// inject our own polyfill | ||
if (!this.hasNative || this.config.PROVIDE_MOBILE_VRDISPLAY && Util.isMobile()) { | ||
this.enable(); | ||
} | ||
// Put a shim in place to update the API to 1.1 if needed. | ||
InstallWebVRSpecShim(); | ||
} | ||
WebVRPolyfill.prototype.isWebVRAvailable = function() { | ||
return ('getVRDisplays' in navigator); | ||
}; | ||
WebVRPolyfill.prototype.isDeprecatedWebVRAvailable = function() { | ||
return ('getVRDevices' in navigator) || ('mozGetVRDevices' in navigator); | ||
}; | ||
WebVRPolyfill.prototype.connectDisplay = function(vrDisplay) { | ||
vrDisplay.fireVRDisplayConnect_(); | ||
this.displays.push(vrDisplay); | ||
}; | ||
WebVRPolyfill.prototype.populateDevices = function() { | ||
if (this.devicesPopulated) { | ||
return; | ||
WebVRPolyfill.prototype.getPolyfillDisplays = function() { | ||
if (this._polyfillDisplaysPopulated) { | ||
return this.polyfillDisplays; | ||
} | ||
// Initialize our virtual VR devices. | ||
var vrDisplay = null; | ||
// Add a Cardboard VRDisplay on compatible mobile devices | ||
if (this.isCardboardCompatible()) { | ||
vrDisplay = new CardboardVRDisplay(); | ||
if (Util.isMobile()) { | ||
var vrDisplay = new CardboardVRDisplay({ | ||
DEBUG: this.config.DEBUG, | ||
DPDB_URL: this.config.DPDB_URL, | ||
CARDBOARD_UI_DISABLED: this.config.CARDBOARD_UI_DISABLED, | ||
K_FILTER: this.config.K_FILTER, | ||
PREDICTION_TIME_S: this.config.PREDICTION_TIME_S, | ||
TOUCH_PANNER_DISABLED: this.config.TOUCH_PANNER_DISABLED, | ||
ROTATE_INSTRUCTIONS_DISABLED: this.config.ROTATE_INSTRUCTIONS_DISABLED, | ||
YAW_ONLY: this.config.YAW_ONLY, | ||
BUFFER_SCALE: this.config.BUFFER_SCALE, | ||
DIRTY_SUBMIT_FRAME_BINDINGS: this.config.DIRTY_SUBMIT_FRAME_BINDINGS, | ||
}); | ||
this.connectDisplay(vrDisplay); | ||
// For backwards compatibility | ||
if (window.WebVRConfig.ENABLE_DEPRECATED_API) { | ||
this.devices.push(new VRDisplayHMDDevice(vrDisplay)); | ||
this.devices.push(new VRDisplayPositionSensorDevice(vrDisplay)); | ||
} | ||
vrDisplay.fireVRDisplayConnect_(); | ||
this.polyfillDisplays.push(vrDisplay); | ||
} | ||
// Add a Mouse and Keyboard driven VRDisplay for desktops/laptops | ||
if (!this.isMobile() && !window.WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED) { | ||
vrDisplay = new MouseKeyboardVRDisplay(); | ||
this.connectDisplay(vrDisplay); | ||
// For backwards compatibility | ||
if (window.WebVRConfig.ENABLE_DEPRECATED_API) { | ||
this.devices.push(new VRDisplayHMDDevice(vrDisplay)); | ||
this.devices.push(new VRDisplayPositionSensorDevice(vrDisplay)); | ||
} | ||
} | ||
// Uncomment to add positional tracking via webcam. | ||
//if (!this.isMobile() && window.WebVRConfig.ENABLE_DEPRECATED_API) { | ||
// positionDevice = new WebcamPositionSensorVRDevice(); | ||
// this.devices.push(positionDevice); | ||
//} | ||
this.devicesPopulated = true; | ||
this._polyfillDisplaysPopulated = true; | ||
return this.polyfillDisplays; | ||
}; | ||
WebVRPolyfill.prototype.enablePolyfill = function() { | ||
// Provide navigator.getVRDisplays. | ||
navigator.getVRDisplays = this.getVRDisplays.bind(this); | ||
WebVRPolyfill.prototype.enable = function() { | ||
this.enabled = true; | ||
// Polyfill native VRDisplay.getFrameData | ||
if (this.nativeWebVRAvailable && window.VRFrameData) { | ||
var NativeVRFrameData = window.VRFrameData; | ||
var nativeFrameData = new window.VRFrameData(); | ||
var nativeGetFrameData = window.VRDisplay.prototype.getFrameData; | ||
window.VRFrameData = VRFrameData; | ||
// Polyfill native VRDisplay.getFrameData when the platform | ||
// has native WebVR support, but for use with a polyfilled | ||
// CardboardVRDisplay | ||
if (this.hasNative && this.native.VRFrameData) { | ||
var NativeVRFrameData = this.native.VRFrameData; | ||
var nativeFrameData = new this.native.VRFrameData(); | ||
var nativeGetFrameData = this.native.VRDisplay.prototype.getFrameData; | ||
// When using a native display with a polyfilled VRFrameData | ||
window.VRDisplay.prototype.getFrameData = function(frameData) { | ||
// This should only be called in the event of code instantiating | ||
// `window.VRFrameData` before the polyfill kicks in, which is | ||
// unrecommended, but happens anyway | ||
if (frameData instanceof NativeVRFrameData) { | ||
@@ -137,36 +110,18 @@ nativeGetFrameData.call(this, frameData); | ||
// Provide navigator.getVRDisplays. | ||
navigator.getVRDisplays = this.getVRDisplays.bind(this); | ||
// Provide the `VRDisplay` object. | ||
window.VRDisplay = VRDisplay; | ||
// Provide the `navigator.vrEnabled` property. | ||
if (navigator && typeof navigator.vrEnabled === 'undefined') { | ||
var self = this; | ||
Object.defineProperty(navigator, 'vrEnabled', { | ||
get: function () { | ||
return self.isCardboardCompatible() && | ||
(self.isFullScreenAvailable() || Util.isIOS()); | ||
} | ||
}); | ||
} | ||
if (!('VRFrameData' in window)) { | ||
// Provide the VRFrameData object. | ||
window.VRFrameData = VRFrameData; | ||
} | ||
// Provide the VRFrameData object. | ||
window.VRFrameData = VRFrameData; | ||
}; | ||
WebVRPolyfill.prototype.enableDeprecatedPolyfill = function() { | ||
// Provide navigator.getVRDevices. | ||
navigator.getVRDevices = this.getVRDevices.bind(this); | ||
// Provide the CardboardHMDVRDevice and PositionSensorVRDevice objects. | ||
window.HMDVRDevice = HMDVRDevice; | ||
window.PositionSensorVRDevice = PositionSensorVRDevice; | ||
}; | ||
WebVRPolyfill.prototype.getVRDisplays = function() { | ||
this.populateDevices(); | ||
var polyfillDisplays = this.displays; | ||
this.getPolyfillDisplays(); | ||
var polyfillDisplays = this.polyfillDisplays; | ||
var config = this.config; | ||
if (!this.nativeWebVRAvailable) { | ||
if (!this.hasNative) { | ||
return Promise.resolve(polyfillDisplays); | ||
@@ -178,3 +133,3 @@ } | ||
var timeoutId; | ||
var vrDisplaysNative = this.nativeGetVRDisplaysFunc.call(navigator); | ||
var vrDisplaysNative = this.native.getVRDisplays.call(navigator); | ||
var timeoutPromise = new Promise(function(resolve) { | ||
@@ -184,3 +139,3 @@ timeoutId = setTimeout(function() { | ||
resolve([]); | ||
}, window.WebVRConfig.GET_VR_DISPLAYS_TIMEOUT); | ||
}, config.GET_VR_DISPLAYS_TIMEOUT); | ||
}); | ||
@@ -193,99 +148,8 @@ | ||
clearTimeout(timeoutId); | ||
if (window.WebVRConfig.ALWAYS_APPEND_POLYFILL_DISPLAY) { | ||
return nativeDisplays.concat(polyfillDisplays); | ||
} else { | ||
return nativeDisplays.length > 0 ? nativeDisplays : polyfillDisplays; | ||
} | ||
return nativeDisplays.length > 0 ? nativeDisplays : polyfillDisplays; | ||
}); | ||
}; | ||
WebVRPolyfill.prototype.getVRDevices = function() { | ||
console.warn('getVRDevices is deprecated. Please update your code to use getVRDisplays instead.'); | ||
var self = this; | ||
return new Promise(function(resolve, reject) { | ||
try { | ||
if (!self.devicesPopulated) { | ||
if (self.nativeWebVRAvailable) { | ||
return navigator.getVRDisplays(function(displays) { | ||
for (var i = 0; i < displays.length; ++i) { | ||
self.devices.push(new VRDisplayHMDDevice(displays[i])); | ||
self.devices.push(new VRDisplayPositionSensorDevice(displays[i])); | ||
} | ||
self.devicesPopulated = true; | ||
resolve(self.devices); | ||
}, reject); | ||
} | ||
if (self.nativeLegacyWebVRAvailable) { | ||
return (navigator.getVRDDevices || navigator.mozGetVRDevices)(function(devices) { | ||
for (var i = 0; i < devices.length; ++i) { | ||
if (devices[i] instanceof HMDVRDevice) { | ||
self.devices.push(devices[i]); | ||
} | ||
if (devices[i] instanceof PositionSensorVRDevice) { | ||
self.devices.push(devices[i]); | ||
} | ||
} | ||
self.devicesPopulated = true; | ||
resolve(self.devices); | ||
}, reject); | ||
} | ||
} | ||
self.populateDevices(); | ||
resolve(self.devices); | ||
} catch (e) { | ||
reject(e); | ||
} | ||
}); | ||
}; | ||
WebVRPolyfill.prototype.NativeVRFrameData = window.VRFrameData; | ||
/** | ||
* Determine if a device is mobile. | ||
*/ | ||
WebVRPolyfill.prototype.isMobile = function() { | ||
return /Android/i.test(navigator.userAgent) || | ||
/iPhone|iPad|iPod/i.test(navigator.userAgent); | ||
}; | ||
WebVRPolyfill.prototype.isCardboardCompatible = function() { | ||
// For now, support all iOS and Android devices. | ||
// Also enable the WebVRConfig.FORCE_VR flag for debugging. | ||
return this.isMobile() || window.WebVRConfig.FORCE_ENABLE_VR; | ||
}; | ||
WebVRPolyfill.prototype.isFullScreenAvailable = function() { | ||
return (document.fullscreenEnabled || | ||
document.mozFullScreenEnabled || | ||
document.webkitFullscreenEnabled || | ||
false); | ||
}; | ||
// Installs a shim that updates a WebVR 1.0 spec implementation to WebVR 1.1 | ||
function InstallWebVRSpecShim() { | ||
if ('VRDisplay' in window && !('VRFrameData' in window)) { | ||
// Provide the VRFrameData object. | ||
window.VRFrameData = VRFrameData; | ||
// A lot of Chrome builds don't have depthNear and depthFar, even | ||
// though they're in the WebVR 1.0 spec. Patch them in if they're not present. | ||
if(!('depthNear' in window.VRDisplay.prototype)) { | ||
window.VRDisplay.prototype.depthNear = 0.01; | ||
} | ||
if(!('depthFar' in window.VRDisplay.prototype)) { | ||
window.VRDisplay.prototype.depthFar = 10000.0; | ||
} | ||
window.VRDisplay.prototype.getFrameData = function(frameData) { | ||
return Util.frameDataFromPose(frameData, this.getPose(), this); | ||
} | ||
} | ||
}; | ||
WebVRPolyfill.InstallWebVRSpecShim = InstallWebVRSpecShim; | ||
WebVRPolyfill.version = version; | ||
module.exports.WebVRPolyfill = WebVRPolyfill; | ||
module.exports = WebVRPolyfill; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
3
637396
1
15
28
4869
127
+ Addedcardboard-vr-display@1.0.2
+ Addedcardboard-vr-display@1.0.2(transitive)
+ Addedgl-preserve-state@1.0.0(transitive)
+ Addedwebvr-polyfill-dpdb@1.0.18(transitive)