Source: js/orientation_manager.js

/* global SettingsListener, Service */
'use strict';

(function(exports) {
  /**
   * OrientationManager manages the orientation.
   *
   *
   * There're some cases we need to reset the orientation of the top window:
   * * LockScreen is unlocked.
   * * An attention window is opened.
   * * All attention window is closed.
   * * TrustedUI is closed.
   * * sleepMenu is hidden.
   *
   * Any of them occurs would trigger OrientationManager to dispatch
   * <code>reset-orientation</code> event and AppWindowManager would reset the
   * orientation of the active window.
   *
   * ![Change orientation flow](http://i.imgur.com/KCUgFH6.png)
   *
   * @module OrientationManager
   */
  var OrientationManager = {
    name: 'OrientationManager',

    start: function() {
      this.fetchDefaultOrientation();
      if (SettingsListener) {
        SettingsListener.observe('screen.orientation.lock', false,
          function(value) {
            this.globalOrientation = value ?
              this.fetchCurrentOrientation() : null;
            this.publish('reset-orientation');
          }.bind(this));
      }

      window.addEventListener('lockscreen-appclosing', this);
      window.addEventListener('attentionclosed', this);
      window.addEventListener('sleepmenuhide', this);
      window.addEventListener('trusteduiclose', this);
      window.addEventListener('shrinking-stop', this);
      window.addEventListener('searchclosing', this);
      Service.registerState('globalOrientation', this);
      Service.registerState('defaultOrientation', this);
      Service.registerState('fetchCurrentOrientation', this);
      Service.registerState('isDefaultPortrait', this);
      Service.registerState('isOnRealDevice', this);
    },

    handleEvent: function om_handleEvent(evt) {
      switch (evt.type) {
        case 'attentionclosed':
        case 'sleepmenuhide':
        case 'trusteduiclose':
        case 'lockscreen-appclosing':
        case 'searchclosing':
          // We don't need to reset orientation if lockscreen is locked.
          if (Service.query('locked')) {
            return;
          }
        /**
         * Fired when the orientation needs to be locked/unlocked again.
         * @event module:OrientationManager#reset-orientation
         */
          this.publish('reset-orientation');
          break;
        case 'shrinking-stop':
          this.publish('reset-orientation');
          break;
      }
    },

    globalOrientation: null,

    /**
     * Default orientation of this device, possible values are:
     *
     * * portrait-primary
     * * landscape-primary
     *
     * @type {String}
     * @memberOf module:OrientationManager
     */
    defaultOrientation: screen.mozOrientation,

    /**
     * Test if our default orientation is portrait.
     * @return {Boolean} If our default orientation is portrait.
     * @memberOf module:OrientationManager
     */
    isDefaultPortrait: function() {
      return (this.defaultOrientation === 'portrait-primary');
    },

    /**
     * Record if we are on real device or not.
     * @access private
     * @type {Boolean}
     * @memberOf module:OrientationManager
     */
    _isOnRealDevice: undefined,

    /**
     * Test if we are on real device by checking the available width.
     * @return {Boolean} If we are on real device or not.
     * @memberOf module:OrientationManager
     */
    isOnRealDevice: function sl_isOnRealDevice() {
      if (typeof(this._isOnRealDevice) !== 'undefined') {
        return this._isOnRealDevice;
      }

      // XXX: A hack to know we're using real device or not
      // is to detect screen size.
      // The screen size of b2g running on real device
      // is the same as the size of system app.
      if (window.innerWidth === screen.availWidth) {
        this._isOnRealDevice = true;
      } else {
        this._isOnRealDevice = false;
      }

      return this._isOnRealDevice;
    },

    /**
     * Get the default orientation of the device when device booted.
     * This is a trick done by locking the orientation at first and
     * then get by <code>screen.mozOrientation</code>.
     *
     * If we are not on a real device, we will guess the orientation by
     * the ratio of width and height of window.
     *
     * @memberOf module:OrientationManager
     */
    fetchDefaultOrientation: function sl_fetchDefaultOrientation() {
      if (!this.isOnRealDevice()) {
        // Fallback to use width/height to calculate default orientation
        // if we're running on desktop browser or simulator.
        this.defaultOrientation = window.innerWidth > window.innerHeight ?
          'landscape-primary' : 'portrait-primary';
      } else {
        screen.mozLockOrientation('default');
        this.defaultOrientation = screen.mozOrientation;
      }
    },

    /**
     * Get current orientation
     * @return {String} Current orientation, possible values: portrait-primary,
     *                  portrait-secondary, landscape-primary,
     *                  landscape-secondary.
     *
     * @memberOf module:OrientationManager
     */
    fetchCurrentOrientation: function sl_fetchCurrentOrientation() {
      if (!this.isOnRealDevice()) {
        // Fallback to use width/height to calculate default orientation
        // if we're running on desktop browser or simulator.
        return window.innerWidth > window.innerHeight ?
          'landscape-primary' : 'portrait-primary';
      } else {
        return screen.mozOrientation;
      }
    },

    publish: function sl_publish(eventName, detail) {
      var evt = document.createEvent('CustomEvent');
      evt.initCustomEvent(eventName, true, false, detail);
      window.dispatchEvent(evt);
    }
  };

  exports.OrientationManager = OrientationManager;
}(window));