Source: Redjs.js

/**
 * @namespace Rsd
 * @copyright redmicro all Copyright (c)
 * @author Created by seeker910 on 14-3-18.
 */
 
/**
 *
 * @constructor Redjs
 * @param config
 * */
function Redjs(config) {

    //console.debug(Date.now(),'redjs begin');

    Rsd.apply(this, config || {});

    /**
     * redjs 框架正在加载
     */
    this.isLoading = function isLoading()
    {
        //没有开始时间时间,表示未加载
        if(this.__loadStartTime==undefined || this.__loadStartTime==undefined || !this.__loadStartTime) 
        {
            return false;
        }
        //没有加载结束时间,表示加载中
        return this.__loadEndTime==undefined || this.__loadEndTime == null || !this.__loadEndTime
    };
    /**
     * @public
     * @description 验证浏览是否兼容redjs框架
     * @function
     * @memberof Redjs
     */
    this.validateBrowser = function validateBrowser() {
        return typeof Element !== "undefined";
    };

    /**
     * @public
     * @description 兼容处理,定义EventListener对象
     * @function
     * @memberof Redjs
     * */
    this.defineEventListener = function defineEventListener() {
        //"use strict";
        if (this.validateBrowser() == false) {
            return
        }


        (function () {
            var _add = 'addEventListener';

            if (Element.prototype.hasOwnProperty(_add)) {
                return;
            }

            Element.prototype[_add] == function (name, fn, useCapture) {

                if (this.addEventListener) {
                    this.addEventListener(name, fn, useCapture);

                } else if (this.attachEvent) {
                    this.attachEvent('on' + name, fn)
                }
                else {
                    this['on' + name] = fn;
                }
            };
        })();
        (function () {
            var _remove = 'removeEventListener';
            if (Element.prototype.hasOwnProperty(_remove)) {
                return;
            }
            Element.prototype[_remove] == function (name, fn, useCapture) {
                if (this.removeEventListener) {
                    this.removeEventListener(name, fn, useCapture);

                } else if (this.detachEvent) {
                    this.detachEvent('on' + name, fn)
                }
                else {
                    delete this['on' + name];
                }
            };
        })();

    };

    /**
     * @public
     * @description 兼容处理,定义ClassList对象
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.defineClassList = function defineClassList(type) {
        //"use strict";
        if (this.validateBrowser() == false) {
            return
        }
        var classListPropertyName = "classList";
        if (type.prototype.hasOwnProperty(classListPropertyName)) {
            return;
        }
        var trim = /^\s+|\s+$/g;
        var setClasses = function (elem, classes) {
            elem.className = classes.join(" ");
        };

        var checkAndGetIndex = function (classes, token) {
            if (token === "") {
                throw "SYNTAX_ERR";
            }
            if (/\s/.test(token)) {
                throw "INVALID_CHARACTER_ERR";
            }

            return classes.indexOf(token);
        };

        var classListGetter = function () {

            var elem = this;
            var classes;
            if (typeof(elem.className) == '[object String]') {
                classes = elem.className.replace(trim, "").split(/\s+/);
            } else {
                classes = [];
            }


            return {
                length: classes.length,
                item: function (i) {
                    return classes[i] || null;
                },
                contains: function (token) {
                    return checkAndGetIndex(classes, token) !== -1;
                },
                add: function (token) {
                    if (checkAndGetIndex(classes, token) === -1) {
                        classes.push(token);
                        this.length = classes.length;
                        setClasses(elem, classes);
                    }
                },
                remove: function (token) {
                    var index = checkAndGetIndex(classes, token);
                    if (index !== -1) {
                        classes.splice(index, 1);
                        this.length = classes.length;
                        setClasses(elem, classes);
                    }
                },
                toggle: function (token) {
                    if (checkAndGetIndex(classes, token) === -1) {
                        this.add(token);
                    } else {
                        this.remove(token);
                    }
                },
                toString: function () {
                    return elem.className;
                }
            };
        };

        if (Object.defineProperty) {
            Object.defineProperty(type.prototype, classListPropertyName, {get: classListGetter, enumerable: true});
        } else if (Object.prototype.__defineGetter__) {
            type.prototype.__defineGetter__(classListPropertyName, classListGetter);
        }

    };

    /**
     *
     *窗体自适应
     * */
    this.defineRem = function defineRem(maxWidth) {
        var doc = document;

        var _maxWidth = maxWidth || 640;
        var docEl = doc.documentElement;
        var clientWidth = docEl.clientWidth;
        if (!clientWidth) return;

        if(this.isMobile())
        {
            this.___rem = 14;
            if (clientWidth < _maxWidth) {
                this.___rem = 14 * (clientWidth / _maxWidth);
            }
        }else
        {
            this.___rem = 12;
        }

        docEl.style.fontSize = this.___rem + 'px';
    };

    /*
    * */
    this.getRem = function getRem() {

        if(this.isEmpty(this.___rem))
        {
            var docEl = doc.documentElement;
            this.___rem = parseFloat(docEl.style.fontSize);
        }
        return this.___rem;
    };

    /**
     * @public
     * @description 兼容处理,定义全屏功能;在window对象上定义以下方法: window.isFullScreen(),window.requestFullScreen(),window.cancelFullScreen()
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.defineFullScreen = function defineFullScreen() {
        /*
         Native FullScreen JavaScript API
         Assumes Mozilla naming conventions instead of W3C for now
         */
        var fullScreenApi = {

            supportsFullScreen: false,
            isFullScreen: function () {
                return false;
            },
            requestFullScreen: function () {
            },
            cancelFullScreen: function () {
            },
            fullScreenEventName: '',
            prefix: ''
        };
        var browserPrefixes = 'webkit moz o ms khtml'.split(' ');

        // check for native support
        if (typeof document.cancelFullScreen != 'undefined') {
            fullScreenApi.supportsFullScreen = true;
        }
        else 
        {
            // check for fullscreen support by vendor prefix
            for (var i = 0, il = browserPrefixes.length; i < il; i++) {
                fullScreenApi.prefix = browserPrefixes[i];

                if (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] != 'undefined') 
                { 
                    fullScreenApi.supportsFullScreen = true;
                    break;
                }
            }
        }

        // update methods to do something useful
        if (fullScreenApi.supportsFullScreen) {

            fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';

            fullScreenApi.isFullScreen = function () {
                switch (this.prefix) {
                    case '':
                        return document.fullScreen;
                    case 'webkit':
                        return document.webkitIsFullScreen;
                    default:
                        return document[this.prefix + 'FullScreen'];
                }
            }
            fullScreenApi.requestFullScreen = function (el) {
                return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
            }
            fullScreenApi.cancelFullScreen = function (el) {
                return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
            }
        }

        // 将方法注册到 jQuery中
        if (typeof jQuery != 'undefined') {
            
            jQuery.fn.requestFullScreen = function () {

                return this.each(function () {
                    var el = jQuery(this);
                    if (fullScreenApi.supportsFullScreen) {
                        fullScreenApi.requestFullScreen(el);
                    }
                });
            };
        }

        // export api
        this.apply(window, fullScreenApi);

 
    };
    
    /**
     * 
     * @returns 
     */
    this.fullScreen = function fullScreen(element) {

        if (window.supportsFullScreen == false) {
            alert('SORRY: Your browser does not support FullScreen');
            return;
        }

        if (window.isFullScreen()) {
            window.cancelFullScreen(element||document.body);
        }
        else {
            window.requestFullScreen(element||document.body);
        }
        
        return window.isFullScreen();
    }

    /**
     * @public
     * @description重写 window.console 对象
     * @function
     * @memberof Redjs
     * */
    this.defineConsole = function defineConsole() {
        if (!window.console) {
            var console = {};
            console.info = function (message) {
                //alert(message);
            };
            console.log = function (message) {
                alert(message);
            };
            console.warn = function (message) {
                alert(message);
            };
            console.error = function (message) {
                alert(message);
            };
            window.console = console;
        }
    };

    /**
     * @public
     * @description 创建一个前端程序
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.createApplication = function createApplication(config) {
      
        var me = Rsd || this; 
        if (me.isEmpty(config)) {
            console.error("配置不得为空")
            return;
        }
        if(me.app)
        {
            window.alert('当前页面redjs application已创建,不可重复创建。');
        }
        me.isDebug = config.isDebug ? true : false;
        me.isDemo = config.isDemo ? true : false;

        config.launch = config.launch || function () {
            alert('config launch funtion is null.')
        };
       
        var _config = me.apply(me.app,config);

        _config.appName = config.appName || 'unamed_application';
        _config.appFolder = config.appFolder || './src';
        _config.getUserUrl = config.getUserUrl || 'authority/user';
        _config.indexUrl = config.indexUrl || 'login.html';
        _config.appEdition = config.appEdition || 'beta';
        _config.appVersion = config.appVersion || '3.0';
        _config.logoUrl = config.version || 'resources/images/sys/logo.jpg';
        _config.requires = config.requires || [];
       
        me.app = me.create('Rsd.common.Application', _config);
        me.app.createdTime = Date.now();

        //console.debug(me.app.createdTime,'app created.');

        if (me.isMobile()) {
            
            window.onerror = function (error) {
                alert(error);
            }
        }
        
        /*
         * */
        me.onReady(function appStartup() {

            //console.debug(Date.now(),'app startup...');
            me.app.startTime = new Date().getTime();
            
            //等待框架加载
            var _id = setInterval(function(){

                if(!Rsd.isLoading())
                {
                    clearInterval(_id);
                }
                //程序启动前执行
                if (me.isFunction(me.app.beforeRun)) {
                    me.app.beforeRun.call(me.app);
                }
                //console.trace('me.app.run');
                me.app.run();

            },100)
             
           
        });

        return me.app;
    };

    /*
    *
    * */
    this.clearHotKey = function clearHotKey(key) {
        if (this.isEmpty(this.__hotKey)) {
            return;
        }
        if (arguments.length == 0) {
            this.__hotKey = {};
            return;
        }
        delete this.__hotKey[key.toLowerCase()];
    };
    /**
     *@desc 注册全局热键
     * */
    this.registerHotKey = function hotKey(key, fn, caller, args) {
        this.__hotKey = this.__hotKey || {};
        this.__hotKey[key.toLowerCase()] = {fn: fn, caller: caller, args: args};
    };
    /*
    *
    * */
    this.exeHotKeyFun = function exeHotKeyFun(key) {
        if (this.isEmpty(key)) {
            return;
        }
        if (this.isEmpty(this.__hotKey)) {
            return;
        }
        var _hotKey = this.__hotKey[key.toLowerCase()];
        if (_hotKey) {
            this.callFunction(_hotKey.caller || this, _hotKey.fn, _hotKey.args);
        }

    };

    /**
     * @public
     * @description 注册事件,在 document onload 事件执行结束
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.onReady = function onReady(callback) {

        var me = Rsd || this;

        var _fn = function (){

            if (me.isFunction(callback)) {

                callback.call();
            }

            window.Rsd.events.fire(null,'ready',arguments);
        }

        if(me.isReady())
        {
            _fn();
        }
        else
        {
            if (document.addEventListener) {
                document.addEventListener("DOMContentLoaded", _fn, false);
            }
        }

    };
    /**  
    *  DOMContentLoaded 事件 即:HTML 文档被加载和解析完成后
    * */
    this.isReady = function isReady( ) {
        return this.__readyTime  > 0;
    }
    /**
     * @public
     * @description 注册 document Resize事件
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.onResize = function onResize(obj, callback) {
        if(this.isWeChatApp())
        {
            return;
        }
        var me = Rsd || this;
        var events = me.events;
        var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
        events.add(obj, resizeEvt, callback);
    };

    /** 
     * @description 注销 document Resize事件
    */
    this.unResize = function unResize(callback) {
        if(this.isWeChatApp())
        {
            return;
        }
        var me = Rsd || this;
        var _fn = callback;
        var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
        var events = me.events;
        events.remove(resizeEvt, _fn);
    };
    /**
     *  @description 触发 document Resize事件
     * @param {*} evt 
     * @returns 
     */
    this.fireResize = function fireResize(evt)
    {
        if(this.isWeChatApp())
        {
            return;
        }
        var me = Rsd || this;
       
        var resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
        var events = me.events;
        events.fire(me, resizeEvt,evt);
    }; 
    /**
     *  @public
     * @description Fires on the source object continuously during a drag operation.
     * @function
     * @param dom 可拖动区域的dom对象
     * @param callback 控件拖动结束后回调方法
     * @memberof Redjs
     * */
    "use strict";
    this.onDrag = function onDrag(dom, callback) {
        if(this.isWeChatApp())
        {
            return;
        }
        var _dom = dom || document;
        _dom.ondrag = function (event) {
            event.preventDefault();
            if (Rsd.isFunction(callback)) {
                callback(event);
            }
        }
    };

    /*
    *
    * */
    "use strict";
    this.onDrop = function onDrop(dom, callback) {
        if(this.isWeChatApp())
        {
            return;
        }
        var _dom = dom || document;
        _dom.ondrop = function (event) {
            event.preventDefault();
            if (Rsd.isFunction(callback)) {
                callback(event);
            }
        }

    };

    /*
    * 使用 onDrop 前必须注册onDragOver
    * Fires on the target element continuously while the user drags the object over a valid drop target.
    * */
    "use strict";
    this.onDragOver = function onDragOver(dom, callback) {
        if(this.isWeChatApp())
        {
            return;
        }
        var _dom = dom || document;
        _dom.ondragover = function (event) {
            event.preventDefault();
            if (Rsd.isFunction(callback)) {
                callback(event);
            }
        }
    };
    /*
    *
    *  Fires on the target object when the user moves the mouse out of a valid drop target during a drag operation.
    * */
    "use strict";
    this.onDragLeave = function onDragLeave(dom, callback) {
        if(this.isWeChatApp())
        {
            return;
        }
        var _dom = dom || document;
        _dom.ondragleave = function (event) {
            event.preventDefault();
            if (Rsd.isFunction(callback)) {
                callback(event);
            }
        }
    };
    /*
    * Fires on the source object when the user releases the mouse at the close of a drag operation.
    * */
    "use strict";
    this.onDragEnd = function onDragEnd(dom, callback) {
        if(this.isWeChatApp())
        {
            return;
        }
        var _dom = dom || document;
        _dom.ondragend = function (event) {
            event.preventDefault();
            if (Rsd.isFunction(callback)) {
                callback(event);
            }
        }
    };
    /*
    *
    * Fires on the target element when the user drags the object to a valid drop target.
    * */
    this.onDragEnter = function onDragEnter(dom, callback) {
        if(this.isWeChatApp())
        {
            return;
        }
        var _dom = dom || document;
        _dom.ondragenter = function (event) {
            event.preventDefault();
            if (Rsd.isFunction(callback)) {
                callback(event);
            }
        }
    };
    /**
     * @public
     * @description 获取dom 计算后样式
     * @param {stirng} id dom ID值
     * @param {string} style 样式名称
     * @function
     * @memberof Redjs
     * */
    "use strict";
    this.getComputedStyle = function getComputedStyle(id, style) {
        if(this.isWeChatApp())
        {
            console.error('Wechat小程序环境下getComputedStyle方法不可用');
            return null;
        }
        var el = document.getElementById(id);
        var _value = null;
        if (el) {
            if (el.currentStyle) {
                _value = el.currentStyle[style];
                alert('ie:' + _value);
            } else if (window.getComputedStyle) {
                _value = window.getComputedStyle(el, null)[style];
                alert('firefox:' + _value);
            }
        }
        return _value;

    };
    /**
     * @description 预加载图片对象
     * */
    this.loadImage = function loadImage(src) {
        var img = new Image();
        img.src =  src;
        return img;
    };
    /**
     * @public
     * @description 展示提示信息,可自定义按钮
     * @param {string} title 标题
     * @param {string} msg 消息内容
     * @param {array} btns 按钮集合,如:[{text:'OK',fn:function(){}}]
     * @function
     * @memberof Redjs
     * */
    this.showMessage = function showMessage(title, msg, btns) {
        var me = Rsd || this;
        var _title = "提示信息"
        var _msg = '';
        var _btns = [{text: '确 定', fn: null}];
        if (arguments.length > 2) {
            _title = arguments[0];
            _msg = arguments[1];
            _btns = arguments[2];
        }
        if (arguments.length == 2) {
            _msg = arguments[0];
            _btns = arguments[1];
        }
        if (arguments.length == 1) {
            _msg = arguments[0];
        }

        var box = me.create('Rsd.control.MessageBox', {
            title: _title, 
            messageType: 'text', 
            messageStyle:{fontSize:'130%'},
            message:_msg,
            buttons:_btns,
            parent: me
        });

        return box.showDialog(document.body);
    };
    /**
     *  @function
     * @memberof Redjs
     * */
    this.showHtml = function showHtml(title, html) {
        var me = Rsd || this;
        var _title = title;
        var _html = html||'';
        if (arguments.length == 1) {
            _title = "提示信息";
            _html = arguments[0];
        }
        var _btns = [{text: '确 定', fn: null}];
        var box = me.create('Rsd.control.MessageBox', {
            width: 600,
            height: 400,
            title: _title,
            messageType: 'html',
            message:_html,
            buttons:_btns,
            parent: me
        });
        return box.showDialog(document.body);
    };
    /**
     * @description 展示【确定】或【取消】操作的信息提示框
     * @function
     * @memberof Redjs
     * */
    this.yesOrNo = function yesOrNo(msg, callback_yes, callback_no) {
        var me = Rsd || this;
        var _title = "重要操作确认"
        var _msg = msg;
        var _btns = [{text: '确 定', fn: callback_yes}, {text: '取 消', fn: callback_no}];

        var box = me.create('Rsd.control.MessageBox', {title: _title, message:_msg,buttons:_btns,parent: this});
        return box.showDialog(document.body);
    };


    /**
     * @public
     * @description 提示popup信息
     * @param {number} x x轴方向位置信息,以提示框居中时为位置 0
     * @param {number} y y轴方向位置信息
     * @function
     * @memberof Redjs
     * */
    this.showPopup = function showPopup(msg, x, y) {
        var me = Rsd || this;

        var _height = 50;
        var _width = 250;
        me.__popup_count = this.__popup_count||0;

        var _popup = me.create('Rsd.container.Component', {
            border: false,
            header:{visible:false},
            width: '100%',
            height: _height,
            fixed:true,
            layout: 'fit',
            style: {zIndex: 99999,top:(y||0) + me.__popup_count*50},
            bodyCls:'x-popup-bar',
            items: [
                {
                    xtype: 'label',
                    cls: 'x-popup-box',
                    label:{visible:false},
                    style:{left:x||0,lineHeight: _height}, 
                    ctrlCls: 'x-text', 
                    height: _height,
                    width: _width, 
                    text: msg
                }
            ]
        });

        _popup.header.visible = false;
        me.__popup_count++;
        _popup.show(function () {
            _popup.animate({top:-50},500,function () {});
        });


        setTimeout(function () {
            me.__popup_count--; 
            _popup.close({opacity:0,top:-50},2000,0);

        }, 3000);

    };

    /**
     * @public
     * @description 以动画的形式 将TipBox(有尖头指向)样式显示到目标(x,y)位置
     * @param string msg
     * @param int w
     * @param int h
     * @param int x
     * @param int x
     * @param int y
     * @param int position
     * */
    this.showTip = function showTip(msg ,w,h,x,y,position) {

        var _msg = null;
        var _w = null;
        var _h = null;
        var _x = null;
        var _y = null;
        var _position = null;

        for(var i =0;i <  arguments.length;i++)
        {
            if(this.isString(arguments[i]) && _msg == null)
            {
                _msg = arguments[i];
                continue;
            }
            if(this.isString(arguments[i]) && _position == null)
            {
                _position = arguments[i];
                continue;
            }
            if(this.isNumber(arguments[i]) && _w ==null)
            {
                _w = arguments[i];
                continue;
            }
            if(this.isNumber(arguments[i]) && _h ==null)
            {
                _h = arguments[i];
                continue;
            }
            if(this.isNumber(arguments[i]) && _x==null)
            {
                _x = arguments[i];
                continue;
            }
            if(this.isNumber(arguments[i]) && _y==null)
            {
                _y = arguments[i];
                continue;
            }

        }
        //
        this.loadClass('Rsd.control.HtmlBox');

        var _tip = this.create("Rsd.container.TipBox",{
            header:{position:_position||'top',space:0},
            width:_w||200,
            height:_h||50,
            border:false, 
            style:{opacity:0},
            layout:{type:'border',align:'center'},
            listeners:{
                'click':{
                    element:'dom',
                    fn:function (sender,event) {

                        this.close(1000,{opacity:0,top:_y-200,left:_x-200},0);
                        Rsd.events.remove(Rsd,'click',this.id);
                    }
                }

            },
            items:[
                {
                    xtype:'html-box',
                    label:{visible:false},
                    //margin:'10 10 10 10',
                    region:'center',
                    style:{align:'center'},
                    height:'100%',width:'100%',html:_msg||''
                }
            ]
        });

        _tip.show({left:_x==null?100:_x,top:_y==null?50:_y },1000,function () {
        
            //延时加入 队列 防止click 事件 关闭显示
            Rsd.events.add(Rsd,'click',this.id,function () {
                _tip.close(1000,{opacity:0,top:_y-200,left:_x-200},0);
            },true);
        });

        return _tip.id;
    };

    /**
     * @description 提示JSON数据信息
     * @function
     * @memberof Redjs
     * */
    this.alertLog = function alertLog(obj) {
        alert(Rsd.toString(obj));
    };

    /**
     * @public
     * @description 提示信息
     * @function
     * @memberof Redjs
     * */
    this.alert = function alert(title, msg, callback) {
        //console.log(arguments);
        var me = Rsd || this;
        var _title = "提示信息";
        var _msg = "";
        var _callback = null;
        if (arguments.length > 2) {
            _title = arguments[0];
            _msg = arguments[1];
            _callback = arguments[2];
        }
        if (arguments.length == 2) {
            if (Rsd.isFunction(arguments[1])) {
                _msg = arguments[0];
                _callback = arguments[1];
            }
            else {
                _title = arguments[0];
                _msg = arguments[1];
            }

        }
        if (arguments.length == 1) {
            _msg = arguments[0];
        }

        var _btns = null;
        if (Rsd.isFunction(_callback)) {
            _btns = [{text: '确 定', fn: _callback}];
        }else {
            _btns = [{text: '确 定', fn: null }];
        }
        var box = me.create('Rsd.control.MessageBox', {title: _title, message:_msg,messageType:((_msg.indexOf('<')>-1||_msg.indexOf('>')>-1 ) ? 'html':'text'),buttons:_btns,parent: this});
        box.showDialog(document.body);
    };

    /**
     * @public
     * @description 关闭窗口
     * @function
     * @memberof Redjs
     * */
    this.closeWindow = function closeWindow() {
        var win = window.open("about:blank", "_self");
        win.close();
    };
    /*
    * */
    this.getTiming = function getTiming(ext) {
        var _timing ={
            redjsLoadStartTime: Rsd.__loadStartTime,
            redjsLoadEndTime: Rsd.__loadEndTime,
            redjsLoadTime: (Rsd.__loadEndTime || 0) - (Rsd.__loadStartTime || 0),
            redjsBuildStartTime: Rsd.__buildStartTime,
            redjsBuildEndTime: Rsd.__buildEndTime,
            redjsBuildTime: (Rsd.__buildEndTime || 0) - (Rsd.__buildStartTime || 0)
        };
        var _per = window.performance;
        if (_per && _per.timing) {

            _timing.start = _per.timing.navigationStart;
            //DNS查询耗时
            _timing.DNS = _per.timing.domainLookupEnd - _per.timing.domainLookupStart;
            //TCP链接耗时
            _timing.TCP = _per.timing.connectEnd - _per.timing.connectStart;
            //request请求耗时
            _timing.request = _per.timing.responseEnd - _per.timing.responseStart;
            //解析dom树耗时
            _timing.domComplete = _per.timing.domComplete - _per.timing.domInteractive;
            //白屏时间
            _timing.white = _per.timing.responseStart - _per.timing.navigationStart;
            //domready时间(用户可操作时间节点) :
            _timing.domReady = _per.timing.domContentLoadedEventEnd - _per.timing.navigationStart;
            //onload时间(总下载时间)
            _timing.total = _per.timing.loadEventEnd - _per.timing.navigationStart;

            _timing.end = _per.timing.loadEventEnd;
        }

        var _data =  Rsd.apply( _timing,ext||{});

        return _data;
    };

    /**
     * @public
     * @description 本地log日志,不向日志服务器发送
     * @function
     * @memberof Redjs
     * */
    this.log = function log(msg) {
          
        var _args = [];
        var stack =  new Error().stack.split("@\n");
        stack.splice(0,2);
        stack = stack.join("<-");
        _args.push.apply(_args,arguments);
        _args.push.apply(_args,['call stack:' + stack + 'in class (' + this.$className||'' + ') by object(id:'+this.id||'' + ')']);
        console.log.apply(this,_args);
         
    };

    /**
     * @public
     * @description 记录调试信息
     * @param {string} msg
     * @param {object} data
     * @function
     * @memberof Redjs
     * */
    this.debug = function debug(msg, data) {
        var me = Rsd || this;
        //debugger;
        if (me.isDebug) {

            var _args = [];
            var stack =  new Error().stack.split("@\n");
            stack.splice(0,2);
            stack = stack.join("<-");
            _args.push.apply(_args,arguments);
            _args.push.apply(_args,['call stack:' + stack + 'in class (' + this.$className||'' + ') by object(id:'+this.id||'' + ')']);
            console.debug.apply(this,_args);
            //发送到日志服务器
            if (me.Logger) {
                me.Logger.debug(msg, data);
            }
        }

    };

    
    /**
     * @public
     * @description info日志
     * @param {string} msg
     * @param {object} data
     * @function
     * @memberof Redjs
     * */
    this.info = function info(msg, data) {
        var me = Rsd || this;
        var _args = [];
        var stack =  new Error().stack.split("@\n");
        stack.splice(0,2);
        stack = stack.join("<-");
        _args.push.apply(_args,arguments);
        _args.push.apply(_args,['call stack:' + stack + 'in class (' + this.$className||'' + ') by object(id:'+this.id||'' + ')']);
        console.log.apply(this,_args);
        //发送到日志服务器
        if (me.Logger) {
            me.Logger.info(msg, data);
        }
    };

    /**
         * @public
         * @description 警告日志
         * @param {string} msg
         * @param {object} data
         * */
    this.warn = function warn(msg, data) {
        var me = Rsd || this;
        var _args = [];
        var stack =  new Error().stack.split("@\n");
        stack.splice(0,2);
        stack = stack.join("<-");
        _args.push.apply(_args,arguments);
        _args.push.apply(_args,['call stack:' + stack + 'in class (' + this.$className||'' + ') by object(id:'+this.id||'' + ')']);
        console.warn.apply(this,_args);
        //发送到日志服务器
        if (me.Logger) {
            me.Logger.warn(msg, data);
        } 
    };

    /**
     * @public
     * @desc 调试模式下,提示错误信息,非调试模式打印到控制台;如日志接口(Rsd.Logger)存在发送日志到服务端
     * @param {string} msg
     * @param {string} url
     * @param {object} ex
     * */
    this.error = function error(msg, url, ex) {
        var _msg = msg;
        var _url = url;
        var _ex = ex;

        if (arguments.length == 1 && this.isObject(arguments[0])) {
            _msg = null;
            _url = null;
            _ex = arguments[0];
        }

        if (arguments.length == 2) {
            _url = null;
            _ex = null;
            if(this.isString(arguments[1]))
            {
                _url = arguments[1];
            }else
            {
                _ex = arguments[1];
            }
            
        }
        var me = Rsd || this;
     
        if (me.isDebug) {
            console.error(arguments);
            alert(msg + ((_ex && _ex.message) ? (':' + _ex.message) : '')); 
        }
        else {
            console.trace(arguments);
            console.error((Rsd.isEmpty(msg) ? '运行异常' : _msg) + (_url ? '(' + _url + ')' : ''));
        }
        if (_ex) {
            console.trace(_ex);
        }
        if (me.Logger) {
            me.Logger.error(_msg + '(' + _url + ')', ex);
        }
    };

    
    /**
     * @public
     * @description 写入会话缓存
     * @param {string} key
     * @param {string} value
     * */
    this.writeSession = function writeSession(key, value) {
        var me = Rsd || this;
        var storage = window.sessionStorage;
        if (storage) {
            storage.setItem(key, me.toString(value));
        }
    };

    /**
     * @public
     * @description 读取指定会话缓存
     * @param {string} key
     * */
    this.readSession = function readSession(key) {
        var me = Rsd || this;
        var storage = window.sessionStorage;
        var data = null;
        if (storage && storage.getItem(key) != null) {
            data = me.toJson(storage.getItem(key));
        }

        return data;
    };

    /**
     * @public
     * @description 清除指定会话缓存
     * @param {string} key
     * */
    this.removeSession = function removeSession(key) {
        var me = Rsd || this;
        var storage = window.sessionStorage;
        if (key && storage && storage.getItem(key) != null) {
            return storage.removeItem(key);
        }
    };

    /**
     * @public
     * @description 清除所有会话缓存
     * */
    this.clearSession = function clearSession()
    {
        window.sessionStorage.clear();
    };

    /**
     * @public
     * @description 写到本地缓存
     * @param {string} key
     * */
    this.writeLocal = function writeLocal(key, value) {
        var me = Rsd || this;
        var storage = window.localStorage;
        if (storage) {
            storage.setItem(key, me.toString(value));
        }
    };

    /**
     * @public
     * @description 读取指定本地缓存
     * @param {string} key
     * */
    this.readLocal = function readLocal(key) {
        var me = Rsd || this;
        var storage = window.localStorage;
        var data = null;
        if (storage && storage.getItem(key) != null) {
            data = me.toJson(storage.getItem(key));
        }

        return data;
    };

    /**
     * @public
     * @description 清除指定本地缓存
     * @param {string} key
     * */
    this.removeLocal = function removeLocal(key) {
        var me = Rsd || this;
        var storage = window.localStorage;

        if (key && storage && storage.getItem(key) != null) {
            return storage.removeItem(key);
        }

    };

    /**
     * @public
     * @description 清除所有本地缓存
     * */
    this.clearLocal = function clearLocal() {
        window.localStorage.clear();
    };

    /**
     * @public
     * @description 添加Cookie
     * @param {*} name 
     * @param {*} value 
     * @param {*} expiresHours  单位:小时
     */
    this.addCookie = function addCookie(name, value, expiresHours) {
        var me = Rsd || this;
        var cookieString = name + "=" + escape(value);
        //判断是否设置过期时间
        if (expiresHours > 0) {
            var date = new Date();
            date.setTime(date.getTime + expiresHours * 3600 * 1000);
            cookieString = cookieString + "; expires=" + date.toGMTString();
        }
        document.cookie = cookieString;
    };
    /**
     * @public
     * */
    this.getCookie = function getCookie(name) {
        var me = Rsd || this;
        var strCookie = document.cookie;
        var arrCookie = strCookie.split("; ");
        for (var i = 0; i < arrCookie.length; i++) {
            var arr = arrCookie[i].split("=");
            if (arr[0] == name) return arr[1];
        }
        return "";
    };
    /**
     * @public
     * */
    this.deleteCookie = function deleteCookie(name) {
        var me = Rsd || this;
        var date = new Date();
        date.setTime(date.getTime() - 10000);
        document.cookie = name + "=null; expires=" + date.toGMTString();
    };

    /**
     * @public
     * */
    "use strict";
    this.getScript = function getScript(obj, f) {
        if (obj == undefined) {
            return "null";
        }

        var r = [];
        if (typeof obj == "string") {
            return "\'" + obj.replace(/([\"\\])/g, "\\$1").replace(/(\n)/g, "\\n").replace(/(\r)/g, "\\r").replace(/(\t)/g, "\\t").replace(/(\')/g, "\\'") + "\'";
        }
        if (typeof obj == "object") {
            var _type = this.getType(obj);
            if (_type == '[object Array]') {
                for (var i = 0; i < obj.length; i++)
                    r.push(Rsd.getScript(obj[i], true))
                r = "[" + r.join() + "]";
            }
            if (_type == '[object Object]') {
                if (Rsd.isCreated(obj)) {
                    this.error('类型为[' + obj.xtype + ']属性定义为已创建对象,该类无法定义。');
                } else {

                    for (var i in obj) {
                        if(obj.hasOwnProperty(i))
                        {
                            if (typeof obj[i] != 'function') {

                                r.push((f ? '' : 'this.') + i + (f ? ':' : '=') + this.getScript(obj[i], true));
                            }
                            else {
                                //无法实现自动为未命名的方法加上方法名
                                r.push((f ? '' : 'this.') + i + (f ? ':' : '=') + obj[i].toString());
                            }
                        }

                    }
                    r = (f ? '{' : '') + r.join(f ? ',' : ';') + (f ? '}' : '');
                }

            }

            return r;
        }

        return obj.toString().replace(/\"\:/g, '":""');
    };

    /**
     * @public
     * @param className string
     * @param config object
     * @param callback function
     *
     * */
    "use strict";
    this.define = function define(className, config, callback) {

        if(!Rsd.isString(className))
        {
            console.error(className);
            return;
        }
        //此处需要进行 命名规范 约束
        
        //
        var me = Rsd || this;

        me.debug('define class:' + className);

        if (me.classes[className] != null) {

            me.error('Failed to define class ['+  className + '],because it has defined.'); 
            return;
        }

        config = config || {};
        //模拟类静态方法,在类型定义之前被执行
        if (config.static && config.static instanceof Function) {
            var _static = config.static;
            delete  config.static;

            if (_static.length > 0) {
                _static(function () {
                    me.define(className, config, callback);
                });

                return;
            } else {
                _static();
            }

        }

        var _extend = config.extend;
        //delete config.extend;
        var _requires = config.requires;
        delete config.requires;
        var _xtype = config.xtype ? config.xtype : className;
        delete config.xtype;
        var _properties = {};
        var _functions = {};
        if (_extend === className) {
            this.error('Failed to define class', className, new Error('extend class don\'t allow same to className'));
            return;
        }

        if (_extend) {
            if (!me.classes[_extend]) {

                me.debug('load class '+className+' extend class:' + _extend);
                me.loadClass(_extend);

            }

            if (typeof me.classes[_extend] !== 'function') {
                throw new Error('define ' + className + ' Fail ,Inheritance dependency error:' + _extend + ' can not found.');
            }

        }

        var _namespace=null;
        var _namespaces = className.split('.');

        for (var i in config) {

            if(!config.hasOwnProperty(i))
            {
                continue;
            }
            if (me.isFunction(config[i])) {
                _functions[i] = config[i]
                continue;
            }

            //定义对象属性
            _properties[i] = config[i];
            continue;
        }

        if (!_functions.hasOwnProperty('constructor') || !me.isFunction(_functions.constructor)) {
            _functions['constructor'] = function () {
                me.warn('Could not find function constructor in ' + this.$className + '.');
            };
        }


        for (var n in _namespaces) {

            if(!_namespaces.hasOwnProperty(n))
            {
                continue;
            }

            var _script="";


            if (!_namespaces[n]) {
                continue;
            }

            if (_namespace) {
                _namespace += '.';
            }
            else {
                _namespace = '';
            }

            _namespace += _namespaces[n];

            if (n < _namespaces.length - 1) {
                _script = 'if(!' + _namespace + '){' + _namespace + '={};}; ';
            }
            else {

                _script = '' + _namespace + ' =';
                _script += ' function ' + _namespaces[n] + '(_config){';
                //动态原型技术
                if (_extend) {
                    _script += 'this.__proto__.__proto__ = new ' + (_extend || 'Object') + '(false);';
                }

                //属性初始化
                _script += me.getScript(_properties) + ';';
                //_script += 'console.log(this.constructor);';
                _script += 'this.constructor.call(this,_config);';
                _script += 'if(_config && Rsd.isFunction(this.init)){ this.init.call(this);}';
                _script += 'return this;};';
                _script += 'Rsd.classes[\'' + className + '\']=' + _namespace + ';';

            }

            window.eval(_script);
        }

        var _depClass = _extend;

        for (var p in _functions) {
            me.classes[className].prototype[p] = _functions[p];
        }

        me.classes[className].prototype.$className = className;
        me.classes[className].prototype.xtype = _xtype;
        me.classes[className].prototype.dependencies = new Array();
        me.classes[className].prototype.dependencies[_xtype] = className;

        if (_depClass) {
            me.classes[className].prototype.dependencies[me.types[_depClass]] = _depClass;
        }

        var __isLoading = me.isLoading();
        for (var i in _requires) {
            if(!_requires.hasOwnProperty(i))
            {
                continue;
            }
            _depClass = _requires[i];
            if (_depClass.length == 0) {
                continue;
            }
            //框架加载中, 不加载依赖项
            if (__isLoading === false && !me.classes[_depClass]) {
                me.debug('load class '+className+' dependent :' + _extend);
                this.loadClass(_depClass);
            }

            this.classes[className].prototype.dependencies[me.types[_depClass]] = _depClass;
        }

        me.types[className] = _xtype

        if (me.xtypes.hasOwnProperty(_xtype)) {
            me.error('The xtype \'' + _xtype + '\' of class [' + className + '] is exist.');
        }
        else {
            me.xtypes[_xtype] = className;
        }


        //_properties 属性定义
        var _t = _extend ? new me.classes[_extend]() : {};

        var _script = '';
        for (var p in _properties) {
            if (_t.hasProperty && _t.hasProperty(p)) {

                /** @bug 父类对象属性不能重复定,否则会覆盖父类功能*/
                //console.log('[' + className + '] prototype has property \'' + p + '\',program continue.');
                continue;
            }

            //var enumerable = true;
            //console.log('==>define [' + className + '] property :' + p);
            _script += 'Rsd.defineProperty(Rsd.classes[\'' + className + '\'],\'' + p + '\',function __getter(){return this[\'__' + p + '\'];},function __setter(value){ this[\'__' + p + '\'] = value;},true);';
        }

        //console.log(_script);
        window.eval(_script);

        if (me.isFunction(callback)) {
            callback.call(me, me.classes[className]);
        }

        me.debug('define class:' + className + ' end.');
    };

    /**
     * @public
     * @description 属性定义,如果属性存在,将跳过
     * */
    "use strict";
    this.defineProperty = function defineProperty(type, propertyName, getter, setter, enumerable) {

        var _type = this.isString(type) ? this.classes[type] : type;

        if (!_type) {
            return;
        }
        if (_type.prototype.hasOwnProperty(propertyName)) {
            console.warn('Property [' + propertyName + '] is exsit in ' + _type.prototype.$className);
            return;
        }

        if (Object.defineProperty) {
            var _obj = {enumerable: enumerable, configurable: true};//{value:defaultValue,writable:true,enumerable: enumerable,configurable:true};
            if (getter) {
                _obj['get'] = getter;
            }
            if (setter) {
                _obj['set'] = setter;
            }
            Object.defineProperty(_type.prototype, propertyName, _obj);
        } else {
            if (getter && Object.prototype.__defineGetter__) {
                _type.prototype.__defineGetter__(propertyName, getter);
            }
            if (setter && Object.prototype.__defineSetter__) {
                _type.prototype.__defineSetter__(propertyName, setter);
            }
        }
    };

    /**
     *@public
     * */
    this.setDomToken = function setDomToken(flag, value) {
        this.domFlag = flag;
        this.domFlagValue = value;
    };

    /**
     * @public
     * @description 单列和需要缓存的对象会存入Rsd.objects对象中。
     * */
    "use strict";
    this.create = function create(className, config) {
        var me = Rsd || this;
        //debugger;
        if (!className || className == '') {
            throw new Error('call Rsd.create function argument \'className\' is null. ');
        }
        if (me.singletons[className]) {
            return me.objects[me.singletons[className]];
        }

        me.debug('create object:' + className);

        me.loadClass(className);

        if (me.classes[className] == null) {
            throw new Error('class \'' + className + '\' not found.');
        }

        var _obj = new me.classes[className](config);

        if (_obj.events) {
            _obj.events.fire(_obj, 'aftercreate', {});
        }


        //debugger;
        if (_obj.singleton || _obj.cache) {
            me.objects[_obj.id] = _obj;
        }
        if (_obj.singleton) {
            me.singletons[className] = _obj.id;
        }
        me.debug('return object:' + _obj.id);
        return _obj;
    };
    /**
     * @public
     * @param xtype
     * @param config
     *
     * */
    "use strict";
    this.widget = function widget(xtype, config) {

        var _xtype = xtype;
        var _className = null;
        var _config = config || {};


        if (this.isString(xtype)) {
            _xtype = xtype;
        }

        if (this.isObject(xtype)) {

            _config = xtype;
            _xtype = _config['xtype'];
            delete  _config['xtype'];
        }
        if (this.isEmpty(_xtype)) {
            console.error(arguments);
            throw new Error('call Redjs.widget of argument \'xtype\' is null.');
        }
        _className = this.xtypes[_xtype];
        if (!_className) {
            throw new Error('call Redjs.widget of xtype \'' + _xtype + '\' mapping className not found.');
        }

        return this.create(_className, _config);
    };

    /**
     *@public
     * */
    this.isCreated = function isCreated(obj) {
        return obj && this.isFunction(obj.isCreated) && obj.isCreated();
    };

    /**
     * @private
     * */
    this.getId = function getId(prefix) {
        
        this.__id++;
        var _id = prefix.replaceByRegExp('[-]', '_') + '_' + this.__id;
        if(this.isWeChatApp())
        {
            //console.error('Wechat小程序环境下getId方法不可用');
            return _id;
        }else
        {
            while (document.getElementById(_id)) {
                this.__id++;
            }
        }
         
        return _id;
    };

    /**
     * @public
     * @description zIndex  + offset
     * @return number
     * */
    this.getZIndex = function getZIndex(offset) {
        return this.__zIndex + offset;
    };

    /**
     * @public
     * @description 对象克隆
     * */
    this.clone = function clone(obj) {
        var o;

        switch (typeof obj) {
            case 'undefined':
                break;
            case 'string'   :
                o = obj + '';
                break;
            case 'number'   :
                o = obj - 0;
                break;
            case 'boolean'  :
                o = obj;
                break;
            case 'object'   :
                if (obj === null) {
                    o = null;
                    break;
                }
                if (obj instanceof Array) {
                    o = [];
                    for (var i = 0, len = obj.length; i < len; i++) {
                        o.push(clone(obj[i]));
                    }
                } else {

                    if (obj instanceof Node) {
                        o = obj.cloneNode(true);
                        break;
                    }

                    o = {};

                    for (var k in obj) {
                        o[k] = clone(obj[k]);
                    }
                }
                break;
            default:
                o = obj;
                break;
        }
        return o;
    };

    /**
     @description 将参数config合并到obj中
     * */
    this.apply = function apply(obj, config, deep) {

        var _deep = deep || 1;
        _deep = _deep < 1 ? 1 : _deep;

        if (obj && config && this.isType(obj, Object) && this.isObject(config)) {

            for (var i in config) {
                try {
                    //delete  obj[i];
                    if (_deep == 1) {
                        obj[i] = config[i];
                        continue;
                    }
                    //console.log(i + '--' + _deep);
                    if (obj[i] && config[i] && this.isType(obj[i], Object) && this.isObject(config[i])) {
                        //console.log('1111'); console.log(this.toString(obj[i])); console.log(this.toString(config[i]));
                        this.apply(obj[i], config[i], _deep - 1)
                    } else {

                        obj[i] = config[i];
                    }

                } catch (ex) {
                    this.error('Run function Redjs.apply fail', 'Rsd', ex);
                }
            }
        }

        return obj;
    };

    /**
     * @public
     * */
    this.loadClass = function loadClass(className, callback) {

        if (!this.isString(className)) {
            console.error(className);
            return;
        }
        var me = Rsd || this;

        if (!me.isNullOrUndefined(me.classes[className])) {

            return;
        }

        if(me.classesCallback[className])
        {
            me.classesCallback[className].push(callback);
            return;
        }

        me.classesCallback[className] = [callback];

        me.debug('Load class: ' + className);

        var _url = className;
        var _flag = true;

        //从插件中获取
        if (_flag && me.app && me.app.plugins) {

            var _c_name = className.substring(0,className.lastIndexOf('.'));
            var _p = me.app.getPlugin(_c_name);
            while(_p==undefined || _p==null)
            {
                var i = _c_name.lastIndexOf('.');
                if(i<0)
                {
                    break;
                }
                _c_name = _c_name.substring(0,i);
                _p = me.app.getPlugin(_c_name);
            }
            if(_p)
            {
                _url = _url.replace(_p.name, '');
                _url = _url.replace("Rsd", me.getRedjsHost());
                _url = _p.path + '/' + _url.replaceByRegExp('[.]', '/') + '.js';
                _url += '?t=' + (_p.isDebug ? new Date().getTime() : _p.pluginDate);
               
                _flag = false;
            }
          
        }

        if (_flag && me.app && className.startWith(me.app.appName)) {

            _url = _url.replace(me.app.appName, '');
            _url = _url.replace("Rsd", me.getRedjsHost());
            _url = me.app.appFolder + '/' + _url.replaceByRegExp('[.]', '/') + '.js';
            _url += '?t=' + (me.app.isDebug ? new Date().getTime() : this.app.appDate);

            _url = (this.app && this.app.jsAgentHost) ? (this.app.jsAgentHost + '?' + this.utf8ToBase64(_url)) : _url;

            _flag = false;
        }



        if (_flag) {
            _url = _url.replaceByRegExp('[.]', '/') + '.js';
            if (me.isAgentHost()) {
                _url = _url.replace("Rsd", "");
                if(_url.startWith('/'))
                {
                    _url = _url.substr(1);
                }
                
                _url = me.getRedjsHost() + this.utf8ToBase64(_url + '?t=' + (me.isDebug ? new Date().getTime() : this.timestamp));
            }
            else {
                _url = _url.replace("Rsd", me.getRedjsHost());
                _url += '?t=' + (me.isDebug ? new Date().getTime() : this.timestamp);
            }

        }

        me.loadScriptFile(_url, function (response, status) {

            if (me.classes[className]) {

                me.debug('Load class: ' + className + ' end.');

                var _list = me.classesCallback[className];
                for(var i in _list)
                {
                    me.callFunction(me, _list[i]);
                }

            }
            else {

                me.error(className + ' can not found.Please check class name is case upper or lower.');
            }

        });

    }

    /**
     *@public
     *@desc 用<script> 加载标签 pugins 目录下资源或第三方资源,可以避免异步加载方式失败。
     * */
    this.loadPlugin = function loadPlugin(url, callback) {
        if(this.isWeChatApp())
        {
            console.log(url);
            console.error('Wechat小程序环境下loadPlugin方法不可用,请在document的head中提前加载css文件。');
            return ;
        }
        var me = Rsd || this;
        var name = url;
        if (name == undefined || name == null || name == '') {
            return;
        }
        var src = null;
        if (name.toLowerCase().startWith('http://') || name.toLowerCase().startWith('https://')) {
            src = name;
        } else {
            src = me.getRedjsUrl(name) ;
        }

        me.debug('Load plugin:' + src);

        var el = document.createElement('script');
        el.type = "text/javascript"
        el.src = src;
        el.onload = el.onreadystatechange = function onready() {

            if (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") {
                if (me.isFunction(callback)) callback.call();
                // Handle memory leak in IE
                el.onload = el.onreadystatechange = null;
            }
        };

        document.getElementsByTagName('head')[0].appendChild(el);

    };

    /**
     * @public
     * @description 用ajax【同步加载】脚本文件,加载到本地后并执行 不用用于加载jquery
     * @param {string} url 脚本地址
     * @param {string} callback 加载执行完成回调函数
     * */
    this.loadScriptFile = function loadScriptFile(url, callback) {

        var me = Rsd || this;
        var _url = url;
        if (_url.startWith('http://') || _url.startWith('https://')) {
        }
        else {

            if(_url.startWith('/'))
            {
                _url = _url.substr(1);
            }
           
            _url = (this.app && this.app.jsAgentHost) ? (this.app.jsAgentHost + '?' + this.utf8ToBase64(_url)) : _url;
        }

        me.debug('Load script file: ' + _url);

        // 创建ajax对象
        var xhr = null;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else {
            xhr = new ActiveXObject('Microsoft.XMLHTTP')
        }

        xhr.open('GET', _url, false);//同步加载
        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");//标识为ajax请求
        //xhr.setRequestHeader("Content-type", 'application/x-javascript; charset=utf-8');//开启后会出现 OPTIONS 请求
        xhr.send(null);
        //同步 不出发 onload  onreadystatechange 事件
        //xhr.onload = function(){};
        //xhr.onreadystatechange = function(){};

        if (xhr.readyState == 4 && xhr.status == 200) {

            var str = xhr.responseText;

            try 
            {
                window["eval"].call(window, str);
            } 
            catch (e) 
            {
                try 
                {
                    window.eval(str);
                } 
                catch (e1)
                {

                    console.error('load javascript file [' + url + '] successed,script execute error in function loadScriptFile.');
                    console.error(e1);
                }

            }


            if (me.isFunction(callback)) 
            {
                callback.apply(me,[str]);
            }

        } 
        else 
        {
            me.error('Failed to load script file resource: ' + _url + "(" + xhr.responseText + ")");
        }

        me.debug('Load script file: ' + _url + ' end.');
    };
    /**
     * @public
     * @description 用ajax【异步加载】json文件,加载到本地后并序列化json 不用用于加载jquery
     * @param {string} url 脚本地址
     * @param {string} callback 加载执行完成回调函数 
    */
    this.loadJsonFile = function loadJsonFile(url,callback)
    {
        var me = Rsd || this;
        var _url = url;
        if (_url.startWith('http://') || _url.startWith('https://')) {
        }
        else 
        {
            if(_url.startWith('/'))
            {
                _url = _url.substr(1);
            }
           
            _url = (this.app && this.app.jsAgentHost) ? (this.app.jsAgentHost + '?' + this.utf8ToBase64(_url)) : _url;
        }

        me.debug('Load json file: ' + _url);

        // 创建ajax对象
        var xhr = null;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else {
            xhr = new ActiveXObject('Microsoft.XMLHTTP')
        }

        try
        {
            xhr.open('GET', _url, true);//异步加载
            xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");//标识为ajax请求
            //xhr.setRequestHeader("Content-type", 'application/x-javascript; charset=utf-8');//开启后会出现 OPTIONS 请求
            xhr.send(null);
            xhr.onload = function()
            { 
                if (xhr.readyState == 4 && xhr.status == 200) {

                    var str = xhr.responseText;
        
                    try 
                    {
                        var json = me.toJson(str);
                        if (me.isFunction(callback))
                        {
                            callback.apply(me,[json]);
                        } 
                    } 
                    catch (e) 
                    {
                        console.error('load json file [' + url + '] successed.');
                        console.error(e);
        
                    }
        
                } 
                else 
                {
                    me.error('Failed to load json file resource: ' + _url + "(" + xhr.responseText + ")");
                }
            
                me.debug('Load json file: ' + _url + ' end.');
            };
            xhr.onerror = function(evt)
            {
                console.error('load json file error',evt);
            }
            //xhr.onreadystatechange = function(){ };
        }
        catch(err)
        {
            console.error('load json file occured exception',err);
        }
    },
    /**
     *@public
     * */
    this.loadCssFile = function loadCssFile(url) {

        if(this.isWeChatApp())
        {
            console.error(url);
            console.error('Wechat小程序环境下loadCssFile方法不可用,请在document的head中提前加载css文件。');
            return ;
        }
        var head = document.getElementsByTagName('HEAD').item(0);
        var style = document.createElement('link');
        style.href = url;
        style.rel = 'stylesheet';
        style.type = 'text/css';
        head.appendChild(style);
    };
   
    /**
     * @public
     * */
    this.gbkToUTF8 = function gbkToUTF8(wide) {
        var c, s;
        var enc = "";
        var i = 0;
        while (i < wide.length) {
            c = wide.charCodeAt(i++);
            // handle UTF-16 surrogates
            if (c >= 0xDC00 && c < 0xE000) continue;
            if (c >= 0xD800 && c < 0xDC00) {
                if (i >= wide.length) continue;
                s = wide.charCodeAt(i++);
                if (s < 0xDC00 || c >= 0xDE00) continue;
                c = ((c - 0xD800) << 10) + (s - 0xDC00) + 0x10000;
            }
            // output value
            if (c < 0x80) enc += String.fromCharCode(c);
            else if (c < 0x800) enc += String.fromCharCode(0xC0 + (c >> 6), 0x80 + (c & 0x3F));
            else if (c < 0x10000) enc += String.fromCharCode(0xE0 + (c >> 12), 0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
            else enc += String.fromCharCode(0xF0 + (c >> 18), 0x80 + (c >> 12 & 0x3F), 0x80 + (c >> 6 & 0x3F), 0x80 + (c & 0x3F));
        }
        return enc;
    };

    /**
     *
     * @public
     * */
    this.unicodeToUTF8 = function unicodeToUTF8(str) {
        if (null == str)
            return null;
        var strUni = String(str);
        var strUTF8 = String();
        for (var i = 0; i < strUni.length; i++) {
            var wchr = strUni.charCodeAt(i);
            if (wchr < 0x80) {
                strUTF8 += strUni.charAt(i);
            }
            else if (wchr < 0x800) {
                var chr1 = wchr & 0xff;
                var chr2 = (wchr >> 8) & 0xff;
                strUTF8 += String.fromCharCode(0xC0 | (chr2 << 2) | ((chr1 >> 6) & 0x3));
                strUTF8 += String.fromCharCode(0x80 | (chr1 & 0x3F));
            }
            else {
                var chr1 = wchr & 0xff;
                var chr2 = (wchr >> 8) & 0xff;
                strUTF8 += String.fromCharCode(0xE0 | (chr2 >> 4));
                strUTF8 += String.fromCharCode(0x80 | ((chr2 << 2) & 0x3C) | ((chr1 >> 6) & 0x3));
                strUTF8 += String.fromCharCode(0x80 | (chr1 & 0x3F));
            }
        }
        return strUTF8;
    };

    /*
    *
    * */
    this.utf8ToBase64 = function utf8ToBase64( str ) {
        return window.btoa(unescape(encodeURIComponent(str)));
    }

    /*
    *
    * */
    this.base64ToUTF8 = function base64ToUTF8( str ) {

        return decodeURIComponent(escape(window.atob(str)));
    }

    /**
     * @public
     * @description  获取屏幕设备像素值(1英寸正方形区域(长*宽)像素)
     * */
    this.getDPI = function getDPI() {
        if(this.__dpi)
        {
            return this.__dpi;
        }
        var _width, _height;
        if (window.screen.deviceXDPI) {
            _width = window.screen.deviceXDPI;
            _height = window.screen.deviceYDPI;
        }
        else {
            var tmpNode = document.createElement("DIV");
            tmpNode.style.cssText = "width:1in;height:1in;position:absolute;left:0px;top:0px;z-index:99;visibility:hidden;";
            document.body.appendChild(tmpNode);
            _width = parseInt(tmpNode.offsetWidth);
            _height = parseInt(tmpNode.offsetHeight);
            tmpNode.parentNode.removeChild(tmpNode);
            //console.log(_width,_height);
        }
        this.__dpi = {width: _width, height: _height};
        return this.__dpi;
    };
    /**
     * @description  将一个块区域的长宽的值(px)转换为以mm为计算单位的值
     * @param {number} width
     * @param {number} height
     * @param {number} scale 缩放比例 参考: window.devicePixelRatio;
     * @returns {object} {width:100,height:100}
     **/
    this.pxToMm = function pxToMm(width, height,scale) {

        var dpi = this.getDPI();
        var _scale = scale||1;
        var inch_w = (width||0)/ (dpi.width * _scale);
        var _width = inch_w * 25.4 ;

        var inch_h = (height||0)/(dpi.height * _scale);
        var _height = inch_h * 25.4 ;

        return {width:_width,height:_height};
    };
    /**
     * @description  将一个块区域的长宽的值(mm)转换为以px为计算单位的值
     * @param {number} width
     * @param {number} height
     * @param {number} scale 缩放比例 参考: window.devicePixelRatio;
     * @returns {object} {width:100,height:100}
     */
    this.mmToPx = function mmToPx(width, height,scale) {

        var dpi = this.getDPI();
        var inch_w = (width||0)/25.4;
        var inch_h = (height||0)/25.4;
        var _scale = scale||1;
        var _width = inch_w * dpi.width * _scale;
        var _height = inch_h * dpi.height * _scale;

        return  {width:_width,height:_height};;
    };
    /**
     * 
     * @param {*} url 
     * @param {*} callback 
     */
    this.loadTemplateFile = function loadTemplateFile(url, callback) {

        var me = Rsd || this;
        var _url = url;
        if (_url.startWith('http://') || _url.startWith('https://')) {
        }
        else {

            if(_url.startWith('/'))
            {
                _url = _url.substr(1);
            }
           
        }

        me.debug('Load file: ' + _url);

        // 创建ajax对象
        var xhr = null;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else {
            xhr = new ActiveXObject('Microsoft.XMLHTTP')
        }

        xhr.open('GET', _url, false);
        xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");//标识为ajax请求
        //xhr.setRequestHeader("Content-type", 'application/x-javascript; charset=utf-8');//开启后会出现 OPTIONS 请求
        xhr.send(null);

        if (xhr.readyState == 4 && xhr.status == 200) {

            var str = xhr.responseText;

            if (me.isFunction(callback)) callback.call(me,str);

        } else {
            me.error('Failed to load resource: ' + _url + "(" + xhr.responseText + ")");
        }

        me.debug('Load file: ' + _url + ' end.');

    };
    /***
     * 编译模板 或 编译+渲染模板
     */
    this.template = function template(tplStr, data) {
      
        var _fn = this.compile(tplStr);
        //console.log(_fn)
        return data ? _fn(data) : _fn;
    };
    /**
     * 模板编译 并缓存 方法 ,返回编译后的方法
     * @param {*} tplStr 模板字符串
     * @param {*} caseno 引擎方案:1 or 2
     * @returns 
     */
    this.compile = function compile(tplStr,caseno)
    {
        var _fn = null;
          if(Rsd.isEmpty(caseno)|| caseno==1)
          {
            //模板编译引擎 方案一 

            //两种正则表达式匹配模式: 
            // interpolate:取值变量,需要在其位置替换出相应的值==>可扩展成支持多种模式:{{name}},<%=name%>
            // evaluate:代码,执行代码语句
            var interpolate =/<%=(.+?)%>/g;
            var interpolate1 = /{{=(.+?)}}/g;
            var evaluate =  /<%(.+?)%>/g;
            var evaluate1 = /{{(.+?)}}/g; 

            var regStr = interpolate.source + "|" + interpolate1.source + "|" + evaluate.source + "|" + evaluate1.source + "|$"
            //正则表达式组
            //var matcher = new RegExp(`${interpolate.source}|${interpolate1.source}|${evaluate.source}|${evaluate1.source}|$`, 'g' );
            var matcher = new RegExp(regStr, 'g' );

            var index = 0
            var p = '';
            //需要处理的特殊字符
            var escapes = {
            '\n': 'n',
            '\r': 'r',
            '\u2028': 'u2028',
            '\u2029': 'u2029',
            '\\': '\\',
            "'": "'",
            };

            var escapeReg = /[\n\r\u2028\u2029\\']/g;
            //特殊字符转义 处理
            var escapeChar = function (match) { return '\\' + escapes[match];} ;
            var str = tplStr||'';
            str.replace(matcher, function (match, interpolate, interpolate1, evaluate,evaluate1, offset) {
 
                    // match 匹配到对象   console.log(match);
                    // console.log(interpolate);
                    // console.log(interpolate);
                    // 正则对象的lastIndex属性只有在开启g标志且在regexp.exec和regexp.test方法有效,指定下次匹配的开始位置,此属性可读可写,
                    // 如果方法没找到任何匹配则自动将它设置0,而在这里,用index来模拟lastIndex的作用
                    
                    // 另外需要注意,matcher最后的`$`目的是匹配字符串结束位置,从而得到结束位置的offset,
                    // 当`$`发生匹配时,match是空字符串,因为`$`是零宽断言,确实发生匹配但是没有匹配内容,故返回空字符串

                    // 使用slice方法取子字符串的副本,确保str保持不变
                    // 将本次匹配到的<%=xxx%>或<%xxx%>之前的文本进行特殊字符文本化
                    var line = str.slice(index, offset).replace(escapeReg, escapeChar);
                    p += line;

                    // 记录下次replace匹配的开始位置
                    index = offset + match.length;

                    
                    // 进行替换
                    // 这里巧妙利用了正则表达式的 捕获分组 和 或运算
                    // `/part1(group1)|part2(group2)|part3/g`这是上面matcher的结构,
                    // 由于或运算的存在,只要三者之一匹配成功,整个正则表达式匹配成功,就会执行replace的回调函数,
                    // 由于group1和group2必然要存在(因为它们写在正则表达式里面),
                    // 那么其中某一个就得是undefined,如果是part3发生的匹配,那么group1和group2都是undefined

                    //变量
                    if (interpolate) {

                        p += '\' + ( '+interpolate + '|| \'\') + \'';
                    } 
                    if (interpolate1) {

                        p += '\' + ( ' + interpolate1 + '|| \'\') + \'';
                    } 
                    //模板格式0 要替换的代码
                    if (evaluate) 
                    {
                        p += '\'; ' + evaluate + ' p+=\'';
                    }
                    //模板格式1 要替换的代码
                    if (evaluate1) 
                    {
                        p += '\'; ' + evaluate1 + ' p+=\'';
                    }
                   
                    // 把匹配到的字符串原封不动地还回去,确保str保持不变
                    return match;

            });
         
            // 给p拼上头部和尾部的代码, with 语法影响性能
            var code = "var p = ''; try{ with(data){ p+='" + p + "';}}catch(err){console.error(err);console.log(data);} return p;";
            // 创建模板渲染函数
            try
            {
                _fn = new Function('data', code);
            }
            catch(err)
            {
                console.error(err);
                _fn = function()
                {
                    console.debug("模板参考格式:{{=name}}或<%=name%>");
                }
                console.error("模板编译失败:"+code); 
                
            }
            
            //console.log(code);
          }
          else
          {
            //模版编译引擎 方案二 ,方案二 不支持格式:{{}} {{=user.name}}
            var str=tplStr||'';
            _fn = new Function(
                'data',
                'var p=[],print=function(){p.push.apply(p,arguments);};' +
                    "with(data){p.push('" +
                    str
                    .replace(/[\r\t\n]/g, ' ')
                    .split('<%')
                    .join('\t')
                    .replace(/((^|%>)[^\t]*)'/g, '$1\r')
                    .replace(/\t=(.*?)%>/g, "',$1,'")
                    .split('\t')
                    .join("');")
                    .split('%>')
                    .join("p.push('")
                    .split('\r')
                    .join("\\'") +
                    "');}return p.join('');"
                );
          }

          return _fn;
    };
    
    /**
     * @public
     * @description  格式化模版字符串
     * */
    this.formatTemplateString = function formatTemplateString(str, data) {
       
        if(this.isEmpty(str))
        {
            return "";
        }
        var _str = str;

       if(str.indexOf("#=")>-1 && str.indexOf("=#")>-1)
       {
            _str = str.replace("#=","{{=").replace("=#","}}");
            console.warn('不建议使用的模板语法格式:#=name=#,请使用{{=name}}格式替代');
            console.warn(str);
       }
      
        var html = this.template(_str,data);
        
        return html;
    },
        
    /**
     * @public
     * @description 获取App    
    * */
    this.getApp = function getApp() {
        return this.app;
    };
    /**
     * 添加App属性
     */
    this.setAppConfig = function setAppConfig(name,value)
    {
        if(!this.isEmpty(this.app))
        {
            this.app[name] = value;
        }
    },
    /**
     * 获取APP配置项,即app属性值
     */
    this.getAppConfig = function getAppConfig(name)
    {
        if(this.isEmpty(this.app))
        {
            return '';
        }
        return this.app[name]
    };
    /**
     * @public
     * @description 获取App Id
     * */
     this.getAppId = function getAppId() {

        if(this.isEmpty(this.app))
        {
            return '';
        }
        return this.app.appId || window.Rsd.projectId;
    };
    /**
     * @public
     * @description 获取App token
     * */
    this.getAppToken = function getAppToken() {

        if(this.isEmpty(this.app) )
        {
            return '';
        }

        //console.log('getAppToken',this.app.appToken);
        return this.app.appToken;
    };

    /**
     * @public
     * @description 设置token
     * */
    this.setAppToken = function setAppToken(token) {

        if(this.isEmpty(this.app) && this.getApp)
        {
            this.app = this.getApp();
        }
        this.app.appToken = token
        //console.log('setAppToken',this.app.appToken);
    };
     /**
      * 
      */
    this.getAppAuthorization = function getAppAuthorization()
    {
        if(this.isEmpty(this.app))
        {
            return '';
        }
        return this.app.authorization;
    };
    /**
     * @public
     * */
    this.checkToken = function checkToken() {

    };

    /**
     * @public
     * @param config 将被合并到Ajax对象上
     * @param callback 回调函数
     * */
    this.httpRequest = function httpRequest(config, data, callback) {
        var _c = null;
        var _d = null;
        var _fn = null;
        for(var i in arguments)
        {
            if(_c == null && this.isString(arguments[i]))
            {
                _c = {url:arguments[i]};
                continue;
            }
            if(_c == null && this.isObject(arguments[i]))
            {
                _c = arguments[i];
                continue;
            }
            if(_d == null && this.isObject(arguments[i]))
            {
                _d = arguments[i];
                continue;
            }
            if(_fn == null && this.isFunction(arguments[i]))
            {
                _fn = arguments[i];
                continue;
            }
        }
        if (_fn == null) {

            _fn = void(0);
        }
        var _ajax = this.create('Rsd.data.Ajax', this.apply({token:Rsd.getAppToken(), appId:Rsd.getAppId()}, _c));

        return _ajax.request(_d, _fn);
    };
 
    /**
     * 
     */
     this.httpGet = function httpGet(config,callback)
     {
        throw new Error('Unimplemented');
     };
     /**
      * 
      */
     this.httpPost = function httpPost(config,data,callback)
     {
        throw new Error('Unimplemented');
     };
     
 
    /**
     * 上传文件
     * @param config ajax配置项{url:'',method:'post'} 将被合并到Ajax对象上
     * @param {*} files 文件数组,即 File对象数组
     * @param {*} callback 回调方法
     */
    this.postFiles = function postFiles(config,files,callback)
    {
        var _ajax = this.create('Rsd.data.File', this.apply({token:Rsd.getAppToken(),appId:Rsd.getAppId()}, config));
        return _ajax.upload(files, callback);
    };
    /**
     * 将字符串 通过文件的方式上传到服务器
     * @param {*} config ajax配置项{url:'',method:'post'} 将被合并到Ajax对象上
     * @param {*} content 文件内容 字符串
     * @param {*} fileName 文件名
     * @param {*} fileType  文件类型
     * @param {*} callback 回调方法
     * @returns 
     */
    this.postFileFromString = function postFileFromString(config,content,fileName,fileType,callback)
    {
        var files = [new File([new Blob([content])],fileName, {type: fileType})];
        var _ajax = this.create('Rsd.data.File', this.apply({token:Rsd.getAppToken(),appId:Rsd.getAppId()}, config));
        return _ajax.upload(files, callback);
    };
    /**
    *
    * @description 提交表单数据
    * */
    this.submitForm = function submitForm(form, callback) {

        if(form && form.action)
        {
            var formData = new FormData(form); 
            var _ajax = this.create('Rsd.data.Ajax', {token:Rsd.getAppToken(),appId:Rsd.getAppId(),contentType:"application/x-www-form-urlencoded"});
            return _ajax.request(formData, callback); 
        }

    },
    /**
     *
     * @public
     * */
    this.sleep = function sleep(msec) {
        var start = new Date().getTime();
        while (true) if (new Date().getTime() - start > msec) break;
    };
    /**
     * 解码
     * @param {*} str 
     * @returns 
     */
    this.decodeURI = this.unescape = function unescape(str)
    {  
        if(this.isString(str))
        {
            return window.decodeURIComponent(str);
        }
        return str
    };
    /**
     * @description escape() 函数可对字符串进行编码,这样就可以在所有的计算机上读取该字符串。
     * @description 该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / 。其他所有的字符都会被转义序列替换。
     * @description 提示: 使用 unescape() 方法对字符串进行解码。
     * @param {*} str 
     */
    this.encodeURI = this.escape = function escape(str)
    {
        if(this.isString(str))
        {
            return window.encodeURIComponent(str);
        }
        return str
      
    }
    /**
     * @public
     * */
    this.toJson = function toJson(str) {
        //console.log(str);
        if (str == null || this.getType(str) != '[object String]') {
            return str;
        }
        if (!str || str.length == 0) {
            return null;
        }
        var _data = null;
        try 
        {
            _data = JSON.parse(str); 

        } catch( ex) {

            try
            { 
                _data = window.eval('(' + str + ')');

            } catch(ex1)
            {
                try 
                {
                    _data = window["eval"].call(window, str);

                } catch (ex2) {

                    _data= {msg:'JSON格式化失败',data:str,success:false};
    
                    this.showHtml("JSON格式化失败", str);
                }
            }
           
        }

        return _data;
    };

    /**
     * @public
     * */
    this.toString = function toString(obj) {

        if (obj == undefined) {
            return "null";
        }
        var r = [];
        if (typeof obj == "string") {
            return "\"" + obj.replace(/([\"\\])/g, "\\$1").replace(/(\n)/g, "\\n").replace(/(\r)/g, "\\r").replace(/(\t)/g, "\\t") + "\"";
        }
        if (typeof obj == "object") {
            if (!obj.sort) {

                for (var i in obj) {
                    if (typeof (obj[i]) == 'function') {
                        continue;
                    }
                    r.push("\"" + i + "\":" + this.toString(obj[i]));
                }

                r = "{" + r.join() + "}"
            } else {
                for (var i = 0; i < obj.length; i++)
                    r.push(this.toString(obj[i]))
                r = "[" + r.join() + "]";
            }
            return r;
        }

        return obj.toString().replace(/\"\:/g, '":""');
    };

    /**
     * @description 将字符串转化为dom节点
     * @public
     * */
    this.toHtml = this.parseDom = function parseDom(str) {
        var objE = document.createElement('div');
        objE.innerHTML = str;
        return objE.childNodes;
    };
    /**
     * @public 
     * @description 获取对象的jsonSchema
     * https://json-schema.org/understanding-json-schema/
     * @param {*} json 
     */
    this.getJsonSchema = function getJsonSchema(data)
    {
        var schema = {}; 
        var _data = data||{};

        var _fn_full_common_pro = function(examples){
  
                this.$ref = "";//通过设置id,引用其他类型的
                this.dependencies = null;//属性依赖关系
                this.minProperties = 0;//属性个数限制
                this.maxProperties = 0,//属性个数限制
                this.additionalProperties = false;//是否允许额外属性
                this.enum = null;
                this.default = null;
                this.examples = examples;
                this.allOf = [];//[schema1,schema2]满足allOf数组中的所有Json Schema。
                this.anyOf = [];//[schema1,schema2]满足anyOf数组中的任意个Schema。
                this.oneOf = [];//[schema1,schema2]满足且进满足oneOf数组中的一个Schema,这也是与anyOf的区别。
                this.not = [];//[schema1,schema2]不严格规定Json数据应满足什么要求,它告诉Json不能满足not所对应的Schema。
                
            };
         
        for(var p in _data)
        {  
            schema[p]={
                $id:p,
                title:'',
                description:"",
            };
            if(schema[p] == null)
            {
                schema[p].type ='null';
                _fn_full_common_pro.call(schema[p],[_data[p]]);
                continue;
            }
            if(Rsd.isString(_data[p]))
            { 
                schema[p].type ='string';
                schema[p].minLength = 0;
                schema[p].maxLength = 0;
                schema[p].pattern = "";
                schema[p].format = "";
                _fn_full_common_pro.call(schema[p],[_data[p]]);
                continue;
            }
            if(Rsd.isDate(_data[p]))
            { 
                schema[p].type = 'date';
                schema[p].format = "";
                _fn_full_common_pro.call(schema[p],[_data[p]]);
                continue;
            }
            if(Rsd.isBoolean(_data[p]))
            {
                schema[p].type ='boolean';
                _fn_full_common_pro.call(schema[p],[_data[p]]);
                continue;
            }
            if(Rsd.isNumber(_data[p]))
            { 
                schema[p].type ='number';
                schema[p].multipleOf = 0;//约束目标数值必须是该值的整数倍
                schema[p].minimum = null;
                schema[p].maximum = null;
                schema[p].exclusiveMinimum = null;
                schema[p].exclusiveMaximum = null;
                _fn_full_common_pro.call(schema[p],[_data[p]]);
                continue;
            }
            if(Rsd.isArray(_data[p]))
            { 
                schema[p].type ='array';
                schema[p].minItems = 0;
                schema[p].maxItems = 0; 
                var cols =  _data[p].length > 0?this.getJsonSchema(_data[p][0]):{};
                schema[p].items = {type:'object',properties:cols};//统一的数组元素的Schema 复制对象,不同元素约束用数组
                schema[p].additionalItems = [];//其他数组元素约束
                _fn_full_common_pro.call(schema[p],_data[p]);
                continue;
            }
            if(Rsd.isObject(_data[p]))
            { 
                schema[p].type ='object';
                schema[p].properties = this.getJsonSchema(_data[p]);
                schema[p].required = [];//必填属性名称数组["name","age"]
                _fn_full_common_pro.call(schema[p],[]);
                continue;
            } 
           
        }
         
        return schema;
    }
    /**
     * @public
     * */
    this.getType = function getType(obj) {
        return Object.prototype.toString.apply(obj);
    };

    /**
     *@public
     * */
    this.isEmpty = function isEmpty(obj) {


        if (obj == undefined || obj == null || obj == '') {
            return true;
        }
        if (obj instanceof Function) {
            return false;
        }
        if (obj instanceof Array) {
            return obj.length == 0
        }

        if (obj instanceof Date) {
            return false
        }
        if (Rsd && Rsd.common && Rsd.common.Object && obj instanceof Rsd.common.Object) {
            return false
        }
        if (obj instanceof HTMLElement) {
            return false;
        }
        if ( obj instanceof Node) {
            return false;
        }
        if (obj instanceof Object) {

            return Object.keys ? Object.keys(obj).length == 0 : JSON.stringify(obj) == "{}";
        }

        return false;
    };

    /**
     * @public
     * */
    this.isNull = function isNull(obj) {
        return this.getType(obj) == '[object Null]';

    };

    /**
     *@public
     *
     * */
    this.isNaN = function isNaN(obj) {
        if(window)
        {
            return window.isNaN(obj);
        }
        return Number.isNaN(obj);
    };

    /**
     * */
    this.isUndefined = function isUndefined(obj) {
        return this.getType(obj) == '[object Undefined]';
    };

    /**
     * @public
     * */
    this.isNullOrUndefined = function isNullOrUndefined(obj) {
        return this.isNull(obj) || this.isUndefined(obj);
    };

    /**
     * @public
     * */
    this.isBoolean = function isBoolean(obj) {
        return this.getType(obj) === this.getType(true);
    };

    /**
     * @public 是否为true
     * */
    this.isTrue = function isTrue(obj) {
 
        if (this.isNullOrUndefined(obj)) {
            return false;
        }
       
        if (this.isBoolean(obj)) {
            return obj==true;
        }

        if (this.isString(obj)) {
            return obj.toLowerCase() == 'true';
        }

        if(this.isNumber(obj))
        {
            new Boolean(obj) == true;
        }

        return new Boolean(obj) == true;
    };

    /**
     * @public
     * */
    this.isString = function isString(obj) {
        return this.getType(obj) === this.getType('');
    };

    /**
     * @public
     * @description 是否是数字
     * */
    this.isNumber = function isNumber(obj) {

        return this.getType(obj) === this.getType(0) && !isNaN(Number(obj));
    };

    /**
     * @public
     * @description 是否是数字
     * */
     this.isDate = function isDate(obj) {

        return  this.getType(obj) === this.getType(new Date());
     };
    /**
     * @public
     * */
    this.isFunction = function isFunction(obj) {
        return this.getType(obj) == this.getType(function(){});
    };
    /*
    *
    * */
    this.isArguments = function isArguments(obj) {
        return this.getType(obj) == this.getType(arguments);
    };

    /**
     * @public
     * @description 是否是原生态对象,Rsd.getType(obj) == this.getType({})
     * */
    this.isObject = function isObject(obj) {
        return this.getType(obj) == this.getType({});
    };

    /**
     * @public
     * @description  判断对象是否是数组
     * @return boolean
     * */
    this.isArray = function isArray(obj) {

        return this.getType(obj) == this.getType([]);
    };

    /**
     * @public
     * @description obj instanceof  type
     * @param {object} obj 对象
     * @param {class} type 类型名称
     * @return boolean
     * */
    this.isType = function isType(obj, type) {
        if(this.isString(type))
        {
            return this.getType(obj) == type;
        }
        return obj instanceof type || this.getType(obj) == '[object ' + type.name + ']';
    };

    /**
     * @public
     * @description获取提示语言
     * */
    this.lang = function lang(name) {

        var lang = Rsd.resources.lang||{}; 
        var lang = lang[Rsd.app.lang];
        
        var arr = name.split('.');
        for (var i in arr) { 
            if(lang==null )
            {
                break;
            }
            lang = lang[arr[i]];
        }
        return lang;
    };

    /**
     * @public
     * @description getUserAgent*/
    this.getUserAgent = function getUserAgent() {
        return navigator.userAgent.toLowerCase();
    };

    /**
     * @public
     *@description 判断是否是移动端
     * */
    this.isMobile = function isMobile() {
        return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
    }
    /**
     *
     * @public
     * @description 判断是否是Chrome浏览器
     * */
    this.isChrome = function isChrome() {
        return /\bchrome\b/.test(this.getUserAgent());
    };
    /**
     * @public
     * @description 判断是否是微信浏览器
     * */
    this.isWeChat = function isWeChat(){
 
        if(this.isEmpty(this.app) || this.isEmpty(this.app.appType))
        {
            if(navigator && navigator.userAgent)
            {
                var ua = navigator.userAgent.toLowerCase();
                if(ua.match(/MicroMessenger/i)=="micromessenger") {
                    return true;
                } else {
                    return false;
                }
            }
            return false;
        }

        
    };
    /**
     * @description 判断是否是微信小程序 (this.app.appType == 'wxapp')
     */
    this.isWeChatApp = function isWeChatApp(){

        return this.app && this.app.appType == 'wxapp'; 
    };
    /**
     * @public
     * @description Gets the classname of an object or function if it can.
     *  @description Otherwise returns the provided default.
     *
     */
    this.getFunctionName = function getFunctionName(fn, defaultName) {
        

        var nameFromToStringRegex = /^function \s?([^\s(]*)/;
        var result = "";
        if (typeof fn === 'function') {
            try
            {
                result = fn.name || fn.toString().match(nameFromToStringRegex)[1];
            }
            catch(ex)
            {
                console.error("Get Function Name Fail",fn);
            }
            
        } else if (typeof fn.constructor === 'function') {
            result = getFunctionName(fn.constructor, defaultName);
        }
        return result || defaultName;
    };

    /**
     * @public
     * @description 打印方法调用栈信息
     * */
    this.printCallStack = function printCallStack(text,count) {
        
        var _count = 0;
        console.log('==========='+(text||'')+'============='); 
        var caller = arguments.callee.caller; //当前方法的调用者
        while(caller && (_count < count || !count))
        {
            console.log(caller);
            caller = caller.caller;
            _count++;
        }
    };

    /**
     * @public
     * */
    this.print = function print(id) {
        if(this.isWeChatApp())
        {
            console.error('Wechat小程序环境下print方法不可用,无法打印。');
            return;
        }

        if (arguments.length == 0) {
            window.print();
            return;
        }
        var _list = [];
        var _node;
        for (var i in document.body.childNodes) {
            _node = document.body.childNodes[i];
            if (_node instanceof Element) {
                _list.push({node: _node, display: _node.style.display});
                _node.style.display = 'none';
            }

        }
        // A4 210mm* 297mm 1in=25.4mm
        var _dpi = this.getDPI();
        var _a4_width = _dpi.width * 210 / 25.4;
        document.body.style.width = _a4_width + 'px';
        alert(document.body.style.width);
        var _div = document.getElementById(id);
        var _temp = document.createElement('div');
        _temp.innerHTML = _div.innerHTML;
        document.body.appendChild(_temp);
        window.print();
        document.body.removeChild(_temp);
        for (var i in _list) {
            _list[i].node.style.display = _list[i].display;
        }
        return;
    };

    /**
     * @public
     * @description 复制数据到剪贴板
     * */
    this.copy = function copy(data) {

        if(this.isWeChatApp())
        {
            console.error('Wechat小程序环境下copy方法不可用,无法复制数据。');
            return ;
        }
        var currentFocus = document.activeElement;

        if (window.clipboardData && window.clipboardData.setData('Text', data)) // 赋予 text 格式的数据
        {
            this.showPopup("复制成功!");
            return;
        }

        var _text_for_copy = document.getElementById('rsd_text_for_copy');
        if (_text_for_copy == null) {
            _text_for_copy = document.createElement('textarea');
            _text_for_copy.setAttribute('id', 'rsd_text_for_copy');
            //_text_for_copy.style.width ='0px';
            // _text_for_copy.style.height ='0px';
            _text_for_copy.style.position = "absolute";
            _text_for_copy.style.left = "-9999px";
            _text_for_copy.style.top = "0";


            document.body.appendChild(_text_for_copy);
        }


        _text_for_copy.textContent = data;
        _text_for_copy.setSelectionRange(0, _text_for_copy.value.length);
        _text_for_copy.focus();
        _text_for_copy.select();
        var _flag = document.execCommand != undefined;
        try {
            //第一始终失败
            _flag = document.execCommand('copy');
            //第二次成功
            _flag = _flag || document.execCommand('copy');

        } catch (err) {
            this.error("复制数据失败", "", err);
        }

        if (_flag) {
            this.showPopup("复制成功!");

        } else {
            var _d = this.create('Rsd.container.Dialog', {width: 600, height: 420, layout: 'fit', title: '复制数据'});
            var _t = this.create('Rsd.form.TextArea', {height: 350, value: data, margin: '5 5 5 5'});
            _d.add(_t);
            _d.showDialog();
            _t.focus();
            _t.select();
        }
        if (currentFocus && typeof currentFocus.focus === "function") {
            currentFocus.focus();
        }
        document.body.removeChild(_text_for_copy);

        return;

    };


    /**
     * @public
     * @description 显示等待窗口
     * */
    this.showWaiting = function showWaiting(key, parent,msg) {
        var _key = key || (parent && parent.id) || Math.random();
        if (this.isEmpty(_key)) {

            throw new Erorr('key is null. Rsd.showWaiting = function showWaiting(key, parent,msg)');
        }

        var me = Rsd || this;
        setTimeout(function (parent) {
            me.__waitings = me.__waitings || {};
            if (!me.__waitings[_key]) {
                me.__waitings[_key] = me.create('Rsd.control.WaitingBox', {
                    key:_key,
                    message: msg|| me.lang('common.waitingText'),
                    border: false
                });
            }
            me.__waitings[_key].showDialog(document.body);
        }, 0);

        return _key;
    };

    /**
     * @public
     @description 关闭等待窗口
     * */
    this.closeWaiting = function closeWaiting(key, delay) {
        if (this.isEmpty(key)) {
            throw new Erorr('key is null. Rsd.closeWaiting=function closeWaiting(key, delay)');
        }
        var me = Rsd || this;
        setTimeout(function () {
            if (me.__waitings && me.__waitings[key]) {
                me.__waitings[key].close();
                delete me.__waitings[key];
            }
        }, (delay == undefined || delay == null) ? 500 : delay);
    };

    /**
     * @description 使用window.open 打开新的url 
     * @public
     * */
    this.showModalDialog = function showModalDialog(url, title, height, width, data) {

        if (window.showModalDialog) {
            window.showModalDialog(url);
        }
        else {
            var _h = height || 400;
            var _w = width || 500;
            var _params = 'height=' + _h + ', width=' + _w + ', top=' + (((window.screen.height - _h) / 2) - 50) + ',left=' + ((window.screen.width - _w) / 2) + ',toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no';
            window.open(url, title, _params);
        }
    };

    /**
     * @public
     * */
    this.get = function get(id) {
        return this.objects[id];
    };

    /**
     * @public
     * */
    this.getDom = function getDom(id) {
        if(this.isWeChatApp())
        {
            console.error('小程序getDom方法不可用,无法获取dom对象。');
            return {};
        }
        return document.getElementById(id);
    };

    /*
    *
    * */
    this.getFileExt = function getFileExt(url) {
        var file = url;

        var FileExt = file.replace(/.+\./, "");   //正则表达式获取后缀
        return FileExt;
    };

    /**
     * */
    this.getFileName = function getFileName(url) {
        var file = url;
        var pos = file.lastIndexOf('/');
        file = file.substring(pos + 1);
        pos = file.lastIndexOf('.');
        return file.substring(0, pos);
    };

    /**
     * @public
     * @desc 映射成当前网站网站目录
     * */
    this.mappingUrl = function mappingUrl(url) {
        if (this.isNullOrUndefined(this.__homePath )) {
            this.__homePath = window.location.protocol + '//' + window.location.host + '/' + window.location.pathname + '/';
        }
        return  this.__homePath + url;
    };
    /*
    * RedjsHost是否是代理服务器
    * */
    this.isAgentHost = function isAgentHost( ) {
        return this.__isAgentHost || false;
    };
    /**
     *@public
     *@description 获取redjs框架服务器地址,支持将框架部署在远程服务器上
     * */
    this.getRedjsHost = function getRedjsHost() {
     
        //debugger;
        if (this.isEmpty(this.__jsHomeHost )) {

            if(this.isWeChatApp())
            {
                console.error('Wechat小程序环境下getRedjsHost方法不可用,默认返回:https://js.redmicro.cn。');
                return 'https://js.redmicro.cn';
            }

            var result = ""

            var e = Error('redjs');
            if (e.fileName) {//firefox
                result = e.fileName;
            } else if (e.sourceURL) {//safari
                result = e.sourceURL;
            } else if (e.stacktrace) {//opera
                var m = e.stacktrace.match(/\(\) in\s+(.*?\:\/\/\S+)/m);
                if (m && m[1])
                    result = m[1];
            }
            if (!result) {//IE与chrome
                var scripts = document.getElementsByTagName("script");
                var reg = /rsd([.-]\d)*\.js(\W|$)/i
                for (var i = 0, n = scripts.length; i < n; i++) {
                    var src = !!document.querySelector ? scripts[i].src : scripts[i].getAttribute("src", 4);
                    if (src && reg.test(src)) {
                        result = src
                        break;
                    }
                }
            }
            var _path = result.substr(0, result.lastIndexOf('/') + 1);

            var _d = _path.split('/');
            this.__jsHomeHost = _path.replace(_d[_d.length - 1], '');
        }

        if(window.location.protocol=='https:' && this.__jsHomeHost.startsWith('http://'))
        {
            this.__jsHomeHost = 'https://' + this.__jsHomeHost.substring(7);
        }
        if(window.location.protocol=='http:' && this.__jsHomeHost.startsWith('https://'))
        {
            //this.__jsHomeHost = 'http://' + this.__jsHomeHost.substring(8);
        }
       
        return this.__jsHomeHost;
    };
    /** 
     * 获取完整的redjs框架地址
     * isAgentHost:true 表示jsHomePath指向的服务期是否代理服务器,代理服务器的求情格式为jsHomePath/base64(path),需要在代理服务上 先解析path内容 
     * isAgentHost:false 表示正常结构
    */
    this.getRedjsUrl = function getRedjs(url) {
        var _url  = url;
        
        if(_url.startWith('/'))
        {
            _url = _url.substr(1);
        }
        
        if(this.isAgentHost())
        {
            return this.getRedjsHost() +  this.utf8ToBase64(_url);
        }
        else
        {
            return this.getRedjsHost() + _url;
        }
    }
    /**
     *@public
     *@description重定向
     * */
    this.redirect = function redirect(url) {
        window.location.href = url;
    };

    /**
     * @public
     * @description 获取url参数
     * */
    this.getUrlParam = function getUrlParam(url, name) {
        var _url = '';
        var _name = name;
        if (arguments.length == 1) {
            _url = window.location.search.substr(1);
            _name = arguments[0];
        }
        if (arguments.length > 1) {
            var _list = (url || '?').split('?');
            _url = _list[_list.length - 1];
            _name = name;
        }
        var reg = new RegExp("(^|&)" + _name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象
        var r = _url.match(reg);  //匹配目标参数
        if (r != null) return unescape(r[2]);
        return null; //返回参数值
    };

    /**
     *
     *@public
     *@description 设置url参数
     * */
    this.setUrlParam = function setUrlParam(url,name,value) {

        var _url = url || (window.location.protocol + '//' + window.location.host + '/' + window.location.pathname + '/');
         
        var _params = {};

        if (arguments.length == 2 && this.isObject(arguments[1])) {
            _params = arguments[1];
        }
        if (arguments.length == 3) {

            if (this.getUrlParam(name) == value) {
                return url;
            }
            else 
            {
                _params[name] = value;
            }
        }

        var currentUrl = _url.split('#')[0];

        for (var i in _params) {
            var _name = i;
            var _value = _params[i];

            if (/\?/g.test(currentUrl)) {
                var exp = new RegExp(_name + '[%20]{0,100}=[%20]{0,100}[-\\w]{0,100}');
                if (exp.test(currentUrl)) {
                    currentUrl = currentUrl.replace(exp, _name + "=" + _value);
                } else {
                    currentUrl += "&" + _name + "=" + _value;
                }
            } else {
                currentUrl += "?" + _name + "=" + _value;
            }
        }
        
        if (_url.split('#')[1]) {
            currentUrl = currentUrl + '#' + _url.split('#')[1];
        }  
       
        return currentUrl;
    };

    /**
     *@public
     * */
    this.getByteLength = function getByteLength(val) {
        var len = 0;
        for (var i = 0; i < val.length; i++) {
            if (val[i].match(/[^x00-xff]/ig) != null) //全角
                len += 2;
            else
                len += 1;
        }
        ;
        return len;
    };

    /**
     * @public
     * @description 调用方法执行
     * @param {Rsd.common.Object} caller,
     * @param {function|string} fn,
     * @param {array} args []
     * */
    this.callFunction = function callFunction(caller, fn, args) {
        var _caller = null;
        var _fn = null;
        var _args = null;

        for(var i=0;i< arguments.length;i++)
        {
            if (this.isFunction(arguments[i]) || this.isString(arguments[i])) {
                _fn = arguments[i];
                continue;
            }
            if (Rsd.common && Rsd.common.Object && arguments[i] instanceof Rsd.common.Object) {
                _caller = arguments[i];
                continue;
            }
            if (this.isArray(arguments[i]) ) {
                _args = arguments[i];
                continue;
            }
            if(_args==null && this.isArguments(arguments[i]))
            {
                var _args = Array.prototype.slice.call(arguments[i]);
                continue;
            }
        }

        if (this.isFunction(_fn)) {

            return _fn.apply(_caller || window.Rsd, _args||[]);
        }
        if (this.isString(_fn)) {
            if (!(_caller instanceof Rsd.common.Object)) {
                this.error('argument caller is not instanceof Rsd.common.Object,when call function callFunction(caller,fn,args).');
                return true;
            }
            return _caller.funApplyByIOC(_fn, _args||[]);
        }
        return null;
    };

    /**
     @public
     @description 获取字符宽度,像素值
     @param val:文本,
     @param size:单个英文字符文本宽度(像素)
     @return 文本宽度
     @type Number
     * */
    this.getStringWidth = function getStringWidth(container, str) {
        var span = document.createElement("span");
        var _parent = container || document.body;
        _parent.appendChild(span);
        span.style.visibility = "hidden";
        span.style.whiteSpace = "nowrap";
        span.innerText = str;
        var _width = span.offsetWidth;
        _parent.removeChild(span);
        return _width;
    };

    /**
     * @public
     *  @description 货币格式化,不做四舍五入处理
     *  @param {number} num 金额
     *  @param {string} currency 货币符号 £(英镑) ¥(人民币,日元) €(欧元) $(美元)
     *  @return string
     * */
    this.formatCurrency = function formatCurrency(num, currency) {
        if (num == undefined || num == null) {
            return "";
        }
        num = num.toFixed(2);
        //num = this.toString(num);
        var str = (currency || '') + num.split('').reverse().join('').replace(/(\d{3}(?=\d)(?!\d+\.|$))/g, '$1,').split('').reverse().join('');
        return str;
    };

    /**
     * @public
     * @description 金额小写转大写
     * */
    this.formatCurrencyCN = function formatCurrencyCN(num) {
        var fraction = ['角', '分'];
        var digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
        var unit = [ ['元', '万', '亿'], ['', '拾', '佰', '仟']  ];
        var head = num < 0? '欠': '';
        num = Math.abs(num);

        var s = '';

        for (var i = 0; i < fraction.length; i++)
        {
            s += (digit[Math.floor(num * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '');
        }
        s = s || '整';
        num = Math.floor(num);

        for (var i = 0; i < unit[0].length && num > 0; i++)
        {
            var p = '';
            for (var j = 0; j < unit[1].length && num > 0; j++)
            {
                p = digit[num % 10] + unit[1][j] + p;
                num = Math.floor(num / 10);
            }
            s = p.replace(/(零.)*零$/, '').replace(/^$/, '零')  + unit[0][i] + s;
        }
        return head + s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整');

    };

    /**
     * @public
     * @description 时间转化
     * @param {Number|String} timestamp
     * @param {string} [format] 格式:yyyy-MM-dd|yyyy-MM-dd hh:mm:ss|yyyy-MM-dd hh:mm:ss S
     * */
    this.formatTimestamp = function formatTimestamp(timestamp, format) {
        var _s = timestamp;
        if (this.isString(_s)) {
            _s = Number(_s);
        }
        if(_s>0)
        {
            while(_s.toString().length < 13)
            {
                _s = _s*10;
            }
        }
        
        if (this.isNumber(_s)) {
            return new Date(_s).format(format || 'yyyy-MM-dd hh:mm:ss');
        }
        return timestamp;
    };

    /**
     * @public
     * @description 数字格式化
     * @param {Number|String} value
     * @param {Number} precision
     * */
    this.formatNumber = function formatNumber(value, precision) {
        var _n = Number(value);
        if (isNaN(_n)) {
            return 0;
        }
        return _n.toFixed(precision || 0);
    };

    /**
     * @public
     * @description 格式化json
     * @param {string|object} json
     * @param {Function|String|Number} [replacer]
     * @param {Number|String} [space]
     * */
    this.formatJson = function formatJson(json, replacer, space) {
        return JSON.stringify(json, replacer || null, space || 4);
    };

    /**
     * @public
     * @description 检测浏览器及其版本
     * */
    this.detectNavigator = function detectNavigator() {

        var ua = navigator.userAgent.toLowerCase();
        var s;
        (s = ua.match(/msie ([\d.]+)/)) ? this.ie = s[1] :
            (s = ua.match(/firefox\/([\d.]+)/)) ? this.firefox = s[1] :
                (s = ua.match(/chrome\/([\d.]+)/)) ? this.chrome = s[1] :
                    (s = ua.match(/opera.([\d.]+)/)) ? this.opera = s[1] :
                        (s = ua.match(/version\/([\d.]+).*safari/)) ? this.safari = s[1] : 0;

        //以下进行测试
        if (this.ie) return ('IE: ' + this.ie);
        if (this.firefox) return ('Firefox: ' + this.firefox);
        if (this.chrome) return ('Chrome: ' + this.chrome);
        if (this.opera) return ('Opera: ' + this.opera);
        if (this.safari) return ('Safari: ' + this.safari);
    };

    /**
     * @public
     @description 检查操作系统
     * */
    this.detectOS = function detectOS() {
        var sUserAgent = navigator.userAgent;
        var isWin = (navigator.platform == "Win32") || (navigator.platform == "Windows");
        var isMac = (navigator.platform == "Mac68K") || (navigator.platform == "MacPPC") || (navigator.platform == "Macintosh") || (navigator.platform == "MacIntel");
        if (isMac) return "Mac";
        var isUnix = (navigator.platform == "X11") && !isWin && !isMac;
        if (isUnix) return "Unix";
        var isLinux = (String(navigator.platform).indexOf("Linux") > -1);
        if (isLinux) return "Linux";
        if (isWin) {
            var isWin2K = sUserAgent.indexOf("Windows NT 5.0") > -1 || sUserAgent.indexOf("Windows 2000") > -1;
            if (isWin2K) return "Win2000";
            var isWinXP = sUserAgent.indexOf("Windows NT 5.1") > -1 || sUserAgent.indexOf("Windows XP") > -1;
            if (isWinXP) return "WinXP";
            var isWin2003 = sUserAgent.indexOf("Windows NT 5.2") > -1 || sUserAgent.indexOf("Windows 2003") > -1;
            if (isWin2003) return "Win2003";
            var isWinVista = sUserAgent.indexOf("Windows NT 6.0") > -1 || sUserAgent.indexOf("Windows Vista") > -1;
            if (isWinVista) return "WinVista";
            var isWin7 = sUserAgent.indexOf("Windows NT 6.1") > -1 || sUserAgent.indexOf("Windows 7") > -1;
            if (isWin7) return "Win7";
        }
        return "other";

    };

    /**
     *@public
     * @description 数组拼接
     * arr1,arr2.... arr n
     * */
    this.arrayConcat = function arrayConcat(arr1, arr2) {
        var _arr = [];

        for (var i in arguments) {
            if (this.isArray(arguments[i])) {
                Array.prototype.push.apply(_arr, arguments[i]);
            }

        }

        return _arr;
    };
    /**
     * @description 定义枚举类型
     * url:'http://hermes.redmicro.cn/dev/getenum?enumtype=' + enumName
     * 
     */
     this.defineEnum = function defineEnum(url,enumName,valueMember)
     {
         if (!Rsd.classes[enumName]) {
               
             Rsd.httpRequest({url:url ,async:false},function(data){
 
                 if(!Rsd.isEmpty(data.data))
                 {
                     Rsd.define(data.data.name,{
                         extend:'Rsd.data.Enum',
                         "valueMember": valueMember||"Value",
                         "codeMember": "Code",
                         "textMember": "Text",
                         "colorMember":"Color",
                         items:data.data.items,
                         constructor:function constructor()
                         {
         
                         }
                     });
 
                    _enum = Rsd.widget(enumName,{});
 
                 }else
                 {
                     Rsd.warn('类型:' + enumName + '不存在。');
                 }
 
               
               }); 
 
         } 
     },
     /**
      * 
      * @param {*} url 枚举数据获取地址
      * url:'http://hermes.redmicro.cn/dev/getenumlist'
      */
     this.defineEnums = function defineEnums(url,method){
          
         this.httpRequest({url:url,method:method||'GET' },function(data){
 
             
             if(data.success)
             {
                 var list = data.data;
                 for(var i in list)
                 {
                     var _enum = list[i];
                     if(_enum == null )
                     {
                         continue;
                     }
                     var _typeName = _enum.typeName||_enum.TypeName
                     if(Rsd.isEmpty(_typeName))
                     {
                         console.error('枚举信息错误:',_enum);
                         continue;
                     }
                     if(Rsd.classes && Rsd.classes[_typeName])
                     {  
                         continue;
                     }
                    
                     try
                     { 
                         Rsd.define(_typeName,{
                             extend:'Rsd.data.Enum',
                             "valueMember": "Value",
                             "codeMember": "Code",
                             "textMember": "Text",
                             items:_enum.Items,
                             constructor:function constructor()
                             {
                   
                             }
                          });
                     }
                     catch(err)
                     {
                         console.error(err);
                     }
                    
                 }
               
             }
         
           });
     };
    /**
     * @description 为dom 附加一个按钮
     * @param {*} targetDom 
     * @param {*} text 
     * @param {*} handler function(tpl,dataSource);
     * @param {*} tip 文字提示 
     */
    this.patchButton = function patchButton(targetDom,text,handler,tip)
    { 
          
        var _fn = function(dom)
        { 
            dom.style.border = "1px red dotted";
            dom.style.boxSizing = "border-box";
            var id = dom.getAttribute('rsdbtn');
            var _btn = null;
            if(id)
            {
                 _btn = document.getElementById(id);
            }
            else
            {
                id = Date.now(); 
                _btn = document.createElementNS("http://www.w3.org/2000/svg",'svg');
                _btn.setAttribute("id",id);
                _btn.style.color = "red";
                _btn.style.backgroundColor="#c34b4b54";
                _btn.style.position = "absolute";
                _btn.style.width = "56px";
                _btn.style.height = "26px";
                _btn.style.zIndex = 9999;
                _btn.style.fontSize = "80%";
                _btn.style.cursor = "pointer";
                //_btn.style.transform = "translate(-7px,-4px)"; 
                _btn.setAttribute("viewBox","-5 -5 46 2");
                dom.appendChild(_btn); 
                dom.setAttribute('rsdbtn',id);
            }
         
            var _txt = document.createElementNS("http://www.w3.org/2000/svg", "text");
            _txt.textContent = text||'未设置'; 
            //_txt.setAttributeNS(null, "width","55px");
            //_txt.setAttributeNS(null, "height","25px");
            while (_btn.lastChild) {
                _btn.removeChild(_btn.lastChild);
            }
            _btn.appendChild(_txt); 

            if(tip)
            {
                var _title = document.createElementNS("http://www.w3.org/2000/svg",'title');
                _title.textContent = tip;
                _btn.appendChild(_title);
            }
            
            _btn.onclick =  function(){
                if(handler)
                {
                    var list = [];
                    for(var i=0;i < this.parentNode.children.length;i++)
                    {
                        var item = this.parentNode.children[i];
                      
                        if(item.id == id)
                        {
                            continue;
                        }
                        list.push(item);
                    }
                    var parent = this.parentNode;
                    var tpl =  parent.getAttribute('rsd-tpl');
                    //数据源名称
                    var ds = parent.getAttribute('rsd-ds');
                    
                    //从app.data中提取数据,app.data在页面数据加载时赋值
                    handler.call(parent,tpl,Rsd.app.data[ds]||ds);
                }
                else
                {
                    alert(' not set handler value ');
                }
               
            };
          
           
        };

            if(Rsd.isString(targetDom))
            {
                var list = Rsd.select(targetDom);
              
                list.forEach(_fn); 
                return list.length;
            } 

            if(targetDom instanceof Node)
            {
                var dom = targetDom;
                dom.style.border = "1px red dotted";
                dom.style.boxSizing = "border-box";
                _fn(dom);

                return 1;
            }
            console.error( "不支持的dom对象", targetDom);
            return 0;
    }
    /**
     * 
     * @param {*} checked 
     * @param {*} readonly 
     * @returns 
     */
    this.checkbox = function checkbox(checked,readonly)
    {
        var chk = document.createElement('input');
        chk.setAttribute('type','checkbox');
        chk.setAttribute('readonly',readonly);
        chk.setAttribute('disabled',readonly);
        chk.cheched = checked;
        return chk;
    };
    /** 
     * @description 生成标签 
     * @param {object} style 文本颜色或样式对象{color:'red'}
     * @param {string} tip tip 
     * */  
    this.label = function label(text, style,tip) {

        var _lbl = document.createElement('label');
        _lbl.classList.add('x-text-label');
        _lbl.innerHTML = text;
        _lbl.onmouseenter = function()
        {
            var id = "del_"+Date.now();
            var _del = document.createElementNS("http://www.w3.org/2000/svg",'svg');
            _del.setAttribute("id",id);
            _del.style.color = "red";
            //_del.style.backgroundColor="#ffffff";
            _del.style.position = "absolute";
            _del.style.width = "14px";
            _del.style.height = "14px";
              
            _del.style.transform = "translate(-7px,-4px)"; 
            _del.setAttribute("viewBox","0 0 1024 1024");
        
            var _path = document.createElementNS("http://www.w3.org/2000/svg", "path");
            var _d = "M511.232 438.8352L112.9984 40.6016A51.2 51.2 0 0 0 40.6016 112.9984L438.784 511.232 40.6016 909.4656a51.2 51.2 0 1 0 72.3968 72.448l398.2336-398.2848 398.2336 398.2848a51.2 51.2 0 1 0 72.448-72.448l-398.2848-398.2336 398.2848-398.2336A51.2 51.2 0 0 0 909.4656 40.6016L511.232 438.784z";
            _path.setAttributeNS(null, "d", _d);
            _path.setAttributeNS(null, "fill", 'red');
            _del.appendChild(_path);

            var _title = document.createElementNS("http://www.w3.org/2000/svg",'title');
            _title.textContent = "点击删除该标签";
            _del.appendChild(_title);

            _del.onclick = function(){
                alert('delete ');
            };
          
            this.appendChild(_del); 
            this.setAttribute("for",id);
        }
        _lbl.onmouseleave = function()
        {
            var id = this.getAttribute("for"); 
            var _del = document.getElementById(id);
            if(_del)
            { 
                _del.parentNode.removeChild(_del);
            }
        }

        if(Rsd.isString(tip))
        {
            _lbl.title = tip;
        }
        if(this.isObject(style))
        {
            this.setElStyle(_lbl,style);
        }
        if(this.isString(style))
        {
            _lbl.style.backgroundColor = style;
            _lbl.style.borderColor = style;
        }
        return _lbl;
    };
    /**
     * @public
     * @description 生成text HtmlElement
     * @param {string} text 文本信息 
     * @param {string} style 样式对象{color:'red'}
     * @param {string} tip tip 
     * */
    this.text = function text(text, style,tip) {
        if(Rsd.isNumber(arguments[1]))
        {
            console.error('最大宽度应使用样式属性{maxWidth:'+arguments[1]+'px}控制')
            style = null;
        }
        if(Rsd.isString(arguments[1]))
        {
            console.error('颜色应使用样式属性{color:'+arguments[1]+'}控制');
            style = null;
        }

        var _t = text || ''; 
        var _style = style || {overflow:'hidden',color:'grey'};
        var _tip = tip || _t;

        var txt = document.createElement('span');
         
        this.setElStyle(txt,_style);
        txt.appendChild(document.createTextNode(_t))

        if(Rsd.isString(_tip))
        {
            txt.title = _tip;
        }
        return txt;
    };
    
    /**
     * @description 将枚举值,转化为对应的文字输出
     * @param {*} enumName 
     * @param {*} value 
     * @param {*} style 样式对象{color:'red'} 
     * @returns 
     */
    this.enumText = function enumText(enumName,value,style)
    {
        var _value = value;
        var _txt = Rsd.isEmpty(_value)?"":_value; 
        var _style = style;
        if (!Rsd.xtypes[enumName])
        {
            Rsd.warn('类型:' + enumName + '不存在,请先定义该类型。');
             
            return Rsd.isEmpty(_value)?"":_value; 
        }
        else
        {
            _enum = Rsd.widget(enumName,{});
            var _enumItem = _enum.getItem(_value);  
            if(_enumItem)
            {
                _txt = _enumItem.text;
                _style = _style || {color:_enumItem.color};
            }
        }
 
        return Rsd.text(_txt,_style)
    };
    /**
     * @description 将枚举值,转化为对应的文字标签输出
     * @param {*} enumName 
     * @param {*} value 
     * @param {*} style 样式对象{color:'red'}  
     * @returns 
     */
    this.enumLabel = function enumLabel(enumName,value,style)
    {
        var _value = value;
        var _txt = Rsd.isEmpty(_value)?"":_value; 
        var _style = style;
        if (!Rsd.xtypes[enumName])
        {
            Rsd.warn('类型:' + enumName + '不存在,请先定义该类型。');
        }
        else
        {
            _enum = Rsd.widget(enumName,{});
            var _enumItem = _enum.getItem(_value);  
            if(_enumItem)
            {
                _txt = _enumItem.text;
                _style = _style || {color:_enumItem.color};
            }
        } 
        return Rsd.label(_txt,_style);
    };
    /**
     * 
     * @param {*} text 
     * @param {*} width 
     * @param {*} color 样式对象{color:'red'}  
     * @param {*} tip 
     */
    this.nobr = function nobr(text,style,tip) {
        if(Rsd.isNumber(arguments[1]))
        {
            console.error('最大宽度应使用样式属性{maxWidth:'+arguments[1]+'px}控制')
            style = null;
        }
        if(Rsd.isString(arguments[1]))
        {
            console.error('颜色应使用样式属性{color:'+arguments[1]+'}控制');
            style = null;
        }
        var _t = text || ''; 
        var _style = style || {overflow:'hidden',color:'grey'};
        var _tip = tip || _t;
       
        var nobr = document.createElement('span');
        nobr.appendChild(document.createTextNode(_t));

        this.setElStyle(nobr,_style);
         
        if(Rsd.isString(_tip))
        {
            nobr.title = _tip;
        }

        return nobr;
    };
    /**
     * @public
     * @description 创建圆dom对象 function circle(size, color)
     * */
    this.circle = function circle(size, color,text) {
     
        var _s = size || 10;
        var _c = color || 'green';

        return this.parseDom('<div class="x-icon_circle" style="text-align:center;width: ' + _s + 'px;height: ' + _s + 'px;line-height:'+ _s +'px;color:#ffffff;background-color: ' + _c + ';">'+(text||'')+'</div>')[0];
    };

    /**
     * @public
     * @description 创建空格dom对象
     * */
    this.blankspan = function blankspan(count,style) {
        var _c = count || 1;
        var _s = '';
        for (var i = 0; i < _c; i++) {
            _s = _s + '&nbsp;';
        }
        var span =  this.parseDom('<span>' + _s + '</span>')[0];
        this.setElStyle(span,style||{});
        return span;
    };

    /**
     * @public
     * @description 创建换行dom对象
     * */
    this.newLine = function newLine() {
        return  this.parseDom('<br>')[0]
    };

    /**
     * @public
     * @description 创建button <input type='button'> 对象,function (text,fn,caller,args)
     * @param {string|object}  text 文字信息
     * @param {string|function} fn 点击事件函数
     * @param {object}  caller 调用对象
     * @param {array}  args fn参数
     * @param {object} style 样式对象{text:'',backroud}
     * */
    this.button = function button(text, fn, caller, args,style,tip) {
        var me = Rsd||this;
        var btn = document.createElement('input');
        btn.type = 'button';
        btn.classList.add('x-text-button');
        btn.value = text;
        if(Rsd.isString(tip))
        {
            btn.title = tip;
        }
        if(style && style.backgroundColor)
        {
            style.borderColor = style.borderColor||style.backgroundColor;
        }
        this.setElStyle(btn,style||{});
        if (me.isEmpty(fn)) {
            btn.title = "暂不可操作。";
            btn.classList.add("x-control-disabled");
        }
        else {
            btn.onclick = function (e) {
                me.callFunction(caller, fn, Rsd.arrayConcat([e],args||[]));
            };
        }

        return btn;
    };
   
    /**
     * @description 样式简单 无背景的 <a>标签
     * @param {*} text 
     * @param {*} fn 
     * @param {*} caller 
     * @param {*} args 
     * @param {*} style 
     * @param {*} tip 
     */
    this.link = function link(text, fn, caller, args,style,tip) {
        var me = Rsd||this;
        var btn = document.createElement('a');
        btn.classList.add('x-text-link');
        btn.href = 'javascript:void(0);';
        btn.innerHTML = text;
        if(Rsd.isString(tip))
        {
            btn.title = tip;
        }
        this.setElStyle(btn,style||{});
        if (me.isEmpty(fn)) {
            btn.title = "暂不可操作。";
            btn.classList.add("x-control-disabled");
        }
        else {
            btn.onclick = function (e) {
                
                me.callFunction(caller, fn, Rsd.arrayConcat([e],args||[]));
            };
        }

        return btn;
    };
    /**
     * @description 空白透明PNG 图片base64编码
     * */
    this.emptyImage = function emptyImage() {
        return 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
    };
    /**
     *  @public
     * @description 创建mailTo dom对象
     * */
    this.mailTo = function mailTo() {
        console.error('暂未实现。')
        return [];
    };

    /**
     * @public
     * @description md5加密
     * */
    this.md5 = function md5(str) {

        if ($.md5) {
            return $.md5(str);
        }
        else {
            this.error('$.md5(jquery md5插件) 加密插件不存在。')
        }
        return null;
    };

    /**
     * @public
     * @description 是否包含属性 name ,含其原型的属性。
     * */
    this.hasProperty = function hasProperty(obj, name) {
        if(obj==null || obj== undefined)
        {
            return false;
        }
        if (obj instanceof Object) {
            return name in obj;
        }
        if(obj.hasOwnProperty)
        {
            return obj.hasOwnProperty(name);
        }
        return false;
    };

    /**
     *
     * @desc 清空dom对象
     * */
    this.empty = function empty(ctrl) {

        if (this.isType(ctrl, Element)) {
            if (ctrl.childNodes && ctrl.childNodes.forEach) {
                ctrl.childNodes.forEach(function (node) {
                    ctrl.removeChild(node);
                });
                return;
            }
            ctrl.innerHTML = null;
        }

    };

    /**
     *@description 查询dom
     * @param {dom} dom 默认为document
     * @param {string} selector
     * */
    this.select = function select(dom,selector)
    {
        var _doc = dom;
        if(Rsd && Rsd.common && Rsd.common.ComponentX)
        {
            if(_doc instanceof Rsd.common.ComponentX)
            {
                _doc = _doc.dom;
            }
        }
        
        var _selector = selector;

        if(arguments.length == 1)
        {
            _selector = arguments[0];
            _doc = document;
        }
        //console.debug(_selector);
        return Array.from(_doc.querySelectorAll(_selector));
    };

    /**
     * @description 为Dom绑定事件
     * @param {*} selector 
     * @param {*} eventName 
     * @param {*} fn 
     */
    this.bindEvent = function(selector,eventName,fn)
    {
        var list = this.select(selector);
        for(var i in list)
        {
            list[i][eventName] = fn;
        }
    };
    /**
     * @decription 获取各主机Controller 
     * @description 设置dom 元素style属性值
     * */
    this.setElStyle=function setElStyle(el,style,sizeUnit)
    {
        var _dom = el;
        var _style = style||{};
        //
        if (_dom instanceof Element||_dom instanceof  Node) {
            var _sizeUnit = this.isEmpty(sizeUnit) ? 'px':sizeUnit;

            var _list = [
                'width',
                'height',
                'maxWidth',
                'maxHeight',
                'minWidth',
                'minHeight',
                'lineHeight',
                'top',
                'right',
                'bottom',
                'left',
                'marginTop',
                'marginRight',
                'marginBottom',
                'marginLeft'
            ];
            var _p = null;

            for (var i in _list) {

                _p = _list[i];
                if (this.isNullOrUndefined(_style[_p])) {

                    continue;
                }

                if (this.isString(_style[_p])) {
                    if(_style[_p]=='auto')
                    {
                        continue;
                    }
                    if (_style[_p].endWith('%') || _style[_p].endWith('px') || _style[_p].endWith('rem') || _style[_p].endWith('em')) {

                        continue;
                    }

                    _style[_p] = parseFloat(_style[_p]) + _sizeUnit;

                    continue;
                }
                if (this.isNumber(_style[_p])) {

                    _style[_p] = _style[_p] + _sizeUnit;

                    continue;
                }

            }

            if (_dom.style.setProperty) {

                for (var p in _style) {

                    if (/^[0-9a-z]*$/.test(p)) {
                       
                        _dom.style.setProperty(p, _style[p], 'important');
                        
                    } 
                    else
                    {
                        //大写字母替换为小写,并在前插入 -
                        var _l = p.match(/[A-Z]/g);
                        var _new_p = p;
                        for (var i in _l) {
                            _new_p = _new_p.replaceByRegExp(_l[i], '-' + _l[i].toLowerCase());
                        }

                        _dom.style.setProperty(_new_p, _style[p], 'important');

                    }

                }
            } else {
                this.apply(_dom.style, _style);
            }
        }
    };
    /**
     * @description 加载本地配置文件,将配置项合并到service对象中
     * @param localConfig 本地服务配置文件 [{group:},{}]
     */
    this.loadRouteTable = function loadRouteTable(localConfig,callback)
    { 
        var me = this;
        
    
        //加载所有host 并 配置合并本地配置
        var _fnLoadAll = function (configList) {
            var _configMap = {};
            //console.log(configList);
            for(var i in configList)
            {
                _configMap[configList[i].group.toLowerCase()] = configList[i];
            }
            //console.log(_configMap); 

            var _hosts = Rsd.app.apiHosts;
            for(var i in _hosts)
            {  
                me.loadServices(_hosts[i],_configMap,callback);
            } 

        }

        //config 是配置文件
        if(this.isString(localConfig))
        { 
            //获取controller的本地配置
            var configStore =  Rsd.create('Rsd.data.Store',{
                proxy: { 
                    url:localConfig,
                    method:'get'
                }
            });
 
            configStore.load({},function (groupList) { 
                _fnLoadAll(groupList);
            });

            return;
        }
        //config 是配置数组
        if(this.isArray(localConfig)||this.isEmpty(localConfig))
        {
            _fnLoadAll(localConfig||[]);
            return;
        }
        
    },
 
    /**
       { 
           text:'电商服务',
           name:'',
           url:'http://hermes.redmicro.cn/', 
           index:'dev/index', 
           isLoaded:false//已加载
           controllers:[{name:'',path:'',GetDescription:''},{},{}],
           routeList:'/dev/getroutetable',//服务端路由表接口
           useSSL:false
        },
        
     * @decription 将一个apihost加载到本地 
     * @param host  一个独立API项目 {name:'',text:'',url:'',useSSL:true,controllers:[]}
     * @param config 配置文件
     * @param callback
     */
    this.loadServices = function loadServices(host,config,callback)
    { 
        //console.log(host);
        //console.log(config);  

        var _config = config ||{};
       //获取主机的controlls 的api
       var _fnLoad = function(host,data)
       {
           //console.log(data);
           //controller list
           var _controllers = [];
           for(var i in data.data)
           {
               //合并本地配置 => controller 级别 配置项
               var group = (data.data[i].Name||data.data[i].name).toLowerCase();
              
               var _url =  host.url + (data.data[i].Path||data.data[i].path).toLowerCase().trim('/') + "/" + ((data.data[i].GetDescription||data.data[i].GetDescription||'GetDes').trim('/'));
               
               if(!Rsd.isEmpty(host.name))
               {
                   group = host.name + "." + group;
               }
                
               var gConfig = _config[group]||{};
               //console.log(gConfig);
               //初始化contoller
               var _ctrl = {
                   "group":group,
                   "url":_url,
                   "useSSL":host.useSSL,
                   "errorHandler": gConfig.errorHandler || "errorHandler",
                   "failureHandler": gConfig.failureHandler || "failureHandler",
                   "successHandler": gConfig.emptyHandler || "emptyHandler",
                   "api": {}
               };

               _controllers.push(_ctrl);

               Rsd.services[_ctrl.group]=_ctrl;
           } 
              
           var _data = {success:true,msg:'',data:{total:0,pagecount:1,pageindex:0,pagesize:1000,modeltype:'',rows:[]}};

           var _fnConfig = function (__data) {

               for(var i in __data.data)
               {
                   var key = this.group + '.' + __data.data[i].MethodName.toLowerCase();

                   var apiService = Rsd.app.getService(key);
                   apiService.parent = Rsd.app;
                   apiService.data = __data.data[i];
                   _data.data.total++;
                   _data.data.rows.push(apiService);
               }
               
               var gConfig = _config[this.group]||{};
               //合并本地配置 => api级别配置项
               if(!Rsd.isEmpty(gConfig))
               {
                   //console.log(gConfig);
                   //api 指定配置
                   for(var i in gConfig.api)
                   {
                       var _c = gConfig.api[i];//特定 api的配置项
                       var key = this.group + '.' + gConfig.api[i].name.toLowerCase();//console.log(key);
                       var apiService = Rsd.app.getService(key);
                       if(apiService)
                       { 
                           apiService.errorHandler =_c.errorHandler || apiService.errorHandler;
                           apiService.failureHandler = _c.failureHandler || apiService.failureHandler;
                           apiService.successHandler = _c.successHandler || apiService.successHandler;
                           apiService.progressHandler= _c.progressHandler||apiService.progressHandler;
                           apiService.ajaxType =_c.ajaxType ||apiService.ajaxType;
                           apiService.local={method:'get',url:_c.localUrl};

                       }

                   }

               }
           }

           
           for(var i in _controllers)
           { 
               Rsd.loadServiceApi(_controllers[i],_fnConfig);
           }

           Rsd.callFunction(Rsd.app, callback, [_data]);
       };
             
        if(host.isLoaded)
        {
            return;
        }
        if(Rsd.isEmpty(host.url))
        {
            console.error("apihost未设置url属性。")
            return;
        }
        
        Rsd.services = Rsd.services || {}; 
        //加载策略1:根据路由表返回加载
        if(!Rsd.isEmpty(host.routeList))
        {
            var _services = Rsd.create('Rsd.data.Store',{
                host:host,
                proxy: {
                    url:  host.url + host.routeList,
                    method:'get'
                }
             });

             _services.load({},function(data){
             
                Rsd.callFunction(Rsd.app, _fnLoad, [this.host,data]); 

            });
            return ;
        }
       
        //加载策略2:指定要加的controller
        if(!Rsd.isEmpty(host.controllers))
        {  
            //适配返回数据
            Rsd.callFunction(Rsd.app, _fnLoad, [host,{data:host.controllers}]); 
            return ;
        }

        //提示错误:未设置要加载的数据
        if(Rsd.isEmpty(host.routeList) && Rsd.isEmpty(host.controllers))
        {
            console.error("apihost未设置routeList属性 ,且未设置controllers属性。")
            return;
        }
    };
      
  /**
   * @description  加载单个controller,获取controller拥有的API(方法)
   * @param {*} controller  {group:'',url:'',useSSL:false,errorHandler:'',failureHandler:'',successHandler:'',api:{}} 获取api描述地址URL
   * @param {*} callback 
   */
    this.loadServiceApi = function loadServiceApi(controller,callback) {

        controller.isLoading = true;
        //console.log(controller);
        var _group = controller.group.toLowerCase();
        var _ssl = controller.useSSL;

        //console.log('load =>' + _group);
      
        Rsd.httpRequest({ url: controller.url,method:'GET'}, {}, function(data) {

            if(data.success===false)
            {
                console.error('获取API描述(' + controller.url + ' )失败:',data);
                return;
            }
            var list = data.data || data||[]; 
          
            for(var j in list)
            {
                var item = list[j];
               
                if(!Rsd.isString(item.Url))
                    {
                        console.error('属性Url的值('+item.Url+')无效',item);
                        item.Url = ''; 
                    }
                    if( _ssl && item.Url.startWith('http://'))
                    { 
                        item.Url = 'https://' + item.Url.substr(7);
                    }

                    var config = {
                        key:_group + '.' + item.MethodName.toLowerCase(),
                        name:item.MethodName.toLowerCase(),
                        group:_group,
                        text:item.Description,
                        parent : Rsd.app,
                        errorHandler :controller.errorHandler||'',
                        failureHandler : controller.failureHandler||'',
                        successHandler : controller.successHandler||'',
                        progressHandler: controller.progressHandler||'',
                        ajaxType:item.ajaxType ||'ajax',
                        local:{method:'get',url:''},
                        server:{
                            url:item.Url||'',
                            method:item.Method||'POST',
                            contentType:item.ContentType||'application/x-www-form-urlencoded',
                            //dataType: 'json',
                            async:true
                        }
                    }
                    if(Rsd.services[_group])
                    {
                        Rsd.services[_group].api[item.MethodName.toLowerCase()] = new Rsd.data.Service(config);
                    }
                    else
                    {
                        console.error(_group + '不存在');
                    }
                    
            }
            controller.isLoading = false;

            //console.log('loaded =>' + _group);
            if(Rsd.isFunction(callback))
            {
                callback.call(controller,data);
            }

        },'加载服务');
    };

    /**
     *
     * @description 请求API服务,接口数据加载中时,自动等待。
     * @param name
     * @param data
     * @param callback 请求成功(status==200)回调方法,且优先执行,执行返回true时,后续方法(success,failure)继续执行,返回false时,后续方法终止
     * @param msg
     * @param timeout 毫秒
     * */
    this.requestService = function requestService(name, data, callback,msg,timeout) {
        
        if(Rsd.isEmpty(name))
        {
            throw new Error('param [name] is null when call  Rsd.requestService method.');
        }
        var _name = name.toLowerCase();
        var _group = _name.substr(0,_name.lastIndexOf('.'));
        var _method = _name.substr(_name.lastIndexOf('.')+1);

        //组
        var serviceGroup = Rsd.services[_group];

        if(Rsd.isEmpty(serviceGroup))
        {
            var _error =  '服务['+ name +']不存在,请先注册';
            Rsd.warn(_group);
            console.error(_error);
            return;
        }

        var _fn = function () {

            var apiService = serviceGroup.api[_method];
            if(Rsd.isEmpty(apiService))
            {
                var _error =  '服务['+ name +']不存在,请确认';
                Rsd.warn(_method);
                console.error(_error);
                return;
            }
             
            apiService.request(data,callback,msg,timeout); 

        }

        var _timer = 0;
        var _request = function () {

            if(serviceGroup.isLoading)
            {
                //如果正在加载服务 延时200毫秒 最多延时5次
                _timer++;
                if(_timer>10)
                {
                    Rsd.warn('系统繁忙,请稍后再试');
                    return;
                }
                console.log(_timer + '- call '+_name + ' => controller ' + serviceGroup.group + ' is loading.' );
                setTimeout(_request,200*_timer);
            }
            else
            {
                _fn();
            }

        }
        if(Rsd.isEmpty(serviceGroup.api))
        {

            if(serviceGroup.isLoading)
            {
                _request();
            }
            else
            {
                Rsd.loadServiceApi(serviceGroup,_fn);
            }

        }else
        {
            _fn();
        }

    };

    //console.debug(Date.now(),'redjs end');
}