/*
*  SFS JavaScript framework, version 1.0.0
*  FileName SFS.OOP.js 
*  TypeName OOP
*  Created by Chuxuewen 2010
*  Date: 2010-05-04
*  Help: http://jfm-s.gicp.net/
*  
*----------------------------------注不要改动文件名称'richweb.js'----------------------------------------*/

var SFS = SFS || { Assembly: { Type: 'Namespace', Title: 'SFS'} };

SFS.OOP = SFS.OOP || (function (e) {

    document.write('<span id="SFSRootNode" style="position:absolute;left:0px;top:0px;z-index:' +
                '99999;overflow:inline;"><span style="display:none;">SFS 1.0</span></span>');

    var OOP = {
        Assembly: { Type: 'Class', Title: 'OOP' },
        AppDirectory: '',
        CurrentDirectory: document.location.href.replace(/\?.*$/, '').replace(/\/[^\/]*$/, '') + '/',
        RootDirectory: document.location.href.match(/http:\/\/[^\/]*|file:\/\//) + '/',
        Empty: function () { },
        IsReady: false,
        OnReadys: [],

        Extend: function (a, b) {
            for (var i in b) a[i] = b[i];
            return a;
        },

        RandomVar: function () {
            return '_SFS' + Math.round(Math.random() * 100000000000);
        },

        ToUrl: function (url) {
            switch (url.substr(0, 1)) {
                case '/': url = OOP.RootDirectory + url.substr(1); break;
                default: if (url.substr(0, 7) != 'http://' && url.substr(0, 8) != 'file:///') url = OOP.CurrentDirectory + url; break;
            }
            while (url.match(/\.{2,}\//)) {
                var url_ = url.replace(/\.{2,}\/.*$/, '');
                var _url = url.match(/\.{2,}\/.*$/)[0].replace(/^\.{2,}\//, '');
                if (url_.length > OOP.RootDirectory.length) url_ = url_.replace(/[^\/]+\/$/, '');
                url = url_ + _url;
            }
            return url;
        },

        ToArray: function (i) {
            if (i.constructor === Array)
                return i;
            if (i == null || typeof i === 'string' || i.setInterval)
                return [i];
            var j = i.length, r = [];
            while (j--) r[j] = i[j];
            return r;
        },

        GetScriptDirectory: function (fileName) {
            fileName = fileName.toLowerCase();
            var scripts = document.getElementsByTagName('script');
            for (var i = 0; i < scripts.length; i++) {
                if (scripts[i].src) {
                    var src = scripts[i].src.replace(/\?.*$/, '');
                    if (src.match(/[^\/]+$/)[0].toLowerCase() == fileName) return OOP.ToUrl(src).replace(/[^\/]+$/i, '');
                }
            }
        },

        AddScript: function (path, self_) {
            return OOP.AddFile(path, 'js', self_);
        },

        AddCss: function (path, self_) {
            return OOP.AddFile(path, 'css', self_);
        },

        AddFile: function (path, type, self_) {
            self_ = self_ || window;
            type = type.toLowerCase();
            //if (OOP.IsReady || !(window.attachEvent && !window.opera)) {
            var node = self_.document.createElement(type == 'css' ? 'link' : 'script');
            self_.document.getElementsByTagName('head')[0].appendChild(node);
            if (type == 'css') {
                node.type = 'text/css';
                node.rel = 'stylesheet';
                node.href = OOP.ToUrl(path);
            }
            else {
                node.type = 'text/javascript';
                node.src = OOP.ToUrl(path);
            }
            //node.charset = 'UTF-8';
            return node;
            /*
            }
            else {
            var id = OOP.RandomVar();
            if (type == 'css')
            self_.document.write('<link id="' + id + '" type="text\/css" rel="stylesheet" href="' + OOP.ToUrl(path) + '" \/>');
            else
            self_.document.write('<script id="' + id + '" type="text\/javascript" src="' + OOP.ToUrl(path) + '"><\/script>');
            return self_.document.getElementById(id);
            }
            */
        },

        IsBeingScript: function (fileName, self_) {
            return OOP.IsBeingFile(fileName, 'js', self_);
        },

        IsBeingCss: function (fileName, self_) {
            return OOP.IsBeingFile(fileName, 'css', self_);
        },

        IsBeingFile: function (fileName, type, self_) {
            self_ = self_ || window;
            type = type.toLowerCase();
            fileName = OOP.ToUrl(fileName.toLowerCase());
            var nodes = self_.document.getElementsByTagName(type == 'css' ? 'link' : 'script');
            var href = type == 'css' ? 'href' : 'src';
            for (var i = 0; i < nodes.length; i++) {
                if (nodes[i][href] && fileName == OOP.ToUrl(nodes[i][href].toLowerCase())) return true;
            }
            return false;
        },

        IsVariableName: function (name) {
            return (/[^a-zA-Z0-9\_\u4e00-\u9fa5]/.test(name) || !/[a-zA-Z\_\u4e00-\u9fa5]/.test(name.substr(0, 1)))
        },

        Eval: function (x) {
            var obj;
            try {
                obj = eval(x);
            }
            catch (ex) { }
            return obj;
        },

        GetParams: function (fn) {
            var names = fn.toString('sourceToString').match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].replace(/ /gm, '').split(',');
            return names.length == 1 && !names[0] ? [] : names;
        },

        Clone: function (Obj) {
            try {
                if (Obj == null) return Obj;
                else if (Obj.constructor === Array) {
                    var myNewObj = [];
                    for (var i = 0; i < Obj.length; i++) myNewObj[i] = OOP.Clone(Obj[i]);
                    return myNewObj
                }
                else if (Obj.nodeType == 1 || Obj.nodeType == 3) {
                    var n = Obj.cloneNode(true);
                    return n;
                }
                else if (Obj.clearInterval || Obj.getElementById || String(Obj.tagName).match(/html/i))
                    return Obj;
                else if (typeof (Obj) == 'object') {
                    var myNewObj = {};
                    for (var i in Obj) myNewObj[i] = OOP.Clone(Obj[i]);
                    return myNewObj;
                }
                else return Obj;
            }
            catch (ex) {
                throw new Error('OOP.Clone 复制对像发生异常, "' + ex.message + '".');
            }
        },

        Destroy: function (obj) {
            try {
                if (obj.nodeType == 1) obj.parentNode.removeChild(obj);
                else {
                    for (var i in obj) {
                        if (typeof (obj[i]) == 'object') OOP.Destroy(obj[i]);
                        obj[i] = undefined;
                        //delete (obj[i]);
                    }
                    if (obj.constructor === Array) obj.splice(0, obj.length);
                }
            }
            catch (e_) { }
        },

        UsingMonitoring: function (names, load, time) {
            time -= 10;
            for (var i = 0; i < names.length; i++) {
                var type = OOP.Eval(names[i]);
                if (!type) {
                    if (time > 0) setTimeout(function () { OOP.UsingMonitoring(names, load, time); }, 10);
                    else throw new Error('加载' + names[i] + '类型错误');
                    return;
                }
                else {
                    var name = names[i].split('.').pop();
                    if (type.Assembly && !e[name]) e[name] = type;
                }
            }
            load();
        },

        //引用类型,第一个参数为需要引用的类型或接口名称,后面参数为执行事件,即类型库从服务器加载完成后执行的事件,
        //要注意的是类型必需在程序工作目录下,类型对应的文件需按照特定要求的命名规则
        //Using('http://www.163.com/Script/:OOP.UI.CSS.A(CSS)(true)');//为重置程序目录写法
        Using: function (name) {
            var argus = OOP.ToArray(arguments);
            var load = typeof (argus[argus.length - 1]) == 'function' ? argus.pop() : OOP.Empty, names = [];
            for (var i = 0; i < argus.length; i++) {
                var m = (/(([^\:]*\:)*)(.*?)(\(.*\))*$/).exec(argus[i]);
                var type = m[4] ? m[4] : '(js)';
                var isRepeat = (/\(true\)|\(false\)$/).exec(type);
                isRepeat = isRepeat ? (isRepeat[0] == '(true)' ? true : false) : false;
                var path = (m[1] ? OOP.ToUrl(m[1].substr(0, m[1].length - 1)) : OOP.AppDirectory) + m[3].replace(/\./g, '/');
                switch ((/^\(.*?\)/).exec(type.toLowerCase())[0]) {
                    case '(js)':
                        names.push(m[3]);
                        if ((OOP.Eval(names[i]) == undefined && !OOP.IsBeingScript(path + '.js')) || isRepeat) OOP.AddScript(path + '.js');
                        break;
                    case ('(css)'): if ((OOP.Eval(names[i]) == undefined && !OOP.IsBeingCss(path + '.css')) || isRepeat) OOP.AddCss(path + '.css'); break;
                    case '(var)': names.push(m[3]); break;
                }
            }
            if (names.length == 0) load();
            else OOP.UsingMonitoring(names, load, 20000);
        },

        Ready: function () {
            var argus = OOP.ToArray(arguments);
            var load = argus[argus.length - 1];
            if (typeof (load) == 'function') {
                argus[argus.length - 1] = function () {
                    if (OOP.IsReady) load();
                    else {
                        for (var i = 0; i < OOP.OnReadys.length; i++) {
                            if (OOP.OnReadys[i] === load) return;
                        }
                        OOP.OnReadys.push(load);
                    }
                }
            }
            OOP.Using.apply(OOP, argus);
        },

        InitReady: function () {
            for (var i = 0; i < OOP.OnReadys.length; i++)
                OOP.OnReadys[i]();
            OOP.IsReady = true;
        },

        Namespace: function (_name, objs) {
            var argus = OOP.ToArray(arguments);
            var name = typeof (argus[0]) == 'string' ? argus.shift().replace(/\s/g, '') : '';
            var names = name == '' ? [] : name.split('.');
            var space = e;
            for (var i = 0; i < names.length; i++) {
                var j = names[i];
                if (OOP.IsVariableName(j)) throw new Error(name + '命名空间名字错误' + j);
                if (!space[j]) space[j] = { Assembly: { Type: 'Namespace', Title: j} };
                else {
                    if (!space[j].Assembly || space[j].Assembly.Type != 'Namespace') throw new Error(name + '错误的命名空间' + j);
                }
                space = space[j];
            }
            for (i = 0; i < argus.length; i++) {
                var obj = argus[i];
                var title = obj.Assembly.Title;
                if (!space[title]) {
                    space[title] = obj;
                    obj.Assembly.Namespace = name;
                    if (obj[title] && typeof (obj[title]) == 'function') obj[title]();
                }
                else throw new Error(name + '.' + title + '该类型已经存在');
            }
        },

        GetNames: function (name) {
            var names = name.split('.');
            var obj = { Name: names.pop(), Space: names.join('.') };
            return obj;
        },

        Class: function (_name, items) {
            var argus = OOP.ToArray(arguments), names = OOP.GetNames(argus.shift()), name = names.Name, obj = [], base = null;
            if (OOP.IsVariableName(name)) throw new Error(name + '类型名字错误');
            var klass = function () {
                var argus = OOP.ToArray(arguments);
                for (var i in this) {
                    if (this[i] && typeof (this[i]) == 'object') this[i] = OOP.Clone(this[i]);
                }
                var self = this;
                function baseInit(klass, params) {
                    var base = klass.GetBaseType();
                    if (base) {
                        var baseTitle = base.Assembly.Title + (klass.Assembly.Title == base.Assembly.Title ? '$' + klass.Assembly.Title : '');
                        var selfInit = self[klass.Assembly.Title];
                        var initstr = selfInit.toString.apply(selfInit, params).replace(/\/\*+.*\*+\//gm, '')
                        .replace(/\/\/.*$/gm, '').replace(/[\s\t\r\n]/gm, '').replace(/'[^']*'/gm, 'OOP').replace(/"[^"]*"/gm, 'OOP')
                        .match(new RegExp('this\s*\.\s*' + baseTitle + '\s*\\([^\\)]*\\)'));
                        if (initstr) { //已经调用基类构造函数,调用参数需要检测
                            var p = initstr.toString().replace(/^[^\(]+\(/, '').replace(/\)$/, '').split(/,/);
                            baseInit(base, p);
                        }
                        else {//没有调用基类构造函数,参数自动为空
                            baseInit(base, []);
                            self[baseTitle]();
                        }
                    }
                }
                baseInit(klass, argus);
                this[name].apply(this, argus);
            }

            for (var i in klass) delete klass[i]; // = undefined;
            var p = klass.prototype = {};
            for (var i = 0, item; (item = argus[i]) != null; i++) {
                if (argus[i].Assembly) {
                    var tltle = item.Assembly.Title;
                    var type = item.Assembly.Type;
                    switch (type) {
                        case 'Class':
                            if (!base) {
                                base = item;
                                OOP.Extend(p, base.prototype);
                            }
                            else throw new Error('派生类只能有一个基类');
                            break;
                        case 'Method':
                        case 'Field':
                            if (item.Assembly.Modifiers == 'static') klass[tltle] = (type == 'Method' ? item : item());
                            else {
                                if (p[tltle] && base) p[base.Assembly.Title + '$' + tltle] = p[tltle];
                                p[tltle] = (type == 'Method' ? item : item());
                            }
                    }
                }
            }

            if (!p[name]) p[name] = OOP.Empty;
            p.Destroy = p.Destroy || function () { OOP.Destroy(this); }
            klass.Assembly = { Type: 'Class', Title: name };
            p.GetBaseType = klass.GetBaseType = function () { return base; }
            p.GetType = function () { return klass; }
            OOP.Namespace(names.Space, klass);
            if (!e[names.Name])
                e[names.Name] = klass;
            return klass;
        },

        Method: function (name, type, items) {
            var argus = OOP.ToArray(arguments),
                title = typeof (argus[0]) == 'string' ? argus.shift() : '',
                modifiers = typeof (argus[0]) == 'string' ? argus.shift() : '',
                __m = argus, fn;
            if (__m.length == 0) throw new Error("用Method声明方法出现错误,缺少参数");
            else if (__m.length == 1) fn = __m[0];
            else {
                var Get = function () {
                    for (var i = 0, item; (item = __m[i]) != null; i++) {
                        if (OOP.GetParams(item).length == arguments.length) return item;
                    }
                    throw new Error('找不到匹配的方法');
                }
                fn = function () {
                    return Get.apply(this, arguments).apply(this, arguments);
                }
                fn.ToString = fn.toString;
                fn.toString = function () {
                    if (arguments.length == 1 && arguments[0] == 'sourceToString') return fn.ToString();
                    else return Get.apply(this, arguments).toString();
                }
                fn.Get = Get;
            }
            fn.Assembly = { Type: 'Method', Title: title, Modifiers: modifiers };
            return fn;
        },

        Field: function (name, type, items) {
            var argus = OOP.ToArray(arguments),
                title = argus.shift(),
                modifiers = argus[0] == 'static' ? argus.shift() : '';
            var fn = function () {
                return argus.length > 1 ? argus : argus[0];
            }
            fn.Assembly = { Type: 'Field', Title: title, Modifiers: modifiers };
            return fn;
        },

        Enum: function (name, items) {
            var argus = OOP.ToArray(arguments), names = OOP.GetNames(argus.shift()), title = names.Name;
            var Enum = {};
            for (var i = 0, item; (item = argus[i]); i++) {
                if (typeof item === 'string')
                    Enum[item] = i;
                else {
                    if (item.constructor === Array) {
                        for (var j in item)
                            Enum[item[j]] = j;
                    }
                    else {
                        for (var j in item)
                            Enum[j] = item[j];
                    }
                }
            }
            Enum.Assembly = { Type: 'Enum', Title: title };
            Enum.ToArray = function () {
                var r = [];
                for (var i in this) {
                    if (i != 'Assembly' && i != 'ToArray') r.push(i);
                }
                return r;
            }
            OOP.Namespace(names.Space, Enum);
            if (!e[names.Name])
                e[names.Name] = Enum;
            return Enum;
        }
    };

    OOP.AppDirectory = OOP.GetScriptDirectory('richweb.js');
    e.O = e.OOP = OOP;
    e.Using = e.U = OOP.Using;
    e.Namespace = e.N = OOP.Namespace;
    e.Class = e.C = OOP.Class;
    e.Method = e.M = OOP.Method;
    e.Field = e.F = OOP.Field;
    e.Enum = e.E = OOP.Enum;
    e.Ready = e.R = OOP.Ready;
    e.static = 'static';
    if (document.addEventListener) document.addEventListener('DOMContentLoaded', function () { OOP.InitReady(); }, false);
    else {
        var _t = document.onreadystatechange || OOP.Empty;
        document.onreadystatechange = function () {
            _t();
            if (document.readyState == 'complete') OOP.InitReady();
        }
    }
    return OOP;
})(window);
