Source: vr/vr-manager.js

Object.assign(pc, function () {
    /**
     * @constructor
     * @name pc.VrManager
     * @classdesc Manage and update {@link pc.VrDisplay}s that are attached to this device.
     * @description Manage and update {@link pc.VrDisplay}s that are attached to this device.
     * @param {pc.Application} app The main application
     * @property {pc.VrDisplay[]} displays The list of {@link pc.VrDisplay}s that are attached to this device
     * @property {pc.VrDisplay} display The default {@link pc.VrDisplay} to be used. Usually the first in the `displays` list
     * @property {Boolean} isSupported Reports whether this device supports the WebVR API
     * @property {Boolean} usesPolyfill Reports whether this device supports the WebVR API using a polyfill
     */
    var VrManager = function (app) {
        pc.events.attach(this);

        var self = this;

        this.isSupported = VrManager.isSupported;
        this.usesPolyfill = VrManager.usesPolyfill;

        // if required initialize webvr polyfill
        if (window.InitializeWebVRPolyfill)
            window.InitializeWebVRPolyfill();

        this._index = { };
        this.displays = [];
        this.display = null; // primary display (usually the first in list)

        this._app = app;

        // bind functions for event callbacks
        this._onDisplayConnect = this._onDisplayConnect.bind(this);
        this._onDisplayDisconnect = this._onDisplayDisconnect.bind(this);

        self._attach();

        this._getDisplays(function (err, displays) {
            if (err) {
                // webvr not available
                self.fire('error', err);
            } else {
                for (var i = 0; i < displays.length; i++) {
                    self._addDisplay(displays[i]);
                }

                self.fire('ready', self.displays);
            }
        });
    };

    /**
     * @event
     * @name pc.VrManager#displayconnect
     * @description Fired when an VR display is connected
     * @param {pc.VrDisplay} display The {@link pc.VrDisplay} that has just been connected
     * @example
     * this.app.vr.on("displayconnect", function (display) {
     *     // use `display` here
     * });
     */

    /**
     * @event
     * @name pc.VrManager#displaydisconnect
     * @description Fired when an VR display is disconnected
     * @param {pc.VrDisplay} display The {@link pc.VrDisplay} that has just been disconnected
     * @example
     * this.app.vr.on("displaydisconnect", function (display) {
     *     // `display` is no longer connected
     * });
     */

    /**
     * @static
     * @name pc.VrManager.isSupported
     * @type Boolean
     * @description Reports whether this device supports the WebVR API
     */
    VrManager.isSupported = !!navigator.getVRDisplays;

    /**
     * @static
     * @name pc.VrManager.usesPolyfill
     * @type Boolean
     * @description Reports whether this device supports the WebVR API using a polyfill
     */
    VrManager.usesPolyfill = !!window.InitializeWebVRPolyfill;

    Object.assign(VrManager.prototype, {
        _attach: function () {
            window.addEventListener('vrdisplayconnect', this._onDisplayConnect);
            window.addEventListener('vrdisplaydisconnect', this._onDisplayDisconnect);
        },

        _detach: function () {
            window.removeEventListener('vrdisplayconnect', this._onDisplayConnect);
            window.removeEventListener('vrdisplaydisconnect', this._onDisplayDisconnect);
        },

        /**
         * @function
         * @name pc.VrManager#destroy
         * @description Remove events and clear up manager
         */
        destroy: function () {
            this._detach();
        },

        /**
         * @function
         * @name pc.VrManager#poll
         * @description Called once per frame to poll all attached displays
         */
        poll: function () {
            var l = this.displays.length;
            if (!l) return;
            for (var i = 0; i < l; i++) {
                if (this.displays[i]._camera) this.displays[i].poll();
            }
        },

        _getDisplays: function (callback) {
            if (navigator.getVRDisplays) {
                navigator.getVRDisplays().then(function (displays) {
                    if (callback) callback(null, displays);
                });
            } else {
                if (callback) callback(new Error('WebVR not supported'));
            }
        },

        _addDisplay: function (vrDisplay) {
            if (this._index[vrDisplay.displayId])
                return;

            var display = new pc.VrDisplay(this._app, vrDisplay);
            this._index[display.id] = display;
            this.displays.push(display);

            if (!this.display)
                this.display = display;

            this.fire('displayconnect', display);
        },

        _onDisplayConnect: function (e) {
            if (e.detail && e.detail.display) {
                // polyfill has different event format
                this._addDisplay(e.detail.display);
            } else {
                // real event API
                this._addDisplay(e.display);
            }

        },

        _onDisplayDisconnect: function (e) {
            var id;
            if (e.detail && e.detail.display) {
                // polyfill has different event format
                id = e.detail.display.displayId;
            } else {
                // real event API
                id = e.display.displayId;
            }

            var display = this._index[id];
            if (!display)
                return;

            display.destroy();

            delete this._index[display.id];

            var ind = this.displays.indexOf(display);
            this.displays.splice(ind, 1);

            if (this.display === display) {
                if (this.displays.length) {
                    this.display = this.displays[0];
                } else {
                    this.display = null;
                }
            }

            this.fire('displaydisconnect', display);
        }
    });

    return {
        VrManager: VrManager
    };
}());