Source: utils.js

/**
 * Utils module.
 * @module utils
 */

define([
  'exports',
  'errors',
  'settings'
], function(exports, errors, settings) {

  'use strict';

 /**
  * Populates an object with defaults if the key is not yet defined.
  * Similar to _.defaults except this takes only a single defaults object.
  * @param {object} object - the object to populate defaults on
  * @param {object} defaults - the defaults to use
  * @returns {object}
  */
  exports.defaults = function(object, defaults) {
    object = object || {};
    Object.keys(defaults).forEach(function(key) {
      if (typeof object[key] === 'undefined') {
        object[key] = defaults[key];
      }
    });
    return object;
  };

 /**
  * Gets the app origin
  * @returns {string}
  */
  exports.getSelfOrigin = function(settingsObj) {
    settingsObj = settingsObj || settings;
    if (settingsObj.appSelf) {
      // This might be null for type:web packaged apps.
      // If that's a requirement, the caller should check for nulls.
      return settingsObj.appSelf.origin;
    } else {
      var win = settingsObj.window;
      if (win.location.origin) {
        return win.location.origin;
      } else {
        return win.location.protocol + '//' + win.location.hostname;
      }
    }
  };

 /**
  * Gets the the origin of the URL provided.
  * @param {string} url - the URL to introspect the origin from
  * @returns {string}
  */
  exports.getUrlOrigin = function(url) {
    var a = document.createElement('a');
    a.href = url;
    return a.origin || (a.protocol + '//' + a.host);
  };

 /**
  * Gets the center coordinates for a passed width and height.
  * Uses centering calcs that work on multiple monitors (bug 1122683).
  * @param {number} w - width
  * @param {number} h - height
  * @returns {list}
  */
  exports.getCenteredCoordinates = function(w, h) {
    var x = window.screenX +
      Math.max(0, Math.floor((window.innerWidth - w) / 2));
    var y = window.screenY +
      Math.max(0, Math.floor((window.innerHeight - h) / 2));
    return [x, y];
  };

 /**
  * Re-center an existing window.
  * @param {object} winRef - A reference to an existing window
  * @param {number} [w] - width
  * @param {number} [h] - height
  * @returns {undefined}
  */
  exports.reCenterWindow = function(winRef, w, h) {
    w = w || settings.winWidth;
    h = h || settings.winHeight;
    var xy = exports.getCenteredCoordinates(w, h);
    try {
      // Allow for the chrome as resizeTo args are the external
      // window dimensions not the internal ones.
      w = w + (winRef.outerWidth - winRef.innerWidth);
      h = h + (winRef.outerHeight - winRef.innerHeight);
      settings.log.log('width: ', w, 'height:', h);
      winRef.resizeTo(w, h);
      winRef.moveTo(xy[0], xy[1]);
    } catch(e) {
      settings.log.log("We don't have permission to resize this window");
    }
  };

 /**
  * Open a window
  * @param {object} [options] - the settings object
  * @param {string} [options.url] - the window url
  * @param {string} [options.title] - the window title
  * @param {number} [options.w] - the window width
  * @param {number} [options.h] - the window height
  * @returns {object} windowRef - a window reference.
  */
  exports.openWindow = function(options) {
    var defaults = {
      url: '',
      title: 'FxPay',
      w: settings.winWidth,
      h: settings.winHeight,
    };

    options = exports.defaults(options, defaults);
    var xy = exports.getCenteredCoordinates(options.w, options.h);

    var winOptString = 'toolbar=no,location=yes,directories=no,' +
      'menubar=no,scrollbars=yes,resizable=no,copyhistory=no,' +
      'width=' + options.w + ',height=' + options.h +
      ',top=' + xy[1] + ',left=' + xy[0];

    var windowRef = settings.window.open(options.url, options.title,
                                         winOptString);
    if (!windowRef) {
      settings.log.error('window.open() failed. URL:', options.url);
    }
    return windowRef;
  };

 /**
  * Get the App object returned from [`mozApps.getSelf()`](http://goo.gl/x4BDqs)
  * @param {module:utils~getAppSelfCallback} callback - the callback function.
  * @returns {undefined}
  */
  exports.getAppSelf = function getAppSelf(callback) {
    function storeAppSelf(appSelf) {
      if (appSelf === null) {
        throw new Error('cannot store a null appSelf');
      }
      settings.appSelf = appSelf;
      return appSelf;
    }

    if (settings.appSelf !== null) {
      // This means getAppSelf() has already run successfully so let's
      // return the value immediately.
      return callback(null, settings.appSelf);
    }

    if (!settings.mozApps) {
      settings.log.info(
          'web platform does not define mozApps, cannot get appSelf');
      return callback(null, storeAppSelf(false));
    }
    var appRequest = settings.mozApps.getSelf();

    appRequest.onsuccess = function() {
      var appSelf = this.result;
      // In the case where we're in a Firefox that supports mozApps but
      // we're not running as an app, this could be falsey.
      settings.log.info('got appSelf from mozApps.getSelf()');
      callback(null, storeAppSelf(appSelf || false));
    };

    appRequest.onerror = function() {
      var errCode = this.error.name;
      settings.log.error('mozApps.getSelf() returned an error', errCode);
      // We're not caching an appSelf result here.
      // This allows nested functions to report errors better.
      callback(errors.InvalidApp('invalid application: ' + errCode,
                                 {code: errCode}), settings.appSelf);
    };
  };

 /**
  * The callback called by {@link module:utils.getAppSelf }
  * @callback module:utils~getAppSelfCallback
  * @param {object} error - an error object. Will be null if no error.
  * @param {object} appSelf - the [appSelf object](http://goo.gl/HilsmA)
  */


 /**
  * Log a deprecation message with some extra info.
  * @param {string} msg - log message
  * @param {string} versionDeprecated - the version when deprecated
  * @returns {undefined}
  */
  exports.logDeprecation = function(msg, versionDeprecated) {
    settings.log.warn(
        msg + '. This was deprecated in ' + versionDeprecated + '. ' +
        'More info: https://github.com/mozilla/fxpay/releases/tag/' +
        versionDeprecated);
  };

 /**
  * Take an object of key value pairs and serialize it into a url-encoded
  * query string.
  * @example
  * // returns foo=bar&baz=zup
  * utils.serialize({"foo": "bar", "baz": "zup"});
  * @param {object} obj - object to serialize
  * @returns {string}
  */
  exports.serialize = function serialize(obj) {
    var str = [];
    for (var p in obj){
      if (obj.hasOwnProperty(p)) {
        str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
      }
    }
    return str.join("&");
  };

});