/**
 * Sea.js 2.2.1 | jsPlus.org/LICENSE.md
 */
(function (global, undefined) {

    // Avoid conflicting when `sea.js` is loaded multiple times
    if (global.jsPlus) {
        return
    }

    var jsPlus = global.jsPlus = {
        // The current version of Sea.js being used
        version: "2.0"
    }

    var data = jsPlus.data = {}


    /**
     * util-lang.js - The minimal language enhancement
     */

    function isType(type) {
        return function (obj) {
            return {}.toString.call(obj) == "[object " + type + "]"
        }
    }

    var isObject = isType("Object")
    var isString = isType("String")
    var isArray = Array.isArray || isType("Array")
    var isFunction = isType("Function")
    var isJson = function (obj) {
        var isjson = typeof (obj) == "object" && Object.prototype.toString.call(obj).toLowerCase() == "[object object]";
        return isjson;
    }
    var _cid = 0
    function cid() {
        return _cid++
    }

    /**
     * util-events.js - The minimal events support
     */
    var events = data.events = {}

    // Bind event
    jsPlus.on = function (name, callback) {
        var list = events[name] || (events[name] = [])
        list.push(callback)
        return jsPlus
    }

    // Remove event. If `callback` is undefined, remove all callbacks for the
    // event. If `event` and `callback` are both undefined, remove all callbacks
    // for all events
    jsPlus.off = function (name, callback) {
        // Remove *all* events
        if (!(name || callback)) {
            events = data.events = {}
            return jsPlus
        }

        var list = events[name]
        if (list) {
            if (callback) {
                for (var i = list.length - 1; i >= 0; i--) {
                    if (list[i] === callback) {
                        list.splice(i, 1)
                    }
                }
            }
            else {
                delete events[name]
            }
        }

        return jsPlus
    }

    // Emit event, firing all bound callbacks. Callbacks receive the same
    // arguments as `emit` does, apart from the event name
    var emit = jsPlus.emit = function (name) {
        var list = events[name], fn

        if (list) {
            // Copy callback lists to prevent modification
            list = list.slice()
            var args = Array.prototype.slice.call(arguments, 1);
            // Execute event callbacks
            while ((fn = list.shift())) {
                fn.apply(null, args);
            }
        }

        return jsPlus
    }


    /**
     * util-path.js - The utilities for operating path such as id, uri
     */

    var DIRNAME_RE = /[^?#]*\//

    var DOT_RE = /\/\.\//g
    var DOUBLE_DOT_RE = /\/[^/]+\/\.\.\//
    var DOUBLE_SLASH_RE = /([^:/])\/\//g

    // Extract the directory portion of a path
    // dirname("a/b/c.js?t=123#xx/zz") ==> "a/b/"
    // ref: http://jsperf.com/regex-vs-split/2
    function dirname(path) {
        return path.match(DIRNAME_RE)[0]
    }

    // Canonicalize a path
    // realpath("http://test.com/a//./b/../c") ==> "http://test.com/a/c"
    function realpath(path) {
        // /a/b/./c/./d ==> /a/b/c/d
        path = path.replace(DOT_RE, "/")

        // a/b/c/../../d  ==>  a/b/../d  ==>  a/d
        while (path.match(DOUBLE_DOT_RE)) {
            path = path.replace(DOUBLE_DOT_RE, "/")
        }

        // a//b/c  ==>  a/b/c
        path = path.replace(DOUBLE_SLASH_RE, "$1/")

        return path
    }

    // Normalize an id
    // normalize("path/to/a") ==> "path/to/a.js"
    // NOTICE: substring is faster than negative slice and RegExp
    function normalize(path) {
        var last = path.length - 1
        var lastC = path.charAt(last)

        // If the uri ends with `#`, just return it without '#'
        if (lastC === "#") {
            return path.substring(0, last)
        }

        return (path.substring(last - 2) === ".js" ||
            path.indexOf("?") > 0 ||
            path.substring(last - 3) === ".css" ||
            lastC === "/") ? path : path + ".js"
    }


    var PATHS_RE = /^([^/:]+)(\/.+)$/
    var VARS_RE = /{([^{]+)}/g

    function parseAlias(id) {
        var alias = data.alias
        return alias && isString(alias[id]) ? alias[id] : id
    }

    function parsePaths(id) {
        var paths = data.paths
        var m

        if (paths && (m = id.match(PATHS_RE)) && isString(paths[m[1]])) {
            id = paths[m[1]] + m[2]
        }

        return id
    }

    function parseVars(id) {
        var vars = data.vars

        if (vars && id.indexOf("{") > -1) {
            id = id.replace(VARS_RE, function (m, key) {
                return isString(vars[key]) ? vars[key] : m
            })
        }

        return id
    }

    function parseMap(uri) {
        var map = data.map
        var ret = uri

        if (map) {
            for (var i = 0, len = map.length; i < len; i++) {
                var rule = map[i]

                ret = isFunction(rule) ?
                    (rule(uri) || uri) :
                    uri.replace(rule[0], rule[1])

                // Only apply the first matched rule
                if (ret !== uri) break
            }
        }

        return ret
    }


    var ABSOLUTE_RE = /^\/\/.|:\//
    var ROOT_DIR_RE = /^.*?\/\/.*?\//

    function addBase(id, refUri) {
        var ret
        var first = id.charAt(0)

        // Absolute
        if (ABSOLUTE_RE.test(id)) {
            ret = id
        }
            // Relative
        else if (first === ".") {
            ret = realpath((refUri ? dirname(refUri) : data.cwd) + id)
        }
            // Root
        else if (first === "/") {
            var m = data.cwd.match(ROOT_DIR_RE)
            ret = m ? m[0] + id.substring(1) : id
        }
            // Top-level
        else {
            ret = data.base + id
        }

        // Add default protocol when uri begins with "//"
        if (ret.indexOf("//") === 0) {
            ret = location.protocol + ret
        }

        return ret
    }

    function id2Uri(id, refUri) {
        if (!id) return ""

        id = parseAlias(id)
        id = parsePaths(id)
        id = parseVars(id)
        id = normalize(id)

        var uri = addBase(id, refUri)
        uri = parseMap(uri)

        return uri
    }


    var doc = document
    var cwd = dirname(doc.URL)
    var scripts = doc.scripts

    // Recommend to add `jsPlusnode` id for the `sea.js` script element
    var loaderScript = doc.getElementById("jsPlusnode") ||
        scripts[scripts.length - 1]

    // When `sea.js` is inline, set loaderDir to current working directory
    var loaderDir = dirname(getScriptAbsoluteSrc(loaderScript) || cwd)

    function getScriptAbsoluteSrc(node) {
        return node.hasAttribute ? // non-IE6/7
            node.src :
          // see http://msdn.microsoft.com/en-us/library/ms536429(VS.85).aspx
            node.getAttribute("src", 4)
    }


    // For Developers
    jsPlus.resolve = id2Uri


    /**
     * util-request.js - The utilities for requesting script and style files
     * ref: tests/research/load-js-css/test.html
     */

    var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement
    var baseElement = head.getElementsByTagName("base")[0]

    var IS_CSS_RE = /\.css(?:\?|$)/i
    var currentlyAddingScript
    var interactiveScript

    // `onload` event is not supported in WebKit < 535.23 and Firefox < 9.0
    // ref:
    //  - https://bugs.webkit.org/show_activity.cgi?id=38995
    //  - https://bugzilla.mozilla.org/show_bug.cgi?id=185236
    //  - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events
    var isOldWebKit = +navigator.userAgent
        .replace(/.*(?:AppleWebKit|AndroidWebKit)\/(\d+).*/, "$1") < 536


    function request(url, callback, charset) {
        var isCSS = IS_CSS_RE.test(url)
        var node = doc.createElement(isCSS ? "link" : "script")

        if (charset) {
            var cs = isFunction(charset) ? charset(url) : charset
            if (cs) {
                node.charset = cs
            }
        }

        addOnload(node, callback, isCSS, url)

        if (isCSS) {
            node.rel = "stylesheet"
            node.href = url
        }
        else {
            node.async = true
            node.src = url
        }

        // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
        // the end of the insert execution, so use `currentlyAddingScript` to
        // hold current node, for deriving url in `define` call
        currentlyAddingScript = node

        // ref: #185 & http://dev.jquery.com/ticket/2709
        baseElement ?
            head.insertBefore(node, baseElement) :
            head.appendChild(node)

        currentlyAddingScript = null
    }

    function addOnload(node, callback, isCSS, url) {
        var supportOnload = "onload" in node

        // for Old WebKit and Old Firefox
        if (isCSS && (isOldWebKit || !supportOnload)) {
            setTimeout(function () {
                pollCss(node, callback)
            }, 1) // Begin after node insertion
            return
        }

        if (supportOnload) {
            node.onload = onload
            node.onerror = function () {
                emit("error", { uri: url, node: node })
                onload()
            }
        }
        else {
            node.onreadystatechange = function () {
                if (/loaded|complete/.test(node.readyState)) {
                    onload()
                }
            }
        }

        function onload() {
            // Ensure only run once and handle memory leak in IE
            node.onload = node.onerror = node.onreadystatechange = null

            // Remove the script to reduce memory leak
            if (!isCSS && !data.debug) {
                head.removeChild(node)
            }

            // Dereference the node
            node = null

            callback()
        }
    }

    function pollCss(node, callback) {
        var sheet = node.sheet
        var isLoaded

        // for WebKit < 536
        if (isOldWebKit) {
            if (sheet) {
                isLoaded = true
            }
        }
            // for Firefox < 9.0
        else if (sheet) {
            try {
                if (sheet.cssRules) {
                    isLoaded = true
                }
            } catch (ex) {
                // The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
                // to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
                // in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
                if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") {
                    isLoaded = true
                }
            }
        }

        setTimeout(function () {
            if (isLoaded) {
                // Place callback here to give time for style rendering
                callback()
            }
            else {
                pollCss(node, callback)
            }
        }, 20)
    }

    function getCurrentScript() {
        if (currentlyAddingScript) {
            return currentlyAddingScript
        }

        // For IE6-9 browsers, the script onload event may not fire right
        // after the script is evaluated. Kris Zyp found that it
        // could query the script nodes and the one that is in "interactive"
        // mode indicates the current script
        // ref: http://goo.gl/JHfFW
        if (interactiveScript && interactiveScript.readyState === "interactive") {
            return interactiveScript
        }

        var scripts = head.getElementsByTagName("script")

        for (var i = scripts.length - 1; i >= 0; i--) {
            var script = scripts[i]
            if (script.readyState === "interactive") {
                interactiveScript = script
                return interactiveScript
            }
        }
    }


    // For Developers
    jsPlus.request = request


    /**
     * util-deps.js - The parser for dependencies
     * ref: tests/research/parse-dependencies/test.html
     */

    var USING_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*using|(?:^|[^$])\busing\s*\(\s*(["'])(.+?)\1\s*\)/g
    var SLASH_RE = /\\\\/g

    function parseDependencies(code) {
        var ret = []

        code.replace(SLASH_RE, "")
            .replace(USING_RE, function (m, m1, m2) {
                if (m2) {
                    ret.push(m2)
                }
            })

        return ret
    }
    var EXTEND_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*extend|(?:^|[^$])\bextend\s*\(\s*(["'])(.+?)\1\s*\)/g
    function parseExtends(code) {
        var ret = []

        code.replace(SLASH_RE, "")
            .replace(EXTEND_RE, function (m, m1, m2) {
                if (m2) {
                    ret.push(m2)
                }
            })

        return ret
    }

    /**
     * module.js - The core of module loader
     */

    var cachedMods = jsPlus.cache = {}
    var anonymousMeta

    var fetchingList = {}
    var fetchedList = {}
    var callbackList = {}

    var STATUS = Module.STATUS = {
        // 1 - The `module.uri` is being fetched
        FETCHING: 1,
        // 2 - The meta data has been saved to cachedMods
        SAVED: 2,
        // 3 - The `module.dependencies` are being loaded
        LOADING: 3,
        // 4 - The module are ready to execute
        LOADED: 4,
        // 5 - The module is being executed
        EXECUTING: 5,
        // 6 - The `module.exports` is available
        EXECUTED: 6
    }


    var Class = {};
    Class.check = function (mod) {
        var exports = mod.exports;
        if (mod.extend && mod.extend.length) {
            for (var i = 0; i < mod.extend.length; i++) {
                var superObject = Module.get(Module.resolve(mod.extend[i]));
                var curExtendObj = mod.extend[i];
                Class.clone(exports, superObject.exports, false, function (key, newObj, oldObj) {
                    //                if ((superObject.type == jsPlus.ObjectType.INTERFACE
                    //                    || superObject.type == jsPlus.ObjectType.ABSTRACT)
                    //                    && mod.type == jsPlus.ObjectType.CLASS){
                    //                    if (!exports[key] && key.indexOf("_") != 0)
                    //                        throw new Error("Super class:"+ key + " function must defind");
                    //                } else {
                    //
                    //                }
                    if ((superObject.type == jsPlus.ObjectType.INTERFACE
                        || superObject.type == jsPlus.ObjectType.ABSTRACT)
                        && mod.type == jsPlus.ObjectType.CLASS) {
                        if (!newObj[key] && oldObj[key]) {
                            newObj[key] = function () {
                                return this.sup(curExtendObj)[key].apply(this, arguments);
                            }
                        } else {
                            if (!newObj[key] && key.indexOf("_") != 0)
                                newObj[key] = oldObj[key];
                            // throw new Error("Super class:"+ key + " function must defind");
                        }
                    } else
                        newObj[key] = oldObj[key];
                });
            }
        }
    }
    Class.build = function (mod) {
        var exports = mod.exports;
        if (typeof exports != "undefined"
              && exports) {
            // If construct isn't a function, will create no params construct
            !isFunction(exports.construct) && mod.type == jsPlus.ObjectType.CLASS && (exports.__construct_0 = function () { });
            !isFunction(exports.New) && mod.type == jsPlus.ObjectType.CLASS && (exports.New = Class.newMe);
            //!isFunction(exports.destruct) && mod.type == jsPlus.ObjectType.CLASS && (exports.destruct = function () {
            //    Class.release(this);
            //});
            exports.__super__ = mod.extend;

            exports.func = function (p1, p2) {
                var funcName = "";
                if (isFunction(p1)) {
                    funcName = "construct";
                    var newFuncName = "__construct_" + p1.length;
                    this[newFuncName] = p1;
                } else if (isString(p1) && isFunction(p2)) {
                    funcName = p1;
                    var newFuncName = "__" + p1 + "_" + p2.length;
                    this[newFuncName] = p2;
                } else if (isString(p1)) {
                    this[p1] = null;
                }

                

                funcName && (this[funcName] = function () {
                    return this["__" + funcName + "_" + arguments.length].apply(this, arguments);
                });
            };

            exports.static = function (p1, p2) {
                if (isString(p1)) {
                    this.__staticObject = this.__staticObject || [];
                    this.__staticObject.push(p1);
                    this[p1] = p2;
                }
            };
        }

        return exports;
    }
    Class.release = function (obj, flag) {
        for (var i in obj) {
            if (isJson(obj[i]) || isArray(obj[i])) {
                flag && this.release(obj[i]);
                delete obj[i];
                //window.console.log(i);
            }
            else if (isJson(obj)) {
                obj[i] = null;
                delete obj[i];
                //window.console.log(i);
            }
            else if (isString(obj[i]) || isObject(obj[i]) || isFunction(obj[i]))
                obj[i] = null;
        }
        obj = null;
    }

    Class.clone = function (newObj, oldObj, isCopy, onProgress, noCopyObjList) {
        if (isJson(oldObj) || isArray(oldObj)) {
            for (var k in oldObj) {
                var reg = /^New|type|func|__staticObject|__super__$/;
                noCopyObjList = noCopyObjList || [];

                if (reg.test(k) || ("," + noCopyObjList.join(',') + ",").indexOf("," + k + ",") != -1)
                    continue;
                if (isJson(oldObj[k])) {
                    newObj[k] = newObj[k] || {};
                    newObj[k] = Class.clone(newObj[k], oldObj[k], isCopy);
                } else if (isArray(oldObj[k])) {
                    newObj[k] = [];
                    newObj[k] = Class.clone(newObj[k], oldObj[k], isCopy);
                } else if (isCopy || !newObj[k]) {
                    if (isFunction(onProgress)) onProgress(k, newObj, oldObj);
                    else newObj[k] = oldObj[k];
                }
            }
        } else
            newObj = oldObj;
        return newObj;
    }
    Class.newMe = function () {
        var newObj = Class.clone({}, this, false, null, this.__staticObject || []), me = this;
        newObj.type = 'object';
        var getSup = function (key, sups) {
            for (var i = 0; i < sups.length; i++) {
                if (sups[i].className == key) {
                    return sups[i];
                }
            }
            return key;
        }
        newObj.sup = function (key) {
            var sups = me.__super__;
            key = getSup(key, sups) || sups[0];
            if (key.type == 'object') {
                return key;
            } else {

                var supObject = newObj;

                for (var i = 0; i < sups.length; i++) {
                    if (sups[i] == key) {
                        var curExports = Module.get(Module.resolve(sups[i])).exports;
                        supObject = isFunction( curExports.New ) ? curExports.New() :  Class.clone({}, curExports);//Class.clone({}, );//Class.clone(Class.clone({}, Module.get(Module.resolve(sups[i])).exports), supObject);
                        supObject.type = 'object';
                        supObject.className = key;
                        sups[i] = supObject;
                        break;
                    }
                }



                return supObject;
            }
        }


        // finish new object builded, apply construct function
        newObj.construct.apply(newObj, arguments);
        return newObj;
    }

    function Module(uri, deps) {
        this.uri = uri
        this.dependencies = deps || []
        this.exports = null
        this.status = 0

        // Who depends on me
        this._waitings = {}

        // The number of unloaded dependencies
        this._remain = 0
    }
    // Resolve module.dependencies
    Module.prototype.resolve = function () {
        var mod = this
        var ids = mod.dependencies
        var uris = []

        for (var i = 0, len = ids.length; i < len; i++) {
            uris[i] = Module.resolve(ids[i], mod.uri)
        }
        return uris
    }

    // Load module.dependencies and fire onload when all done
    Module.prototype.load = function () {
        var mod = this

        // If the module is being loaded, just wait it onload call
        if (mod.status >= STATUS.LOADING) {
            return
        }

        mod.status = STATUS.LOADING

        // Emit `load` event for plugins such as combo plugin
        var uris = mod.resolve()
        emit("load", uris, mod)

        var len = mod._remain = uris.length
        var m

        // Initialize modules and register waitings
        for (var i = 0; i < len; i++) {
            m = Module.get(uris[i])

            if (m.status < STATUS.LOADED) {
                // Maybe duplicate: When module has dupliate dependency, it should be it's count, not 1
                m._waitings[mod.uri] = (m._waitings[mod.uri] || 0) + 1
            }
            else {
                mod._remain--
            }
        }

        if (mod._remain === 0) {
            mod.onload()
            return
        }

        // Begin parallel loading
        var requestCache = {}

        for (i = 0; i < len; i++) {
            m = cachedMods[uris[i]]

            if (m.status < STATUS.FETCHING) {
                m.fetch(requestCache)
            }
            else if (m.status === STATUS.SAVED) {
                m.load()
            }
        }

        // Send all requests at last to avoid cache bug in IE6-9. Issues#808
        for (var requestUri in requestCache) {
            if (requestCache.hasOwnProperty(requestUri)) {
                requestCache[requestUri]()
            }
        }
    }

    // Call this method when module is loaded
    Module.prototype.onload = function () {
        var mod = this
        mod.status = STATUS.LOADED

        if (mod.callback) {
            mod.callback()
        }

        // Notify waiting modules to fire onload
        var waitings = mod._waitings
        var uri, m

        for (uri in waitings) {
            if (waitings.hasOwnProperty(uri)) {
                m = cachedMods[uri]
                m._remain -= waitings[uri]
                if (m._remain === 0) {
                    m.onload()
                }
            }
        }

        // Reduce memory taken
        delete mod._waitings
        delete mod._remain
    }

    // Fetch a module
    Module.prototype.fetch = function (requestCache) {
        var mod = this
        var uri = mod.uri

        mod.status = STATUS.FETCHING

        // Emit `fetch` event for plugins such as combo plugin
        var emitData = { uri: uri }
        emit("fetch", emitData)
        var requestUri = emitData.requestUri || uri

        // Empty uri or a non-CMD module
        if (!requestUri || fetchedList[requestUri]) {
            mod.load()
            return
        }

        if (fetchingList[requestUri]) {
            callbackList[requestUri].push(mod)
            return
        }

        fetchingList[requestUri] = true
        callbackList[requestUri] = [mod]

        // Emit `request` event for plugins such as text plugin
        emit("request", emitData = {
            uri: uri,
            requestUri: requestUri,
            onRequest: onRequest,
            charset: data.charset
        })

        if (!emitData.requested) {
            requestCache ?
                requestCache[emitData.requestUri] = sendRequest :
                sendRequest()
        }

        function sendRequest() {
            jsPlus.request(emitData.requestUri, emitData.onRequest, emitData.charset)
        }

        function onRequest() {
            delete fetchingList[requestUri]
            fetchedList[requestUri] = true

            // Save meta data of anonymous module
            if (anonymousMeta) {
                Module.save(uri, anonymousMeta)
                anonymousMeta = null
            }

            // Call callbacks
            var m, mods = callbackList[requestUri]
            delete callbackList[requestUri]
            while ((m = mods.shift())) m.load()
        }
    }

    // Execute a module
    Module.prototype.exec = function () {
        var mod = this

        // When module is executed, DO NOT execute it again. When module
        // is being executed, just return `module.exports` too, for avoiding
        // circularly calling
        if (mod.status >= STATUS.EXECUTING) {
            return mod.exports
        }

        mod.status = STATUS.EXECUTING

        // Create using
        var uri = mod.uri

        function using(id) {
            return Module.get(using.resolve(id)).exec()
        }

        using.resolve = function (id) {
            return Module.resolve(id, uri)
        }

        using.async = function (ids, callback) {
            Module.use(ids, callback, uri + "_async_" + cid())
            return using
        }
        var extend = using;
        // Exec factory
        var factory = mod.factory
        mod.exports = mod.exports || {};

        Class.build(mod);
        var exports = isFunction(factory) ?
            factory(extend, using, mod.exports, mod) :
            factory

        if (exports === undefined) {
            exports = mod.exports
        }

        // Reduce memory leak
        delete mod.factory

        mod.exports = exports
        mod.status = STATUS.EXECUTED

        // Emit `exec` event
        emit("exec", mod)

        Class.check(mod);

        return exports
    }

    // Resolve id to uri
    Module.resolve = function (id, refUri) {
        // Emit `resolve` event for plugins such as text plugin
        var emitData = { id: id, refUri: refUri }
        emit("resolve", emitData)

        return emitData.uri || jsPlus.resolve(emitData.id, refUri)
    }

    // Define a module
    Module.define = function (id, deps, factory, type) {
        var argsLen = arguments.length

        // define(factory)
        if (isFunction(id)) {
            factory = id
            id = undefined
        }
        else if (id && deps && !factory) {
            factory = deps

            // define(deps, factory)
            if (isArray(id)) {
                deps = id
                id = undefined
            }
                // define(id, factory)
            else {
                deps = undefined
            }
        }
        var extendArray = null;
        // Parse dependencies according to the module factory code
        if (isFunction(factory)) {
            !isArray(deps) && (deps = parseDependencies(factory.toString()));
            extendArray = parseExtends(factory.toString());
            deps = deps.concat(extendArray);
        }


        var meta = {
            id: id,
            uri: Module.resolve(id),
            deps: deps,
            extend: extendArray,
            factory: factory,
            type: type || jsPlus.ObjectType.CLASS
        }

        // Try to derive uri in IE6-9 for anonymous modules
        if (!meta.uri && doc.attachEvent) {
            var script = getCurrentScript()

            if (script) {
                meta.uri = script.src
            }

            // NOTE: If the id-deriving methods above is failed, then falls back
            // to use onload event to get the uri
        }

        // Emit `define` event, used in nocache plugin, jsPlus node version etc
        emit("define", meta)

        meta.uri ? Module.save(meta.uri, meta) :
        // Save information for "saving" work in the script onload event
            anonymousMeta = meta
    }

    // Save meta data to cachedMods
    Module.save = function (uri, meta) {
        var mod = Module.get(uri)

        // Do NOT override already saved modules
        if (mod.status < STATUS.SAVED) {
            mod.id = meta.id || uri
            mod.dependencies = meta.deps || []
            mod.factory = meta.factory
            mod.status = STATUS.SAVED
            mod.extend = meta.extend
            mod.type = meta.type
        }
    }

    // Get an existed module or create a new one
    Module.get = function (uri, deps) {
        return cachedMods[uri] || (cachedMods[uri] = new Module(uri, deps))
    }

    // Use function is equal to load a anonymous module
    Module.use = function (ids, callback, uri) {
        var mod = Module.get(uri, isArray(ids) ? ids : [ids])

        mod.callback = function () {
            var exports = []
            var uris = mod.resolve()

            for (var i = 0, len = uris.length; i < len; i++) {
                exports[i] = cachedMods[uris[i]].exec()
            }

            if (callback) {
                callback.apply(global, exports)
            }

            delete mod.callback
        }

        mod.load()
    }

    // Load preload modules before all other modules
    Module.preload = function (callback) {
        var preloadMods = data.preload
        var len = preloadMods.length

        if (len) {
            Module.use(preloadMods, function () {
                // Remove the loaded preload modules
                preloadMods.splice(0, len)

                // Allow preload modules to add new preload modules
                Module.preload(callback)
            }, data.cwd + "_preload_" + cid())
        }
        else {
            callback()
        }
    }


    // Public API

    jsPlus.main = function (ids, callback) {
        Module.preload(function () {
            Module.use(ids, callback, data.cwd + "_use_" + cid())
        })
        return jsPlus
    }

    Module.define.cmd = {}
    global.Class = Module.define;
    
    global.release = Class.release;
    global.Interface = function () {
        Module.define.call(Module, arguments[0], arguments[1], arguments[2], jsPlus.ObjectType.INTERFACE);
    }
    global.Abstract = function () {
        Module.define.call(Module, arguments[0], arguments[1], arguments[2], jsPlus.ObjectType.ABSTRACT);
    }
    // For Developers

    jsPlus.Module = Module
    jsPlus.Class = Class;
    jsPlus.ObjectType = {
        INTERFACE: 1,
        CLASS: 2,
        ABSTRACT: 3
    }
    data.fetchedList = fetchedList
    data.cid = cid

    jsPlus.require = function (id) {
        var mod = Module.get(Module.resolve(id))
        if (mod.status < STATUS.EXECUTING) {
            mod.onload()
            mod.exec()
        }
        return mod.exports
    }


    /**
     * config.js - The configuration for the loader
     */

    var BASE_RE = /^(.+?\/)(\?\?)?(jsPlus\/)+/

    // The root path to use for id2uri parsing
    // If loaderUri is `http://test.com/libs/jsPlus/[??][jsPlus/1.2.3/]sea.js`, the
    // baseUri should be `http://test.com/libs/`
    data.base = (loaderDir.match(BASE_RE) || ["", loaderDir])[1]

    // The loader directory
    data.dir = loaderDir

    // The current working directory
    data.cwd = cwd

    // The charset for requesting files
    data.charset = "utf-8"

    // Modules that are needed to load before all other modules
    data.preload = (function () {
        var plugins = []

        // Convert `jsPlus-xxx` to `jsPlus-xxx=1`
        // NOTE: use `jsPlus-xxx=1` flag in uri or cookie to preload `jsPlus-xxx`
        var str = location.search.replace(/(jsPlus-\w+)(&|$)/g, "$1=1$2")

        // Add cookie string
        str += " " + doc.cookie

        // Exclude jsPlus-xxx=0
        str.replace(/(jsPlus-\w+)=1/g, function (m, name) {
            plugins.push(name)
        })

        return plugins
    })()

    // data.alias - An object containing shorthands of module id
    // data.paths - An object containing path shorthands in module id
    // data.vars - The {xxx} variables in module id
    // data.map - An array containing rules to map module uri
    // data.debug - Debug mode. The default value is false

    jsPlus.assembly = function (configData) {

        for (var key in configData) {
            var curr = configData[key]
            var prev = data[key]

            // Merge object config such as alias, vars
            if (prev && isObject(prev)) {
                for (var k in curr) {
                    prev[k] = curr[k]
                }
            }
            else {
                // Concat array config such as map, preload
                if (isArray(prev)) {
                    curr = prev.concat(curr)
                }
                    // Make sure that `data.base` is an absolute path
                else if (key === "base") {
                    // Make sure end with "/"
                    if (curr.slice(-1) !== "/") {
                        curr += "/"
                    }
                    curr = addBase(curr)
                }

                // Set config
                data[key] = curr
            }
        }

        emit("config", configData)
        return jsPlus
    }

})(this);

