Source: utils/remote.js

goog.provide('M.remote');
goog.provide('M.remote.method');
goog.provide('M.remote.Response');

goog.require('M.utils');
goog.require('M.exception');

goog.require('goog.dom');
goog.require('goog.dom.xml');

/**
 * @namespace M.remote
 */
(function(window) {
   /**
    * This function gets a resource throw a
    * HTTP GET method and checks if the request
    * is ajax or jsonp based
    *
    * @function
    * @param {string} url
    * @param {Object} options
    * @returns {Promise}
    * @api stable
    */
   M.remote.get = function(url, data, options) {
      var req;

      var useProxy = ((M.utils.isNullOrEmpty(options) || (options.jsonp !== false)) && M.proxy_ !== false);

      if (useProxy === true) {
         req = M.remote.jsonp_(url, data, options);
      }
      else {
         req = M.remote.ajax_(url, data, M.remote.method.GET, false);
      }

      return req;
   };

   /**
    * This function gets a resource throw a
    * HTTP POST method using ajax
    *
    * @function
    * @param {string} url
    * @param {Object} data
    * @returns {Promise}
    * @api stable
    */
   M.remote.post = function(url, data, options) {
      return M.remote.ajax_(url, data, M.remote.method.POST);
   };

   M.remote.jsonp_ = function(url, data, options) {
      if (!M.utils.isNullOrEmpty(data)) {
         url = M.utils.addParameters(url, data);
      }

      if (M.proxy_) {
         url = M.remote.manageProxy_(url, M.remote.method.GET);
      }

      // creates a random name to avoid clonflicts
      var jsonpHandlerName = M.utils.generateRandom('mapea_jsonp_handler_');
      url = M.utils.addParameters(url, {
         'callback': jsonpHandlerName
      });

      var req = new Promise(function(success, fail) {
         var userCallback = success;
         // get the promise of the script tag
         var scriptTagPromise = new Promise(function(scriptTagSuccess) {
            window[jsonpHandlerName] = scriptTagSuccess;
         });
         /* when the script tag was executed remove
          * the handler and execute the callback
          */
         scriptTagPromise.then(function(proxyResponse) {
            // remove the jsonp handler from global window
            delete window[jsonpHandlerName];

            // remove the script tag from the html
            M.remote.removeScriptTag_(jsonpHandlerName);

            var response = new M.remote.Response();
            response.parseProxy(proxyResponse);

            userCallback(response);
         });
      });

      // creates the script tag
      M.remote.createScriptTag_(url, jsonpHandlerName);

      return req;
   };

   M.remote.ajax_ = function(url, data, method, useProxy) {
      if ((useProxy !== false) && (M.proxy_ === true)) {
         url = M.remote.manageProxy_(url, method);
      }

      // parses parameters to string
      if (M.utils.isObject(data)) {
         data = JSON.stringify(data);
      }

      return new Promise(function(success, fail) {
         var xhr;
         if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
         }
         else if (window.ActiveXObject) {
            xhr = new ActiveXObject("Microsoft.XMLHTTP");
         }
         xhr.onreadystatechange = function() {
            if (xhr.readyState == 4) {
               var response = new M.remote.Response();
               response.parseXmlHttp(xhr);
               success(response);
            }
         };
         xhr.open(method, url, true);
         xhr.send(data);
      });
   };

   M.remote.manageProxy_ = function(url, method) {
      // deafult GET
      var proxyUrl = M.config.PROXY_URL;
      if (method === M.remote.method.POST) {
         proxyUrl = M.config.PROXY_POST_URL;
      }

      proxyUrl = M.utils.addParameters(proxyUrl, {
         'url': url
      });

      return proxyUrl;
   };

   M.remote.createScriptTag_ = function(proxyUrl, jsonpHandlerName) {
      var scriptTag = document.createElement("script");
      scriptTag.type = "text/javascript";
      scriptTag.id = jsonpHandlerName;
      scriptTag.src = proxyUrl;
      scriptTag.setAttribute("async", "");
      goog.dom.appendChild(window.document.body, scriptTag);
   };

   M.remote.removeScriptTag_ = function(jsonpHandlerName) {
      var scriptTag = document.getElementById(jsonpHandlerName);
      goog.dom.removeNode(scriptTag);
   };

   /**
    * @classdesc
    * Response for proxy requests
    *
    * @constructor
    * @extends {M.Object}
    * @param {Object} response from proxy requests
    * @api stable
    */
   M.remote.Response = function(xmlHttpResponse) {
      /**
       * @public
       * @type {string}
       * @api stable
       */
      this.text = null;

      /**
       * @public
       * @type {XML}
       * @api stable
       */
      this.xml = null;

      /**
       * @public
       * @type {Object}
       * @api stable
       */
      this.headers = {};

      /**
       * @public
       * @type {boolean}
       * @api stable
       */
      this.error = false;

      /**
       * @public
       * @type {int}
       * @api stable
       */
      this.code = 0;
   };

   /**
    * This function parses a XmlHttp response
    * from an ajax request
    *
    * @function
    * @param {Object} url
    * @api stable
    */
   M.remote.Response.prototype.parseXmlHttp = function(xmlHttpResponse) {
      this.text = xmlHttpResponse['responseText'];
      this.xml = xmlHttpResponse['responseXML'];
      this.code = xmlHttpResponse['status'];
      this.error = (xmlHttpResponse['statusText'] !== 'OK');

      var headers = xmlHttpResponse.getAllResponseHeaders();
      headers = headers.split('\n');
      headers.forEach(function(head) {
         head = head.trim();
         var headName = head.replace(/^([^\:]+)\:(.+)$/, '$1').trim();
         var headValue = head.replace(/^([^\:]+)\:(.+)$/, '$2').trim();
         if (headName !== '') {
            this.headers[headName] = headValue;
         }
      }, this);
   };

   /**
    * This function parses a XmlHttp response
    * from an ajax request
    *
    * @function
    * @param {Object} url
    * @api stable
    */
   M.remote.Response.prototype.parseProxy = function(proxyResponse) {
      this.code = proxyResponse.code;
      this.error = proxyResponse.error;

      // adds content
      if ((this.code === 200) && (this.error !== true)) {
         this.text = proxyResponse.content.trim();
         try {
            // it uses DOMParser for html responses
            // google XML parser in other case
            let contentType = proxyResponse.headers['Content-Type'];
            if ((typeof DOMParser !== 'undefined') && /text\/html/i.test(contentType)) {
               this.xml = (new DOMParser()).parseFromString(this.text, 'text/html');
            }
            // it avoids responses that aren't xml format
            else if (/xml/i.test(contentType)) {
               this.xml = goog.dom.xml.loadXml(this.text);
            }
         }
         catch (err) {
            this.xml = null;
            this.error = true;
         }
      }

      // adds headers
      Object.keys(proxyResponse.headers).forEach(function(head) {
         this.headers[head] = proxyResponse.headers[head];
      }, this);
   };

   /**
    * HTTP method GET
    * @const
    * @type {string}
    * @public
    * @api stable
    */
   M.remote.method.GET = 'GET';

   /**
    * HTTP method POST
    * @const
    * @type {string}
    * @public
    * @api stable
    */
   M.remote.method.POST = 'POST';
})(window || {});