var JSManager = (function () {
    /*
        utility variable
    */
    var toString = Object.prototype.toString;
    /*
        utility methods
    */
    function extend (base, ext) {
        for ( var i in ext ) base[i] = ext[i];
        return base;
    }
    function isFunction (obj) {
        return toString.call(obj) === "[object Function]";
    }
    function isArray (obj) {
        return toString.call(obj) === "[object Array]";
    }   
    function isString (obj) {
        return typeof obj === 'string';
    }
    function isRegExp (obj) {
        return obj.constructor === RegExp;
    }
    function parallelScriptLoader (plugins, callback, load_after) {
        var plugins       = isArray(plugins) ? plugins : [plugins],
            callback      = callback || function(){},
            load_after    = load_after || function(){},
            plugin_length = plugins.length,
            loaded_length = 0,
            load_observer = setInterval(function () {
                if ( plugin_length == loaded_length ) {
                    clearInterval(load_observer), ( load_observer = null), setTimeout(load_after, 0);
                };
            }, 10),
            head = document.getElementsByTagName('head')[0];
        plugins.forEach(function (plugin) {
            var script = document.createElement('script'),
                src    = plugin;
            script.setAttribute('charset', 'UTF-8');
            script.setAttribute('type', 'text/javascript');
            script.setAttribute('src', src);
            script.onload = script.onreadystatechange = function () {
                if ( !this.readyState || this.readyState == 'loaded' || this.readyState == 'complete' ) {
                    // console.log(src);
                    callback(plugin);
                    loaded_length++;
                }
            };
            head.appendChild(script);
        });
    }
    function seriesScriptLoader (plugins, callback, load_after, head_cache) {
        var plugins    = isArray(plugins) ? plugins : [plugins],
            callback   = callback || function(){},
            load_after = load_after || function(){},
            head       = head_cache || document.getElementsByTagName('head')[0];
        if ( plugins.length ) {
            var src    = plugins.shift(),
                script = document.createElement('script');
            script.setAttribute('charset', 'UTF-8');
            script.setAttribute('type', 'text/javascript');
            script.setAttribute('src', src);
            script.onload = script.onreadystatechange = function () {
                if ( !this.readyState || this.readyState == 'loaded' || this.readyState == 'complete' ) {
                    // console.log(src);
                    callback(src);
                    seriesScriptLoader(plugins, callback, load_after, head);
                }
            };
            head.appendChild(script);
        } else {
            load_after();
        }
    }
    var ScriptLoader = {
        series   : seriesScriptLoader,
        parallel : parallelScriptLoader
    };
    function no_cache (path) {
        var value = +( new Date );
        return path.indexOf('?') == -1 ? [path, "?", value].join('') : [path, '&uniq=', value].join('');
    }
    function list () {
        var ary = [];
        for ( var i = 0, l = arguments.length; i < l; i++ ) {
            Array.prototype.push.apply(ary, isArray( arguments[i] ) ? arguments[i] : [arguments[i]]);
        }
        return ary;
    }
    /*
        core object extend
    */
    extend(Array.prototype, {
        forEach: function (callback) {
            for ( var i = 0, l = this.length; i < l; i++ ) {
                callback(this[i], i, this);
            }
        },
        map: function (callback) {
            var res = [];
            for ( var i = 0, l = this.length; i < l; i++ ) {
                res.push(callback(this[i], i, this));
            }
            return res;
        },
        flatten: function () {
            var element = (arguments.length == 0) ? this : arguments[0];
            return isArray(element)
                   ? Array.prototype.concat.apply([], Array.map(element, arguments.callee))
                   : element;
        },
        no_cache: function (is_no_cache) {
            return is_no_cache ? this.map(no_cache) : this;
        }
    });
    Array.map = function (array, callback, thisObject) {
        return Array.prototype.map.call(array, callback, thisObject);
    };
    extend(String.prototype, {
        no_cache: function (is_no_cache) {
            return is_no_cache ? no_cache(this) : this;
        }
    });
    /*
        main manager
    */
    return new extend((function(){}).prototype, {
        setPages: function (using_str, rules) {
            var rule, is_match = false;
            for ( var name in rules ) {
                rule = rules[name], is_match =
                isRegExp( rule.pattern ) ? using_str.match( rule.pattern ) : isString( rule.pattern )
                                         ? using_str == rule.pattern       : isFunction( rule.pattern )
                                         ? rule.pattern(using_str)         : false;
                if ( is_match ) break;
            }
            return {
                exec: function () {
                    if ( this.can() ) {
                        var loader  = ScriptLoader.hasOwnProperty( rule.method ) ? ScriptLoader[ rule.method ] : ScriptLoader.parallel,
                            plugins = isArray( rule.plugins ) ? rule.plugins.flatten() : [rule.plugins];
                        loader( plugins.no_cache( rule.no_cache ), function(){}, function () {
                            rule.page_js && loader( rule.page_js.no_cache( rule.no_cache ) );
                        });
                    } else {}
                },
                can: function () {
                    return is_match;
                }
            }
        },
        commonProcess: function (common_js, plugins, config) {
            var config = config || {},
                loader = ScriptLoader.hasOwnProperty( config.method ) ? ScriptLoader[ config.method ] : ScriptLoader.parallel;
            loader(plugins, function(){}, isFunction( common_js ) ? common_js : function () {
                loader(common_js);
            });
            return this;
        },
        utility: {
            list: list
        }
    });
})();


