/**
 * @file
 * @module UE.ajax
 * @since 1.2.6.1
 */

/**
 * 提供对ajax请求的支持
 * @module UE.ajax
 */
UE.ajax = (function() {
  //创建一个ajaxRequest对象
  var fnStr = "XMLHttpRequest()";
  try {
    new ActiveXObject("Msxml2.XMLHTTP");
    fnStr = "ActiveXObject('Msxml2.XMLHTTP')";
  } catch (e) {
    try {
      new ActiveXObject("Microsoft.XMLHTTP");
      fnStr = "ActiveXObject('Microsoft.XMLHTTP')";
    } catch (e) {}
  }
  var creatAjaxRequest = new Function("return new " + fnStr);

  /**
     * 将json参数转化成适合ajax提交的参数列表
     * @param json
     */
  function json2str(json) {
    var strArr = [];
    for (var i in json) {
      //忽略默认的几个参数
      if (
        i == "method" ||
        i == "timeout" ||
        i == "async" ||
        i == "dataType" ||
        i == "callback"
      )
        continue;
      //忽略控制
      if (json[i] == undefined || json[i] == null) continue;
      //传递过来的对象和函数不在提交之列
      if (
        !(
          (typeof json[i]).toLowerCase() == "function" ||
          (typeof json[i]).toLowerCase() == "object"
        )
      ) {
        strArr.push(encodeURIComponent(i) + "=" + encodeURIComponent(json[i]));
      } else if (utils.isArray(json[i])) {
        //支持传数组内容
        for (var j = 0; j < json[i].length; j++) {
          strArr.push(
            encodeURIComponent(i) + "[]=" + encodeURIComponent(json[i][j])
          );
        }
      }
    }
    return strArr.join("&");
  }

  function doAjax(url, ajaxOptions) {
    var xhr = creatAjaxRequest(),
      //是否超时
      timeIsOut = false,
      //默认参数
      defaultAjaxOptions = {
        method: "POST",
        timeout: 5000,
        async: true,
        data: {}, //需要传递对象的话只能覆盖
        onsuccess: function() {},
        onerror: function() {}
      };

    if (typeof url === "object") {
      ajaxOptions = url;
      url = ajaxOptions.url;
    }
    if (!xhr || !url) return;
    var ajaxOpts = ajaxOptions
      ? utils.extend(defaultAjaxOptions, ajaxOptions)
      : defaultAjaxOptions;

    var submitStr = json2str(ajaxOpts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
    //如果用户直接通过data参数传递json对象过来，则也要将此json对象转化为字符串
    if (!utils.isEmptyObject(ajaxOpts.data)) {
      submitStr += (submitStr ? "&" : "") + json2str(ajaxOpts.data);
    }
    //超时检测
    var timerID = setTimeout(function() {
      if (xhr.readyState != 4) {
        timeIsOut = true;
        xhr.abort();
        clearTimeout(timerID);
      }
    }, ajaxOpts.timeout);

    var method = ajaxOpts.method.toUpperCase();
    var str =
      url +
      (url.indexOf("?") == -1 ? "?" : "&") +
      (method == "POST" ? "" : submitStr + "&noCache=" + +new Date());
    xhr.open(method, str, ajaxOpts.async);
    xhr.onreadystatechange = function() {
      if (xhr.readyState == 4) {
        if (!timeIsOut && xhr.status == 200) {
          ajaxOpts.onsuccess(xhr);
        } else {
          ajaxOpts.onerror(xhr);
        }
      }
    };
    if (method == "POST") {
      xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
      xhr.send(submitStr);
    } else {
      xhr.send(null);
    }
  }

  function doJsonp(url, opts) {
    var successhandler = opts.onsuccess || function() {},
      scr = document.createElement("SCRIPT"),
      options = opts || {},
      charset = options["charset"],
      callbackField = options["jsonp"] || "callback",
      callbackFnName,
      timeOut = options["timeOut"] || 0,
      timer,
      reg = new RegExp("(\\?|&)" + callbackField + "=([^&]*)"),
      matches;

    if (utils.isFunction(successhandler)) {
      callbackFnName =
        "bd__editor__" + Math.floor(Math.random() * 2147483648).toString(36);
      window[callbackFnName] = getCallBack(0);
    } else if (utils.isString(successhandler)) {
      callbackFnName = successhandler;
    } else {
      if ((matches = reg.exec(url))) {
        callbackFnName = matches[2];
      }
    }

    url = url.replace(reg, "\x241" + callbackField + "=" + callbackFnName);

    if (url.search(reg) < 0) {
      url +=
        (url.indexOf("?") < 0 ? "?" : "&") +
        callbackField +
        "=" +
        callbackFnName;
    }

    var queryStr = json2str(opts); // { name:"Jim",city:"Beijing" } --> "name=Jim&city=Beijing"
    //如果用户直接通过data参数传递json对象过来，则也要将此json对象转化为字符串
    if (!utils.isEmptyObject(opts.data)) {
      queryStr += (queryStr ? "&" : "") + json2str(opts.data);
    }
    if (queryStr) {
      url = url.replace(/\?/, "?" + queryStr + "&");
    }

    scr.onerror = getCallBack(1);
    if (timeOut) {
      timer = setTimeout(getCallBack(1), timeOut);
    }
    createScriptTag(scr, url, charset);

    function createScriptTag(scr, url, charset) {
      scr.setAttribute("type", "text/javascript");
      scr.setAttribute("defer", "defer");
      charset && scr.setAttribute("charset", charset);
      scr.setAttribute("src", url);
      document.getElementsByTagName("head")[0].appendChild(scr);
    }

    function getCallBack(onTimeOut) {
      return function() {
        try {
          if (onTimeOut) {
            options.onerror && options.onerror();
          } else {
            try {
              clearTimeout(timer);
              successhandler.apply(window, arguments);
            } catch (e) {}
          }
        } catch (exception) {
          options.onerror && options.onerror.call(window, exception);
        } finally {
          options.oncomplete && options.oncomplete.apply(window, arguments);
          scr.parentNode && scr.parentNode.removeChild(scr);
          window[callbackFnName] = null;
          try {
            delete window[callbackFnName];
          } catch (e) {}
        }
      };
    }
  }

  return {
    /**
         * 根据给定的参数项，向指定的url发起一个ajax请求。 ajax请求完成后，会根据请求结果调用相应回调： 如果请求
         * 成功， 则调用onsuccess回调， 失败则调用 onerror 回调
         * @method request
         * @param { URLString } url ajax请求的url地址
         * @param { Object } ajaxOptions ajax请求选项的键值对，支持的选项如下：
         * @example
         * ```javascript
         * //向sayhello.php发起一个异步的Ajax GET请求, 请求超时时间为10s， 请求完成后执行相应的回调。
         * UE.ajax.requeset( 'sayhello.php', {
         *
         *     //请求方法。可选值： 'GET', 'POST'，默认值是'POST'
         *     method: 'GET',
         *
         *     //超时时间。 默认为5000， 单位是ms
         *     timeout: 10000,
         *
         *     //是否是异步请求。 true为异步请求， false为同步请求
         *     async: true,
         *
         *     //请求携带的数据。如果请求为GET请求， data会经过stringify后附加到请求url之后。
         *     data: {
         *         name: 'neditor'
         *     },
         *
         *     //请求成功后的回调， 该回调接受当前的XMLHttpRequest对象作为参数。
         *     onsuccess: function ( xhr ) {
         *         console.log( xhr.responseText );
         *     },
         *
         *     //请求失败或者超时后的回调。
         *     onerror: function ( xhr ) {
         *          alert( 'Ajax请求失败' );
         *     }
         *
         * } );
         * ```
         */

    /**
         * 根据给定的参数项发起一个ajax请求， 参数项里必须包含一个url地址。 ajax请求完成后，会根据请求结果调用相应回调： 如果请求
         * 成功， 则调用onsuccess回调， 失败则调用 onerror 回调。
         * @method request
         * @warning 如果在参数项里未提供一个key为“url”的地址值，则该请求将直接退出。
         * @param { Object } ajaxOptions ajax请求选项的键值对，支持的选项如下：
         * @example
         * ```javascript
         *
         * //向sayhello.php发起一个异步的Ajax POST请求, 请求超时时间为5s， 请求完成后不执行任何回调。
         * UE.ajax.requeset( 'sayhello.php', {
         *
         *     //请求的地址， 该项是必须的。
         *     url: 'sayhello.php'
         *
         * } );
         * ```
         */
    request: function(url, opts) {
      if (opts && opts.dataType == "jsonp") {
        doJsonp(url, opts);
      } else {
        doAjax(url, opts);
      }
    },
    getJSONP: function(url, data, fn) {
      var opts = {
        data: data,
        oncomplete: fn
      };
      doJsonp(url, opts);
    }
  };
})();
