# 模块系统

# 定义模块

//定义模块
Layui.prototype.define = function (deps, factory) {
  var that = this,
    type = typeof deps === "function",
    callback = function () {
      var setApp = function (app, exports) {
        layui[app] = exports;
        config.status[app] = true;
      };
      typeof factory === "function" &&
        factory(function (app, exports) {
          setApp(app, exports);
          config.callback[app] = function () {
            factory(setApp);
          };
        });
      return this;
    };

  type && ((factory = deps), (deps = []));
  that.use(deps, callback, null, "define");
  return that;
};

# 使用模块

  //使用特定模块
  Layui.prototype.use = function (apps, callback, exports, from) {
    var that = this,
      dir = (config.dir = config.dir ? config.dir : getPath),
      head = doc.getElementsByTagName("head")[0];

    apps = (function () {
      if (typeof apps === "string") {
        return [apps];
      }
      //当第一个参数为 function 时,则自动加载所有内置模块,且执行的回调即为该 function 参数;
      else if (typeof apps === "function") {
        callback = apps;
        return ["all"];
      }
      return apps;
    })();

    //如果页面已经存在 jQuery 1.7+ 库且所定义的模块依赖 jQuery,则不加载内部 jquery 模块
    if (win.jQuery && jQuery.fn.on) {
      that.each(apps, function (index, item) {
        if (item === "jquery") {
          apps.splice(index, 1);
        }
      });
      layui.jquery = layui.$ = jQuery;
    }

    var item = apps[0],
      timeout = 0;
    exports = exports || [];

    //静态资源host
    config.host = config.host || (dir.match(/\/\/([\s\S]+?)\//) || ["//" + location.host + "/"])[0];

    //加载完毕
    function onScriptLoad(e, url) {
      var readyRegExp = navigator.platform === "PLaySTATION 3" ? /^complete$/ : /^(complete|loaded)$/;
      if (e.type === "load" || readyRegExp.test((e.currentTarget || e.srcElement).readyState)) {
        config.modules[item] = url;
        head.removeChild(node);
        (function poll() {
          if (++timeout > (config.timeout * 1000) / 4) {
            return error(item + " is not a valid module", "error");
          }
          config.status[item] ? onCallback() : setTimeout(poll, 4);
        })();
      }
    }

    //回调
    function onCallback() {
      exports.push(layui[item]);
      apps.length > 1
        ? that.use(apps.slice(1), callback, exports, from)
        : typeof callback === "function" &&
          (function () {
            //保证文档加载完毕再执行回调
            if (layui.jquery && typeof layui.jquery === "function" && from !== "define") {
              return layui.jquery(function () {
                callback.apply(layui, exports);
              });
            }
            callback.apply(layui, exports);
          })();
    }

    //如果引入了聚合板,内置的模块则不必重复加载
    if (apps.length === 0 || (layui["layui.all"] && modules[item])) {
      return onCallback(), that;
    }

    //获取加载的模块 URL
    //如果是内置模块,则按照 dir 参数拼接模块路径
    //如果是扩展模块,则判断模块路径值是否为 {/} 开头,
    //如果路径值是 {/} 开头,则模块路径即为后面紧跟的字符。
    //否则,则按照 base 参数拼接模块路径

    var url =
      (modules[item] ? dir + "modules/" : /^\{\/\}/.test(that.modules[item]) ? "" : config.base || "") +
      (that.modules[item] || item) +
      ".js";
    url = url.replace(/^\{\/\}/, "");

    //如果扩展模块(即:非内置模块)对象已经存在,则不必再加载
    if (!config.modules[item] && layui[item]) {
      config.modules[item] = url; //并记录起该扩展模块的 url
    }

    //首次加载模块
    if (!config.modules[item]) {
      var node = doc.createElement("script");

      node.async = true;
      node.charset = "utf-8";
      node.src =
        url +
        (function () {
          var version = config.version === true ? config.v || new Date().getTime() : config.version || "";
          return version ? "?v=" + version : "";
        })();

      head.appendChild(node);

      if (
        node.attachEvent &&
        !(node.attachEvent.toString && node.attachEvent.toString().indexOf("[native code") < 0) &&
        !isOpera
      ) {
        node.attachEvent("onreadystatechange", function (e) {
          onScriptLoad(e, url);
        });
      } else {
        node.addEventListener(
          "load",
          function (e) {
            onScriptLoad(e, url);
          },
          false
        );
      }

      config.modules[item] = url;
    } else {
      //缓存
      (function poll() {
        if (++timeout > (config.timeout * 1000) / 4) {
          return error(item + " is not a valid module", "error");
        }
        typeof config.modules[item] === "string" && config.status[item] ? onCallback() : setTimeout(poll, 4);
      })();
    }

    return that;
  };

# 拓展模块

//拓展模块
Layui.prototype.extend = function(options){
  var that = this;

  //验证模块是否被占用
  options = options || {};
  for(var o in options){
    if(that[o] || that.modules[o]){
      error(o+ ' Module already exists', 'error');
    } else {
      that.modules[o] = options[o];
    }
  }

  return that;
};