/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-core.js  V1.31
 * @author K'naan
 * -- Modified from dwz.core.js (author:ZhangHuihua@msn.com)
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-core.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    var BJUI = {
        JSPATH     : 'BJUI/',
        PLUGINPATH : 'BJUI/plugins/',
        IS_DEBUG   : false,
        KeyPressed : { //key press state
            ctrl  : false,
            shift : false
        },
        keyCode: {
            ENTER : 13, ESC  : 27, END : 35, HOME : 36,
            SHIFT : 16, CTRL : 17, TAB : 9,
            LEFT  : 37, RIGHT: 39, UP  : 38, DOWN : 40,
            DELETE: 46, BACKSPACE: 8
        },
        eventType: {
            initUI         : 'bjui.initUI',         // When document load completed or ajax load completed, B-JUI && Plugins init 
            beforeInitUI   : 'bjui.beforeInitUI',   // If your DOM do not init [add to DOM attribute 'data-noinit="true"']
            afterInitUI    : 'bjui.afterInitUI',    // 
            ajaxStatus     : 'bjui.ajaxStatus',     // When performing ajax request, display or hidden progress bar
            resizeGrid     : 'bjui.resizeGrid',     // When the window or dialog resize completed
            beforeAjaxLoad : 'bjui.beforeAjaxLoad', // When perform '$.fn.ajaxUrl', to do something...
            
            beforeLoadNavtab  : 'bjui.beforeLoadNavtab',
            beforeLoadDialog  : 'bjui.beforeLoadDialog',
            afterLoadNavtab   : 'bjui.afterLoadNavtab',
            afterLoadDialog   : 'bjui.afterLoadDialog',
            beforeCloseNavtab : 'bjui.beforeCloseNavtab',
            beforeCloseDialog : 'bjui.beforeCloseDialog',
            afterCloseNavtab  : 'bjui.afterCloseNavtab',
            afterCloseDialog  : 'bjui.afterCloseDialog'
        },
        formColWidth: {L:900, M:680, S:360, SS:240},
        pageInfo: {pageCurrent:'pageCurrent', pageSize:'pageSize', orderField:'orderField', orderDirection:'orderDirection'},
        alertMsg: {displayPosition:'topcenter', alertTimeout: 6000}, //alertmsg display position && close timeout
        ajaxTimeout: 30000,
        statusCode: {ok:200, error:300, timeout:301},
        keys: {statusCode:'statusCode', message:'message'},
        ui: {
            windowWidth      : 0,
            showSideWidth    : 990,
            sidenavWidth     : 260,
            offsetWidth      : 15,
            displayFirst     : true,      
            showSlidebar     : true,      // After the B-JUI initialization, display slidebar
            clientPaging     : true,      // Response paging and sorting information on the client
            overwriteHomeTab : false      // When open an undefined id of navtab, whether overwrite the home navtab
        },
        debug: function(msg) {
            if (this.IS_DEBUG) {
                if (typeof(console) != 'undefined') console.log(msg)
                else alert(msg)
            }
        },
        loginInfo: {
            url    : 'login.html',
            title  : 'Login',
            width  : 420,
            height : 260,
            mask   : true
        },
        loadLogin: function() {
            var login = this.loginInfo
            
            BJUI.dialog({id:'bjui-login', url:login.url, title:login.title, width:login.width, height:login.height, mask:login.mask})
        },
        init: function(options) {
            var op = $.extend({}, options)
            
            $.extend(BJUI.keys, op.keys)
            $.extend(BJUI.statusCode, op.statusCode)
            $.extend(BJUI.pageInfo, op.pageInfo)
            $.extend(BJUI.formColWidth, op.formColWidth)
            $.extend(BJUI.alertMsg, op.alertMsg)
            $.extend(BJUI.loginInfo, op.loginInfo)
            $.extend(BJUI.ui, op.ui)
            
            if (op.JSPATH) this.JSPATH = op.JSPATH
            if (op.PLUGINPATH) this.PLUGINPATH = op.PLUGINPATH
            if (op.ajaxTimeout) this.ajaxTimeout = op.ajaxTimeout
            
            this.IS_DEBUG = op.debug || false
            this.initEnv()
            
            if ((!$.cookie || !$.cookie('bjui_theme')) && op.theme) $(this).theme('setTheme', op.theme)
        },
        initEnv: function() {
            $(window).resize(function() {
                BJUI.initLayout()
                
                setTimeout(function() {$(this).trigger(BJUI.eventType.resizeGrid)}, 30)
            })
            
            setTimeout(function() {
                $(document).initui()
                
                var $collapse = $('#bjui-navbar-collapse'), left = $collapse.prev().width()
                
                $collapse.css('left', left).data('position.left', left)
                
                BJUI.initLayout()
                
                $('#bjui-top-collapse').on('click.theme', '.dropdown-toggle', function() {
                    var $dropdown = $(this).next(), $topcollapse = $('#bjui-top-collapse')
                    
                    if (!$topcollapse.hasClass('navbar-show')) {
                        $topcollapse.attr('style', $dropdown.is(':visible') ? '' : 'overflow:visible !important;')
                    }
                })
                
                $('[data-toggle="collapsenavbar"]').click(function() {
                    var $this = $(this), $target = $($this.data('target')), $parent = $this.closest('.navbar-header')
                    
                    if ($target.length) {
                        $target.toggleClass('navbar-show')
                        $parent.toggleClass('navbar-show')
                        
                        $(document).on('click', function(e) {
                            if(!($target.has(e.target).length) && !($parent.has(e.target).length)) {
                                $target.removeClass('navbar-show')
                                $parent.removeClass('navbar-show')
                            }
                        })
                    }
                })
            }, 10)
        },
        initLayout: function() {
            var ww = $(window).width(), hh = $(window).height(),
                $top = $('#bjui-top'), $navbar = $('#bjui-navbar'), $collapse = $('#bjui-top-collapse, #bjui-navbar-collapse'),
                th = $top.height(), nh = parseInt($navbar.css('min-height'), 10),
                eventName = 'click.bjui.sidenav.hide', opts = {},
                $navtab  = $('#bjui-navtab'), $sidenav = $('#bjui-sidenav'), $sidenavcol = $('#bjui-sidenav-col'),
                $sidenavarrow = $('#bjui-sidenav-arrow'), $sidenavbtn = $('#bjui-sidenav-btn'), $sidenavbox = $('#bjui-sidenav-box'),
                tnh = $sidenavbtn.outerHeight() - 2
            
            $('#bjui-navtab .tabsPageContent').height(hh - th - nh - tnh)
            $sidenav.height(hh - th - nh - 1)
            $sidenavcol.width(BJUI.ui.sidenavWidth)
            
            $collapse.scrollTop(5).eq(0).removeAttr('style')
            $collapse.each(function() {
                var $this = $(this), $collapsebtn = $this.prev().find('.navbar-toggle'), isCollapse = ($this.scrollTop() == 5 || $this.hasClass('navbar-show'))
                
                isCollapse && $this.scrollTop(0)
                
                $this.toggleClass('position', isCollapse)
                $collapsebtn.toggleClass('position', isCollapse)
                
                if ($this.data('position.left') && (ww / 2) < $this.data('position.left')) {
                    $this.prev().find('> .navbar-brand').hide()
                    $this.css('left', 0)
                } else {
                    $this.prev().find('> .navbar-brand').show()
                    $this.css('left', $this.data('position.left'))
                }
            })
            
            if (ww < BJUI.ui.showSideWidth) {
                $sidenavcol.css('left', -(BJUI.ui.sidenavWidth + BJUI.ui.offsetWidth)).addClass('autohide')
                $navtab.css('margin-left', 0)
                $sidenavbtn.show()
            } else {
                $sidenavcol.css('left', 0).removeClass('autohide')
                $navtab.css('margin-left', (BJUI.ui.sidenavWidth + BJUI.ui.offsetWidth))
                $sidenavbtn.hide()
            }
            
            $sidenavarrow.off(eventName).on(eventName, function() {
                $sidenavcol
                    .stop()
                    .animate({
                        left: -(BJUI.ui.sidenavWidth + BJUI.ui.offsetWidth)
                    }, 'fast', function() {
                        $sidenavbtn.show()
                        
                        $(window).trigger(BJUI.eventType.resizeGrid)
                    })
                
                $navtab
                    .stop()
                    .animate({
                        'margin-left': 0
                    }, 'fast')
            })
            
            $sidenavbtn.off(eventName).on(eventName, function() {
                $sidenavbtn.hide()
                
                $sidenavcol
                    .stop()
                    .animate({
                        left: 0
                    }, 'fast', function() {
                        $(window).trigger(BJUI.eventType.resizeGrid)
                    })
                
                opts['margin-left'] = (BJUI.ui.sidenavWidth + BJUI.ui.offsetWidth)
                if (ww < BJUI.ui.showSideWidth)
                    opts['margin-left'] = 0
                
                $navtab
                    .stop()
                    .animate(opts, 'fast')
            })
            
            /* fixed pageFooter */
            /*setTimeout(function() {
                $('#bjui-navtab > .tabsPageContent > .navtabPage').resizePageH()
                $('#bjui-navtab > .tabsPageContent > .navtabPage').find('.bjui-layout').resizePageH()
            }, 10)*/
        },
        regional: {},
        setRegional: function(key, value) {
            BJUI.regional[key] = value
        },
        getRegional : function(key) {
            if (String(key).indexOf('.') >= 0) {
                var msg, arr = String(key).split('.')
                
                for (var i = 0; i < arr.length; i++) {
                    if (!msg) msg = BJUI.regional[arr[i]]
                    else msg = msg[arr[i]]
                }
                
                return msg
            } else {
                return BJUI.regional[key]
            }
        },
        doRegional: function(frag, regional) {
            $.each(regional, function(k, v) {
                frag = frag.replaceAll('#'+ k +'#', v)
            })
            
            return frag
        },
        // is ie browser
        isIE: function(ver) {
            var b = document.createElement('b')
            
            b.innerHTML = '<!--[if IE '+ ver +']><i></i><![endif]-->'
            
            return b.getElementsByTagName('i').length === 1
        },
        StrBuilder: function() {
            return new StrBuilder()
        }
    }
    
    function StrBuilder() {
        this.datas = new Array()
    }
    
    StrBuilder.prototype.add = function(str) {
        if (typeof str !== 'undefined') this.datas.push(str)
        return this
    }
    
    StrBuilder.prototype.toString = function(str) {
        var string = this.datas.join(str || '')
        
        this.clear()
        
        return string
    }
    
    StrBuilder.prototype.isEmpty = function(){
        return this.datas.length == 0
    }
    
    StrBuilder.prototype.clear = function(){
        this.datas = []
        this.datas.length = 0
    }
    
    window.BJUI = BJUI
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-regional.zh-CN.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-regional.zh-CN.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    $(function() {
        
        /* 消息提示框 */
        BJUI.setRegional('alertmsg', {
            title  : {error : '错误提示', info : '信息提示', warn : '警告信息', correct : '成功信息', confirm : '确认信息', prompt : '确认信息'},
            btnMsg : {ok    : '确定', yes  : '是',   no   : '否',   cancel  : '取消'}
        })
        
        /* dialog */
        BJUI.setRegional('dialog', {
            close    : '关闭',
            maximize : '最大化',
            restore  : '还原',
            minimize : '最小化',
            title    : '弹出窗口'
        })
        
        /* order by */
        BJUI.setRegional('orderby', {
            asc  : '升序',
            desc : '降序'
        })
        
        /* 分页 */
        BJUI.setRegional('pagination', {
            total   : '总记录数/总页数',
            first   : '首页',
            last    : '末页',
            prev    : '上一页',
            next    : '下一页',
            jumpto  : '输入跳转页码，回车确认',
            jump    : '跳转',
            page    : '页',
            refresh : '刷新'
        })
        
        BJUI.setRegional('datagrid', {
            asc       : '升序',
            desc      : '降序',
            showhide  : '显示/隐藏 列',
            filter    : '过滤',
            clear     : '清除',
            lock      : '锁定列',
            unlock    : '解除锁定',
            add       : '添加',
            edit      : '编辑',
            save      : '保存',
            update    : '更新',
            cancel    : '取消',
            del       : '删除',
            prev      : '上一条',
            next      : '下一条',
            refresh   : '刷新',
            query     : '查询',
            'import'  : '导入',
            'export'  : '导出',
            exportf   : '导出筛选',
            all       : '全部',
            'true'    : '是',
            'false'   : '否',
            noData    : '没有数据！',
            fAndS     : '过滤和排序！',
            expandMsg : '点我展开行！',
            shrinkMsg : '点我收缩行！',
            selectMsg : '未选中任何行！',
            editMsg   : '请先保存编辑行！',
            saveMsg   : '没有需要保存的行！',
            delMsg    : '确定要删除该行吗？',
            delMsgM   : '确定要删除选中行？',
            errorData : '未获取到正确的数据！',
            failData  : '请求datagrid数据失败！'
        })
        
        BJUI.setRegional('findgrid', {
            choose : '选择选中项',
            append : '追加选择',
            empty  : '清空现有值'
        })
        
        /* ajax加载提示 */
        BJUI.setRegional('progressmsg', '正在努力加载数据，请稍等...')
        
        /* 日期选择器 */
        BJUI.setRegional('datepicker', {
            close      : '关闭',
            prev       : '上月',
            next       : '下月',
            clear      : '清空',
            ok         : '确定',
            dayNames   : ['日', '一', '二', '三', '四', '五', '六'],
            monthNames : ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
        })
        
        /* navtab右键菜单  */
        BJUI.setRegional('navtabCM', {
            refresh    : '刷新本标签',
            close      : '关闭本标签',
            closeother : '关闭其他标签',
            closeall   : '关闭所有标签'
        })
        
        /* dialog右键菜单 */
        BJUI.setRegional('dialogCM', {
            refresh    : '刷新本窗口',
            close      : '关闭本窗口',
            closeother : '关闭其他窗口',
            closeall   : '关闭所有窗口'
        })
    
        /* 503错误提示 */
        BJUI.setRegional('statusCode_503', '服务器当前负载过大或者正在维护！')
        
        /* AJAX 状态返回 0 提示 */
        BJUI.setRegional('ajaxnosend', '与服务器的通信中断，请检查URL链接或服务器状态！')
        
        /* timeout提示 */
        BJUI.setRegional('sessiontimeout', '会话超时，请重新登陆！')
        
        /* 占位符对应选择器无有效值提示 */
        BJUI.setRegional('plhmsg', '占位符对应的选择器无有效值！')
        
        /* 未定义复选框组名提示 */
        BJUI.setRegional('nocheckgroup', '未定义选中项的组名[复选框的"data-group"]！')
        
        /* 未选中复选框提示 */
        BJUI.setRegional('notchecked', '未选中任何一项！')
        
        /* 未选中下拉菜单提示 */
        BJUI.setRegional('selectmsg', '请选择一个选项！')
        
        /* 表单验证错误提示信息 */
        BJUI.setRegional('validatemsg', '提交的表单中 [{0}] 个字段有错误，请更正后再提交！')
        
        /* ID检查 */
        BJUI.setRegional('idChecked', '不规范，ID需以字母开头，组成部分包括（0-9，字母，中横线，下划线）')
        
        /* 框架名称 */
        BJUI.setRegional('uititle', 'B-JUI')
        
        /* 主navtab标题 */
        BJUI.setRegional('maintab', '我的主页')
        
        /**
         * 
         *  Plugins regional setting
         * 
         */
        /* nice validate - Global configuration */
        $.validator && $.validator.config({
            // Custom rules
            rules: {
                digits: [/^\d+$/, '请输入整数']
                ,number: [/^[\-\+]?((([0-9]{1,3})([,][0-9]{3})*)|([0-9]+))?([\.]([0-9]+))?$/, '请输入有效的数字']
                ,letters: [/^[a-z]+$/i, '{0}只能输入字母']
                ,upletterandnumber: [/^[A-Z0-9]+$/, '只能输入大写字母和数字']
                ,tel: [/^(?:(?:0\d{2,3}[\- ]?[1-9]\d{6,7})|(?:[48]00[\- ]?[1-9]\d{6}))$/, "请填写有效的电话号码"]
                ,mobile: [/^1[3-9]\d{9}$/, '手机号格式不正确']
                ,email: [/^[\w\+\-]+(\.[\w\+\-]+)*@[a-z\d\-]+(\.[a-z\d\-]+)*\.([a-z]{2,4})$/i, "请填写有效的邮箱"]
                ,qq: [/^[1-9]\d{4,}$/, 'QQ号格式不正确']
                //,date: [/^\d{4}-\d{1,2}-\d{1,2}$/, '请输入正确的日期,例:yyyy-mm-dd']
                ,date:[/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/, '请输入正确的日期，例：yyyy-MM-dd']
                //,time: [/^([01]\d|2[0-3])(:[0-5]\d){1,2}$/, '请输入正确的时间,例:14:30或14:30:00']
                ,time: [/^(2[0123]|(1|0?)[0-9]){1}:([0-5][0-9]){1}:([0-5][0-9]){1}$/, '请输入正确的时间，例：HH:mm:ss']
                ,datetime: [/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(2[0123]|(1|0?)[0-9]){1}:([0-5][0-9]){1}:([0-5][0-9]){1}$/,
                            '请输入正确的日期时间，例：yyyy-MM-dd HH:mm:ss']
                ,IDcard: [/^\d{6}(19|2\d)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)?$/, "请填写正确的身份证号码"]
                ,url: [/^(https?|s?ftp):\/\/\S+$/i, "请填写有效的网址"]
                ,zipcode: [/^\d{6}$/, "请检查邮政编码格式"]
                ,chinese: [/^[\u0391-\uFFE5]+$/, '请输入中文']
                ,username: [/^\w{3,12}$/, '请输入3-12位数字、字母、下划线']
                ,password: [/^[\S]{6,16}$/, "请填写6-16位字符，不能包含空格"]
                ,pattern:function(element, params) {
                    if (!params) return true
                    
                    var date = element.value.parseDate(params)
                    
                    return (!date ? this.renderMsg('错误的日期时间格式！', params) : true)
                }
                ,accept: function (element, params){
                    if (!params) return true;
                    var ext = params[0],
                        value = $(element).val();
                    return (ext === '*') ||
                           (new RegExp(".(?:" + ext + ")$", "i")).test(value) ||
                           this.renderMsg("只接受{1}后缀的文件", ext.replace(/\|/g, ','));
                }
            },
            
            // Default error messages
            messages: {
                0: "此处",
                fallback: "{0}格式不正确",
                loading: "正在验证...",
                error: "网络异常",
                timeout: "请求超时",
                required: "{0}不能为空",
                remote: "{0}已被使用",
                integer: {
                    '*': "请填写整数",
                    '+': "请填写正整数",
                    '+0': "请填写正整数或0",
                    '-': "请填写负整数",
                    '-0': "请填写负整数或0"
                },
                match: {
                    eq: "{0}与{1}不一致",
                    neq: "{0}与{1}不能相同",
                    lt: "{0}必须小于{1}",
                    gt: "{0}必须大于{1}",
                    lte: "{0}不能大于{1}",
                    gte: "{0}不能小于{1}"
                },
                range: {
                    rg: "请填写{1}到{2}的数",
                    gte: "请填写不小于{1}的数",
                    lte: "请填写最大{1}的数",
                    gtlt: "请填写{1}到{2}之间的数",
                    gt: "请填写大于{1}的数",
                    lt: "请填写小于{1}的数"
                },
                checked: {
                    eq: "请选择{1}项",
                    rg: "请选择{1}到{2}项",
                    gte: "请至少选择{1}项",
                    lte: "请最多选择{1}项"
                },
                length: {
                    eq: "请填写{1}个字符",
                    rg: "请填写{1}到{2}个字符",
                    gte: "请至少填写{1}个字符",
                    lte: "请最多填写{1}个字符",
                    eq_2: "",
                    rg_2: "",
                    gte_2: "",
                    lte_2: ""
                }
            }
        })
        
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-frag.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-frag.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    BJUI.setRegional('alertmsg', {
        title  : {error : 'Error', info : 'Info', warn : 'Warning', correct : 'Correct', confirm : 'Confirm', prompt:'Prompt'},
        btnMsg : {ok    : 'OK',    yes  : 'YES',  no   : 'NO',      cancel  : 'Cancel'}
    })
    
    BJUI.setRegional('dialog', {
        close    : 'Close',
        maximize : 'Maximize',
        restore  : 'Restore',
        minimize : 'Minimize',
        title    : 'Popup window'
    })
    
    BJUI.setRegional('orderby', {
        asc  : 'Asc',
        desc : 'Desc'
    })
    
    BJUI.setRegional('pagination', {
        first  : 'First page',
        last   : 'Last page',
        prev   : 'Prev page',
        next   : 'Next page',
        jumpto : 'Jump page number',
        jump   : 'Jump'
    })
    
    BJUI.setRegional('datagrid', {
        asc       : 'ASC',
        desc      : 'DESC',
        showhide  : 'Show/Hide columns',
        filter    : 'Filter',
        clear     : 'Clear',
        lock      : 'Lock',
        unlock    : 'Unlock',
        add       : 'Add',
        edit      : 'Edit',
        save      : 'Save',
        update    : 'Update',
        cancel    : 'Cancel',
        del       : 'Delete',
        prev      : 'Prev',
        next      : 'Next',
        refresh   : 'Refresh',
        query     : 'Query',
        'import'  : 'Import',
        'export'  : 'Export',
        exportf   : 'Export filter',
        all       : 'All',
        'true'    : 'True',
        'false'   : 'False',
        noData    : 'No data!',
        fAndS     : 'Filter && Sort!',
        expandMsg : 'Click here to expand the tr!',
        shrinkMsg : 'Click here to shrink the tr!',
        selectMsg : 'Not selected any rows!',
        saveMsg   : 'No rows need to save!',
        editMsg   : 'Please save the edited row!',
        delMsg    : 'Sure you want to delete this row?',
        delMsgM   : 'Sure you want to delete selected rows?',
        errorData : 'Did not get the correct data!',
        failData  : 'Request data failed!'
    })
    
    BJUI.setRegional('findgrid', {
        choose : 'Choose the selected item',
        append : 'Append choose the selected item',
        empty  : 'Empty existing values'
    })
    
    BJUI.setRegional('progressmsg', 'Data loading, please waiting...')
    
    BJUI.setRegional('datepicker', {
        close      : 'Close',
        prev       : 'Prev month',
        next       : 'Next month',
        clear      : 'Clear',
        ok         : 'OK',
        dayNames   : ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
        monthNames : ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    })
    
    BJUI.setRegional('navtabCM', {
        refresh    : 'Refresh navtab',
        close      : 'Close navtab',
        closeother : 'Close other navtab',
        closeall   : 'Close all navtab'
    })
    
    BJUI.setRegional('dialogCM', {
        refresh    : 'Refresh dialog',
        close      : 'Close dialog',
        closeother : 'Close other dialog',
        closeall   : 'Close all dialog'
    })
    
    BJUI.setRegional('statusCode_503', 'HTTP status 503, the current server load is too large or is down for maintenance!')
    
    BJUI.setRegional('ajaxnosend', 'Communication with the server is interrupted, please check the URL link or server status!')
    
    BJUI.setRegional('sessiontimout', 'Session timeout, please login!')
    
    BJUI.setRegional('plhmsg', 'Placeholder corresponding selector None Valid!')
    
    BJUI.setRegional('nocheckgroup', 'Undefined group name selected item [check box "data-group"]!')
    
    BJUI.setRegional('notchecked', 'Unchecked any one!')
    
    BJUI.setRegional('selectmsg', 'Please select one option!')
    
    BJUI.setRegional('validatemsg', 'Submitted form data has [{0}] field an error, please after modified submitting!')
    
    BJUI.setRegional('idChecked', 'is not standardized, ID need to start with a letter, components include (0-9, letters, hyphens, underscore)')
    
    BJUI.setRegional('uititle', 'B-JUI')
    
    BJUI.setRegional('maintab', 'My home')
    
    
    window.FRAG = {
        dialog: '<div class="bjui-dialog bjui-dialog-container" style="top:150px; left:300px;">' +
                '    <div class="dialogHeader" onselectstart="return false;" oncopy="return false;" onpaste="return false;" oncut="return false;">' +
                '        <a class="close" href="javascript:;" title="#close#"><i class="fa fa-times"></i></a>' +
                '        <a class="maximize" href="javascript:;" title="#maximize#"><i class="fa fa-square-o"></i></a>' +
                '        <a class="restore" href="javascript:;" title="#restore#"><i class="fa fa-clone fa-rotate-90"></i></a>' +
                '        <a class="minimize" href="javascript:;" title="#minimize#"><i class="fa fa-minus"></i></a>' +
                '        <h1><span><i class="fa fa-th-large"></i></span> <span class="title">#title#</span></h1>' +
                '    </div>' +
                '    <div class="dialogContent unitBox"></div>' +
                '    <div class="resizable_h_l" tar="nw"></div>' +
                '    <div class="resizable_h_r" tar="ne"></div>' +
                '    <div class="resizable_h_c" tar="n"></div>' +
                '    <div class="resizable_c_l" tar="w" style="height:100%;"></div>' +
                '    <div class="resizable_c_r" tar="e" style="height:100%;"></div>' +
                '    <div class="resizable_f_l" tar="sw"></div>' +
                '    <div class="resizable_f_r" tar="se"></div>' +
                '    <div class="resizable_f_c" tar="s"></div>' +
                '</div>'
        ,
        taskbar: '<div id="bjui-taskbar" style="left:0px; display:none;">' +
                 '    <div class="taskbarContent">' +
                 '        <ul></ul>' +
                 '    </div>' +
                 '    <div class="taskbarLeft taskbarLeftDisabled"><i class="fa fa-angle-double-left"></i></div>' +
                 '    <div class="taskbarRight"><i class="fa fa-angle-double-right"></i></div>' +
                 '</div>'
        ,
        splitBar: '<div id="bjui-splitBar"></div>',
        splitBarProxy: '<div id="bjui-splitBarProxy"></div>',
        resizable: '<div id="bjui-resizable" class="bjui-resizable"></div>',
        alertBackground: '<div class="bjui-alertBackground"></div>',
        maskBackground: '<div class="bjui-maskBackground bjui-ajax-mask"></div>',
        maskProgress: '<div class="bjui-maskProgress bjui-ajax-mask"><i class="fa fa-cog fa-spin"></i>&nbsp;&nbsp;#progressmsg#<div class="progressBg"><div class="progress"></div></div></div>',
        progressBar_custom: '<div id="bjui-progressBar-custom" class="progressBar"><i class="fa fa-cog fa-spin"></i> <span></span></div>',
        dialogMask: '<div class="bjui-dialogBackground"></div>',
        orderby: '<a href="javascript:;" class="order asc" data-order-direction="asc" title="#asc#"><i class="fa fa-angle-up"></i></a>' +
                 '<a href="javascript:;" class="order desc" data-order-direction="desc" title="#desc#"><i class="fa fa-angle-down"></i></a>'
        ,
        slidePanel: '<div class="panel panel-default">' +
                    '    <div class="panel-heading">' +
                    '        <h4 class="panel-title"><a data-toggle="collapse" data-parent="#bjui-accordionmenu" href="##id#" class="#class#">#icon#&nbsp;#title#<b>#righticon#</b></a></h4>' +
                    '    </div>' +
                    '    <div id="#id#" class="panel-collapse collapse#bodyclass#">' +
                    '        <div class="panel-body">' +
                    '        </div>' +
                    '    </div>' +
                    '</div>'
        ,
        gridPaging: '<ul class="pagination">' +
                    '    <li class="page-total">' +
                    '        <span title="#total#">#count#</span>' +
                    '    </li>' +
                    '    <li class="page-jumpto"><span class="page-input"><input class="form-control input-sm-pages" type="text" size="3.2" value="#pageCurrent#" title="#jumpto#"></span></li>' +
                    '    <li class="page-first btn-nav">' +
                    '        <a href="javascript:;" title="#first#"><i class="fa fa-step-backward"></i></a>' +
                    '    </li>' +
                    '    <li class="page-prev btn-nav">' +
                    '        <a href="javascript:;" title="#prev#"><i class="fa fa-backward"></i></a>' +
                    '    </li>' +
                    '    #pageNumFrag#' +
                    '    <li class="page-next btn-nav">' +
                    '        <a href="javascript:;" title="#next#"><i class="fa fa-forward"></i></a>' +
                    '    </li>' +
                    '    <li class="page-last btn-nav">' +
                    '        <a href="javascript:;" title="#last#"><i class="fa fa-step-forward"></i></a>' +
                    '    </li>' +
                    '</ul>'
        ,
        gridPageNum : '<li class="page-num#active#"><a href="javascript:;">#num#</a></li>',
        gridMenu : '<div class="datagrid-menu-box">'
                 + '    <ul>'
                 + '        <li class="datagrid-li-asc"><a href="javascript:;"><span class="icon"><i class="fa fa-sort-amount-asc"></i></span><span class="title">#asc#</span></a></li>'
                 + '        <li class="datagrid-li-desc"><a href="javascript:;"><span class="icon"><i class="fa fa-sort-amount-desc"></i></span><span class="title">#desc#</span></a></li>'
                 + '        <li class="datagrid-li-filter"><a href="javascript:;"><span class="icon"><i class="fa fa-filter"></i></span><span class="title">#filter#</span><span class="arrow"></span></a></li>'
                 + '        <li class="datagrid-li-showhide"><a href="javascript:;"><span class="icon"><i class="fa fa-check-square-o"></i></span><span class="title">#showhide#</span><span class="arrow"></span></a></li>'
                 + '        <li class="datagrid-li-lock"><a href="javascript:;"><span class="icon"><i class="fa fa-lock"></i></span><span class="title">#lock#</span></a></li>'
                 + '        <li class="datagrid-li-unlock disable"><a href="javascript:;"><span class="icon"><i class="fa fa-unlock"></i></span><span class="title">#unlock#</span></a></li>'
                 + '    </ul>'
                 + '</div>'
        ,
        gridFilter: '<div class="datagrid-filter-box">'
                  + '<fieldset>'
                  + '<legend>#label#</legend>'
                  + '<span class="filter-a"></span>'
                  + '<span class="filter-and"><select data-toggle="selectpicker" data-container="true" data-width="100%"><option value="and">AND</option><option value="or">OR</option></select></span>'
                  + '<span class="filter-b"></span>'
                  + '<span class="filter-ok"><button type="button" class="btn-green ok" data-icon="check">#filter#</button><button type="button" class="btn-orange clear" data-icon="remove">#clear#</button></span>'
                  + '</fieldset>'
                  + '</div>'
        ,
        gridShowhide: '<li data-index="#index#" class="datagrid-col-check"><a href="javascript:;"><i class="fa fa-check-square-o"></i>#label#</a></li>',
        gridEditBtn : '<button type="button" class="btn btn-green bjui-datagrid-btn edit"><i class="fa fa-edit"></i> #edit#</button>'
                    + '<button type="button" class="btn btn-green bjui-datagrid-btn update"><i class="fa fa-edit"></i> #update#</button>'
                    + '<button type="button" class="btn btn-green bjui-datagrid-btn save"><i class="fa fa-check"></i> #save#</button>'
                    + '<button type="button" class="btn btn-orange bjui-datagrid-btn cancel"><i class="fa fa-undo"></i> #cancel#</button>'
                    + '<button type="button" class="btn btn-red bjui-datagrid-btn delete"><i class="fa fa-remove"></i> #del#</button>'
        ,
        gridDialogEditBtns: '<ul>'
                          + '    <li class="pull-left"><button type="button" class="btn btn-orange prev" data-icon="arrow-up">#prev#</button></li>'
                          + '    <li class="pull-left"><button type="button" class="btn btn-orange next" data-icon="arrow-down">#next#</button></li>'
                          + '    <li><button type="button" class="btn btn-red cancel" data-icon="remove">#cancel#</button></li>'
                          + '    <li><button type="button" class="btn btn-default save" data-icon="save">#save#</button></li>'
                          + '</ul>'
        ,
        gridExpandBtn: '<span title="#expandMsg#"><i class="fa fa-plus"></i></span>',
        gridShrinkBtn: '<span title="#shrinkMsg#"><i class="fa fa-minus"></i></span>',
        alertBoxFrag: '<div id="bjui-alertMsgBox" class="bjui-alert"><div class="alertContent"><div class="#type#"><div class="alertInner"><h1><i class="fa #fa#"></i>#title#</h1><div class="msg">#message##prompt#</div></div><div class="toolBar clearfix"><ul>#btnFragment#</ul></div></div></div></div>',
        alertBtnFrag: '<li><button class="btn btn-#class#" rel="#callback#" type="button">#btnMsg#</button></li>',
        calendarFrag: '<div id="bjui-calendar">' +
                      '    <div class="main">' +
                      '        <a class="close" href="javascript:;" title="#close#"><i class="fa fa-times-circle"></i></a>' +
                      '        <div class="head">' +
                      '            <table width="100%" border="0" cellpadding="0" cellspacing="2">' +
                      '                <tr>' +
                      '                    <td width="20"><a class="prev" href="javascript:;" title="#prev#"><i class="fa fa-arrow-left"></i></a></td>' +
                      '                    <td><select name="year"></select></td>' +
                      '                    <td><select name="month"></select></td>' +
                      '                    <td width="20"><a class="next" href="javascript:;" title="#next#"><i class="fa fa-arrow-right"></i></a></td>' +
                      '                </tr>' +
                      '            </table>' +
                      '        </div>' +
                      '        <div class="body">' +
                      '            <dl class="dayNames"><dt>7</dt><dt>1</dt><dt>2</dt><dt>3</dt><dt>4</dt><dt>5</dt><dt>6</dt></dl>' +
                      '            <dl class="days"><!-- date list --></dl>' +
                      '            <div style="clear:both;height:0;line-height:0"></div>' +
                      '        </div>' +
                      '        <div class="foot">' +
                      '            <table class="time">' +
                      '                <tr>' +
                      '                    <td>' +
                      '                        <input type="text" class="hh" maxlength="2" data-type="hh" data-start="0" data-end="23">:<input' +
                      '                         type="text" class="mm" maxlength="2" data-type="mm" data-start="0" data-end="59">:<input' +
                      '                         type="text" class="ss" maxlength="2" data-type="ss" data-start="0" data-end="59">' +
                      '                    </td>' +
                      '                    <td><ul><li class="up" data-add="1">&and;</li><li class="down">&or;</li></ul></td>' +
                      '                </tr>' +
                      '            </table>' +
                      '            <button type="button" class="clearBtn btn btn-orange">#clear#</button>' +
                      '            <button type="button" class="okBtn btn btn-default">#ok#</button>' +
                      '        </div>' +
                      '        <div class="tm">' +
                      '            <ul class="hh">' +
                      '                <li>0</li>' +
                      '                <li>1</li>' +
                      '                <li>2</li>' +
                      '                <li>3</li>' +
                      '                <li>4</li>' +
                      '                <li>5</li>' +
                      '                <li>6</li>' +
                      '                <li>7</li>' +
                      '                <li>8</li>' +
                      '                <li>9</li>' +
                      '                <li>10</li>' +
                      '                <li>11</li>' +
                      '                <li>12</li>' +
                      '                <li>13</li>' +
                      '                <li>14</li>' +
                      '                <li>15</li>' +
                      '                <li>16</li>' +
                      '                <li>17</li>' +
                      '                <li>18</li>' +
                      '                <li>19</li>' +
                      '                <li>20</li>' +
                      '                <li>21</li>' +
                      '                <li>22</li>' +
                      '                <li>23</li>' +
                      '            </ul>' +
                      '            <ul class="mm">' +
                      '                <li>0</li>' +
                      '                <li>5</li>' +
                      '                <li>10</li>' +
                      '                <li>15</li>' +
                      '                <li>20</li>' +
                      '                <li>25</li>' +
                      '                <li>30</li>' +
                      '                <li>35</li>' +
                      '                <li>40</li>' +
                      '                <li>45</li>' +
                      '                <li>50</li>' +
                      '                <li>55</li>' +
                      '            </ul>' +
                      '            <ul class="ss">' +
                      '                <li>0</li>' +
                      '                <li>10</li>' +
                      '                <li>20</li>' +
                      '                <li>30</li>' +
                      '                <li>40</li>' +
                      '                <li>50</li>' +
                      '            </ul>' +
                      '        </div>' +
                      '    </div>' +
                      '</div>'
        ,
        spinnerBtn:  '<ul class="bjui-spinner"><li class="up" data-add="1">&and;</li><li class="down">&or;</li></ul>',
        findgridBtn: '<a class="bjui-lookup" href="javascript:;" data-toggle="findgridbtn"><i class="fa fa-search"></i></a>',
        dateBtn:     '<a class="bjui-lookup" href="javascript:;" data-toggle="datepickerbtn"><i class="fa fa-calendar"></i></a>',
        navtabCM: '<ul id="bjui-navtabCM">' +
                  '    <li rel="reload"><span class="icon"><i class="fa fa-refresh"></i></span><span class="title">#refresh#</span></li>' +
                  '    <li rel="closeCurrent"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#close#</li>' +
                  '    <li rel="closeOther"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#closeother#</li>' +
                  '    <li rel="closeAll"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#closeall#</li>' +
                  '</ul>'
        ,
        dialogCM: '<ul id="bjui-dialogCM">' +
                  '    <li rel="reload"><span class="icon"><i class="fa fa-refresh"></i></span><span class="title">#refresh#</span></li>' +          
                  '    <li rel="closeCurrent"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#close#</span></li>' +
                  '    <li rel="closeOther"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#closeother#</span></li>' +
                  '    <li rel="closeAll"><span class="icon"><i class="fa fa-remove"></i></span><span class="title">#closeall#</span></li>' +
                  '</ul>'
        ,
        externalFrag: '<iframe src="{url}" style="width:100%;height:{height};" frameborder="no" border="0" marginwidth="0" marginheight="0"></iframe>'
    }
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-extends.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-extends.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    $.fn.extend({
        /**
         * @param {Object} op: {type:GET/POST, url:ajax请求地址, data:ajax请求参数列表, callback:回调函数 }
         */
        ajaxUrl: function(op) {
            var $this = $(this), $target = $this//$this.closest('.bjui-pageContent')
            
            if (!$target.length)
                $target = $this
                
            $this.trigger(BJUI.eventType.beforeAjaxLoad)
            
            if (op.loadingmask) {
                $target.trigger(BJUI.eventType.ajaxStatus)
            }
            
            $.ajax({
                type     : op.type || 'GET',
                url      : op.url,
                data     : op.data || {},
                cache    : false,
                dataType : 'html',
                timeout  : BJUI.ajaxTimeout,
                success  : function(response) {
                    var json = response.toJson(), $ajaxMask = $target.find('> .bjui-ajax-mask')
                    
                    if (!json[BJUI.keys.statusCode]) {
                        $this.empty().html(response).initui()
                    } else {
                        if (json[BJUI.keys.statusCode] == BJUI.statusCode.error) {
                            if (json[BJUI.keys.message]) $this.alertmsg('error', json[BJUI.keys.message])
                            if (!$this.closest('.bjui-layout').length) {
                                if ($this.closest('.navtab-panel').length) $this.navtab('closeCurrentTab')
                                else $this.dialog('closeCurrent')
                            }
                        } else if (json[BJUI.keys.statusCode] == BJUI.statusCode.timeout) {
                            if ($this.closest('.bjui-dialog').length) $this.dialog('closeCurrent')
                            if ($this.closest('.navtab-panel').length) $this.navtab('closeCurrentTab')
                            
                            BJUI.alertmsg('info', (json[BJUI.keys.message] || BJUI.regional.sessiontimeout))
                            BJUI.loadLogin()
                        }
                        $ajaxMask.fadeOut('normal', function() {
                            $(this).remove()
                        })
                    }
                    
                    if ($.isFunction(op.callback)) op.callback(response)
                },
                error      : function(xhr, ajaxOptions, thrownError) {
                    $this.bjuiajax('ajaxError', xhr, ajaxOptions, thrownError)
                    if (!$this.closest('.bjui-layout').length) {
                        if ($this.closest('.navtab-panel').length) $this.navtab('closeCurrentTab')
                        else $this.dialog('closeCurrent')
                    }
                    $this.trigger('bjui.ajaxError')
                },
                statusCode : {
                    0  : function(xhr, ajaxOptions, thrownError) {
                        BJUI.alertmsg('error', BJUI.regional.ajaxnosend)
                    },
                    503: function(xhr, ajaxOptions, thrownError) {
                        BJUI.alertmsg('error', BJUI.regional.statusCode_503)
                    }
                }
            })
        },
        loadUrl: function(url,data,callback) {
            $(this).ajaxUrl({url:url, data:data, callback:callback})
        },
        doAjax: function(op) {
            var $this = $(this), $target, $ajaxMask
            
            $this.data('holdSubmit', true)
            
            if (!op.url) {
                BJUI.debug('The ajax url is undefined!')
                return
            }
            if (!op.callback) {
                BJUI.debug('The ajax callback is undefined!')
                return
            } else {
                op.callback = op.callback.toFunc()
            }
            if (op.loadingmask) {
                $target = $this.isTag('form') ? $this.closest('.bjui-pageContent') : $this
                
                if (!$target.length)
                    $target = $this
                
                $target.trigger(BJUI.eventType.ajaxStatus)
                $ajaxMask = $target.find('> .bjui-ajax-mask')
            }
            if (!op.type) op.type = 'POST'
            if (!op.dataType) op.dataType = 'json'
            if (!op.cache) op.cache = false
            op.timeout = op.ajaxTimeout || BJUI.ajaxTimeout
            op.success = function(response) {
                if ($ajaxMask) {
                    if (op.callback) {
                        $.when(op.callback(response)).done(function() {
                            $target.trigger('bjui.ajaxStop')
                        })
                    } else {
                        $target.trigger('bjui.ajaxStop')
                    }
                } else {
                    op.callback(response)
                }
                
                $this.data('holdSubmit', false)
            }
            op.error = function(xhr, ajaxOptions, thrownError) {
                if ($ajaxMask)
                    $target.trigger('bjui.ajaxError')
                
                $this.bjuiajax('ajaxError', xhr, ajaxOptions, thrownError)
                
                $this.data('holdSubmit', false)
            }
            op.statusCode = {
                0  : function(xhr, ajaxOptions, thrownError) {
                    BJUI.alertmsg('error', BJUI.regional.ajaxnosend)
                    
                    $this.data('holdSubmit', false)
                },
                503: function(xhr, ajaxOptions, thrownError) {
                    BJUI.alertmsg('error', BJUI.regional.statusCode_503)
                    
                    $this.data('holdSubmit', false)
                }
            }
            
            $.ajax(op)
        },
        getPageTarget: function() {
            var $target
            
            if (this.closest('.bjui-layout').length) $target = this.closest('.bjui-layout')
            else if (this.closest('.navtab-panel').length) $target = $.CurrentNavtab
            else $target = $.CurrentDialog
            
            return $target
        },
        resizePageH: function() {
            return this.each(function() {
                if ($(this).closest('.tab-content').length) return
                
                var $box         = $(this),
                    $pageHeader  = $box.find('> .bjui-pageHeader'),
                    $pageContent = $box.find('> .bjui-pageContent'),
                    $pageFooter  = $box.find('> .bjui-pageFooter'),
                    headH        = $pageHeader.outerHeight() || 0,
                    footH        = $pageFooter.outerHeight() || 0
                
                if ($pageFooter.css('bottom')) footH += parseInt($pageFooter.css('bottom')) || 0
                
                $pageContent.css({top:headH, bottom:footH})
                
                if ($box.hasClass('dialogContent') && !$pageContent.length) {
                    $box.css({padding:10, overflow:'auto'})
                }
            })
        },
        getMaxIndexObj: function($elements) {
            var zIndex = 0, index = 0
            
            $elements.each(function(i) {
                var newZIndex = parseInt($(this).css('zIndex')) || 1
                
                if (zIndex < newZIndex) {
                    zIndex = newZIndex
                    index  = i
                }
            })
            
            return $elements.eq(index)
        },
        /**
         * 将表单数据转成JSON对象 用法：$(form).serializeJson() Author: K'naan
         */
        serializeJson: function () {
            var o = {}
            var a = this.serializeArray()
            
            $.each(a, function () {
                if (o[this.name] !== undefined) {
                    if (!o[this.name].push) {
                        o[this.name] = [o[this.name]]
                    }
                    o[this.name].push(this.value || '')
                } else {
                   o[this.name] = this.value || ''
                }
            })
            
            return o
        },
        isTag: function(tn) {
            if (!tn) return false
            if (!$(this).prop('tagName')) return false
            return $(this)[0].tagName.toLowerCase() == tn ? true : false
        },
        /**
         * 判断当前元素是否已经绑定某个事件
         * @param {Object} type
         */
        isBind: function(type) {
            var _events = $(this).data('events')
            return _events && type && _events[type]
        },
        /**
         * 输出firebug日志
         * @param {Object} msg
         */
        log: function(msg) {
            return this.each(function() {
                if (console) console.log('%s: %o', msg, this)
            })
        }
    })
    
    /**
     * 扩展String方法
     */
    $.extend(String.prototype, {
        isPositiveInteger: function() {
            return (new RegExp(/^[1-9]\d*$/).test(this))
        },
        isInteger: function() {
            return (new RegExp(/^\d+$/).test(this))
        },
        isNumber: function() {
            return (new RegExp(/^([-]{0,1}(\d+)[\.]+(\d+))|([-]{0,1}(\d+))$/).test(this))
        },
        isNormalID: function() {
            return (new RegExp(/^[a-zA-Z][0-9a-zA-Z_-]*$/).test(this))
        },
        includeChinese: function() {
            return (new RegExp(/[\u4E00-\u9FA5]/).test(this))
        },
        trim: function() {
            return $.trim(this)
        },
        startsWith: function (pattern) {
            return this.indexOf(pattern) === 0
        },
        endsWith: function(pattern) {
            var d = this.length - pattern.length
            return d >= 0 && this.lastIndexOf(pattern) === d
        },
        replaceSuffix: function(index) {
            return this.replace(/\[[0-9]+\]/,'['+index+']').replace('#index#',index)
        },
        replaceSuffix2: function(index) {
            return this.replace(/\-(i)([0-9]+)$/, '-i'+ index).replace('#index#', index)
        },
        trans: function() {
            return this.replace(/&lt;/g, '<').replace(/&gt;/g,'>').replace(/&quot;/g, '"')
        },
        encodeTXT: function() {
            return (this).replaceAll('&', '&amp;').replaceAll('<','&lt;').replaceAll('>', '&gt;').replaceAll(' ', '&nbsp;')
        },
        replaceAll: function(os, ns) {
            return this.replace(new RegExp(os, 'gm'), ns)
        },
        /*替换占位符为对应选择器的值*/ //{^(.|\#)[A-Za-z0-9_-\s]*}
        replacePlh: function($box) {
            $box = $box || $(document)
            return this.replace(/{\/?[^}]*}/g, function($1) {
                var $input = $box.find($1.replace(/[{}]+/g, ''))
                
                return $input && $input.val() ? $input.val() : $1
            })
        },
        replaceMsg: function(holder) {
            return this.replace(new RegExp('({.*})', 'g'), holder)
        },
        replaceTm: function($data) {
            if (!$data) return this
            
            return this.replace(RegExp('({[A-Za-z_]+[A-Za-z0-9_-]*})','g'), function($1) {
                return $data[$1.replace(/[{}]+/g, '')]
            })
        },
        replaceTmById: function(_box) {
            var $parent = _box || $(document)
            
            return this.replace(RegExp('({[A-Za-z_]+[A-Za-z0-9_-]*})','g'), function($1) {
                var $input = $parent.find('#'+ $1.replace(/[{}]+/g, ''))
                return $input.val() ? $input.val() : $1
            })
        },
        isFinishedTm: function() {
            return !(new RegExp('{\/?[^}]*}').test(this))
        },
        skipChar: function(ch) {
            if (!this || this.length===0) return ''
            if (this.charAt(0)===ch) return this.substring(1).skipChar(ch)
            return this
        },
        isValidPwd: function() {
            return (new RegExp(/^([_]|[a-zA-Z0-9]){6,32}$/).test(this))
        },
        isValidMail: function() {
            return(new RegExp(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/).test(this.trim()))
        },
        isSpaces: function() {
            for (var i = 0; i < this.length; i += 1) {
                var ch = this.charAt(i)
                
                if (ch!=' '&& ch!='\n' && ch!='\t' && ch!='\r') return false
            }
            return true
        },
        isPhone:function() {
            return (new RegExp(/(^([0-9]{3,4}[-])?\d{3,8}(-\d{1,6})?$)|(^\([0-9]{3,4}\)\d{3,8}(\(\d{1,6}\))?$)|(^\d{3,8}$)/).test(this))
        },
        isUrl:function() {
            return (new RegExp(/^[a-zA-z]+:\/\/([a-zA-Z0-9\-\.]+)([-\w .\/?%&=:]*)$/).test(this))
        },
        isExternalUrl:function() {
            return this.isUrl() && this.indexOf('://'+ document.domain) == -1
        },
        toBool: function() {
            return (this.toLowerCase() === 'true') ? true : false
        },
        toJson: function() {
            var json = this
            
            try {
                if (typeof json === 'object') json = json.toString()
                if (!json.trim().match("^\{(.+:.+,*){1,}\}$")) return this
                else return JSON.parse(this)
            } catch (e) {
                return this
            }
        },
        toObj: function() {
            var obj = null
            
            try {
                obj = (new Function('return '+ this))()
            } catch (e) {
                obj = this
                BJUI.debug('String toObj：Parse "String" to "Object" error! Your str is: '+ this)
            }
            return obj
        },
        /**
         * String to Function
         * 参数(方法字符串或方法名)： 'function(){...}' 或 'getName' 或 'USER.getName' 均可
         * Author: K'naan
         */
        toFunc: function() {
            if (!this || !this.length) return undefined
            
            if (this.startsWith('function')) {
                return (new Function('return '+ this))()
            }
            
            try {
                var m_arr = this.split('.'), fn = window
                
                for (var i = 0; i < m_arr.length; i++) {
                    fn = fn[m_arr[i]]
                }
                
                if (typeof fn === 'function') {
                    return fn
                }
            } catch (e) {
                return undefined
            }
        }
    })
    
    /* Function */
    $.extend(Function.prototype, {
        //to fixed String.prototype -> toFunc
        toFunc: function() {
            return this
        }
    })
    
    /* Array */
    $.extend(Array.prototype, {
        remove: function(index) {
            if (index < 0) return this
            else return this.slice(0, index).concat(this.slice(index + 1, this.length))
        },
        swap: function(indexA, indexB) {
            var arr = this, temp = arr[indexA]
            
            arr[indexA] = arr[indexB]
            arr[indexB] = temp
            
            return this
        },
        move: function(old_index, new_index) {
            if (old_index < 0 || new_index < 0)
                return this
            
            if (new_index >= this.length) {
                var k = new_index - this.length
                
                while ((k--) + 1) {
                    this.push(undefined)
                }
            }
            
            this.splice(new_index, 0, this.splice(old_index, 1)[0])
            
            return this
        },
        // move continuous item
        moveItems: function(startIndex, newIndex, len) {
            if (!len || newIndex == startIndex) return this
            
            var moveArr = this.slice(startIndex, startIndex + len)
            
            if (startIndex > newIndex)
                this.splice(startIndex, len)
                
            this.splice.apply(this, [newIndex, 0].concat(moveArr))
            
            if (startIndex < newIndex)
                this.splice(startIndex, len)
            
            return this
        },
        unique: function() {
            var temp = new Array()
            
            this.sort()
            for (var i = 0; i < this.length; i++) {
                if (this[i] == this[i + 1]) continue
                temp[temp.length] = this[i]
            }
            
            return temp
        },
        myIndexOf: function(e) {
            if (!this || !this.length) return -1
            
            for (var i = 0, j; j = this[i]; i++) {
                if (j == e) return i
            }
            
            return -1
        },
        /* serializeArray to json */
        toJson: function() {
            var o = {}
            var a = this
            
            $.each(a, function () {
                if (o[this.name] !== undefined) {
                    if (!o[this.name].push) {
                        o[this.name] = [o[this.name]]
                    }
                    o[this.name].push(this.value || '')
                } else {
                   o[this.name] = this.value || ''
                }
            })
            
            return o
        }
    })
    
    /* Global */
    $.isJson = function(obj) {
        var flag = true
        
        try {
            flag = $.parseJSON(obj)
        } catch (e) {
            return false
        }
        return flag ? true : false
    }
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-basedrag.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-basedrag.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // BASEDRAG CLASS DEFINITION
    // ======================
    
    var Basedrag = function(element, options) {
        this.$element = $(element)
        this.options  = options
        this.tools    = this.TOOLS()
    }
    
    Basedrag.prototype.TOOLS = function() {
        var tools = {
            isOver: function($drop, x, y) {
                var offset = $drop.offset(),
                    left   = offset.left,
                    top    = offset.top,
                    width  = $drop.outerWidth(),
                    height = $drop.outerHeight()
                
                return (x > left && x < (left + width)) && (y > top && y < (top + height))
            },
            mouseDirection: function (element, opts) {
                var $element = $(element), dirs = ['top', 'right', 'bottom', 'left']
                var calculate = function (e) {
                    var w = $element.outerWidth(),
                        h = $element.outerHeight(),
                        offset = $element.offset(),
                        x = (e.pageX - offset.left - (w / 2)) * (w > h ? (h / w) : 1),
                        y = (e.pageY - offset.top - (h / 2)) * (h > w ? (w / h) : 1)
                    
                    return Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180) / 90) + 3) % 4
                }
                
                //enter leave代表鼠标移入移出时的回调
                opts = $.extend({}, {
                    move: $.noop
                }, opts || {})
                
                var r = calculate(opts.e)
                
                opts.move($element, dirs[r])
                /*
                $element.off('mouseenter.basedrag.direction').on('mouseenter.basedrag.direction', function (e) {
                    var r = calculate(e)
                    
                    opts.enter($element, dirs[r])
                }).off('mouseleave.basedrag.direction').on('mouseleave.basedrag.direction', function (e) {
                    var r = calculate(e)
                    
                    opts.leave($element, dirs[r])
                })*/
            }
        }
        
        return tools
    }
    
    Basedrag.prototype.init = function() {
        var that = this
        
        that.$drag = this.$element
        that.options.$obj = this.$element
        if (that.options.obj) this.options.$obj = this.options.obj
        if (that.options.event) {
            that.hasEvent = true
            that.start(this.options.event)
        } else {
            if (that.options.selector)
                that.$drag = that.$element.find(that.options.selector)
                
            if (that.$drag.length) {
                that.$drag.on('mousedown', function(e) {
                    that.start.apply(that, [e])
                    that.options.event = e
                })
            }
        }
    }
    
    Basedrag.prototype.start = function(e) {
        $(document).on('selectstart', false)
        var that = this, options = that.options
        
        if (options.exclude) {
            var $exclude = that.$element.find(options.exclude)
            
            if ($exclude.length && $.inArray(e.target, $exclude.add($exclude.find('*'))) != -1) {
                return false
            }
        }
        
        if (this.$element.css('position') === 'static') {
            this.$element.css('position', 'absolute')
            !options.oleft && (options.oleft = this.$element.position().left)
            !options.otop  && (options.otop  = this.$element.position().top)
            this.$element.css('position', '')
        }
        if (!options.oleft) options.oleft = parseInt(this.$element.css('left')) || 0
        if (!options.otop)  options.otop  = parseInt(this.$element.css('top')) || 0
        
        $(document)
            .on('mouseup.bjui.basedrag', function(e) { that.stop.apply(that, [e]) })
            .on('mousemove.bjui.basedrag', function(e) { that.drag.apply(that, [e]) })
    }
    
    Basedrag.prototype.drop = function(e) {
        var that = this, options = that.options, $drop = options.drop
        
        if (!$drop instanceof jQuery)
            $drop = $($drop)
        
        if ($drop.length) {
            that.$drops = $drop
            $drop.each(function() {
                if (that.tools.isOver($(this), e.pageX, e.pageY)) {
                    $(this).trigger('dropover.bjui.basedrag', [e.pageX, e.pageY, that.$element])
                    
                    that.isDrop = true
                    that.$drop  = $(this)
                    
                    return false
                } else {
                    $(this).trigger('dropout.bjui.basedrag', [e.pageX, e.pageY, that.$element])
                    that.isDrop = false
                }
            }).css('-moz-user-select', 'none')
        }
    }
    
    Basedrag.prototype.drag = function(e) {
        if (!e) e = window.event
        
        BJUI['bjui.basedrag'] = this.$element
        
        var that       = this,
            options    = this.options,
            beforeDrag = options.beforeDrag,
            trData     = options.treeData,      // only for drop Datagrid
            dataIndex  = this.$element.index(), // only for drop Datagrid
            left       = options.oleft + (e.pageX - options.event.pageX),
            top        = options.otop + (e.pageY - options.event.pageY)
        
        if (beforeDrag && typeof beforeDrag === 'string')
            beforeDrag = beforeDrag.toFunc()
        if (beforeDrag && typeof beforeDrag === 'function') {
            if (this.$element.next().hasClass('datagrid-child-tr'))
                dataIndex = dataIndex / 2
            
            if (!beforeDrag.apply(this, [this.$element, (trData ? trData[dataIndex] : '')])) {
                return false
            }
        }
        
        if (options.drop) options.move = ''
        //if (top < 1) top = 0
        if (options.move === 'horizontal') {
            if ((options.minW && left >= parseInt(this.options.$obj.css('left')) + options.minW) && (options.maxW && left <= parseInt(this.options.$obj.css('left')) + options.maxW)) {
                this.$element.css('left', left)
            } else if (options.scop) {
                if (options.relObj) {
                    if ((left - parseInt(options.relObj.css('left'))) > options.cellMinW)
                        this.$element.css('left', left)
                    else
                        this.$element.css('left', left)
                }
            }
        } else if (options.move === 'vertical') {
            this.$element.css('top', top)
        } else {
            var $selector = options.selector ? this.options.$obj.find(options.selector) : this.options.$obj
            
            if (options.drop) {
                var $placeholder = $('#bjui-drag-placeholder')
                
                if (!$placeholder.length)
                    $placeholder = $('<div style="position:absolute; z-index:999;" id="bjui-drag-placeholder"></div>').appendTo($('body'))
                    
                $placeholder.css({left:e.pageX, top:e.pageY, opacity:1}).show().empty().append(this.$element.clone())
                
                this.$placeholder = $placeholder
            } else {
                this.$element.css({left:left, top:top})
            }
        }
        if (options.drag)
            options.drag.apply(this.$element, [this.$element, e, left, top])
        
        if (options.drop) {
            that.drop(e)
        }
        if (options.container) {
            if (!options.container instanceof jQuery)
                options.container = $(options.container)
            
            var scrollTop = options.container.scrollTop()
            
            if (!scrollTop) {
                options.container.scrollTop(1)
            }
            if (options.container.scrollTop()) {
                that.tools.mouseDirection(options.container, {
                    e: e,
                    move: function ($element, dir) {
                        if (dir === 'top' || dir === 'bottom') {
                            options.container.scrollTop(options.container.scrollTop() + (e.clientY - options.event.clientY) / 13)
                        }
                    }
                })
            }
        }
        
        return this.preventEvent(e)
    }
    
    Basedrag.prototype.stop = function(e) {
        var that = this
        
        $(document).off('mousemove.bjui.basedrag').off('mouseup.bjui.basedrag').off('mouseover.bjui.basedrag')
        
        that.options.drop && that.options.drop.off('mouseenter.bjui.basedrag')
        
        if (this.options.stop)
            this.options.stop.apply(this.$element, [this.$element, e])
        
        if (this.hasEvent)
            this.destroy()
        else {
            that.$drag.off('mousedown').on('mousedown', $.proxy(function(e) {
                that.options.event = e
                that.options.oleft = 0
                that.options.otop  = 0
                
                that.start(e)
            }, that))
        }
        
        if (that.options.drop && !that.isDrop) {
            var offset = that.$element.offset()
            
            that.$placeholder && that.$placeholder.animate({top:offset.top, left:offset.left, opacity:0.2}, 'normal', function() {
                that.$placeholder.hide()
            })
        }
        if (that.isDrop) {
            that.$placeholder && that.$placeholder.hide()
            that.$drop && that.$drop.trigger('drop.bjui.basedrag', [that.$element])
        }
        
        that.$drops && that.$drops.css('-moz-user-select', '')
        $(document).off('selectstart')
        return this.preventEvent(e)
    }
    
    Basedrag.prototype.preventEvent = function(e) {
        if (e.stopPropagation) e.stopPropagation()
        if (e.preventDefault) e.preventDefault()
        return false
    }
    
    Basedrag.prototype.destroy = function() {
        this.$element.removeData('bjui.basedrag')
        if (!this.options.nounbind) this.$element.off('mousedown')
    }
    
    // BASEDRAG PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments
        var property = option
        
        return this.each(function () {
            var $this   = $(this)
            var options = $.extend({}, $this.data(), typeof option === 'object' && option)
            var data    = $this.data('bjui.basedrag')
            
            if (!data) $this.data('bjui.basedrag', (data = new Basedrag(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.basedrag

    $.fn.basedrag             = Plugin
    $.fn.basedrag.Constructor = Basedrag
    
    // BASEDRAG NO CONFLICT
    // =================
    
    $.fn.basedrag.noConflict = function () {
        $.fn.basedrag = old
        return this
    }
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-sidenav.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-sidenav.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // SLIDEBAR CLASS DEFINITION
    // ======================
    
    var Sidenav = function(element, options) {
        this.$element = $(element)
        this.options  = options
    }
    
    Sidenav.prototype.ajaxHnav = function() {
        var that = this, options = that.options, childKey = options.childKey || 'children', $li = that.$element.parent(), $box = $('#bjui-sidenav-box'), html = BJUI.StrBuilder(),
            $sidenavbtn = $('#bjui-sidenav-btn')
        var createA = function(data) {
            var liHtml = BJUI.StrBuilder(), target = data.target || 'navtab'
            
            liHtml.add('<a href="'+ (data.url || 'javascript:;') +'" ')
            
            if (data.url && (target === 'dialog' || target === 'navtab')) {
                liHtml.add('data-toggle="'+ target +'" data-options="{id:\''+ data.id +'\', title:\''+ (data.title || data.name) +'\'')
                
                if (data.width)  liHtml.add(', width:'+ data.width)
                if (data.height) liHtml.add(', height:'+ data.height)
                if (data.fresh)  liHtml.add(', fresh:'+ data.fresh)
                if (data.mask)   liHtml.add(', mask:'+ data.mask)
                
                liHtml.add('}"')
            } else if (data.url)
                liHtml.add('target="'+ target +'"')
            
            if (data[childKey])
                liHtml.add(' class="right-arrow"')
            
            liHtml.add('><i class="fa fa-'+ (data.icon || 'caret-right') +'"></i>&nbsp;'+ data.name)
            
            if (data[childKey])
                liHtml.add('<b><i class="fa fa-angle-right"></i></b>')
            
            liHtml
                .add('</a>')
                .add(createChild(data))
            
            return liHtml.toString()
        }
        var createChild = function(data) {
            var childHtml = BJUI.StrBuilder()
            
            if (data[childKey]) {
                childHtml.add('<ul class="nav">')
                
                $.each(data[childKey], function(i, m) {
                    var target = m.target || 'navtab', id = m.id && String(m.id).isNormalID && m.id
                    
                    childHtml.add('<li')
                    
                    if (target === 'navtab' && id && m.url)
                        childHtml.add(' class="'+ target +'-'+ id +'"')
                    
                    childHtml
                        .add('>')
                        .add(createA(m))
                        .add('</li>')
                })
                
                childHtml.add('</ul>')
            }
            
            return childHtml.toString()
        }
        var okCallback = function(json) {
            if (!$.isArray(json)) {
                json = [json]
            }
            
            html.add('<ul class="nav">')
            
            $.each(json, function(i, n) {
                var target = n.target || 'navtab', id = n.id && String(n.id).isNormalID && n.id
                
                html.add('<li')
                
                if (target === 'navtab' && id && n.url)
                    html.add(' class="'+ target +'-'+ id +'"')
                
                html.add('>')
                    .add(createA(n))
                    .add('</li>')
            })
            
            html.add('</ul>')
            
            $box.html(html.toString())
                .off('click').on('click', '.nav > li > a', function(e) {
                    var $this = $(this), $nav = $this.next('.nav'), $li = $this.closest('li'), $other = $li.siblings()
                    
                    if ($nav.length) {
                        $nav.stop().slideToggle(function() {
                            $li.toggleClass('open', $nav.is(':visible'))
                        })
                        
                        if (!$li.hasClass('open')) {
                            $other.find('> .nav').stop().slideUp(function() {
                                $(this).closest('li').removeClass('open')
                            })
                        }
                    }
                })
            
            $li.data('bjui.sidenav.hnav.panels', $box.find('> ul'))
            
            $(document).on('switch.bjui.navtab', function(e) {
                var datas = $('body').data('bjui.navtab'), $lis
                
                if (datas && datas.current) {
                    $box.find('li').removeClass('active').filter('.navtab-'+ datas.current).addClass('active')
                }
            })
            
            if (BJUI.ui.displayFirst)
                $box.find('> .nav > li:first > a').trigger('click')
            
            !$('body').data('bjui.sidenav.init') && $sidenavbtn.is(':visible') && ($sidenavbtn.trigger('click'))
        }
        var treeCallback = function(json) {
            var single = (typeof options.single === 'undefined' || single), $ul, html = BJUI.StrBuilder()
            
            $box.empty()
            
            if (single) {
                $ul = 
                    $('<ul class="ztree" data-toggle="ztree" id="bjui-sidenav-ztree-0"></ul>')
                    .data('options', options.treeOptions)
                    .data('nodes', json)
                
                $box.append($ul).initui()
            } else {
                if (!$.isArray(json))
                    json = [json]
                
                $.each(json, function(i, n) {
                    $ul = 
                        $('<ul class="ztree" data-toggle="ztree" id="bjui-sidenav-ztree-'+ i +'"></ul>')
                        .data('options', options.treeOptions)
                        .data('nodes', json)
                    
                    $box.append($ul)
                })
                
                $box.initui()
            }
            
            !$('body').data('bjui.sidenav.init') && $sidenavbtn.is(':visible') && ($sidenavbtn.trigger('click'))
        }
        
        BJUI.ajax('doajax', {
            url         : options.url,
            loadingmask : false,
            target      : $box,
            okCallback  : options.tree ? treeCallback : okCallback
        })
    }
    
    Sidenav.prototype.initHnav = function() {
        var that = this, options = that.options, childKey = options.childKey || 'children', targetKey = options.targetKey || 'target', idKey = options.idKey || 'id',
            $li = that.$element.parent(), $box = $('#bjui-sidenav-box'), $sidenavbtn = $('#bjui-sidenav-btn'),
            html = BJUI.StrBuilder(), json
        var createA = function(data) {
            if (!data) data = {}
            
            var liHtml = BJUI.StrBuilder(), target = data[targetKey] || 'navtab'
            
            liHtml.add('<a href="'+ (data.url || 'javascript:;') +'" ')
            
            if (data.url && (target === 'dialog' || target === 'navtab')) {
                liHtml.add('data-toggle="'+ target +'" data-options="{id:\''+ data[idKey] +'\', title:\''+ (data.title || data.name) +'\'')
                
                if (data.width)  liHtml.add(', width:'+ data.width)
                if (data.height) liHtml.add(', height:'+ data.height)
                if (data.fresh)  liHtml.add(', fresh:'+ data.fresh)
                if (data.mask)   liHtml.add(', mask:'+ data.mask)
                
                liHtml.add('}"')
            } else if (data.url)
                liHtml.add('target="'+ target +'"')
            
            if (data[childKey])
                liHtml.add(' class="right-arrow"')
            
            liHtml.add('><i class="fa fa-'+ (data.icon || 'caret-right') +'"></i>&nbsp;'+ data.name)
            
            if (data[childKey])
                liHtml.add('<b><i class="fa fa-angle-right"></i></b>')
            
            liHtml
                .add('</a>')
                .add(createChild(data))
            
            return liHtml.toString()
        }
        
        var createChild = function(data) {
            var childHtml = BJUI.StrBuilder()
            
            if (data[childKey]) {
                childHtml.add('<ul class="nav">')
                
                $.each(data[childKey], function(i, m) {
                    var target = m.target || 'navtab', id = m[idKey] && String(m[idKey]).isNormalID && m[idKey]
                    
                    childHtml.add('<li')
                    
                    if (target === 'navtab' && id && m.url)
                        childHtml.add(' class="'+ target +'-'+ id +'"')
                    
                    childHtml
                        .add('>')
                        .add(createA(m))
                        .add('</li>')
                })
                
                childHtml.add('</ul>')
            }
            
            return childHtml.toString()
        }
        var okCallback = function(json) {
            if (!$.isArray(json)) {
                json = [json]
            }
            
            html.add('<ul class="nav">')
            
            $.each(json, function(i, n) {
                var target = n.target || 'navtab', id = n[idKey] && String(n[idKey]).isNormalID && n[idKey]
                
                html.add('<li')
                
                if (target === 'navtab' && id && n.url)
                    html.add(' class="'+ target +'-'+ id +'"')
                
                html.add('>')
                    .add(createA(n))
                    .add('</li>')
            })
            
            html.add('</ul>')
            
            $box.html(html.toString())
                .off('click').on('click', '.nav > li > a', function(e) {
                    var $this = $(this), $nav = $this.next('.nav'), $li = $this.closest('li'), $other = $li.siblings()
                    
                    if ($nav.length) {
                        $nav.stop().slideToggle(function() {
                            $li.toggleClass('open', $nav.is(':visible'))
                        })
                        
                        if (!$li.hasClass('open')) {
                            $other.find('> .nav').stop().slideUp(function() {
                                $(this).closest('li').removeClass('open')
                            })
                        }
                    }
                })
            
            $li.data('bjui.sidenav.hnav.panels', $box.find('> ul'))
            
            $(document).on('switch.bjui.navtab', function(e) {
                var datas = $('body').data('bjui.navtab'), $lis
                
                if (datas && datas.current) {
                    $box.find('li').removeClass('active').filter('.navtab-'+ datas.current).addClass('active')
                }
            })
            
            if (BJUI.ui.displayFirst)
                $box.find('> .nav > li:first > a').trigger('click')
            
            !$('body').data('bjui.sidenav.init') && $sidenavbtn.is(':visible') && ($sidenavbtn.trigger('click'))
        }
        var treeCallback = function(json) {
            var single = (typeof options.single === 'undefined' || single), $ul, html = BJUI.StrBuilder()
            
            $box.empty()
            
            if (single) {
                $ul = 
                    $('<ul class="ztree" data-toggle="ztree" id="bjui-sidenav-ztree-0"></ul>')
                    .data('options', options.treeOptions)
                    .data('nodes', json)
                
                $box.append($ul).initui()
            } else {
                if (!$.isArray(json))
                    json = [json]
                
                $.each(json, function(i, n) {
                    $ul = 
                        $('<ul class="ztree" data-toggle="ztree" id="bjui-sidenav-ztree-'+ i +'"></ul>')
                        .data('options', options.treeOptions)
                        .data('nodes', json)
                    
                    $box.append($ul)
                })
                
                $box.initui()
            }
            
            !$('body').data('bjui.sidenav.init') && $sidenavbtn.is(':visible') && ($sidenavbtn.trigger('click'))
        }
        
        json = that.$element.next('.items').html()
        if (json) {
            json = $.parseJSON(json)
            options.tree ? treeCallback(json) : okCallback(json)
        }
    }
    
    // SLIDEBAR PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments
        var property = option
        
        return this.each(function () {
            var $this   = $(this)
            var options = $.extend({}, $this.data(), typeof option === 'object' && option)
            var data    = $this.data('bjui.sidenav')
            
            if (!data) $this.data('bjui.sidenav', (data = new Sidenav(this, options)))
            
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            }
        })
    }

    var old = $.fn.sidenav

    $.fn.sidenav             = Plugin
    $.fn.sidenav.Constructor = Sidenav
    
    // SLIDEBAR NO CONFLICT
    // =================
    
    $.fn.basedrag.noConflict = function () {
        $.fn.sidenav = old
        return this
    }
    
    // SLIDEBAR DATA-API
    // ==============
    $(function() {
        $('#bjui-sidenav-col').on('click.bjui.sidenav', '.nav > li > a', function() {
            var $this = $('#bjui-sidenav-col')
            
            if ($(this).hasClass('right-arrow'))
                return false
            
            if ($this.hasClass('autohide'))
                $('#bjui-sidenav-arrow').trigger('click')
        })
    })
    
    $(document).on('click.bjui.sidenav.data-api', '[data-toggle="sidenav"]', function(e) {
        e.preventDefault()
        
        var $body = $('body'), $this = $(this), href = $this.data('url') || $this.attr('href'), cache = $this.data('cache'),
            $li   = $(this).parent(), $box = $('#bjui-sidenav-box'), $panels = $li.data('bjui.sidenav.hnav.panels')
        
        if (typeof $body.data('bjui.sidenav.init') === 'undefined')
            $body.data('bjui.sidenav.init', true)
        else
            $body.data('bjui.sidenav.init', false)
        
        $li.addClass('active').siblings().removeClass('active')
        
        if (href && !(href.startsWith('#') || href.startsWith('javascript'))) {
            if (cache && $panels && $panels.length) {
                $box.append($panels)
            } else {
                $this.data('url', href)
                Plugin.call($this, 'ajaxHnav')
            }
        } else {
            $box.find('> .nav').detach()
            
            if ($panels && $panels.length) {
                $box.append($panels)
            } else {
                Plugin.call($this, 'initHnav')
            }
        }
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-contextmenu.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-contextmenu.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // CONTEXTMENU GLOBAL ELEMENTS
    // ======================
    
    var $menu, $shadow, hash
    
    $(function() {
        var INIT_CONTEXTMENU = function() {
            $menu   = $('<div id="bjui-contextmenu"></div>').hide()
            $shadow = $('<div id="bjui-contextmenuShadow"></div>').hide()
            hash    = []
            
            $('body').append('<!-- contextmenu -->').append($menu).append($shadow)
        }
        
        INIT_CONTEXTMENU()
    })
    
    // CONTEXTMENU CLASS DEFINITION
    // ======================
    var Contextmenu = function(element, options) {
        this.$element = $(element)
        this.options  = options
    }
    
    Contextmenu.DEFAULTS = {
        id       : undefined,
        shadow   : true,
        bindings : {},
        ctrSub   : null
    }
    
    Contextmenu.prototype.init = function() {
        var that  = this
        var op    = this.options
        
        if (!op.id) return
        hash.push({
            id       : op.id,
            shadow   : op.shadow,
            bindings : op.bindings || {},
            ctrSub   : op.ctrSub
        })
        
        var index = hash.length - 1
        
        this.$element.on('contextmenu', function(e) {
            that.display(index, this, e, op)
            return false
        })
    }
    
    Contextmenu.prototype.display = function(index, trigger, e, options) {
        var that    = this
        var cur     = hash[index]
        var cp      = BJUI.regional[cur.id]
        var content = FRAG[cur.id]
        
        $.each(cp, function(i, n) {
            content = content.replace('#'+ i +'#', cp[i])
        })
        
        // Send the content to the menu
        $menu.html(content)
        $.each(cur.bindings, function(id, func) {
            $('[rel="'+ id +'"]', $menu).on('click', function(e) {
                that.hide()
                func($(trigger), $('#bjui-'+ cur.id))
            })
        })
        
        var posX = e.pageX
        var posY = e.pageY
        
        if ($(window).width() < posX + $menu.width())   posX -= $menu.width()
        if ($(window).height() < posY + $menu.height()) posY -= $menu.height()
        
        $menu.css({'left':posX, 'top':posY}).show()
        if (cur.shadow)
            $shadow.css({width:$menu.width(), height:$menu.height(), left:posX + 3, top:posY + 3}).show()
        $(document).one('click', that.hide)
        
        if ($.isFunction(cur.ctrSub))
            cur.ctrSub($(trigger), $('#bjui-'+ cur.id))
    }
    
    Contextmenu.prototype.hide = function() {
        $menu.hide()
        $shadow.hide()
    }
    
    /* Custom contextmenu */
    Contextmenu.prototype.show = function(options) {
        var that = this
        
        if (options.items && options.items.length) {
            that.$element.on('contextmenu', function(e) {
                var isShow = true
                
                /*exclude*/
                if (options.exclude) {
                    that.$element.find(options.exclude).each(function() {
                        if (this == e.target || $(this).find(e.target).length) {
                            isShow = false
                            return
                        }
                    })
                }
                
                if (!isShow) {
                    e.stopPropagation()
                    return !isShow
                } else {
                    that.custom(options.items, e)
                }
                
                return false
            })
        }
    }
    
    Contextmenu.prototype.custom = function(items, e) {
        $menu.empty().html('<ul></ul>')
        
        var that    = this
        var options = that.options
        var $ul     = $menu.find('> ul'), $li
        
        $.each(items, function(i, n) {
            var icon = ''
            
            if (n.icon) icon = '<i class="fa fa-'+ n.icon +'"></i>'
            if (n.title == 'diver') {
                $li = $('<li class="diver"></li>')
            } else {
                $li = $('<li><span class="icon">'+ icon +'</span><span class="title">'+ n.title +'</span></li>')
                if (n.func && typeof n.func === 'string') n.func = n.func.toFunc()
                if (n.func) {
                    $li.on('click', function(evt) {
                        that.hide()
                        n.func(that.$element, $li)
                    })
                }
            }
            $li.appendTo($ul)
        })
        
        var posX = e.pageX
        var posY = e.pageY
        
        if ($(window).width() < posX + $menu.width())   posX -= $menu.width()
        if ($(window).height() < posY + $menu.height()) posY -= $menu.height()
        
        $menu.css({'left':posX, 'top':posY}).show()
        
        if (options.shadow)
            $shadow.css({width:$menu.width(), height:$menu.height(), left:posX + 3, top:posY + 3}).show()
        
        $(document).one('click', that.hide)
    }
    
    // CONTEXTMENU PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments
        var property = option
        
        return this.each(function () {
            var $this   = $(this)
            var options = $.extend({}, Contextmenu.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data    = $this.data('bjui.contextmenu')
            
            if (!data) $this.data('bjui.contextmenu', (data = new Contextmenu(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.contextmenu

    $.fn.contextmenu             = Plugin
    $.fn.contextmenu.Constructor = Contextmenu
    
    // CONTEXTMENU NO CONFLICT
    // =================
    
    $.fn.contextmenu.noConflict = function () {
        $.fn.contextmenu = old
        return this
    }
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-navtab.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-navtab.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // NAVTAB GLOBAL ELEMENTS
    // ======================
    
    var currentIndex, $currentTab, $currentPanel, $box, $tabs, $panels, $prevBtn, $nextBtn, $moreBtn, $moreBox, $main, $mainLi,
        autorefreshTimer
    
    $(function() {
        var INIT_NAVTAB = function() {
            currentIndex = 0
            $box         = $('#bjui-navtab')
            $tabs        = $box.find('> .tabsPageHeader > .tabsPageHeaderContent > .navtab-tab')
            $panels      = $box.find('> .navtab-panel')
            $prevBtn     = $box.find('> .tabsPageHeader > .tabsLeft')
            $nextBtn     = $box.find('> .tabsPageHeader > .tabsRight')
            $moreBtn     = $box.find('> .tabsPageHeader > .tabsMore')
            $moreBox     = $box.find('> .tabsMoreList')
            $main        = $tabs.find('li:first')
            $mainLi      = $moreBox.find('li:first')
            
            $prevBtn.click(function() { $(this).navtab('scrollPrev') })
            $nextBtn.click(function() { $(this).navtab('scrollNext') })
            $moreBtn.click(function() {
                $moreBox.show()
                
                var hh = $(window).height(), mh = $moreBox.height(), th = $('#bjui-top').height() + $('#bjui-navbar').height(), sh = hh - th - 26
                
                if (sh < mh)
                    $moreBox.height(sh - 10)
                else
                    $moreBox.height('')
            })
            
            $(document).on('click.bjui.navtab.switchtab', function(e) {
                var $target = e.target.tagName == 'I' ? $(e.target).parent() : $(e.target)
                
                if ($moreBtn[0] != $target[0]) $moreBox.hide()
            })
            
            var mainTit, options = $.extend({}, Navtab.DEFAULTS, $main.data(), {id:'main', title:mainTit}), mainTab = new Navtab(options)
            
            $('body').data('bjui.navtab', {main:mainTab, current:'main'})
            
            if ($main.attr('data-url')) {
                $(document).one(BJUI.eventType.initUI, function(e) {
                    $main.removeAttr('data-url').navtab('reload', options)
                })
            }
            
            $main
                .data('options', $.extend({}, options))
                .navtab('contextmenu', $main)
                .click(function() { if (!$(this).hasClass('active')) BJUI.navtab('switchTab', 'main') })
                .find('> a > span').html(function(n, c) { return (mainTit = c.replace('#maintab#', BJUI.regional.maintab)) })
            
            if ($main.attr('data-url')) {
                $(document).one(BJUI.eventType.initUI, function(e) {
                    $main.removeAttr('data-url').navtab('reload', options)
                })
            }
            
            setTimeout(function() {
                $main.trigger('click')
            }, 100)
            
            $mainLi
                .click(function() {
                    if ($(this).hasClass('active')) $moreBox.hide()
                    else BJUI.navtab('switchTab', 'main')
                })
                .find('> a').html(function(n, c) { return c.replace('#maintab#', BJUI.regional.maintab) })
            
            $.CurrentNavtab = $panels.find('> .navtabPage').eq(0)
        }
        
        INIT_NAVTAB()
    })
    
    // NAVTAB CLASS DEFINITION
    // ======================
    
    var Navtab = function(options) {
        this.options  = options
        this.tools    = this.TOOLS()
    }
    
    Navtab.DEFAULTS = {
        id          : null,
        title       : 'New tab',
        url         : null,
        type        : 'GET',
        data        : {},
        loadingmask : true,
        fresh       : false,
        autorefresh : false,
        onLoad      : null,
        beforeClose : null,
        onClose     : null
    }
    
    Navtab.prototype.TOOLS = function() {
        var that = this
        var tools = {
            getDefaults: function() {
                return Navtab.DEFAULTS
            },
            getTabs: function() {
                return $tabs.find('> li')
            },
            getPanels: function() {
                return $panels.find('> div')
            },
            getMoreLi: function() {
                return $moreBox.find('> li')
            },
            getTab: function(tabid) {
                var index = this.indexTabId(tabid)
                
                if (index >= 0) return this.getTabs().eq(index)
            },
            getPanel: function(tabid) {
                var index = this.indexTabId(tabid)
                
                if (index >= 0) return this.getPanels().eq(index)
            },
            getTabsW: function(iStart, iEnd) {
                return this.tabsW(this.getTabs().slice(iStart, iEnd))
            },
            tabsW: function($tabs) {
                var iW = 0
                
                $tabs.each(function() {
                    iW += $(this).outerWidth(true)
                })
                
                return iW
            },
            indexTabId: function(tabid) {
                var iOpenIndex = -1
                
                if (!tabid) return iOpenIndex
                
                this.getTabs().each(function(index) {
                    if ($(this).data('options').id === tabid) {
                        iOpenIndex = index
                        return false
                    }
                })
                
                return iOpenIndex
            },
            getLeft: function() {
                return $tabs.position().left
            },
            getScrollBarW: function() {
                return $box.width() - 55
            },
            visibleStart: function() {
                var $tabs = this.getTabs(), iLeft = this.getLeft(), iW = 0, index = 0
                
                $tabs.each(function(i) {
                    if (iW + iLeft >= 0) {
                        index = i
                        return false
                    }
                    iW += $(this).outerWidth(true)
                })
                
                return index
            },
            visibleEnd: function() {
                var tools = this, $tabs = this.getTabs(), iLeft = this.getLeft(), iW = 0, index = $tabs.length
                
                $tabs.each(function(i) {
                    iW += $(this).outerWidth(true)
                    if (iW + iLeft > tools.getScrollBarW()) {
                        index = i
                        return false
                    }
                })
                
                return index
            },
            scrollPrev: function() {
                var iStart = this.visibleStart()
                
                if (iStart > 0)
                    this.scrollTab(- this.getTabsW(0, iStart - 1))
            },
            scrollNext: function() {
                var iEnd = this.visibleEnd()
                
                if (iEnd < this.getTabs().size())
                    this.scrollTab(- this.getTabsW(0, iEnd + 1) + this.getScrollBarW())
            },
            scrollTab: function(iLeft, isNext) {
                $tabs.animate({ left: iLeft }, 150, function() { that.tools.ctrlScrollBtn() })
            },
            scrollCurrent: function() { // auto scroll current tab
                var iW = this.tabsW(this.getTabs()), scrollW = this.getScrollBarW()
                
                if (iW <= scrollW)
                    this.scrollTab(0)
                else if (this.getLeft() < scrollW - iW)
                    this.scrollTab(scrollW-iW)
                else if (currentIndex < this.visibleStart())
                    this.scrollTab(- this.getTabsW(0, currentIndex))
                else if (currentIndex >= this.visibleEnd())
                    this.scrollTab(scrollW - this.getTabs().eq(currentIndex).outerWidth(true) - this.getTabsW(0, currentIndex))
            },
            ctrlScrollBtn: function() {
                var iW = this.tabsW(this.getTabs())
                
                if (this.getScrollBarW() > iW) {
                    $prevBtn.hide()
                    $nextBtn.hide()
                    $tabs.parent().removeClass('tabsPageHeaderMargin')
                } else {
                    $prevBtn.show().removeClass('tabsLeftDisabled')
                    $nextBtn.show().removeClass('tabsRightDisabled')
                    $tabs.parent().addClass('tabsPageHeaderMargin')
                    if (this.getLeft() >= 0)
                        $prevBtn.addClass('tabsLeftDisabled')
                    else if (this.getLeft() <= this.getScrollBarW() - iW)
                        $nextBtn.addClass('tabsRightDisabled')
                }
            },
            switchTab: function(iTabIndex) {
                var $tab = this.getTabs().removeClass('active').eq(iTabIndex).addClass('active'), $panels = this.getPanels(), $panel = $panels.eq(iTabIndex), onSwitch = that.options.onSwitch ? that.options.onSwitch.toFunc() : null
                var $ajaxBackground = $(FRAG.maskBackground)
                
                $panels.css({top:'-10000000px'})
                
                if ($tab.data('reloadFlag')) {
                    $panel.animate({top:0})
                    that.refresh($tab.data('options').id)
                } else {
                    if ($panel.find('.bjui-ajax-mask').length) {
                        $panel.css({top:0})
                    } else {
                        $panel
                            .css({top:0})
                            .append($ajaxBackground)
                        
                        $ajaxBackground.fadeOut('normal', function() {
                            $(this).remove()
                        })
                    }
                }
                
                this.getMoreLi().removeClass('active').eq(iTabIndex).addClass('active')
                currentIndex = iTabIndex
                this.scrollCurrent()
                $currentTab     = $tab
                $.CurrentNavtab = $currentPanel = $panel
                
                if (onSwitch) onSwitch.apply(that)
                
                // set current to body data
                var datas = $('body').data('bjui.navtab'), id = $tab.data('options').id
                
                if (id !== that.options.id)
                    datas.current = id
                else {
                    datas.current = that.options.id
                }
                
                // events
                $panel.trigger('switch.bjui.navtab')
            },
            closeTab: function(index, openTabid) {
                var $tab        = this.getTabs().eq(index),
                    $more       = this.getMoreLi().eq(index),
                    $panel      = this.getPanels().eq(index),
                    options     = $tab.data('options'),
                    beforeClose = options.beforeClose ? options.beforeClose.toFunc() : null,
                    onClose     = options.onClose ? options.onClose.toFunc() : null,
                    canClose    = true
                
                if (beforeClose) canClose = beforeClose.apply(that, [$panel])
                if (!canClose) {
                    that.tools.switchTab(index)
                    return
                }
                $tab.remove()
                $more.remove()
                $panel.trigger(BJUI.eventType.beforeCloseNavtab).remove()
                
                if (autorefreshTimer) clearInterval(autorefreshTimer)
                if (onClose) onClose.apply(that)
                if (currentIndex >= index) currentIndex--
                if (openTabid) {
                    var openIndex = this.indexTabId(openTabid)
                    
                    if (openIndex > 0) currentIndex = openIndex
                }
                
                // remove from body
                var datas = $('body').data('bjui.navtab')
                
                if (datas[options.id])
                    delete datas[options.id]
                
                this.scrollCurrent()
                this.switchTab(currentIndex)
            },
            closeOtherTab: function(index) {
                index = index || currentIndex
                
                this.getTabs().each(function(i) {
                    if (i > 0 && index != i) $(this).find('> .close').trigger('click')
                })
            },
            loadUrlCallback: function($panel) {
                $panel.find(':button.btn-close').click(function() { that.closeCurrentTab() })
            },
            updateTit: function(index, title) {
                this.getTabs().eq(index).find('> a').attr('title', title).find('> span').html(title)
                this.getMoreLi().eq(index).find('> a').attr('title', title).html(title)
            },
            reload: function($tab, flag) {
                flag = flag || $tab.data('reloadFlag')
                
                var options = $tab.data('options')
                
                if (flag) {
                    $tab.data('reloadFlag', false)
                    var $panel = that.tools.getPanel(options.id)
                    
                    if ($tab.hasClass('external')) {
                        that.openExternal(options.url, $panel, options.data)
                    } else {
                        that.tools.reloadTab($panel, options)
                    }
                }
            },
            reloadTab: function($panel, options) {
                var onLoad = options.onLoad ? options.onLoad.toFunc() : null,
                    arefre = options.autorefresh && (isNaN(String(options.autorefresh)) ? 15 : options.autorefresh)
                
                $panel
                    .trigger(BJUI.eventType.beforeLoadNavtab)
                    .ajaxUrl({
                        type:(options.type || 'GET'), url:options.url, data:options.data || {}, loadingmask:options.loadingmask, callback:function(response) {
                            that.tools.loadUrlCallback($panel)
                            if (onLoad) onLoad.apply(that, [$panel])
                            if (autorefreshTimer) clearInterval(autorefreshTimer)
                            if (arefre) autorefreshTimer = setInterval(function() { $panel.navtab('refresh') }, arefre * 1000)
                            if (BJUI.ui.clientPaging && $panel.data('bjui.clientPaging')) $panel.pagination('setPagingAndOrderby', $panel)
                        }
                    })
            }
        }
        
        return tools
    }
    
    Navtab.prototype.contextmenu = function($obj) {
        var that = this
        
        $obj.contextmenu({
            id: 'navtabCM',
            bindings: {
                reload: function(t, m) {
                    if (t.data('options').url)
                        that.refresh(t.data('options').id)
                },
                closeCurrent: function(t, m) {
                    var tabId = t.data('options').id
                    
                    if (tabId) that.closeTab(tabId)
                    else that.closeCurrentTab()
                },
                closeOther: function(t, m) {
                    if (!t.index()) {
                        that.closeAllTab()
                    } else {
                        var index = that.tools.indexTabId(t.data('options').id)
                        
                        that.tools.closeOtherTab(index > 0 ? index : currentIndex)
                    }
                },
                closeAll: function(t, m) {
                    that.closeAllTab()
                }
            },
            ctrSub: function(t, m) {
                var mReload = m.find('[rel="reload"]'),
                    mCur    = m.find('[rel="closeCurrent"]'),
                    mOther  = m.find('[rel="closeOther"]'),
                    mAll    = m.find('[rel="closeAll"]'),
                    $tabLi  = that.tools.getTabs()
                
                if (!t.index()) {
                    mCur.addClass('disabled')
                    if (!t.data('options').url) mReload.addClass('disabled')
                }
            }
        })
    }
    
    // if found tabid replace tab, else create a new tab.
    Navtab.prototype.openTab = function() {
        var that = this, options = this.options, tools = this.tools, iOpenIndex
        
        if (!options.url && options.href) options.url = options.href
        
        if (!options.url) {
            BJUI.debug('BJUI.Navtab: Error trying to open a navtab, url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh()
            
            if (!options.url.isFinishedTm()) {
                $('body').alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.Navtab: The new navtab\'s url is incorrect, url: '+ options.url)
                return
            }
            
            options.url = encodeURI(options.url)
        }
        
        iOpenIndex = options.id ? tools.indexTabId(options.id) : currentIndex
        
        if (iOpenIndex >= 0) {
            var $tab   = tools.getTabs().eq(iOpenIndex),
                $panel = tools.getPanels().eq(iOpenIndex),
                op     = $tab.data('options')
            
            if (options.fresh || options.url != op.url)
                that.reload(options)
            else if (options.title != op.title) {
                op.title = options.title
                tools.updateTit(iOpenIndex, options.title)
            }
            
            currentIndex = iOpenIndex
            
            if (options.id == 'main') {
                this.contextmenu($tab)
            }
        } else {
            var tabFrag = '<li><a href="javascript:" title="#title#"><span>#title#</span></a><span class="close">&times;</span></li>',
                $tab = $(tabFrag.replaceAll('#title#', options.title)),
                $panel = $('<div class="navtabPage unitBox"></div>'),
                $more  = $('<li><a href="javascript:" title="#title#">#title#</a></li>'.replaceAll('#title#', options.title))
            
            $tab.appendTo($tabs)
            $panel.appendTo($panels)
            $more.appendTo($moreBox)
            
            $tab.data('options', $.extend({}, options))
            
            if (options.external || (options.url && options.url.isExternalUrl())) {
                $tab.addClass('external')
                this.openExternal(options.url, $panel, options.data)
            } else {
                $tab.removeClass('external')
                tools.reloadTab($panel, options)
            }
            
            currentIndex = tools.getTabs().length - 1
            this.contextmenu($tab)
            
            //events
            $tab.on('click', function(e) {
                if (!$(this).hasClass('active'))
                    that.switchTab(options.id)
            }).on('click.bjui.navtab.close', '.close', function(e) {
                that.closeTab(options.id)
            }).on('mousedown.bjui.navtab.drag', 'a', function(e) {
                $tab.data('bjui.navtab.drag', true)
                
                setTimeout($.proxy(function () {
                    if ($tab.data('bjui.navtab.drag')) that.drag(e, $tab, $panel, $more)
                }, that), 150)
                
                e.preventDefault()
            }).on('mouseup.bjui.navtab.drag', 'a', function(e) {
                $tab.data('bjui.navtab.drag', false)
            })
            
            $more.on('click', function() {
                that.switchTab(options.id)
            })
        }
        
        tools.switchTab(currentIndex)
        tools.scrollCurrent()
    }
    
    Navtab.prototype.closeTab = function(tabid) {
        var index = this.tools.indexTabId(tabid)
        
        if (index > 0)
            this.tools.closeTab(index)
    }
    
    Navtab.prototype.closeCurrentTab = function(openTabid) { //openTabid can be empty. close current tab by default, and open the last tab
        if (currentIndex > 0)
            this.tools.closeTab(currentIndex, openTabid)
    }
    
    Navtab.prototype.closeAllTab = function() {
        this.tools.getTabs().find('> .close').trigger('click')
    }
    
    Navtab.prototype.reloadFlag = function(tabids) {
        var arr = tabids.split(',')
        
        for (var i = 0; i < arr.length; i++) {
            var $tab = this.tools.getTab(arr[i].trim())
            
            if ($tab) {
                if (this.tools.indexTabId(arr[i]) == currentIndex) this.tools.reload($tab, true)
                else $tab.data('reloadFlag', true)
            }
        }
    }
    
    Navtab.prototype.switchTab = function(tabid) {
        var index = this.tools.indexTabId(tabid)
        
        this.tools.switchTab(index)
    }
    
    Navtab.prototype.scrollPrev = function() {
        this.tools.scrollPrev()
    }
    
    Navtab.prototype.scrollNext = function() {
        this.tools.scrollNext()
    }
    
    Navtab.prototype.refresh = function(tabid) {
        var $tab, $panel
        
        if (!tabid) {
            $tab = $currentTab
        } else if (typeof tabid === 'string') {
            $tab = this.tools.getTab(tabid)
        } else {
            $tab = tabid
        }
        
        if ($tab && $tab.length) {
            $panel = this.tools.getPanel($tab.data('options').id)
            $panel.removeData('bjui.clientPaging')
            
            this.reload($tab.data('options'))
        }
    }
    
    Navtab.prototype.reload = function(option) {
        var that    = this,
            options = $.extend({}, typeof option === 'object' && option),
            $tab    = options.id ? this.tools.getTab(options.id) : this.tools.getTabs().eq(currentIndex)
        
        if ($tab) {
            var op      = $tab.data('options')
            var _reload = function() {
                if (options.title && options.title != op.title) {
                    that.tools.updateTit($tab.index(), options.title)
                }
                $tab.data('options', $.extend({}, op, options))
                that.tools.reload($tab, true)
            }
            
            if (op.reloadWarn) {
                $('body').alertmsg('confirm', op.reloadWarn, {
                    okCall: function() {
                        _reload()
                    }
                })
            } else {
                _reload()
            }
        }
    }
    
    Navtab.prototype.reloadForm = function(clearQuery, option) {
        var options = $.extend({}, typeof option === 'object' && option),
            $tab    = options.id ? this.tools.getTab(options.id) : $currentTab,
            $panel  = options.id ? this.tools.getPanel(options.id) : $currentPanel
        
        if ($tab && $panel) {
            var op         = $tab.data('options'),
                data       = {},
                pageData   = {},
                $pagerForm = options.form
            
            if ($pagerForm && $pagerForm.length) {
                options.type = options.type || $pagerForm.attr('method') || 'POST'
                options.url  = options.url || $pagerForm.attr('action')
                
                pageData = $pagerForm.serializeJson()
                
                if (clearQuery) {
                    var pageInfo = BJUI.pageInfo
                    
                    for (var key in pageInfo) {
                        data[pageInfo[key]] = pageData[pageInfo[key]]
                    }
                } else {
                    data = pageData
                }
            }
            
            options.data = $.extend({}, options.data || {}, data)
            
            if (!$tab.hasClass('external')) {
                this.tools.reloadTab($panel, options)
            } else {
                this.openExternal(options.url, $panel, options.data)
            }
        }
    }
    
    Navtab.prototype.getCurrentPanel = function() {
        return this.tools.getPanels().eq(currentIndex)
    }
    
    Navtab.prototype.checkTimeout = function() {
        var json = JSON.parse($currentPanel.html())
        
        if (json && json[BJUI.keys.statusCode] == BJUI.statusCode.timeout) this.closeCurrentTab()
    }
    
    Navtab.prototype.openExternal = function(url, $panel, data) {
        var ih = $panel.closest('.navtab-panel').height()
        
        if (data && !$.isEmptyObject(data)) {
            url.indexOf('?') ? url += '&' : '?'
            url += $.param(data)
        }
        
        $panel.html(FRAG.externalFrag.replaceAll('{url}', url).replaceAll('{height}', ih +'px'))
    }
    
    Navtab.prototype.drag = function(e, $tab, $panel, $more) {
        var that  = this,
            $lis  = that.tools.getTabs(), $panels = that.tools.getPanels(), $mores = that.tools.getMoreLi(),
            $drag = $tabs.next('.bjui-navtab-drag'),
            $prev = $tab.prev(), $next  = $tab.next(),
            index = $tab.index(), width = $tab.width(),
            oleft = $tab.position().left,
            leftarr = [], newArr = [], newIndex
        
        if ($lis.length <= 2) return
        
        if (!$drag.length) {
            $drag = $('<div class="bjui-navtab-drag" style="position:absolute; top:0; left:1px; width:2px; height:25px; background:#ff6600; display:none;"></div>')
            $drag.insertAfter($tabs)
        }
        
        $drag.css('left', oleft - 2)
        
        $tabs.find('> li').each(function() {
            leftarr.push($(this).position().left)
        })
        
        $tab.find('> a').basedrag({
            move:'horizontal',
            oleft    : oleft,
            drag     : function($target, e, left, top) {
                $tab.addClass('navtab-drag')
                
                newArr = [left]
                $.merge(newArr, leftarr)
                newArr.sort(function(a, b) { return a - b })
                
                newIndex = $.inArray(left, newArr)
                if (!newIndex) newIndex = 1
                if (newIndex == $lis.length)
                    $drag.css('left', leftarr[newIndex - 1] - 2 + $lis.last().width())
                else
                    $drag.css('left', leftarr[newIndex] - 2)
                    
                $drag.show()
            },
            stop     : function() {
                $tab.removeClass('navtab-drag')
                $drag.hide()
                
                if (index != newIndex) {
                    if (newIndex == $lis.length) {
                        if (index != $lis.length - 1) {
                            $tab.insertAfter($lis.eq(newIndex - 1))
                            $panel.insertAfter($panels.eq(newIndex - 1))
                            $more.insertAfter($mores.eq(newIndex - 1))
                        }
                    } else {
                        $tab.insertBefore($lis.eq(newIndex))
                        $panel.insertBefore($panels.eq(newIndex))
                        $more.insertBefore($mores.eq(newIndex))
                    }
                }
            },
            event    : e,
            nounbind : true
        })
    }
    
    // NAVTAB PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments,
            property = option,
            navtab   = 'bjui.navtab',
            $body    = $('body'),
            datas    = $body.data(navtab) || {}
        
        return this.each(function () {
            var $this   = $(this),
                options = $.extend({}, Navtab.DEFAULTS, typeof option === 'object' && option),
                id      = options && options.id,
                data    = datas && datas[datas.current]
            
            if (typeof property === 'string' && $.isFunction(data[property])) {
                if (!data) return
                
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                if (!id) {
                    id = datas ? datas.current : 'main'
                    
                    if (!BJUI.ui.overwriteHomeTab && id === 'main')
                        id = 'navtab'
                } else {
                    if (!id.isNormalID()) {
                        BJUI.debug('BJUI.Navtab: ID ['+ id +'] '+ BJUI.regional.idChecked)
                        
                        return
                    }
                }
                
                options.id = id
                
                if (!datas[id]) {
                    datas[id] = (data = new Navtab(options))
                } else {
                    data = datas[id]
                    if (typeof option === 'object' && option)
                        $.extend(data.options, option)
                }
                
                $body.data(navtab, datas)
                
                data.openTab()
            }
        })
    }
    
    var old = $.fn.navtab
    
    $.fn.navtab             = Plugin
    $.fn.navtab.Constructor = Navtab
    
    // NAVTAB NO CONFLICT
    // =================
    
    $.fn.navtab.noConflict = function () {
        $.fn.navtab = old
        return this
    }
    
    // NOT SELECTOR
    // ==============
    
    BJUI.navtab = function() {
        Plugin.apply($('body'), arguments)
    }
    
    // NAVTAB DATA-API
    // ==============
    
    $(document).on('click.bjui.navtab.data-api', '[data-toggle="navtab"]', function(e) {
        e.preventDefault()
        
        var $this = $(this), href = $this.attr('href'), data = $this.data(), options = data.options
        
        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object')
                $.extend(data, options)
        }
        
        if (!data.title) data.title = $this.text()
        if (href && !data.url) data.url = href
        
        Plugin.call($this, data)
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-dialog.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-dialog.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // DIALOG GLOBAL ELEMENTS
    // ======================
    
    var $resizable
    var $current, shadow, zindex
    
    $(function() {
        var INIT_DIALOG = function() {
            $resizable = $('#bjui-resizable')
            shadow     = 'dialogShadow'
            zindex     = Dialog.ZINDEX
            
            $('body').append('<!-- dialog resizable -->').append(FRAG.resizable)
        }
        
        INIT_DIALOG()
    })
    
    // DIALOG CLASS DEFINITION
    // ======================
    var Dialog = function(options) {
        this.$element = $('body')
        this.options  = options
        this.tools    = this.TOOLS()
    }
    
    Dialog.DEFAULTS = {
        id          : null,
        title       : 'New Dialog',
        url         : null,
        type        : 'GET',
        data        : {},
        loadingmask : true,
        width       : 500,
        height      : 300,
        minW        : 65,
        minH        : 40,
        max         : false,
        mask        : false,
        resizable   : true,
        drawable    : true,
        maxable     : true,
        minable     : true,
        fresh       : false,
        onLoad      : null,
        beforeClose : null,
        onClose     : null
    }
    
    Dialog.ZINDEX = 30
    
    Dialog.prototype.TOOLS = function() {
        var that  = this
        var tools = {
            getDefaults: function() {
                return Dialog.DEFAULTS
            },
            init: function($dialog) {
                var width  = that.options.width > that.options.minW ? that.options.width : that.options.minW
                var height = that.options.height > that.options.minH ? that.options.height : that.options.minH
                var wW     = $(window).width(),
                    wH     = $(window).height(),
                    iTop   = that.options.max ? 0 : ((wH - height) / 3)
                
                if (width > wW)  width  = wW
                if (height > wH) height = wH
                
                $dialog
                    .height(height)
                    .width(width)
                    .show()
                    .css({left:(wW - width) / 2, top:0, opacity:0.1})
                    .animate({top:iTop > 0 ? iTop : 0, opacity:1})
                    .addClass(shadow)
                    .find('> .dialogContent').height(height - $('> .dialogHeader', $dialog).outerHeight())
                
                $('body').find('> .bjui-dialog-container').not($dialog).removeClass(shadow)
            },
            reload: function($dialog, options) {
                var tools = this, $dialogContent = $dialog.find('> .dialogContent'), onLoad, data = options && options.data, html
                
                options = options || $dialog.data('options')
                onLoad  = options.onLoad ? options.onLoad.toFunc() : null
                
                $dialog.trigger(BJUI.eventType.beforeLoadDialog)
                
                if (options.url) {
                    if (data) {
                        if (typeof data === 'string') {
                            if (data.trim().startsWith('{')) {
                                data = data.toObj()
                            } else {
                                data = data.toFunc()
                            }
                        }
                        if (typeof data === 'function') {
                            data = data.apply()
                        }
                    }
                    $dialogContent.ajaxUrl({
                        type:options.type || 'GET', url:options.url, data:data || {}, loadingmask:options.loadingmask, callback:function(response) {
                            if (onLoad) onLoad.apply(that, [$dialog])
                            if (BJUI.ui.clientPaging && $dialog.data('bjui.clientPaging')) $dialog.pagination('setPagingAndOrderby', $dialog)
                            
                            tools.resizeBjuiRow($dialog)
                        }
                    })
                } else {
                    if (options.image) {
                        html = '<img src="'+ decodeURIComponent(options.image) +'" style="max-width:100%;">'
                    } else if (options.html) {
                        html = options.html
                        if (typeof html === 'string' && $.isFunction(html.toFunc()))
                            html = html.toFunc()
                    } else if (options.target) {
                        html = $(options.target).html() || $dialog.data('bjui.dialog.target')
                        $(options.target).empty()
                        $dialog.data('bjui.dialog.target', html)
                    }
                    
                    $dialogContent.trigger(BJUI.eventType.beforeAjaxLoad).html(html).initui()
                    
                    if (onLoad) onLoad.apply(that, [$dialog])
                    
                    this.resizeBjuiRow($dialog)
                }
            },
            resizeContent: function($dialog) {
                var $dialogContent = $dialog.find('> .dialogContent')
                
                $dialogContent
                    .css({height:($dialog.height() - $dialog.find('> .dialogHeader').outerHeight())}) 
                
                $(window).trigger(BJUI.eventType.resizeGrid)
            },
            resizeBjuiRow: function($dialog) {
                var width = $dialog.width(), colWidth = BJUI.formColWidth
                
                $dialog.find('.bjui-row').each(function() {
                    var $rows = $(this)
                    
                    if (($rows.attr('class')).indexOf('col-') === -1) {
                        $rows.addClass('col-none')
                    }
                    
                    $rows.filter('.col-4')
                        .toggleClass('col-3', (width < colWidth.L && width > colWidth.M))
                        .end()
                        .filter('.col-3, .col-4')
                        .toggleClass('col-2', (width < colWidth.M && width > colWidth.S))
                        .end()
                        .filter('.col-2, .col-3, .col-4')
                        .toggleClass('col-1', (width < colWidth.S && width > colWidth.SS))
                        .end()
                        .filter('.col-none, .col-1, .col-2, .col-3, .col-4')
                        .toggleClass('col-0', (width < colWidth.SS))
                })
            }
        }
        
        return tools
    }
    
    Dialog.prototype.open = function() {
        var that    = this,
            options = that.options,
            $body   = $('body'),
            datas   = $body.data('bjui.dialog'),
            data    = datas[options.id],
            $dialog = data && data.dialog
            
        if (!(options.target || $(options.target).length) && !(options.html || options.image)) {
            if (!options.url && options.href) options.url = options.href
            if (!options.url) {
                BJUI.debug('BJUI.Dialog: Error trying to open a dialog, url is undefined!')
                return
            } else {
                options.url = decodeURI(options.url).replacePlh()
                
                if (!options.url.isFinishedTm()) {
                    BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                    BJUI.debug('BJUI.Dialog: The new dialog\'s url is incorrect, url: '+ options.url)
                    return
                }
                
                options.url = encodeURI(options.url)
            }
        } else {
            options.url = undefined
        }
        if ($dialog) { //if the dialog id already exists
            var op = $dialog.data('options')
            
            this.switchDialog($dialog)
            
            if ($dialog.is(':hidden')) $dialog.show()
            if (options.fresh || options.url != op.url) {
                that.reload(options)
            }
        } else { //open a new dialog
            var dr     = BJUI.regional.dialog,
                dialog = FRAG.dialog
                    .replace('#close#', dr.close)
                    .replace('#maximize#', dr.maximize)
                    .replace('#restore#', dr.restore)
                    .replace('#minimize#', dr.minimize)
                    .replace('#title#', dr.title)
            
            $dialog = $(dialog)
                .data('options', $.extend({}, options))
                .css('z-index', (++ zindex))
                .hide()
                .appendTo($body)
            
            $dialog.find('> .dialogHeader > h1 > span.title').html(options.title)
            
            this.tools.init($dialog)
            
            if (options.maxable) $dialog.find('a.maximize').show()
            else $dialog.find('a.maximize').hide()
            if (options.minable) $dialog.find('a.minimize').show()
            else $dialog.find('a.minimize').hide()
            if (options.max) that.maxsize($dialog)
            if (options.mask) this.addMask($dialog)
            else if (options.minable && $.fn.taskbar) this.$element.taskbar({id:options.id, title:options.title})
            
            $dialog.on('click', function(e) {
                if (!$(e.target).data('bjui.dialog'))
                    if ($current && $current[0] != $dialog[0]) that.switchDialog($dialog)
            }).on('click', '.btn-close', function(e) {
                that.close($dialog)
                
                e.preventDefault()
            }).on('click', '.dialogHeader > a', function(e) {
                var $a = $(this)
                
                if ($a.hasClass('close')) that.close($dialog)
                if ($a.hasClass('minimize')) {
                    that.minimize($dialog)
                }
                if ($a.hasClass('maximize')) {
                    that.switchDialog($dialog)
                    that.maxsize($dialog)
                }
                if ($a.hasClass('restore')) that.restore($dialog)
                
                e.preventDefault()
                e.stopPropagation()
            }).on('dblclick', '.dialogHeader > h1', function(e) {
                if (options.maxable) {
                    if ($dialog.find('> .dialogHeader > a.restore').is(':hidden')) $dialog.find('a.maximize').trigger('click')
                    else $dialog.find('> .dialogHeader > a.restore').trigger('click')
                }
            }).on('mousedown.bjui.dialog.drag', '.dialogHeader > h1', function(e) {
                that.switchDialog($dialog)
                
                if (!options.drawable || $dialog.data('max')) return
                
                $dialog.data('bjui.dialog.task', true)
                setTimeout($.proxy(function () {
                    if ($dialog.data('bjui.dialog.task')) that.drag(e, $dialog)
                }, that), 150)
                
                e.preventDefault()
            }).on('mouseup.bjui.dialog.drag', '.dialogHeader > h1', function(e) {
                $dialog.data('bjui.dialog.task', false)
            }).on('mousedown.bjui.dialog.resize', 'div[class^="resizable"]', function(e) {
                if (!options.drawable || $dialog.data('max')) return false
                if (!options.resizable) return false
                
                var $bar = $(this)
                
                that.switchDialog($dialog)
                that.resizeInit(e, $('#bjui-resizable'), $dialog, $bar)
                $bar.show()
                
                e.preventDefault()
            }).on('mouseup.bjui.dialog.resize', 'div[class^="resizable"]', function(e) {
                e.preventDefault()
            })
            
            data.dialog = $dialog
            this.tools.reload($dialog, options)
        }
        
        $.CurrentDialog = $current = $dialog
        
        // set current to body data
        datas.current = options.id
    }
    
    Dialog.prototype.addMask = function($dialog) {
        var $mask = $dialog.data('bjui.dialog.mask')
        
        $dialog.wrap('<div style="z-index:'+ zindex +'" class="bjui-dialog-wrap"></div>')
        $dialog.find('> .dialogHeader > a.minimize').hide()
        if (!$mask || !$mask.length) {
            $mask = $(FRAG.dialogMask)
            $mask.css('z-index', 1).show().insertBefore($dialog)
            $dialog.data('bjui.dialog.mask', $mask)
        }
    }
    
    Dialog.prototype.refresh = function(id) {
        if (id && typeof id === 'string') {
            var arr = id.split(','), datas = $('body').data('bjui.dialog')
            
            for (var i = 0; i < arr.length; i++) {
                var $dialog = datas && datas[arr[i].trim()] && datas[arr[i].trim()].dialog
                
                if ($dialog) {
                    $dialog.removeData('bjui.clientPaging')
                    this.tools.reload($dialog)
                }
            }
        } else {
            if ($current) {
                $current.removeData('bjui.clientPaging')
                this.tools.reload($current)
            }
        }
    }
    
    Dialog.prototype.resize = function($dialog, width, height) {
        var that = this, options, $dialogContent, ww = $(window).width(), hh = $(window).height()
        
        if (!$dialog) {
            $dialog = $current
        }
        
        options = $dialog.data('options')
        $dialogContent = $dialog.find('> .dialogContent')
        
        if (width != options.width) {
            if (width > ww)
                width = ww
            
            if (options.max) {
                $dialog.animate({ width:width, left:(ww - width)/2 }, 'normal')
            } else {
                $dialog.width(width).css('left', (ww - width)/2)
            }
            
            options.width = width
        }
        if (height != options.height) {
            var itop = 0
            
            if (height < hh)
                itop = (hh - height) / 2 - 20
            if (itop < 0)
                itop = 0
            
            if (options.max) {
                $dialog.animate({ height:height, top:itop }, 'normal', function() {
                    $dialogContent.height(height - $dialog.find('> .dialogHeader').outerHeight())
                })
            } else {
                $dialog.height(height).css('top', itop)
                $dialogContent.height(height - $dialog.find('> .dialogHeader').outerHeight())
            }
            
            options.height = height
        }
    }
    
    Dialog.prototype.reload = function(option) {
        var that     = this,
            options  = $.extend({}, typeof option === 'object' && option),
            datas    = $('body').data('bjui.dialog'),
            $dialog  = (options.id && datas[options.id] && datas[options.id].dialog) || that.getCurrent()
        
        if ($dialog && $dialog.length) {
            var op = $dialog.data('options')
            
            options = $.extend({}, op, options)
            
            var _reload = function() {
                var $dialogContent = $dialog.find('> .dialogContent'), ww = $(window).width(), hh = $(window).height(), w = options.width, h = options.height
                
                if (w != op.width) {
                    if (w > ww)
                        w = ww
                    
                    if (options.max) {
                        $dialog.animate({ width:w, left:(ww - w)/2 }, 'normal'/*, function() { $dialogContent.width(options.width) }*/)
                    } else {
                        $dialog.width(w).css('left', (ww - w)/2)
                    }
                }
                if (h != op.height) {
                    var itop = 0
                    
                    if (h < hh)
                        itop = (hh - h) / 2 - 20
                    if (itop < 0)
                        itop = 0
                    
                    if (options.max) {
                        $dialog.animate({ height:h, top:itop }, 'normal', function() {
                            $dialogContent.height(h - $dialog.find('> .dialogHeader').outerHeight())
                        })
                    } else {
                        $dialog.height(h).css('top', itop)
                        $dialogContent.height(h - $dialog.find('> .dialogHeader').outerHeight())
                    }
                }
                if (options.maxable != op.maxable) {
                    if (options.maxable) $dialog.find('a.maximize').show()
                    else $dialog.find('a.maximize').hide()
                } 
                if (options.minable != op.minable) {
                    if (options.minable) $dialog.find('a.minimize').show()
                    else $dialog.find('a.minimize').hide()
                }
                if (options.max != op.max)
                    if (options.max)
                        setTimeout(that.maxsize($dialog), 10)
                if (options.mask != op.mask) {
                    if (options.mask) {
                        that.addMask($dialog)
                        if ($.fn.taskbar) that.$element.taskbar('closeDialog', options.id)
                    } else if (options.minable && $.fn.taskbar) {
                        that.$element.taskbar({id:options.id, title:options.title})
                    }
                }
                if (options.title != op.title) {
                    $dialog.find('> .dialogHeader > h1 > span.title').html(options.title)
                    $dialog.taskbar('changeTitle', options.id, options.title)
                }
                
                $dialog.data('options', $.extend({}, options))
                
                that.tools.reload($dialog, options)
            }
            
            if (options.reloadWarn) {
                $dialog.alertmsg('confirm', options.reloadWarn, {
                    okCall: function() {
                        _reload()
                    }
                })
            } else {
                _reload()
            }
        }
    }
    
    Dialog.prototype.reloadForm = function(clearQuery, option) {
        var options = $.extend({}, typeof option === 'object' && option),
            datas   = $('body').data('bjui.dialog'),
            $dialog
        
        if (options.id) {
            if (datas && datas[options.id])
                $dialog = datas[options.id].dialog
        } else {
            $dialog = $current
        }
        
        if ($dialog) {
            var op         = $dialog.data('options'),
                data       = {},
                pageData   = {},
                $pagerForm = options.form
            
            if ($pagerForm && $pagerForm.length) {
                options.type = options.type || $pagerForm.attr('method') || 'POST'
                options.url  = options.url || $pagerForm.attr('action')
                
                pageData = $pagerForm.serializeJson()
                
                if (clearQuery) {
                    var pageInfo = BJUI.pageInfo
                    
                    for (var key in pageInfo) {
                        data[pageInfo[key]] = pageData[pageInfo[key]]
                    }
                } else {
                    data = pageData
                }
            }
            
            options.data = $.extend({}, options.data || {}, data)
            
            this.tools.reload($dialog, options)
        }
    }
    
    Dialog.prototype.getCurrent = function() {
        return $current
    }
    
    Dialog.prototype.switchDialog = function($dialog) {
        var index = $dialog.css('z-index')
        
        if ($current && $current != $dialog) {
            var cindex = $current.css('z-index'),
                datas  = $('body').data('bjui.dialog'),
                pindex
            
            if ($current.data('options').mask) {
                pindex = $current.parent().css('z-index')
                
                if (Number(pindex) > Number(index))
                    return
            }
            
            $current.css('z-index', index)
            $dialog.css('z-index', cindex)
            $.CurrentDialog = $current = $dialog
            
            // set current to body data
            datas.current = $dialog.data('options').id
            
            if ($.fn.taskbar) this.$element.taskbar('switchTask', datas.current)
        }
        
        $dialog.addClass(shadow)
        $('body').find('> .bjui-dialog-container, > .bjui-dialog-wrap > .bjui-dialog-container').not($dialog).removeClass(shadow)
    }
    
    Dialog.prototype.close = function(dialog) {
        var datas = $('body').data('bjui.dialog'), $dialog = (typeof dialog === 'string') ? datas[dialog].dialog : dialog
        
        if (!$dialog || !$dialog.length) {
            return
        }
        
        var that        = this,
            $mask       = $dialog.data('bjui.dialog.mask'),
            options     = $dialog.data('options'),
            target      = $dialog.data('bjui.dialog.target'),
            beforeClose = options.beforeClose ? options.beforeClose.toFunc() : null,
            onClose     = options.onClose ? options.onClose.toFunc() : null,
            canClose    = true,
            closeFunc   = function() {
                delete datas[options.id]
                
                if (onClose) onClose.apply(that)
                
                $.CurrentDialog = $current = null
                
                var $dialogs  = $('body').find('.bjui-dialog-container'),
                $_current = null
                
                if ($dialogs.length) {
                    $_current = that.$element.getMaxIndexObj($dialogs)
                } else {
                    zindex = Dialog.ZINDEX
                }
                if ($_current && $_current.is(':visible')) {
                    $.CurrentDialog = $current = $_current
                    that.switchDialog($_current)
                }
            }
        
        if (!$dialog || !options) return
        if (beforeClose) canClose = beforeClose.apply(that, [$dialog])
        if (!canClose) {
            that.switchDialog($dialog)
            return
        }
        if (options.target && target) $(options.target).html(target) 
        if ($mask && $mask.length) {
            $mask.remove()
            $dialog.unwrap()
        } else if ($.fn.taskbar) {
            this.$element.taskbar('closeDialog', options.id)
        }
        
        if (options.noanimate) {
            $dialog.trigger(BJUI.eventType.beforeCloseDialog).remove()
            closeFunc()
        } else {
            $dialog.animate({top:- $dialog.outerHeight(), opacity:0.1}, 'normal', function() {
                $dialog.trigger(BJUI.eventType.beforeCloseDialog).remove()
                closeFunc()
            })
        }
    }
    
    Dialog.prototype.closeCurrent = function() {
        this.close($current)
    }
    
    Dialog.prototype.checkTimeout = function() {
        var $dialogConetnt = $current.find('> .dialogContent'),
            json = JSON.parse($dialogConetnt.html())
        
        if (json && json[BJUI.keys.statusCode] == BJUI.statusCode.timeout) this.closeCurrent()
    }
    
    Dialog.prototype.maxsize = function($dialog) {
        var $taskbar = $('#bjui-taskbar'), taskH = ($taskbar.is(':visible') ? $taskbar.height() : 0) + 1
        
        $dialog.data('original', {
            top   : $dialog.css('top'),
            left  : $dialog.css('left'),
            width : $dialog.css('width'),
            height: $dialog.css('height')
        }).data('max', true)
        
        $dialog.find('> .dialogHeader > a.maximize').hide()
        $dialog.find('> .dialogHeader > a.restore').show()
        
        var iContentW = $(window).width() - 1,
            iContentH = $(window).height() - taskH
        
        $dialog.css({ top:0, right:0, left:0, bottom:taskH, width:'100%', height:'auto'})
        
        this.tools.resizeContent($dialog)
        this.tools.resizeBjuiRow($dialog)
    }
    
    Dialog.prototype.restore = function($dialog) {
        var original = $dialog.data('original'),
            dwidth   = original.width,
            dheight  = original.height
        
        $dialog.css({
            top   : original.top,
            right : '',
            bottom: '',
            left  : original.left,
            width : dwidth,
            height: dheight
        })
        
        this.tools.resizeContent($dialog)
        this.tools.resizeBjuiRow($dialog)
        
        $dialog.find('> .dialogHeader > a.maximize').show()
        $dialog.find('> .dialogHeader > a.restore').hide()
        $dialog.data('max', false)
    }
    
    Dialog.prototype.minimize = function($dialog) {
        $dialog.hide()
        if ($.fn.taskbar) this.$element.taskbar('minimize', $dialog)
        
        var $dialogs  = $('body').find('.bjui-dialog-container:visible'),
            $_current = null
        
        if ($dialogs.length) {
            $_current = this.$element.getMaxIndexObj($dialogs)
        }
        if ($_current) this.switchDialog($_current)
    }
    
    Dialog.prototype.drag = function(e, $dialog) {
        var $shadow = $('#bjui-dialogProxy')
        
        $dialog.find('> .dialogContent').css('opacity', '.3')
        $dialog.basedrag({
            selector : '> .dialogHeader',
            stop     : function() {
                $dialog
                    .css({left:$dialog.css('left'), top:$dialog.css('top')})
                    .find('> .dialogContent').css('opacity', 1)
            },
            event    : e,
            nounbind : true
        })
    }
    
    Dialog.prototype.resizeDialog = function($resizable, $dialog, target) {
        var oleft  = parseInt($resizable.css('left'), 10),
            otop   = parseInt($resizable.css('top'), 10),
            height = parseInt($resizable.css('height'), 10),
            width  = parseInt($resizable.css('width'), 10)
        
        if (otop < 0) otop = 0
        
        $dialog.css({top:otop, left:oleft, width:width, height:height})
        
        if (target != 'w' && target != 'e')
            this.tools.resizeContent($dialog)
        if (target != 'n' && target != 's') {
            this.tools.resizeBjuiRow($dialog)
            $(window).trigger(BJUI.eventType.resizeGrid)
        }
    }
    
    Dialog.prototype.resizeInit = function(e, $resizable, $dialog, $bar) {
        var that = this, target = $bar.attr('tar')
        
        $('body').css('cursor', target +'-resize')
        $resizable
            .css({
                top    : $dialog.css('top'),
                left   : $dialog.css('left'),
                height : $dialog.outerHeight(),
                width  : $dialog.css('width')
            })
            .show()
        
        if (!this.options.dragCurrent) {
            this.options.dragCurrent = {
                $resizable : $resizable,
                $dialog    : $dialog,
                target     : target,
                oleft      : parseInt($resizable.css('left'), 10)   || 0,
                owidth     : parseInt($resizable.css('width'), 10)  || 0,
                otop       : parseInt($resizable.css('top'), 10)    || 0,
                oheight    : parseInt($resizable.css('height'), 10) || 0,
                ox         : e.pageX || e.screenX,
                oy         : e.pageY || e.clientY
            }
            $(document).on('mouseup.bjui.dialog.resize', $.proxy(that.resizeStop, that))
            $(document).on('mousemove.bjui.dialog.resize', $.proxy(that.resizeStart, that))
        }
    }
    
    Dialog.prototype.resizeStart = function(e) {
        var current = this.options.dragCurrent
        
        if (!current) return
        if (!e) var e = window.event
        
        var lmove     = (e.pageX || e.screenX) - current.ox,
            tmove     = (e.pageY || e.clientY) - current.oy,
            $mask = current.$dialog.data('bjui.dialog.mask')
        
        if (!$mask || !$mask.length)
            if ((e.pageY || e.clientY) <= 0 || (e.pageY || e.clientY) >= ($(window).height() - current.$dialog.find('> .dialogHeader').outerHeight())) return
        
        var target = current.target,
            width  = current.owidth,
            height = current.oheight
        
        if (target != 'n' && target != 's')
            width += (target.indexOf('w') >= 0) ? -lmove : lmove
        if (width >= this.options.minW) {
            if (target.indexOf('w') >= 0)
                current.$resizable.css('left', (current.oleft + lmove))
            if (target != 'n' && target != 's')
                current.$resizable.css('width', width)
        }
        if (target != 'w' && target != 'e')
            height += (target.indexOf('n') >= 0) ? -tmove : tmove
        if (height >= this.options.minH) {
            if (target.indexOf('n') >= 0)
                current.$resizable.css('top', (current.otop + tmove))
            if (target != 'w' && target != 'e')
                current.$resizable.css('height', height)
        }
    }
    
    Dialog.prototype.resizeStop = function(e) {
        var current = this.options.dragCurrent
        
        if (!current) return false
        
        $(document).off('mouseup.bjui.dialog.resize').off('mousemove.bjui.dialog.resize')
        
        this.options.dragCurrent = null
        this.resizeDialog(current.$resizable, current.$dialog, current.target)
        
        $('body').css('cursor', '')
        current.$resizable.hide()
    }
    
    // DIALOG PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments,
            property = option,
            dialog   = 'bjui.dialog',
            $body    = $('body'),
            datas    = $body.data(dialog) || {}
        
        return this.each(function () {
            var $this   = $(this),
                options = $.extend({}, Dialog.DEFAULTS, typeof option === 'object' && option),
                id      = options && options.id,
                data
            
            if (!id) {
                if (datas.current) id = datas.current
                else id = 'dialog'
            } else {
                if (!id.isNormalID()) {
                    BJUI.debug('BJUI.Dialog: ID ['+ id +'] '+ BJUI.regional.idChecked)
                    
                    return
                }
            }
            
            options.id = id
            data = datas && datas[id]
            
            if (!data) {
                datas[id] = (data = new Dialog(options))
            } else {
                if (typeof option === 'object' && option)
                    $.extend(data.options, option)
            }
            
            $body.data(dialog, datas)
            
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.open()
            }
        })
    }
    
    var old = $.fn.dialog
    
    $.fn.dialog             = Plugin
    $.fn.dialog.Constructor = Dialog
    
    // DIALOG NO CONFLICT
    // =================
    
    $.fn.dialog.noConflict = function () {
        $.fn.dialog = old
        return this
    }
    
    // NOT SELECTOR
    // ==============
    
    BJUI.dialog = function() {
        Plugin.apply($('body'), arguments)
    }
    
    // DIALOG DATA-API
    // ==============
    
    $(document).on('click.bjui.dialog.data-api', '[data-toggle="dialog"]', function(e) {
        var $this   = $(this), href = $this.attr('href'), data = $this.data(), options = data.options
        
        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object')
                $.extend(data, options)
        }
        
        if (!data.title) data.title = $this.text()
        if (href && !data.url) data.url = href
        
        Plugin.call($this, data)
        
        e.preventDefault()
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-taskbar.js  V1.31
 * reference: bjui-dialog.js
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-taskbar.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // TASKBAR GLOBAL ELEMENTS
    // ======================
    
    var $resizable
    var $taskBar, $taskBox, $taskList, $prevBtn, $nextBtn, taskDisabled, taskSelected, taskMargin
    
    $(function() {
        var INIT_TASKBAR = function() {
            $resizable   = $('#bjui-resizable')
            $taskBar     = $(FRAG.taskbar)
            $taskBox     = $taskBar.find('.taskbarContent')
            $taskList    = $taskBox.find('> ul')
            $prevBtn     = $taskBar.find('.taskbarLeft')
            $nextBtn     = $taskBar.find('.taskbarRight')
            taskDisabled = 'disabled'
            taskSelected = 'selected'
            taskMargin   = 'taskbarMargin'
            
            $('body').append('<!-- dialog task bar -->').append($taskBar)
            
            //events
            $prevBtn.click(function(e) { $(this).taskbar('scrollLeft') })
            $nextBtn.click(function(e) { $(this).taskbar('scrollRight') })
        }
        INIT_TASKBAR()
    })
    
    // TASKBAR CLASS DEFINITION
    // ======================
    
    var Taskbar = function(element, options) {
        this.$element = $(element)
        this.$task    = null
        this.options  = options
        this.tools    = this.TOOLS()
    }
    
    Taskbar.DEFAULTS = {
        id: undefined,
        title: undefined
    }
    
    Taskbar.prototype.init = function() {
        var that = this
        var $task = $taskList.find('#task-'+ this.options.id)
        
        this.show()
        if (!$task.length) {
            var taskFrag = '<li id="#taskid#"><div class="taskbutton"><span><i class="fa fa-th-large"></i></span> <span class="title">#title#</span></div><div class="close"><i class="fa fa-times-circle"></i></div></li>';
            
            $task = $(taskFrag.replace('#taskid#', 'task-'+ this.options.id).replace('#title#', this.options.title))
            $task.appendTo($taskList)
        } else {
            $task.find('> div > span.title').html(this.options.title)
        }
        this.contextmenu($task)
        this.switchTask($task)
        this.tools.scrollTask($task)
        
        //events
        $task.click(function(e) {
            if ($(e.target).closest('div').hasClass('close') || $(e.target).hasClass('close')) {
                $task.dialog('close', that.options.id)
            } else {
                var datas   = $('body').data('bjui.dialog'),
                    $dialog = datas[that.options.id].dialog
                
                if ($task.hasClass('selected')) {
                    $dialog.find('.dialogHeader a.minimize').trigger('click')
                } else {
                    if ($dialog.is(':hidden')) {
                        that.restoreDialog($dialog)
                    } else {
                        $dialog.trigger('click')
                        $task.addClass(taskSelected)
                    }   
                }
                that.scrollCurrent($task)
            }
            
            return false
        })
    }
    
    Taskbar.prototype.TOOLS = function() {
        var that  = this
        var tools = {
            scrollCurrent: function() {
                var iW = this.tasksW(this.getTasks())
                
                if (iW > this.getTaskBarW()) {
                    var $tools = this
                    var lTask  = $taskList.find('> li:last-child')
                    var left   = this.getTaskBarW() - lTask.position().left - lTask.outerWidth(true)
                    
                    $taskList.animate({left: left}, 200, function() {
                        $tools.ctrlScrollBtn()
                    })
                } else {
                    this.ctrlScrollBtn()
                }
            },
            getTaskBarW: function() {
                return $taskBox.width()- ($prevBtn.is(':hidden') ? $prevBtn.width() + 2 : 0) - ($nextBtn.is(':hidden') ? $nextBtn.width() + 2 : 0)
            },
            scrollTask: function($task) {
                var $tools = this
                
                if ($task.position().left + this.getLeft() + $task.outerWidth() > this.getBarWidth()) {
                    var left = this.getTaskBarW() - $task.position().left  - $task.outerWidth(true) - 2
                    
                    $taskList.animate({left:left}, 200, function() {
                        $tools.ctrlScrollBtn()
                    })
                } else if ($task.position().left + this.getLeft() < 0) {
                    var left = this.getLeft() - ($task.position().left + this.getLeft())
                    
                    $taskList.animate({left:left}, 200, function() {
                        $tools.ctrlScrollBtn()
                    })
                }
            },
            ctrlScrollBtn: function() {
                var iW = this.tasksW(this.getTasks())
                
                if (this.getTaskBarW() > iW) {
                    $taskBox.removeClass(taskMargin)
                    $nextBtn.hide()
                    $prevBtn.hide()
                    if (this.getTasks().eq(0).length) this.scrollTask(this.getTasks().eq(0))
                } else {
                    $taskBox.addClass(taskMargin)
                    $nextBtn.show().removeClass(taskDisabled)
                    $prevBtn.show().removeClass(taskDisabled)
                    if (this.getLeft() >= 0) $prevBtn.addClass(taskDisabled)
                    if (this.getLeft() <= this.getTaskBarW() - iW) $nextBtn.addClass(taskDisabled)
                }
            },
            getLeft: function(){
                return $taskList.position().left
            },
            visibleStart: function() {
                var iLeft = this.getLeft()
                var jTasks = this.getTasks()
                
                for (var i = 0; i < jTasks.size(); i++) {
                    if (jTasks.eq(i).position().left + jTasks.eq(i).outerWidth(true) + iLeft >= 0) return jTasks.eq(i)
                }
                
                return jTasks.eq(0)
            },
            visibleEnd: function() {
                var iLeft = this.getLeft()
                var jTasks = this.getTasks()
                
                for (var i = 0; i < jTasks.size(); i++) {
                    if (jTasks.eq(i).position().left + jTasks.eq(i).outerWidth(true) + iLeft > this.getBarWidth()) return jTasks.eq(i)
                }
                
                return jTasks.eq(jTasks.size() - 1)
            },
            getTasks: function() {
                return $taskList.find('> li')
            },
            tasksW: function(jTasks) {
                var iW = 0
                
                jTasks.each(function() {
                    iW += $(this).outerWidth(true)
                })
                
                return iW
            },
            getBarWidth: function() {
                return $taskBar.innerWidth()
            },
            getCurrent: function() {
                return $taskList.find('li.'+ taskSelected)
            }
        }
        
        return tools
    }
    
    Taskbar.prototype.contextmenu = function($obj) {
        var that = this
        
        $obj.contextmenu({
            id: 'dialogCM',
            bindings: {
                reload: function(t) {
                    t.dialog('refresh', that.options.id)
                },
                closeCurrent: function(t, m) {
                    var $obj = t.isTag('li') ? t : that.tools.getCurrent()
                    
                    $obj.find('.close').trigger('click')
                },
                closeOther: function(t, m){
                    var $tasks = $taskList.find('> li').not(t)
                    
                    $tasks.each(function(i) {
                        $(this).find('.close').trigger('click')
                    })
                },
                closeAll: function(t, m) {
                    var $tasks = that.tools.getTasks()
                    
                    $tasks.each(function(i) {
                        $(this).find('.close').trigger('click')
                    })
                }
            },
            ctrSub: function(t, m) {
                var mCur = m.find('[rel="closeCurrent"]')
                var mOther = m.find('[rel="closeOther"]')
                
                if (!that.tools.getCurrent().length) {
                    mCur.addClass(taskDisabled)
                    mOther.addClass(taskDisabled)
                } else {
                    if (that.tools.getTasks().size() == 1)
                        mOther.addClass(taskDisabled)
                }
            }
        })
    }
    
    Taskbar.prototype.closeDialog = function(task) {
        var $task = (typeof task === 'string') ? this.getTask(task) : task
        
        if (!$task || !$task.length) return
        
        $task.remove()
        if (!this.tools.getTasks().size()) {
            this.hide()
        }
        this.tools.scrollCurrent()
        this.$element.removeData('bjui.taskbar')
    }
    
    Taskbar.prototype.minimize = function(dialog) {
        var that    = this,
            datas   = $('body').data('bjui.dialog'),
            $dialog = (typeof dialog === 'string') ? datas[dialog].dialog : dialog,
            $task   = this.getTask($dialog.data('options').id)
        
        $resizable.css({
            top: $dialog.css('top'),
            left: $dialog.css('left'),
            height: $dialog.css('height'),
            width: $dialog.css('width')
        }).show().animate({top:$(window).height() - 60, left:$task.position().left, width:$task.outerWidth(), height:$task.outerHeight()}, 250, function() {
            $(this).hide()
            that.inactive($task)
        })
    }
    
    /**
     * @param {Object} id or dialog
     */
    Taskbar.prototype.restoreDialog = function($dialog) {
        var $task = this.getTask($dialog.data('options').id)
        
        $resizable.css({top:$(window).height() - 60, left:$task.position().left, height:$task.outerHeight(), width:$task.outerWidth()})
            .show()
            .animate({top:$dialog.css('top'), left:$dialog.css('left'), width:$dialog.css('width'), height:$dialog.css('height')}, 250, function() {
                $(this).hide()
                
                $dialog.show().trigger('click.bjui.taskbar.restore')
            })
        
        this.switchTask($task)
    }
    
    /**
     * @param {Object} id
     */
    Taskbar.prototype.inactive = function(task) {
        var $task = (typeof task === 'string') ? this.getTask(task) : task
        
        $task.removeClass(taskSelected)
    }
    
    Taskbar.prototype.scrollLeft = function() {
        var $task = this.tools.visibleStart()
        
        this.tools.scrollTask($task)
    }
    
    Taskbar.prototype.scrollRight = function() {
        var $task = this.tools.visibleEnd()
        
        this.tools.scrollTask($task)
    }
    
    Taskbar.prototype.scrollCurrent = function($task) {
        this.tools.scrollTask($task)
    }
    
    /**
     * @param {Object} id or $task
     */
    Taskbar.prototype.switchTask = function(task) {
        this.tools.getCurrent().removeClass(taskSelected)
        var $task = (typeof task === 'string') ? this.getTask(task) : task
        
        $task.addClass(taskSelected)
    }
    
    Taskbar.prototype.getTask = function(id) {
        return $taskList.find('#task-'+ id)
    }
    
    Taskbar.prototype.changeTitle = function(id, title) {
        var $task = this.getTask(id)
        
        if ($task && title) $task.find('.title').html(title)
    }
    
    Taskbar.prototype.show = function() {
        if ($taskBar.is(':hidden')) $taskBar.show().animate({bottom:0}, 500)
    }
    
    Taskbar.prototype.hide = function() {
        if ($taskBar.is(':visible')) $taskBar.animate({bottom:-50}, 500, function() { $taskBar.hide() })
    }
    
    // TASKBAR PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args = arguments
        var property = option
        
        return this.each(function () {
            var $this   = $(this)
            var options = $.extend({}, Taskbar.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data    = $this.data('bjui.taskbar')
            
            if (!data) $this.data('bjui.taskbar', (data = new Taskbar(this, options)))
            else if (data.options.id != options.id) $this.data('bjui.taskbar', (data = new Taskbar(this, options)))
            
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.taskbar

    $.fn.taskbar             = Plugin
    $.fn.taskbar.Constructor = Taskbar
    
    // TASKBAR NO CONFLICT
    // =================
    
    $.fn.taskbar.noConflict = function () {
        $.fn.taskbar = old
        return this
    }
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-ajax.js  V1.31
 * @author K'naan 
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-ajax.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // BJUIAJAX GLOBAL ELEMENTS
    // ======================
    
    var autorefreshTimer
    
    // BJUIAJAX CLASS DEFINITION
    // ======================
    
    var Bjuiajax = function(element, options) {
        var $this     = this
        
        this.$element = $(element)
        this.options  = options
        this.tools    = this.TOOLS()
    }
    
    Bjuiajax.DEFAULTS = {
        okalert         : true,
        reload          : true,
        loadingmask     : true,
        gridrefreshflag : true
    }
    
    Bjuiajax.NAVTAB = 'navtab'
    
    Bjuiajax.prototype.TOOLS = function() {
        var that  = this
        var tools = {
            getTarget: function() {
                if (that.$element.closest('.navtab-panel').length) return Bjuiajax.NAVTAB
                else return 'dialog'
            }
        }
        
        return tools
    }
    
    Bjuiajax.prototype.ajaxdone = function(json) {
        if (json[BJUI.keys.statusCode] == BJUI.statusCode.error) {
            if (json[BJUI.keys.message]) BJUI.alertmsg('error', json[BJUI.keys.message])
        } else if (json[BJUI.keys.statusCode] == BJUI.statusCode.timeout) {
            BJUI.alertmsg('info', (json[BJUI.keys.message] || BJUI.regional.sessiontimeout))
            BJUI.loadLogin()
        } else {
            if (json[BJUI.keys.message]) BJUI.alertmsg('correct', json[BJUI.keys.message])
        }
    }
    
    Bjuiajax.prototype.ajaxerror = function(xhr, ajaxOptions, thrownError) {
        var msg = xhr.responseText, that = this, options = that.$element.data('bjui.ajax.options') || that.options, failCallback = options.failCallback
        
        if (typeof msg === 'string' && msg.startsWith('{')) {
            this.ajaxdone(msg.toObj())
        } else {
            BJUI.alertmsg('error', '<div>Http status: ' + xhr.status + ' ' + xhr.statusText + '</div>' 
                + '<div>ajaxOptions: '+ ajaxOptions +' </div>'
                + '<div>thrownError: '+ thrownError +' </div>'
                + '<div>'+ msg +'</div>')
        }
        
        if (failCallback) {
            if (typeof failCallback === 'string')
                failCallback = failCallback.toFunc()
            if (typeof failCallback === 'function')
                failCallback.apply(that, [msg, options])
            else
                BJUI.debug('The callback function \'failCallback\' is incorrect: '+ failCallback)
        }
    }
    
    Bjuiajax.prototype.ajaxcallback = function(json) {
        var that = this, options = that.options,
            okCallback = options.okCallback, errCallback = options.errCallback, okAfterCallback = options.okAfterCallback,
            tabids = [], dialogids = [], divids = [], datagrids = []
        
        var okFunc = function() {
                if (typeof okCallback === 'string')
                    okCallback = okCallback.toFunc()
                if (typeof okCallback === 'function')
                    okCallback.apply(that, [json, options])
                else
                    BJUI.debug('The callback function \'okCallback\' is incorrect: '+ okCallback)
            },
            okAfterFunc = function() {
                if (typeof okAfterCallback === 'string')
                    okAfterCallback = okAfterCallback.toFunc()
                if (typeof okAfterCallback === 'function')
                    okAfterCallback.apply(that, [json, options])
                else
                    BJUI.debug('The callback function \'okAfterCallback\' is incorrect: '+ okAfterCallback)
            },
            errFunc = function() {
                if (typeof errCallback === 'string')
                    errCallback = errCallback.toFunc()
                if (typeof errCallback === 'function')
                    errCallback.apply(that, [json, options])
                else
                    BJUI.debug('The callback function \'errCallback\' is incorrect: '+ errCallback)
            }
        
        if (typeof json === 'string')
            json = json.toObj()
                
        if (options.okalert)
            that.ajaxdone(json)
        
        if (!json[BJUI.keys.statusCode]) {
            if (okCallback) {
                okFunc()
                return
            }
        }
        if (json[BJUI.keys.statusCode] == BJUI.statusCode.ok) {
            if (okCallback) {
                okFunc()
                return
            }
            
            if (json.tabid)
                $.merge(tabids, json.tabid.split(','))
            if (options.tabid)
                $.merge(tabids, options.tabid.split(','))
            
            if (json.dialogid)
                $.merge(dialogids, json.dialogid.split(','))
            if (options.dialogid)
                $.merge(dialogids, options.dialogid.split(','))
            
            if (json.divid)
                $.merge(divids, json.divid.split(','))
            if (options.divid)
                $.merge(divids, options.divid.split(','))
            
            if (json.datagrid)
                $.merge(datagrids, json.datagrid.split(','))
            if (options.datagrid)
                $.merge(datagrids, options.datagrid.split(','))
            
            // refresh
            if (tabids.length) {
                $.unique(tabids)
                setTimeout(function() { BJUI.navtab('reloadFlag', tabids.join(',')) }, 100)
            }
            if (dialogids.length) {
                $.unique(dialogids)
                setTimeout(function() { BJUI.dialog('refresh', dialogids.join(',')) }, 100)
            }
            if (divids.length) {
                $.unique(divids)
                setTimeout(function() { that.refreshdiv(divids.join(',')) }, 100)
            }
            if (datagrids.length) {
                setTimeout(function() {
                    $.each(datagrids, function(i, n) {
                        $('#'+ n.trim()).datagrid('refresh', options.gridrefreshflag)
                    })
                }, 100)
            }
            
            if (that.$target == $.CurrentNavtab) {
                that.navtabcallback(json)
            } else if (that.$target == $.CurrentDialog) {
                that.dialogcallback(json)
            } else {
                that.divcallback(json)
            }
            
            if (okAfterCallback) {
                okAfterFunc()
            }
        } else {
            if (errCallback) {
                errFunc()
            }
        }
    }
    
    Bjuiajax.prototype.divcallback = function(json, $target, options) {
        var that = this, options = that.options,
            forward = json.forward || options.forward || null,
            forwardConfirm = json.forwardConfirm || options.forwardConfirm || null
        
        
        if (options.reload && !forward) {
            that.refreshlayout()
        }
        
        if (options.reloadNavtab)
            setTimeout(function() { BJUI.navtab('refresh') }, 100)
        
        if (forward) {
            var _forward = function() {
                $.extend(options, {url: forward})
                that.refreshlayout()
            }
            
            if (forwardConfirm) {
                BJUI.alertmsg('confirm', forwardConfirm, {
                    okCall: function() { _forward() }
                })
            } else {
                _forward()
            }
        }
    }
    
    Bjuiajax.prototype.navtabcallback = function(json) {
        var that = this, options = that.options,
            closeCurrent = json.closeCurrent || options.closeCurrent || false,
            forward = json.forward || options.forward || null,
            forwardConfirm = json.forwardConfirm || options.forwardConfirm || null
        
        if (closeCurrent && !forward)
            BJUI.navtab('closeCurrentTab')
        else if (options.reload && !forward)
            setTimeout(function() { BJUI.navtab('refresh') }, 100)
        
        if (forward) {
            var _forward = function() {
                BJUI.navtab('reload', {url:forward})
            }
            
            if (forwardConfirm) {
                BJUI.alertmsg('confirm', forwardConfirm, {
                    okCall: function() { _forward() },
                    cancelCall: function() { if (closeCurrent) { BJUI.navtab('closeCurrentTab') } }
                })
            } else {
                _forward()
            }
        }
    }
    
    Bjuiajax.prototype.dialogcallback = function(json) {
        var that = this, options = that.options,
            closeCurrent = json.closeCurrent || options.closeCurrent || false,
            forward = json.forward || options.forward || null,
            forwardConfirm = json.forwardConfirm || options.forwardConfirm || null
        
        if (closeCurrent && !forward)
            BJUI.dialog('closeCurrent')
        else if (options.reload && !forward)
            setTimeout(function() { BJUI.dialog('refresh') }, 100)
        
        if (options.reloadNavtab)
            setTimeout(function() { BJUI.navtab('refresh') }, 100)
        if (forward) {
            var _forward = function() {
                BJUI.dialog('reload', {url:forward})
            }
            
            if (forwardConfirm) {
                BJUI.alertmsg('confirm', json.forwardConfirm, {
                    okCall: function() { _forward() },
                    cancelCall: function() { if (closeCurrent) { BJUI.dialog('closeCurrent') } }
                })
            } else {
                _forward()
            }
        }
    }
    
    Bjuiajax.prototype.doajax = function(options) {
        var that = this, $target, $element = that.$element
        
        if (!options) options = {}
        if (!options.loadingmask) options.loadingmask = false
        
        options = $.extend({}, Bjuiajax.DEFAULTS, typeof options === 'object' && options)
        that.options = options
        
        if (options.target) {
            if (options.target instanceof jQuery)
                $target = options.target
            else
                $target = $(options.target)
        } else {
            if ($element[0] !== $('body')[0]) {
                $target = $element.closest('.bjui-layout')
            }
        }
        
        if (!$target || !$target.length)
            $target = $.CurrentDialog || $.CurrentNavtab
        
        that.$target = $target
        
        if (!options.url) {
            BJUI.debug('BJUI.ajax: \'doajax\' method: the url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh($target)
            
            if (!options.url.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.ajax: \'doajax\' method: The url is incorrect: '+ options.url)
                return
            }
            
            options.url = encodeURI(options.url)
        }
        
        var callback = options.callback && options.callback.toFunc()
        var todo     = function() {
            if (!options.callback)
                options.callback = $.proxy(function(data) {that.ajaxcallback(data)}, that)
            
            $target.data('bjui.ajax.options', options).doAjax(options)
        }
        
        if (options.confirmMsg) {
            BJUI.alertmsg('confirm', options.confirmMsg,
                {
                    okCall : function() {
                        todo()
                    }
                }
            )
        } else {
            todo()
        }
    }
    
    Bjuiajax.prototype._ajaxform = function($form, options) {
        var that         = this,
            $target      = null,
            beforeSubmit = options.beforeSubmit,
            callback     = options.callback,
            isSubmit     = false,
            enctype
        
        if ($form.data('holdSubmit')) {
            return
        }
        // beforeSubmit
        if (beforeSubmit) {
            if (typeof beforeSubmit === 'string')
                beforeSubmit = beforeSubmit.toFunc()
            if (typeof beforeSubmit === 'function') {
                if (!beforeSubmit.apply(that, [$form])) {
                    return
                }
            } else {
                BJUI.debug('BJUI.ajax: \'ajaxform\' options \'beforeSubmit\': Not a function!')
                return
            }
        }
        
        // for webuploader
        var $uploaders = $form.find('input[data-toggle="webuploader"]'), requiredMsg, error4Uploader = false
        
        if ($uploaders.length && WebUploader) {
            $uploaders.each(function(i, n) {
                var uploader = $(this).data('webuploader'), $lis = $(this).data('webuploader.wrap').find('.filelist > li'), fileCount = $lis.length, waitCount = fileCount - $lis.filter('.uploaded').length 
                
                if (waitCount && uploader) {
                    if (waitCount !== uploader.getStats().successNum) {
                        // 自动上传
                        uploader.upload()
                        // 上传完成后触发
                        uploader.on('uploadFinished', function() {
                            $form.submit()
                        })
                        
                        error4Uploader = true
                        return false
                    }
                }
                if (uploader && uploader.options.required) {
                    if (!fileCount) {
                        requiredMsg = uploader.options.requiredMsg || '请上传图片！'
                        return false
                    }
                }
            })
        }
        
        if (requiredMsg) {
            BJUI.alertmsg('info', requiredMsg)
            return
        }
        if (error4Uploader) {
            BJUI.alertmsg('info', '表单将在图片上传完成后自动提交！')
            return
        }
        
        enctype = $form.attr('enctype')
        
        // target
        if (!options.target)
            options.target = $form.closest('.bjui-layout')
        if (options.target) {
            $target = options.target
            
            if (!($target instanceof jQuery))
                $target = $($target)
        }
        if (!$target || !$target.length)
            $target = $.CurrentDialog || $.CurrentNavtab
        
        that.$target = $target
        
        options.url  = options.url || $form.attr('action')
        // for ie8
        if (BJUI.isIE(8) && !options.type) {
            if (!$form[0].getAttributeNode('method').specified) options.type = 'POST'
        }
        options.type = options.type || $form.attr('method') || 'POST'
        
        $.extend(that.options, options)
        
        if (callback) callback = callback.toFunc()
        
        var successFn = function(data, textStatus, jqXHR) {
            callback ? callback.apply(that, [data, $form]) : $.proxy(that.ajaxcallback(data), that)
        }
        var _submitFn = function() {
            var op = {loadingmask:that.options.loadingmask, type:that.options.type, url:that.options.url, callback:successFn}
            
            if (enctype && enctype == 'multipart/form-data') {
                if (window.FormData) {
                    $.extend(op, {data:new FormData($form[0]), contentType:false, processData:false}) 
                } else {
                    $.extend(op, {data:$form.serializeArray(), files:$form.find(':file'), iframe:true, processData:false})
                }
            } else {
                $.extend(op, {data:$form.serializeArray()})
            }
            
            if (that.options.ajaxTimeout) op.ajaxTimeout = that.options.ajaxTimeout
            
            $form.doAjax(op)
        }
        
        if (that.options.confirmMsg) {
            BJUI.alertmsg('confirm', that.options.confirmMsg, {okCall: _submitFn})
        } else {
            _submitFn()
        }
    }
    
    Bjuiajax.prototype.ajaxform = function(option) {
        var that     = this,
            options  = $.extend({}, typeof option === 'object' && option),
            $element = that.$element,
            $form    = null,
            valiOpts = {
                validClass : 'ok',
                msgClass   : 'n-bottom',
                theme      : 'red_bottom_effect_grid'
            }
        
        // form
        if ($element[0] === $('body')[0]) {
            $form = options.form
            
            if (!($form instanceof jQuery))
                $form = $(options.form)
        } else {
            $form   = $element
        }
        if (!$form || !$form.length || !($form.isTag('form'))) {
            BJUI.debug('BJUI.ajax: \'ajaxform\' method: Not set the form!')
            return
        }
        
        if (typeof options.validate === 'undefined')
            options.validate = true
        
        if ($.fn.validator && options.validate) {
            if (!$form.data('validator'))
                $form.validator(valiOpts)
            
            $form
                .on('valid.form', function(e) {
                    that._ajaxform($form, options)
                })
                .on('invalid.form', function(e, form, errors) {
                    if (options.alertmsg) BJUI.alertmsg('error', BJUI.regional.validatemsg.replaceMsg(errors.length))
                })
            
            $form.trigger('validate')
        } else {
            that._ajaxform($form, options)
        }
    }
    
    Bjuiajax.prototype._ajaxsearch = function($form, options) {
        var that         = this,
            $target      = null,
            beforeSubmit = options.beforeSubmit,
            callback     = options.callback,
            isSubmit     = false,
            enctype
        
        if ($form.data('holdSubmit')) {
            return
        }
        // beforeSubmit
        if (beforeSubmit) {
            if (typeof beforeSubmit === 'string')
                beforeSubmit = beforeSubmit.toFunc()
            if (typeof beforeSubmit === 'function') {
                if (!beforeSubmit.apply(that, [$form])) {
                    return
                }
            } else {
                BJUI.debug('BJUI.ajax: \'ajaxform\' options \'beforeSubmit\': Not a function!')
                return
            }
        }
        // pagination
        if ($form.data('bjui.paging')) {
            if (!options.data)
                options.data = {}
            
            $.extend(options.data, $form.data('bjui.paging'))
        }
        // search for datagrid
        if (options.searchDatagrid) {
            var $datagrid = options.searchDatagrid
            
            if (!($datagrid instanceof jQuery)) {
                $datagrid = $($datagrid)
            }
            
            if (!$datagrid || !$datagrid.length || !$datagrid.data('bjui.datagrid')) {
                BJUI.debug('BJUI.ajax: \'ajaxsearch\' method: The options \'searchDatagrid\' is not incorrect!')
                return
            }
            
            $form.data('holdSubmit', true)
            
            $datagrid.datagrid('filter', $form.serializeJson())
            
            $datagrid.on('afterLoad.bjui.datagrid', function() {
                $form.data('holdSubmit', false)
            })
            
            return
        }
        // target
        if (!options.target)
            options.target = $form.closest('.bjui-layout')
        if (options.target) {
            $target = options.target
            
            if (!($target instanceof jQuery))
                $target = $($target)
        }
        if (!$target || !$target.length)
            $target = $.CurrentDialog || $.CurrentNavtab
        
        that.$target = $target
        
        options.url  = options.url || $form.attr('action')
        // for ie8
        if (BJUI.isIE(8) && !options.type) {
            if (!$form[0].getAttributeNode('method').specified) options.type = 'POST'
        }
        options.type = options.type || $form.attr('method') || 'POST'
        
        if (!options.url) {
            BJUI.debug('BJUI.ajax: \'ajaxsearch\' method: The form\'s action or url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh($form.closest('.unitBox'))
            
            if (!options.url.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.ajax: \'ajaxsearch\' method: The form\'s action or url is incorrect: '+ options.url)
                return
            }
            
            options.url = encodeURI(options.url)
        }
        
        $.extend(that.options, options)
        
        if ($target.hasClass('bjui-layout')) {
            var data = $.extend($form.serializeJson(), options.data)
            
            that.reloadlayout({target:$target[0], type:that.options.type, url:that.options.url, data:data, loadingmask:that.options.loadingmask})
        } else {
            options.form = $form
            if ($target[0] === ($.CurrentNavtab)[0]) {
                BJUI.navtab('reloadForm', that.options.clearQuery, options)
            } else {
                BJUI.dialog('reloadForm', that.options.clearQuery, options)
            }
        }
    }
    
    Bjuiajax.prototype.ajaxsearch = function(option) {
        var that     = this,
            options  = $.extend({}, typeof option === 'object' && option),
            $element = that.$element,
            $form    = null,
            valiOpts = {
                validClass : 'ok',
                msgClass   : 'n-bottom',
                theme      : 'red_bottom_effect_grid'
            }
        
        // form
        if ($element[0] === $('body')[0]) {
            $form = options.form
            
            if (!($form instanceof jQuery)) {
                $form = $(options.form)
            }
        } else {
            $form = $element
        }
        if (!$form || !$form.length || !($form.isTag('form'))) {
            BJUI.debug('BJUI.ajax: \'ajaxsearch\' method: Not set the form!')
            return
        }
        
        if (typeof options.validate === 'undefined')
            options.validate = true
        
        if ($.fn.validator && options.validate) {
            if (!$form.data('validator'))
                $form.validator(valiOpts)
            
            $form
                .on('valid.form', function(e) {
                    that._ajaxsearch($form, options)
                })
                .on('invalid.form', function(e, form, errors) {
                    if (options.alertmsg) BJUI.alertmsg('error', BJUI.regional.validatemsg.replaceMsg(errors.length))
                })
            
            $form.trigger('validate')
        } else {
            that._ajaxsearch($form, options)
        }
    }
    
    Bjuiajax.prototype.doload = function(option) {
        var that = this, $target = null, options = that.options
            
        $.extend(options, typeof option === 'object' && option)
        
        if (options.target) {
            $target = options.target
            
            if (!($target instanceof jQuery))
                $target = $($target)
        }
        
        if (!$target || !$target.length) {
            BJUI.debug('BJUI.ajax: \'doload\' method: Not set loaded container, like [data-target].')
            return
        }
        
        if (!options.url) {
            BJUI.debug('BJUI.ajax: \'doload\' method: The url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh()
            
            if (!options.url.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.ajax: \'doload\' method: The url is incorrect: '+ options.url)
                return
            }
            
            options.url = encodeURI(options.url)
        }
        
        $target.removeData('bjui.clientPaging').data('options', options)
        that.reloadlayout(options)
    }
    
    Bjuiajax.prototype.refreshlayout = function(target) {
        var that = this, $target
        
        if (target) {
            $target = target
            
            if (!($target instanceof jQuery))
                $target = $($target)
        } else {
            $target = that.$target
        }
        
        if (!$target || !$target.length) {
            if (autorefreshTimer) clearInterval(autorefreshTimer)
            BJUI.debug('BJUI.ajax: \'refreshlayout\' method: No argument, can not refresh DIV.(parameters:[selector/jQuery object/element])')
            return
        }
        if ($target && $target.length) {
            if (!$target.data('options')) {
                BJUI.debug('BJUI.ajax: \'refreshlayout\' method: The target(DIV) is not a reload layout!')
                return
            }
            $target.removeData('bjui.clientPaging')
            that.reloadlayout($target.data('options'))
        }
    }
    
    Bjuiajax.prototype.reloadlayout = function(option) {
        var $target = null,
            options = $.extend({}, typeof option === 'object' && option),
            arefre  = options && options.autorefresh && (isNaN(String(options.autorefresh)) ? 15 : options.autorefresh)
        
        if (options.target) {
            $target = options.target
            
            if (!($target instanceof jQuery))
                $target = $($target)
        }
        
        if (!$target || !$target.length) {
            if (autorefreshTimer) clearInterval(autorefreshTimer)
            BJUI.debug('BJUI.ajax: \'refreshlayout\' method: Not set loaded container, like [data-target].')
            return
        } else {
            if (!$target.data('options')) {
                BJUI.debug('BJUI.ajax: \'refreshlayout\' method: This target(DIV) is not initialized!')
                return
            }
            options = $.extend({}, $target.data('options'), options)
        }
        
        $target
            .addClass('bjui-layout')
            .data('options', options)
            .ajaxUrl({ type:options.type, url:options.url, data:options.data, loadingmask:options.loadingmask, callback:function(html) {
                    if (BJUI.ui.clientPaging && $target.data('bjui.clientPaging'))
                        $target.pagination('setPagingAndOrderby', $target)
                    if (options.callback)
                        options.callback.apply(this, [$target, html])
                    if (autorefreshTimer)
                        clearInterval(autorefreshTimer)
                    if (arefre)
                        autorefreshTimer = setInterval(function() { $target.bjuiajax('refreshlayout', $target) }, arefre * 1000)
                    if(!$target.height()) {
                        $target.css('position', 'static')
                    }
                }
            })
    }
    
    Bjuiajax.prototype.refreshdiv = function(divid) {
        if (divid && typeof divid === 'string') {
            var arr = divid.split(',')
            
            for (var i = 0; i < arr.length; i++) {
                this.refreshlayout('#'+ arr[i])
            }
        }
    }
    
    Bjuiajax.prototype.ajaxdownload = function(option) {
        var that = this, $target, options = $.extend({}, {loadingmask: false}, typeof option === 'object' && option)
        
        $.extend(that.options, options)
        
        if (options.target) {
            $target = options.target
            
            if (!($target instanceof jQuery))
                $target = $($target)
        }
        
        if (!$target || !$target.length)
            $target = $.CurrentDialog || $.CurrentNavtab
        
        that.$target = $target
        
        if (!options.url) {
            BJUI.debug('BJUI.ajax: \'ajaxdownload\' method: The url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh($target)
            
            if (!options.url.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.ajax: \'ajaxdownload\' method: The url is incorrect: '+ options.url)
                return
            }
        }
        
        var todo = function() {
            var downloadOptions = {}
            
            downloadOptions.failCallback = function(responseHtml, url) {
                if (responseHtml.trim().startsWith('{')) responseHtml = responseHtml.toObj()
                that.ajaxdone(responseHtml)
                $target.trigger('bjui.ajaxError')
            }
            downloadOptions.prepareCallback = function(url) {
                if (options.loadingmask) {
                    $target.trigger('bjui.ajaxStart')
                }
            }
            downloadOptions.successCallback = function(url) {
                $target.trigger('bjui.ajaxStop')
            }
            
            if (options.type && !options.httpMethod)
                options.httpMethod = options.type
            
            $.extend(downloadOptions, options)
            
            if (!downloadOptions.data) downloadOptions.data = {}
            if (typeof downloadOptions.data.ajaxrequest === 'undefined') downloadOptions.data.ajaxrequest = true
            
            $.fileDownload(options.url, downloadOptions)
        }
        
        if (options.confirmMsg) {
            BJUI.alertmsg('confirm', options.confirmMsg, {
                okCall: function() {
                    todo()
                }
            })
        } else {
            todo()
        }
    }
    
    // BJUIAJAX PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments,
            property = option,
            ajax     = 'bjui.ajax',
            $body    = $('body')
        
        return this.each(function () {
            var $this   = $(this),
                options = $.extend({}, Bjuiajax.DEFAULTS, typeof option === 'object' && option),
                data    = $this.data(ajax),
                func
            
            if (!data) {
                data = new Bjuiajax(this, options)
            } else {
                if (this === $body[0]) {
                    data = new Bjuiajax(this, options)
                } else {
                    $.extend(data.options, typeof option === 'object' && option)
                }
            }
            
            $this.data(ajax, data)
            
            if (typeof property === 'string') {
                func = data[property.toLowerCase()]
                if ($.isFunction(func)) {
                    [].shift.apply(args)
                    if (!args) func()
                    else func.apply(data, args)
                }
            }
        })
    }
    
    var old = $.fn.bjuiajax
    
    $.fn.bjuiajax             = Plugin
    $.fn.bjuiajax.Constructor = Bjuiajax
    
    // BJUIAJAX NO CONFLICT
    // =================
    
    $.fn.bjuiajax.noConflict = function () {
        $.fn.bjuiajax = old
        return this
    }
    
    // NOT SELECTOR
    // ==============
    
    BJUI.ajax = function() {
        Plugin.apply($('body'), arguments)
    }
    
    // BJUIAJAX DATA-API
    // ==============
    
    $(document).on(BJUI.eventType.initUI, function(e) {
        if ($.fn.validator) {
            $(e.target).find('form[data-toggle="ajaxform"], form[data-toggle="ajaxsearch"]').each(function() {
                var $this = $(this), data = $this.data(), options = data.options,
                    valiOpts = {
                        validClass : 'ok',
                        msgClass   : 'n-bottom',
                        theme      : 'red_bottom_effect_grid'
                    }
                
                if (options) {
                    if (typeof options === 'string') options = options.toObj()
                    if (typeof options === 'object') {
                        delete data.options
                        $.extend(data, options)
                    }
                }
                if (typeof data.validate === 'undefined')
                    data.validate = true
                if (!data.validate) {
                    valiOpts.ignore = '*'
                }
                
                $this
                    .validator(valiOpts)
                    .on('valid.form', function(e) {
                        BJUI.ajax('_'+ data.toggle, $this, data)
                    })
                    .on('invalid.form', function(e, form, errors) {
                        if (data.alertmsg) BJUI.alertmsg('error', BJUI.regional.validatemsg.replaceMsg(errors.length))
                    })
            })
        }
    })
    
    $(document).on('submit.bjui.bjuiajax.data-api', 'form[data-toggle="ajaxform"], form[data-toggle="ajaxsearch"]', function(e) {
        if (!$.fn.validator) {
            var $this = $(this), data = $this.data(), options = data.options
            
            if (options) {
                if (typeof options === 'string') options = options.toObj()
                if (typeof options === 'object') {
                    delete data.options
                    $.extend(data, options)
                }
            }
            
            Plugin.call($this, '_'+ data.toggle, $this, data)
            
            e.preventDefault()
        }
    })
    
    $(document).on('click.bjui.bjuiajax.data-api', '[data-toggle="ajaxload"]', function(e) {
        var $this = $(this), data = $this.data(), options = data.options
        
        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object') {
                delete data.options
                $.extend(data, options)
            }
        }
        
        if (!data.url) data.url = $this.attr('href')
        
        Plugin.call($this, 'doload', data)
        
        e.preventDefault()
    })
    
    $(document).on(BJUI.eventType.initUI, function(e) {
        $(e.target).find('[data-toggle="autoajaxload"]').each(function() {
            var $element = $(this), data = $this.data(), options = data.options
            
            if (options) {
                if (typeof options === 'string') options = options.toObj()
                if (typeof options === 'object') {
                    delete data.options
                    $.extend(data, options)
                }
            }
            
            data.target = this
            Plugin.call($element, 'doload', data)
        })
    })
    
    $(document).on('click.bjui.bjuiajax.data-api', '[data-toggle="refreshlayout"]', function(e) {
        var $this = $(this), target = $this.data('target')
        
        Plugin.call($this, 'refreshlayout', target)
        
        e.preventDefault()
    })
    
    $(document).on('click.bjui.bjuiajax.data-api', '[data-toggle="reloadlayout"]', function(e) {
        var $this = $(this), data = $this.data()
        
        Plugin.call($this, 'reloadlayout', data)
        
        e.preventDefault()
    })
    
    $(document).on('click.bjui.bjuiajax.data-api', '[data-toggle="doajax"]', function(e) {
        var $this = $(this), data = $this.data(), options = data.options
        
        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object') {
                delete data.options
                $.extend(data, options)
            }
        }
        
        if (!data.url) data.url = $this.attr('href')
        
        Plugin.call($this, 'doajax', data)
        
        e.preventDefault()
    })
    
    $(document).on('click.bjui.bjuiajax.data-api', '[data-toggle="ajaxdownload"]', function(e) {
        var $this = $(this), data = $this.data(), options = data.options
        
        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object') {
                delete data.options
                $.extend(data, options)
            }
        }
        if (!data.url) data.url = $this.attr('href')
        
        Plugin.call($this, 'ajaxdownload', data)
        
        e.preventDefault()
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-alertmsg.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-alertmsg.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // ALERTMSG GLOBAL ELEMENTS
    // ======================
    
    var $box, $alertbg, timer
    
    $(function() {
        var INIT_ALERTMSG = function() {
            $box     = $(FRAG.alertBoxFrag).hide().html('')
            $alertbg = $(FRAG.alertBackground).hide().html('')
            $('body').append('<!-- alert msg box -->').append($box).append('<!-- alert msg box mask bg -->').append($alertbg)
        }
        
        INIT_ALERTMSG()
    })
    
    // ALERTMSG CLASS DEFINITION
    // ======================
    var Alertmsg = function(options) {
        this.options   = options
        this.tools     = this.TOOLS()
        this.clearTime = null
    }
    
    Alertmsg.DEFAULTS = {
        displayPosition : 'topcenter', // Optional 'topleft, topcenter, topright, middleleft, middlecenter, middleright, bottomleft, bottomcenter, bottomright'
        displayMode     : 'slide',     // Optional 'none, fade, slide'
        autoClose       : null,
        alertTimeout    : 3000,
        mask            : null,
        promptRequired  : 'required',
        types           : {error:'error', info:'info', warn:'warn', correct:'correct', confirm:'confirm', prompt:'prompt'},
        fas             : {error:'fa-times-circle', info:'fa-info-circle', warn:'fa-exclamation-circle', correct:'fa-check-circle', confirm:'fa-question-circle', prompt:'fa-paper-plane-o'}
    }
    
    Alertmsg.prototype.TOOLS = function() {
        var that  = this
        var tools = {
            getTitle: function(key){
                return that.options.title || BJUI.regional.alertmsg.title[key]
            },
            keydownOk: function(event) {
                if (event.which == BJUI.keyCode.ENTER) {
                    event.data.target.trigger('click')
                    return false
                }
                return true
            },
            keydownEsc: function(event) {
                if (event.which == BJUI.keyCode.ESC) event.data.target.trigger('click')
            },
            openPosition: function() {
                var position = BJUI.alertMsg.displayPosition, mode = BJUI.alertMsg.displayMode, width = 460, height = $box.outerHeight(), startCss = {}, endCss = {}
                
                if (position) {
                    if (that.options.displayPosition && that.options.displayPosition != 'topcenter')
                        position = that.options.displayPosition
                } else {
                    position = that.options.displayPosition
                }
                
                if (mode) {
                    if (that.options.displayMode && that.options.displayMode != 'slide')
                        mode = that.options.displayMode
                } else {
                    mode = that.options.displayMode
                }
                
                switch (position) {
                case 'topleft':
                    startCss = {top:0 - height, left:0, 'margin-left':0}
                    endCss   = {top:0}
                    
                    break
                case 'topcenter':
                    startCss = {top:0 - height}
                    endCss   = {top:0}
                    
                    break
                case 'topright':
                    startCss = {top:0 - height, left:'auto', right:0, 'margin-left':0}
                    endCss   = {top:0}
                    
                    break
                case 'middleleft':
                    startCss = {top:'50%', left:0 - width, 'margin-left':0, 'margin-top':0 - height/2}
                    endCss   = {left:0}
                    
                    break
                case 'middlecenter':
                    startCss = {top:'0', 'margin-top':0 - height/2}
                    endCss   = {top:'50%'}
                    
                    break
                case 'middleright':
                    startCss = {top:'50%', left:'auto', right:0 - width, 'margin-top':0 - height/2}
                    endCss   = {right:0}
                    
                    break
                case 'bottomleft':
                    startCss = {top:'auto', left:0, bottom:0 - height, 'margin-left':0}
                    endCss   = {bottom:0}
                    
                    break
                case 'bottomcenter':
                    startCss = {top:'auto', bottom:0 - height}
                    endCss   = {bottom:0}
                    
                    break
                case 'bottomright':
                    startCss = {top:'auto', left:'auto', right:0, bottom:0 - height, 'margin-left':0}
                    endCss   = {bottom:0}
                    
                    break
                }
                
                if (mode == 'slide') {
                    $box.css(startCss).show().animate(endCss, 500)
                } else if (mode == 'fade') {
                    startCss.opacity = 0.1
                    $box.css(startCss).css(endCss).show().animate({opacity:1}, 500)
                } else {
                    $box.css(startCss).css(endCss).show()
                }
            },
            closePosition: function() {
                var position = BJUI.alertMsg.displayPosition, mode = BJUI.alertMsg.displayMode, width = 460, height = $box.outerHeight(), endCss = {}
                
                if (position) {
                    if (that.options.displayPosition && that.options.displayPosition != 'topcenter')
                        position = that.options.displayPosition
                } else {
                    position = that.options.displayPosition
                }
                
                if (mode) {
                    if (that.options.displayMode && that.options.displayMode != 'slide')
                        mode = that.options.displayMode
                } else {
                    mode = that.options.displayMode
                }
                
                switch (position) {
                case 'topleft':
                    endCss   = {top:0 - height}
                    
                    break
                case 'topcenter':
                    endCss   = {top:0 - height}
                    
                    break
                case 'topright':
                    endCss   = {top:0 - height}
                    
                    break
                case 'middleleft':
                    endCss   = {left:0 - width}
                    
                    break
                case 'middlecenter':
                    endCss   = {top:0 - height}
                    
                    break
                case 'middleright':
                    endCss   = {right:0 - width}
                    
                    break
                case 'bottomleft':
                    endCss   = {bottom:0 - height}
                    
                    break
                case 'bottomcenter':
                    endCss   = {bottom:0 - height}
                    
                    break
                case 'bottomright':
                    endCss   = {bottom:0 - height}
                    
                    break
                }
                
                if (mode == 'slide') {
                    $box.animate(endCss, 500, function() {
                        $alertbg.hide()
                        $(this).hide().empty()
                    })
                } else if (mode == 'fade') {
                    $box.animate({opacity:0}, 500, function() {
                        $alertbg.hide()
                        $(this).hide().empty()
                    })
                } else {
                    $box.hide().remove()
                    $alertbg.hide()
                }
            },
            open: function(type, msg, buttons) {
                var tools = this, btnsHtml = '', $newbox, $btns, alertTimeout = BJUI.alertMsg.alertTimeout, input = ''
                
                if (buttons) {
                    for (var i = 0; i < buttons.length; i++) {
                        var sRel = buttons[i].call ? 'callback' : '',
                            sCls = buttons[i].cls  ? buttons[i].cls : 'default',
                            sIco = (buttons[i].cls && buttons[i].cls == 'green') ? 'check' : 'close'
                        
                        btnsHtml += FRAG.alertBtnFrag.replace('#btnMsg#', '<i class="fa fa-'+ sIco +'"></i> '+ buttons[i].name).replace('#callback#', sRel).replace('#class#', sCls)
                    }
                }
                if (type == that.options.types.prompt) {
                    input = '<p style="padding-top:5px;"><input type="text" class="form-control" name="'+ (that.options.promptname || 'prompt') +'" value=""></p>'
                }
                
                $newbox = 
                    $(FRAG.alertBoxFrag.replace('#type#', type)
                    .replace('#fa#', that.options.fas[type])
                    .replace('#title#', this.getTitle(type))
                    .replace('#message#', msg)
                    .replace('#prompt#', input)
                    .replace('#btnFragment#', btnsHtml))
                    .hide()
                    .appendTo('body')
                
                if ($box && $box.length) $box.remove()
                $box = $newbox
                
                tools.openPosition()
                
                if (timer) {
                    clearTimeout(timer)
                    timer = null
                }
                
                if (that.options.mask == null) {
                    if (!(that.options.types.info == type || that.options.types.correct == type))
                        $alertbg.show()
                }
                if (that.options.autoClose == null) {
                    if (that.options.types.info == type || that.options.types.correct == type) {
                        if (alertTimeout) { 
                            if (that.options.alertTimeout && that.options.alertTimeout != 3000)
                                alertTimeout = that.options.alertTimeout
                        } else {
                            alertTimeout = that.options.alertTimeout
                        }
                        timer = setTimeout(function() { tools.close() }, alertTimeout)
                    }
                }
                if (type == that.options.types.prompt) {
                    $box.find(':text').focus().val(that.options.promptval)
                }
                
                $btns = $box.find('.btn')
                
                $btns.each(function(i) {
                    $(this).on('click', $.proxy(function() {
                            if (type !== that.options.types.prompt || $btns.eq(i).hasClass('btn-red'))
                                that.tools.close()
                            
                            var call = buttons[i].call
                            
                            if (typeof call === 'string')   call = call.toFunc() 
                            if (typeof call === 'function') {
                                if (type == that.options.types.prompt) {
                                    var $input = $box.find('input'), value = $input.val()
                                    
                                    $input.focus(function() {
                                        $box.removeClass('has-error')
                                    })
                                    
                                    if (that.options.promptRequired === 'required' && !value) {
                                        $box.addClass('has-error')
                                    } else {
                                        that.tools.close()
                                        call.call(that, value)
                                    }
                                } else {
                                    call.call()
                                }
                            }
                        }, that)
                    )
                    
                    if (buttons[i].keyCode === BJUI.keyCode.ENTER) {
                        $(document).on('keydown.bjui.alertmsg.ok', {target:$btns.eq(i)}, tools.keydownOk)
                    }
                    if (buttons[i].keyCode === BJUI.keyCode.ESC) {
                        $(document).on('keydown.bjui.alertmsg.esc', {target:$btns.eq(i)}, tools.keydownEsc)
                    }
                })
            },
            alert: function(type, msg, btnoptions) {
                $.extend(that.options, typeof btnoptions === 'object' && btnoptions)
                
                var op      = $.extend({}, {okName:BJUI.regional.alertmsg.btnMsg.ok, okCall:null}, that.options)
                var buttons = [
                    {name:op.okName, call:op.okCall, cls:'default', keyCode:BJUI.keyCode.ENTER}
                ]
                
                this.open(type, msg, buttons)
            },
            close: function() {
                $(document).off('keydown.bjui.alertmsg.ok').off('keydown.bjui.alertmsg.esc')
                
                this.closePosition()
            }
        }
        
        return tools
    }
    
    Alertmsg.prototype.error = function(msg, btnoptions) {
        this.tools.alert(this.options.types.error, msg, btnoptions)
    }
    
    Alertmsg.prototype.info = function(msg, btnoptions) {
        this.tools.alert(this.options.types.info, msg, btnoptions)
    }
    
    Alertmsg.prototype.warn = function(msg, btnoptions) {
        this.tools.alert(this.options.types.warn, msg, btnoptions)
    }
    
    Alertmsg.prototype.ok = function(msg, btnoptions) {
        this.tools.alert(this.options.types.correct, msg, btnoptions)
    }
    
    Alertmsg.prototype.correct = function(msg, btnoptions) {
        this.tools.alert(this.options.types.correct, msg, btnoptions)
    }
    
    Alertmsg.prototype.confirm = function(msg, btnoptions) {
        $.extend(this.options, typeof btnoptions === 'object' && btnoptions)
        
        var op      = $.extend({}, {okName:BJUI.regional.alertmsg.btnMsg.ok, okCall:null, cancelName:BJUI.regional.alertmsg.btnMsg.cancel, cancelCall:null}, this.options)
        var buttons = [
            {name:op.okName, call:op.okCall, cls:'green', keyCode:BJUI.keyCode.ENTER},
            {name:op.cancelName, call:op.cancelCall, cls:'red', keyCode:BJUI.keyCode.ESC}
        ]
        
        this.tools.open(this.options.types.confirm, msg, buttons)
    }
    
    Alertmsg.prototype.prompt = function(msg, btnoptions) {
        $.extend(this.options, typeof btnoptions === 'object' && btnoptions)
        
        var that = this, op = $.extend({}, {okName:BJUI.regional.alertmsg.btnMsg.ok, okCall:null, cancelName:BJUI.regional.alertmsg.btnMsg.cancel, cancelCall:null}, this.options)
        
        if (op.okCall == null && (typeof that.options.prompt === 'object' && that.options.prompt)) {
            op.okCall = function(val) {
                var data = {}
                
                data[(that.options.promptname || 'prompt')] = val
                
                if (!that.options.prompt.data)
                    that.options.prompt.data = data
                else
                    $.extend(that.options.prompt.data, data)
                    
                BJUI.ajax('doajax', that.options.prompt)
            }
        }
        
        var buttons = [
            {name:op.okName, call:op.okCall, cls:'green', keyCode:BJUI.keyCode.ENTER},
            {name:op.cancelName, call:op.cancelCall, cls:'red', keyCode:BJUI.keyCode.ESC}
        ]
        
        this.tools.open(this.options.types.prompt, msg, buttons)
    }
    
    // ALERTMSG PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments,
            property = option,
            alertmsg = 'bjui.alertmsg',
            $body    = $('body'),
            data     = $body.data(alertmsg)
        
        return this.each(function () {
            var $this   = $(this),
                options = $.extend({}, Alertmsg.DEFAULTS, typeof option === 'object' && option)
            
            if (!data) {
                data = new Alertmsg(options)
            } else {
                data.options = options
            }
            
            $body.data(alertmsg, data)
            
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            }
        })
    }
    
    var old = $.fn.alertmsg
    
    $.fn.alertmsg             = Plugin
    $.fn.alertmsg.Constructor = Alertmsg
    
    // ALERTMSG NO CONFLICT
    // =================
    
    $.fn.alertmsg.noConflict = function () {
        $.fn.alertmsg = old
        return this
    }
    
    // NOT SELECTOR
    // ==============
    
    BJUI.alertmsg = function() {
        Plugin.apply($('body'), arguments)
    }
    
    // NAVTAB DATA-API
    // ==============
    
    $(document).on('click.bjui.alertmsg.data-api', '[data-toggle="alertmsg"]', function(e) {
        var $this = $(this), data = $this.data(), options = data.options, type
        
        if (options) {
            if (typeof options === 'string') options = options.toObj()
            if (typeof options === 'object') {
                $.extend(data, options)
            }
        }
        
        type = data.type
        if (!type) return false
        if (!data.msg) {
            if (options.msg) data.msg = options.msg
        }
        
        Plugin.call($this, type, data.msg || type, data)
        
        e.preventDefault()
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-pagination.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-pagination.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // PAGINATION CLASS DEFINITION
    // ======================
    
    var Pagination = function(element, options) {
        this.$element = $(element)
        this.options  = options
        this.tools    = this.TOOLS()
    }
    
    Pagination.DEFAULTS = {
        width          : '100%',
        pageSize       : 30,
        selectPageSize : '30,60,90',
        pageCurrent    : 1,
        total          : 0,
        showPagenum    : 5,
        form           : null,
        callback       : null
    }
    
    Pagination.prototype.TOOLS = function() {
        var that    = this
        var tools   = {
            getPageCount: function(pageSize, total) {
                return Math.ceil(total / pageSize)
            },
            getPageInterval: function(count, pageCurrent, showPageNum) {
                var half  = Math.ceil(showPageNum / 2), limit = count - showPageNum,
                    start = pageCurrent > half ? Math.max(Math.min(pageCurrent - half, limit), 0) : 0,
                    end   = pageCurrent > half ? Math.min((pageCurrent + half), count) : Math.min(showPageNum, count)
                
                if (end - start == showPageNum) end = end + 1
                if (end < showPageNum) end = end + 1
                if (start + 1 == end) end = end + 1
                
                return {start:start + 1, end:end}
            },
            // jump to page
            jumpPage: function(pageCurrent, pageSize) {
                var callback = that.options.callback
                
                if (typeof callback === 'string')
                    callback = callback.toFunc()
                
                if (pageCurrent) {
                    that.options.pageCurrent = pageCurrent
                }
                if (pageSize) {
                    that.options.pageSize = pageSize
                    that.pageCount = this.getPageCount(pageSize, that.options.total)
                    
                    if (that.options.pageCurrent > that.pageCount)
                        that.options.pageCurrent = that.pageCount
                }
                
                if (callback && typeof callback === 'function') {
                    callback.call(that, that.options.pageCurrent, pageSize)
                } else {
                    this.setPaging(that.options.pageCurrent, pageSize, true)
                }
                
                that.$element.trigger('bjui.pagination.jump')
            },
            setPaging: function(pageCurrent, pageSize, isSubmit) {
                var $form = that.options.form, paging = {}
                
                if ($form) {
                    if (!($form instanceof jQuery)) {
                        $form = $($form)
                    }
                    if (!$form || !$form.length) {
                        BJUI.debug('BJUI.Pagination: The options \'form\' is not incorrect!')
                        return
                    }
                    
                    paging[BJUI.pageInfo.pageCurrent] = pageCurrent
                    paging[BJUI.pageInfo.pageSize]    = pageSize
                    
                    $form.data('bjui.paging', paging)
                    
                    if (isSubmit)
                        $form.trigger('submit')
                }
            }
        }
        return tools
    }
    
    Pagination.prototype.jumpPage = function(pageCurrent, pageSize) {
        var that = this, options = that.options, pageCount = that.pageCount
        
        if (pageCurrent && isNaN(pageCurrent)) return
        if (pageSize && isNaN(pageSize))       return
        if (pageCurrent) {
            pageCurrent = parseInt(pageCurrent, 10)
            
            if (pageCurrent < 1)         pageCurrent = 1
            if (pageCurrent > pageCount) pageCurrent = pageCount
            if (pageCurrent == options.pageCurrent) return
        }
        if (pageSize) {
            pageSize = parseInt(pageSize, 10)
        }
        
        that.tools.jumpPage(pageCurrent || options.pageCurrent, pageSize || options.pageSize)
    }
    
    Pagination.prototype.init = function() {
        var that = this, tools = that.tools, options = that.options, pr = BJUI.regional.pagination, btnpaging = FRAG.gridPaging, pageNums = [], interval, selectPages = [], pagingHtml = BJUI.StrBuilder()
        
        that.pageCount = tools.getPageCount(options.pageSize, options.total)
        
        interval = tools.getPageInterval(that.pageCount, options.pageCurrent, options.showPagenum)
        
        for (var i = interval.start; i < interval.end; i++) {
            pageNums.push(FRAG.gridPageNum.replace('#num#', i).replace('#active#', (options.pageCurrent == i ? ' active' : '')))
        }
        
        btnpaging = BJUI.doRegional(btnpaging.replaceAll('#pageCurrent#', options.pageCurrent).replaceAll('#count#', options.total +'/'+ parseInt((that.pageCount || 0), 10)), pr)
        
        pagingHtml
            .add('<div class="paging-content">')
            .add('<span></span><div class="paging-pagesize"><button type="button" class="btn-default btn-refresh" title="'+ pr.refresh +'" data-icon="refresh"></button>')
            .add('<select data-toggle="selectpicker"></select>')
            .add('</div>')
            .add('<div class="paging-box">')
            .add(btnpaging.replace('#pageNumFrag#', pageNums.join('')))
            .add('</div>')
            .add('</div>')
        
        that.$element.addClass('bjui-paging-box').html(pagingHtml.toString())
        that.$element.find('> .paging-content').width(options.width).initui()
        
        tools.setPaging(options.pageCurrent, options.pageSize)
        
        //events
        var $select    = that.$element.find('div.paging-pagesize > select'),
            $pagenum   = that.$element.find('ul.pagination'),
            $pagetotal = $pagenum.find('> li.page-total'),
            $jumpto    = $pagetotal.next(),
            $first     = $jumpto.next(),
            $prev      = $first.next(),
            $next      = $prev.nextAll('.page-next'),
            $last      = $next.next()
        
        var disablePrev = function() {
            $first.addClass('disabled')
            $prev.addClass('disabled')
        }
        var enablePrev = function() {
            $first.removeClass('disabled')
            $prev.removeClass('disabled')
        }
        var disableNext = function() {
            $next.addClass('disabled')
            $last.addClass('disabled')
        }
        var enableNext = function() {
            $next.removeClass('disabled')
            $last.removeClass('disabled')
        }
        var pageFirst = function() {
            disablePrev()
            enableNext()
        }
        var pageLast = function() {
            enablePrev()
            disableNext()
        }
        var setPageSize = function(pageSize) {
            $select.empty()
            
            if (!pageSize) pageSize = that.options.pageSize
            
            selectPages = options.selectPageSize.split(',')
            selectPages.push(String(pageSize))
            
            $.unique(selectPages).sort(function(a, b) { return a - b })
            
            var opts = []
            
            $.each(selectPages, function(i, n) {
                opts.push('<option value="'+ n +'"'+ (n == options.pageSize && 'selected') +'>'+ n +'/'+ pr.page +'</option>')
            })
            
            $select.html(opts.join('')).selectpicker('refresh')
        }
        
        if (options.pageCurrent == 1) pageFirst()
        if (options.pageCurrent == that.pageCount) {
            pageLast()
            if (options.pageCurrent == 1) disablePrev()
        }
        if (!options.total) disableNext()
        setPageSize()
        
        that.$element.on('click.bjui.pagination.pagenum', 'li.page-num', function(e) {
            var $num = $(this)
            
            if (!$num.hasClass('active')) {
                that.jumpPage($num.text())
            }
            
            e.preventDefault()
        }).on('click.bjui.pagination.refresh', 'button.btn-refresh', function() {
            that.jumpPage()
        }).on('bjui.pagination.jump', function(e) {
            var pageCurrent = that.options.pageCurrent, interval = tools.getPageInterval(that.pageCount, pageCurrent, options.showPagenum), pageNums = []
            
            for (var i = interval.start; i < interval.end; i++) {
                pageNums.push(FRAG.gridPageNum.replace('#num#', i).replace('#active#', (pageCurrent == i ? ' active' : '')))
            }
            
            $pagenum.find('> li.page-num').remove()
            $prev.after(pageNums.join(''))
            
            if (pageCurrent == 1) {
                pageFirst()
                if (pageCurrent == that.pageCount) disableNext()
                if (!that.options.total) disableNext()
            } else if (pageCurrent == that.pageCount) {
                pageLast()
            } else {
                enablePrev()
                enableNext()
            }
            
            $jumpto.find('input').val(pageCurrent)
            $pagetotal.find('> span').html(that.options.total +'/'+ that.pageCount)
        }).on('bjui.pagination.pageSize', function(e, pageSize) {
            setPageSize(pageSize)
        }).on('change', 'div.paging-pagesize > select', function() {
            var pageSize = $(this).val()
            
            that.pageCount = tools.getPageCount(pageSize, options.total)
            
            that.jumpPage(null, pageSize)
        })
        
        $jumpto.find('input').on('keyup', function(e) {
            if (e.which === BJUI.keyCode.ENTER) {
                var page = $(this).val()
                
                if (page) that.jumpPage(page)
            }
        })
        
        $first.on('click', function() {
            if (that.options.pageCurrent > 1) 
                that.jumpPage(1)
        })
        
        $prev.on('click', function() {
            if (that.options.pageCurrent > 1)
                that.jumpPage(that.options.pageCurrent - 1)
        })
        
        $next.on('click', function() {
            if (that.options.pageCurrent < that.pageCount)
                that.jumpPage(that.options.pageCurrent + 1)
        })
        
        $last.on('click', function() {
            if (that.options.pageCurrent < that.pageCount)
                that.jumpPage(that.pageCount)
        })
    }
    
    Pagination.prototype.destroy = function() {
        this.$element.removeData('bjui.pagination').empty()
    }
    
    // PAGINATION PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments,
            property = option
        
        return this.each(function () {
            var $this   = $(this),
                options = $.extend({}, Pagination.DEFAULTS, $this.data(), typeof option === 'object' && option),
                data    = $this.data('bjui.pagination')
            
            if (!data) $this.data('bjui.pagination', (data = new Pagination(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }
    
    var old = $.fn.pagination
    
    $.fn.pagination             = Plugin
    $.fn.pagination.Constructor = Pagination
    
    // PAGINATION NO CONFLICT
    // =================
    
    $.fn.pagination.noConflict = function () {
        $.fn.pagination = old
        return this
    }
    
    // PAGINATION DATA-API
    // ==============
    
    $(document).on(BJUI.eventType.initUI, function(e) {
        $(e.target).find('[data-toggle="pagination"]').each(function() {
            var $this = $(this), data = $this.data(), options = data.options
            
            if (options) {
                if (typeof options === 'string') options = options.toObj()
                if (typeof options === 'object') {
                    delete data.options
                    $.extend(data, options)
                }
            }
            
            Plugin.call($this, data)
        })
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-util.date.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-util.date.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    var MONTH_NAMES = new Array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')

    var DAY_NAMES   = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')

    function LZ(x) {
        return (x < 0 || x > 9 ? '' :'0') + x
    }

    /**
         * formatDate (date_object, format)
         * Returns a date in the output format specified.
         * The format string uses the same abbreviations as in parseDate()
         * @param {Object} date
         * @param {Object} format
         */
    function formatDate(date, format) {
        format       = format + ''
        var result   = ''
        var i_format = 0
        var c        = ''
        var token    = ''
        var y        = date.getYear() + ''
        var M        = date.getMonth() + 1
        var d        = date.getDate()
        var E        = date.getDay()
        var H        = date.getHours()
        var m        = date.getMinutes()
        var s        = date.getSeconds()
        var yyyy, yy, MMM, MM, dd, hh, h, mm, ss, ampm, HH, H, KK, K, kk, k
        // Convert real date parts into formatted versions
        var value = {}
        
        if (y.length < 4) {
            y = '' + (y - 0 + 1900)
        }
        value['y']    = '' + y
        value['yyyy'] = y
        value['yy']   = y.substring(2, 4)
        value['M']    = M
        value['MM']   = LZ(M)
        value['MMM']  = MONTH_NAMES[M - 1]
        value['NNN']  = MONTH_NAMES[M + 11]
        value['d']    = d
        value['dd']   = LZ(d)
        value['E']    = DAY_NAMES[E + 7]
        value['EE']   = DAY_NAMES[E]
        value['H']    = H
        value['HH']   = LZ(H)
        
        if (!H) {
            value['h'] = 12
        } else if (H > 12) {
            value['h'] = H - 12
        } else {
            value['h'] = H
        }
        value['hh'] = LZ(value['h'])
        
        if (H > 11) {
            value['K'] = H - 12
        } else {
            value['K'] = H
        }
        value['k']  = H + 1
        value['KK'] = LZ(value['K'])
        value['kk'] = LZ(value['k'])
        
        if (H > 11) {
            value['a'] = 'PM'
        } else {
            value['a'] = 'AM'
        }
        value['m']  = m
        value['mm'] = LZ(m)
        value['s']  = s
        value['ss'] = LZ(s)
        
        while (i_format < format.length) {
            c     = format.charAt(i_format)
            token = ''
            
            while (format.charAt(i_format) == c && i_format < format.length) {
                token += format.charAt(i_format++)
            }
            if (value[token] != null) {
                result += value[token]
            } else {
                result += token
            }
        }
        return result
    }

    function _isInteger(val) {
        return new RegExp(/^\d+$/).test(val)
    }

    function _getInt(str, i, minlength, maxlength) {
        for (var x = maxlength; x >= minlength; x--) {
            var token = str.substring(i, i + x)
            
            if (token.length < minlength) {
                return null
            }
            if (_isInteger(token)) {
                return token
            }
        }
        return null
    }

    /**
         * parseDate( date_string , format_string )
         * 
         * This function takes a date string and a format string. It matches
         * If the date string matches the format string, it returns the date. 
         * If it does not match, it returns 0.
         * @param {Object} val
         * @param {Object} format
         */
    function parseDate(val, format) {
        val          = val + ''
        format       = format + ''
        
        var i_val    = 0
        var i_format = 0
        var c        = ''
        var token    = ''
        var token2   = ''
        var x, y
        var now      = new Date(1900, 0, 1)
        var year     = now.getYear()
        var month    = now.getMonth() + 1
        var date     = 1
        var hh       = now.getHours()
        var mm       = now.getMinutes()
        var ss       = now.getSeconds()
        var ampm     = ''
        
        while (i_format < format.length) {
            // Get next token from format string
            c     = format.charAt(i_format)
            token = ''
            while (format.charAt(i_format) == c && i_format < format.length) {
                token += format.charAt(i_format++)
            }
            // Extract contents of value based on format token
            if (token == 'yyyy' || token == 'yy' || token == 'y') {
                if (token == 'yyyy') {
                    x = 4
                    y = 4
                }
                if (token == 'yy') {
                    x = 2
                    y = 2
                }
                if (token == 'y') {
                    x = 2
                    y = 4
                }
                year = _getInt(val, i_val, x, y)
                if (year == null) {
                    return 0
                }
                i_val += year.length
                if (year.length == 2) {
                    if (year > 70) {
                        year = 1900 + (year - 0)
                    } else {
                        year = 2e3 + (year - 0)
                    }
                }
            } else if (token == 'MMM' || token == 'NNN') {
                month = 0
                for (var i = 0; i < MONTH_NAMES.length; i++) {
                    var month_name = MONTH_NAMES[i]
                    
                    if (val.substring(i_val, i_val + month_name.length).toLowerCase() == month_name.toLowerCase()) {
                        if (token == 'MMM' || token == 'NNN' && i > 11) {
                            month = i + 1
                            if (month > 12) {
                                month -= 12
                            }
                            i_val += month_name.length
                            break
                        }
                    }
                }
                if (month < 1 || month > 12) {
                    return 0
                }
            } else if (token == 'EE' || token == 'E') {
                for (var i = 0; i < DAY_NAMES.length; i++) {
                    var day_name = DAY_NAMES[i]
                    
                    if (val.substring(i_val, i_val + day_name.length).toLowerCase() == day_name.toLowerCase()) {
                        i_val += day_name.length
                        break
                    }
                }
            } else if (token == 'MM' || token == 'M') {
                month = _getInt(val, i_val, token.length, 2)
                if (month == null || month < 1 || month > 12) {
                    return 0
                }
                i_val += month.length
            } else if (token == 'dd' || token == 'd') {
                date = _getInt(val, i_val, token.length, 2)
                if (date == null || date < 1 || date > 31) {
                    return 0
                }
                i_val += date.length
            } else if (token == 'hh' || token == 'h') {
                hh = _getInt(val, i_val, token.length, 2)
                if (hh == null || hh < 1 || hh > 12) {
                    return 0
                }
                i_val += hh.length
            } else if (token == 'HH' || token == 'H') {
                hh = _getInt(val, i_val, token.length, 2)
                if (hh == null || hh < 0 || hh > 23) {
                    return 0
                }
                i_val += hh.length
            } else if (token == 'KK' || token == 'K') {
                hh = _getInt(val, i_val, token.length, 2)
                if (hh == null || hh < 0 || hh > 11) {
                    return 0
                }
                i_val += hh.length
            } else if (token == 'kk' || token == 'k') {
                hh = _getInt(val, i_val, token.length, 2)
                if (hh == null || hh < 1 || hh > 24) {
                    return 0
                }
                i_val += hh.length
                hh--
            } else if (token == 'mm' || token == 'm') {
                mm = _getInt(val, i_val, token.length, 2)
                if (mm == null || mm < 0 || mm > 59) {
                    return 0
                }
                i_val += mm.length
            } else if (token == 'ss' || token == 's') {
                ss = _getInt(val, i_val, token.length, 2)
                if (ss == null || ss < 0 || ss > 59) {
                    return 0
                }
                i_val += ss.length
            } else if (token == 'a') {
                if (val.substring(i_val, i_val + 2).toLowerCase() == 'am') {
                    ampm = 'AM'
                } else if (val.substring(i_val, i_val + 2).toLowerCase() == 'pm') {
                    ampm = 'PM'
                } else {
                    return 0
                }
                i_val += 2
            } else {
                if (val.substring(i_val, i_val + token.length) != token) {
                    return 0
                } else {
                    i_val += token.length
                }
            }
        }
        // If there are any trailing characters left in the value, it doesn't match
        if (i_val != val.length) {
            return 0
        }
        // Is date valid for month?
        if (month == 2) {
            // Check for leap year
            if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
                // leap year
                if (date > 29) {
                    return 0
                }
            } else {
                if (date > 28) {
                    return 0
                }
            }
        }
        if (month == 4 || month == 6 || month == 9 || month == 11) {
            if (date > 30) {
                return 0
            }
        }
        // Correct hours value
        if (hh < 12 && ampm == 'PM') {
            hh = hh - 0 + 12
        } else if (hh > 11 && ampm == 'AM') {
            hh -= 12
        }
        
        return new Date(year, month - 1, date, hh, mm, ss)
    }

    Date.prototype.formatDate = function(dateFmt) {
        return formatDate(this, dateFmt)
    }

    String.prototype.parseDate = function(dateFmt) {
        if (this.length < dateFmt.length) {
            dateFmt = dateFmt.slice(0, this.length)
        }
        return parseDate(this, dateFmt)
    }

    /**
     * replaceTmEval('{1+2}-{2-1}')
     */
    function replaceTmEval(data) {
        return data.replace(RegExp('({[A-Za-z0-9_+-]*})', 'g'), function($1) {
            return eval('(' + $1.replace(/[{}]+/g, '') + ')')
        })
    }

    /**
     * dateFmt:%y-%M-%d
     * %y-%M-{%d+1}
     * ex: new Date().formatDateTm('%y-%M-{%d-1}')
     *     new Date().formatDateTm('2012-1')
     */
    Date.prototype.formatDateTm = function(dateFmt) {
        var y = this.getFullYear()
        var m = this.getMonth() + 1
        var d = this.getDate()
        var sDate = dateFmt.replaceAll('%y', y).replaceAll('%M', m).replaceAll('%d', d)
        
        sDate = replaceTmEval(sDate)
        
        var _y = 1900, _m = 0, _d = 1
        var aDate = sDate.split('-')
        
        if (aDate.length > 0) _y = aDate[0]
        if (aDate.length > 1) _m = aDate[1] - 1
        if (aDate.length > 2) _d = aDate[2]
        
        return new Date(_y, _m, _d).formatDate('yyyy-MM-dd')
    }
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-datepicker.js  V1.31
 * reference: util.date.js
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-datepicker.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';

    // DATEPICKER GLOBAL ELEMENTS
    // ======================
    
    var $box, $main, $prev, $next, $year, $month, $time, $timeinps, $spinner, $hh, $mm, $ss, $tm, $close, $days, $dayNames, $clearBtn, $okBtn
    
    $(function() {
        var INIT_DATEPICKER = function() {
            var cp       = BJUI.regional.datepicker
            var calendar = FRAG.calendarFrag
                .replace('#close#', cp.close)
                .replace('#prev#', cp.prev)
                .replace('#next#', cp.next)
                .replace('#clear#', cp.clear)
                .replace('#ok#', cp.ok)
            
            $box      = $(calendar).hide()
            $('body').append('<!-- datepicker -->').append($box)
            $main     = $box.find('> .main')
            $prev     = $box.find('a.prev')
            $next     = $box.find('a.next')
            $year     = $box.find('select[name=year]')
            $month    = $box.find('select[name=month]')
            $time     = $box.find('.time')
            $timeinps = $time.find(':text')
            $spinner  = $time.find('ul > li')
            $hh       = $time.find('.hh')
            $mm       = $time.find('.mm')
            $ss       = $time.find('.ss')
            $tm       = $main.find('> .tm')
            $close    = $box.find('.close')
            $days     = $main.find('> .body > .days')
            $dayNames = $main.find('> .body > .dayNames')
            $clearBtn = $box.find('.clearBtn')
            $okBtn    = $box.find('.okBtn')
            
            //regional
            var dayNames = '', dr = BJUI.regional.datepicker
            
            $.each(dr.dayNames, function(i, v) {
                dayNames += '<dt>'+ v +'</dt>'
            })
            $dayNames.html(dayNames)
            $.each(dr.monthNames, function(i, v) {
                var m = i + 1
                
                $month.append('<option value="'+ m +'">'+ v +'</option>')
            })
        
            $box.on('selectstart', function() { return false })
        }
        
        INIT_DATEPICKER()
    })
    
    // DATEPICKER CLASS DEFINITION
    // ======================
    var Datepicker = function(element, options) {
        this.$element = $(element)
        this.options  = options
        this.tools    = this.TOOLS()
        this.$dateBtn = null
                
        // minDate、maxDate
        var now = new Date()
        
        this.options.minDate = now.formatDateTm(this.options.minDate)
        this.options.maxDate = now.formatDateTm(this.options.maxDate)
        
        //events
        this.events = {
            focus_time    : 'focus.bjui.datepicker.time',
            click_prev    : 'click.bjui.datepicker.prev',
            click_next    : 'click.bjui.datepicker.next',
            click_ok      : 'click.bjui.datepicker.ok',
            click_days    : 'click.bjui.datepicker.days',
            click_clear   : 'click.bjui.datepicker.clear',
            click_close   : 'click.bjui.datepicker.close',
            click_tm      : 'click.bjui.datepicker.tm',
            click_spinner : 'click.bjui.datepicker.spinner',
            mousedown_sp  : 'mousedown.bjui.datepicker.spinner',
            mouseup_sp    : 'mouseup.bjui.datepicker.spinner',
            change_ym     : 'change.bjui.datepicker.ym',
            click_time    : 'click.bjui.datepicker.time',
            keydown_time  : 'keydown.bjui.datepicker.time',
            keyup_time    : 'keyup.bjui.datepicker.time'
        }
    }
    
    Datepicker.DEFAULTS = {
        pattern : 'yyyy-MM-dd',
        minDate : '1900-01-01',
        maxDate : '2099-12-31',
        mmStep  : 1,
        ssStep  : 1
    }
    
    Datepicker.EVENTS = {
        afterChange : 'afterchange.bjui.datepicker'
    }
    
    Datepicker.prototype.TOOLS = function() {
        var that  = this
        var tools = {
            changeTmMenu: function(sltClass) {
                $tm.removeClass('hh').removeClass('mm').removeClass('ss')
                if (sltClass) {
                    $tm.addClass(sltClass)
                    $timeinps.removeClass('slt').filter('.'+ sltClass).addClass('slt')
                }
            },
            clickTmMenu: function($input, type) {
                $tm
                    .find('> ul')
                    .hide()
                    .filter('.'+ type)
                    .show()
                    .find('> li')
                    .off(that.events.click_tm)
                    .on(that.events.click_tm, function() {
                        var $li = $(this)
                        var val = parseInt($li.text()) < 10 ? ('0'+ $li.text()) : $li.text()
                        
                        $input.val(val)
                    })
            },
            keydownInt: function(e) {
                if (!((e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode == BJUI.keyCode.DELETE || e.keyCode == BJUI.keyCode.BACKSPACE))) { return false }
            },
            changeTm: function($input, $btn) {
                var ivalue = parseInt($input.val()), istart = parseInt($input.data('start')) || 0, iend = parseInt($input.data('end'))
                var istep  = parseInt($input.data('step') || 1)
                var type   = $btn ? ($btn.data('add') ? $btn.data('add') : -1) : 0
                var newVal = ivalue
                
                if (type == 1) {
                    if (ivalue <= iend - istep) 
                        newVal = ivalue + istep
                } else if (type == -1) {
                    if (ivalue >= (istart + istep))
                        newVal = ivalue - istep
                } else if (ivalue > iend) {
                    newVal = iend
                } else if (ivalue < istart) {
                    newVal = istart
                }
                if (newVal < 10) newVal = '0'+ newVal
                $input.val(newVal)
            },
            closeCalendar: function(flag) {
                tools.changeTmMenu()
                if (flag) {
                    $(document).off(that.events.click_close)
                    $box.hide()
                }
            },
            get: function(name) {
                return that.options[name]
            },
            getDays: function (y, m) {
                return m == 2 ? (y % 4 || (!(y % 100) && y % 400) ? 28 : 29) : (/4|6|9|11/.test(m) ? 30 : 31)
            },
            minMaxDate: function(sDate) {
                var _count  = sDate.split('-').length - 1
                var _format = 'y-M-d'
                
                if (_count == 1) _format = 'y-M'
                else if (!_count) _format = 'y'
                
                return sDate.parseDate(_format)
            },
            getMinDate: function() {
                return this.minMaxDate(that.options.minDate)
            },
            getMaxDate: function() {
                var _sDate = that.options.maxDate
                var _count = _sDate.split('-').length - 1
                var _date  = this.minMaxDate(_sDate)
                
                if (_count < 2) { //format:y-M、y
                    var _day = this.getDays(_date.getFullYear(), _date.getMonth() + 1)
                    _date.setDate(_day)
                    if (!_count)//format:y
                        _date.setMonth(11)
                }
                
                return _date
            },
            getDateWrap: function(date) {
                if (!date) date = this.parseDate(that.sDate) || new Date()
                
                var y = date.getFullYear(),
                    m = date.getMonth() + 1,
                    days = this.getDays(y, m)
                
                return {
                    year: y, month: m, day: date.getDate(),
                    hour: date.getHours(), minute: date.getMinutes(), second: date.getSeconds(),
                    days: days, date: date
                }
            },
            changeDate: function(y, m, d) {
                var dw = this.getDateWrap(), d = d || 1, date
                
                date = new Date(y, m - 1, d)
                
                if (date.getMonth() + 1 != m) {
                    date = new Date(y, date.getMonth(), 0)
                    date.setMonth(m - 1)
                }
                that.sDate = this.formatDate(date)
                
                return date
            },
            changeDateTime: function(y, M, d, H, m, s) {
                var date    = new Date(y, M - 1, d, H, m, s)
                
                if (date.getMonth() + 1 != M) {
                    date = new Date(y, date.getMonth(), 0, H, m, s)
                    date.setMonth(M - 1)
                }
                that.sDate = this.formatDate(date)
                return date
            },
            changeDay: function(day, chMonth) {
                if (!chMonth) chMonth = 0
                var dw = this.getDateWrap()
                
                return this.changeDate(dw.year, dw.month + parseInt(chMonth), day)
            },
            changeMonth: function(type) {
                var yearIndex = $year.get(0).selectedIndex,
                    maxYear   = $year.find('option').length,
                    month     = ($month.val() * 1) + type
                
                if (!month) {
                    if (!yearIndex) {
                        month = 1
                    } else {
                        month = 12
                        yearIndex--
                        $year.get(0).selectedIndex = yearIndex
                    }
                } else if (month == 13) {
                    if (yearIndex == (maxYear - 1)) {
                        month = 12
                    } else {
                        month = 1
                        yearIndex++
                        $year.get(0).selectedIndex = yearIndex
                    }
                }
                $month.val(month).change()
            },
            parseDate: function(sDate) {
                if (!sDate) return null
                return sDate.parseDate(that.options.pattern)
            },
            formatDate: function(date) {
                return date.formatDate(that.options.pattern)
            },
            hasHour: function() {
                return that.options.pattern.indexOf('H') != -1
            },
            hasMinute: function() {
                return that.options.pattern.indexOf('m') != -1
            },
            hasSecond: function() {
                return that.options.pattern.indexOf('s') != -1
            },
            hasTime: function() {
                return this.hasHour() || this.hasMinute() || this.hasSecond()
            },
            hasDate: function() {
                var _dateKeys = ['y','M','d','E']
                
                for (var i = 0; i < _dateKeys.length; i++) {
                    if (that.options.pattern.indexOf(_dateKeys[i]) != -1) return true
                }
                return false
            },
            afterChange: function(date) {
                var value = date ? this.formatDate(date) : ''
                
                that.$element
                    .trigger(Datepicker.EVENTS.afterChange, {date:date, value:value})
                    .trigger('change')
            }
        }
        return tools
    }
    
    Datepicker.prototype.addBtn = function() {
        var that = this, $element = that.$element, fluid = $element.attr('size') ? '' : ' fluid'
        
        if ($element.data('nobtn')) return
        
        if (!this.$dateBtn && !this.options.addbtn && !$element.parent().hasClass('wrap_bjui_btn_box')) {
            this.$dateBtn = $(FRAG.dateBtn)
            this.$element.css({'paddingRight':'15px'}).wrap('<span class="wrap_bjui_btn_box'+ fluid +'"></span>')
            
            var $box   = this.$element.parent()
            var height = this.$element.addClass('form-control').innerHeight()
            
            $box.css({'position':'relative', 'display':'inline-block'})
            
            this.$dateBtn.css({'height':height, 'lineHeight':height +'px'}).appendTo($box)
            this.$dateBtn.on('selectstart', function() { return false })
        }
    }
    
    Datepicker.prototype.init = function() {
        if (!this.$element.is(':text')) return
        if (this.$element.val()) this.sDate = this.$element.val().trim()
        
        var that      = this,
            options   = this.options,
            tools     = this.tools,
            dw        = tools.getDateWrap(),
            minDate   = tools.getMinDate(), maxDate = tools.getMaxDate(),
            yearstart = minDate.getFullYear(), yearend = maxDate.getFullYear()
        
        if (!options.toggle || options.toggle !== 'datepicker' && !that.JSAPI)
            that.$element.data('JSAPI', true)
        
        $year.empty()
        for (var y = yearstart; y <= yearend; y++) {
            $year.append('<option value="'+ y +'"'+ (dw.year == y ? ' selected' : '') +'>'+ y +'</option>')
        }
        
        $month.val(dw.month)
        $year.add($month).off(this.events.change_ym).on(this.events.change_ym, function() {
            if (tools.hasTime()) {
                var $day = $days.find('.slt'),
                    date = tools.changeDateTime($year.val(), $month.val(), $day.data('day'), dw.hour, dw.minute, dw.second)
                
                that.create(tools.getDateWrap(date), minDate, maxDate)
            } else {
                var $day = $days.find('.slt'),
                    date = tools.changeDate($year.val(), $month.val(), $day.data('day'))
                
                that.create(tools.getDateWrap(date), minDate, maxDate)
            }
        })
        $prev.off(this.events.click_prev).on(this.events.click_prev, function() {
            that.tools.changeMonth(-1)
        })
        $next.off(this.events.click_prev).on(this.events.click_prev, function() {
            that.tools.changeMonth(1)
        })
        $clearBtn.off(this.events.click_clear).on(this.events.click_clear, function() {
            that.$element.val('')
            tools.closeCalendar(true)
            tools.afterChange('')
        })
        $okBtn.off(this.events.click_ok).on(this.events.click_ok, function() {
            var $dd = $days.find('dd.slt')
            
            if ($dd.hasClass('disabled')) return false
            
            var date = tools.changeDay($dd.data('day'), $dd.data('month'))
            
            if (tools.hasTime()) {
                date.setHours(parseInt($hh.val()))
                date.setMinutes(parseInt($mm.val()))
                date.setSeconds(parseInt($ss.val()))
            }
            tools.closeCalendar(true)
            that.$element.val(tools.formatDate(date)).focus()
            
            //changedEvent
            tools.afterChange(date)
        })
        $close.off(this.events.click_close).on(this.events.click_close, function() {
            tools.closeCalendar(true)
        })
        $(document).off(this.events.click_close).on(this.events.click_close, function(e) {
            var $target = $(e.target)
            
            if (e.target == that.$element.get(0)) return
            if ($target.closest('#calendar').length) return
            if ($target.data('toggle') == 'datepicker' || $target.parent().data('toggle') == 'datepickerbtn' || $target.data('toggle') == 'datepickerbtn' || $target.data('JSAPI'))
                tools.closeCalendar(false)
            else
                tools.closeCalendar(true)
        })
        
        that.dw = dw
        that.minDate = minDate
        that.maxDate = maxDate
        
        // if js API
        if (that.$element.data('JSAPI')) {
            // add btn
            if (!options.nobtn)
                that.addBtn()
            // event
            that.$element.off('click.bjui.datepicker.data-api').on('click.bjui.datepicker.data-api', $.proxy(function(e) {
                that.create()
            }, that))
            
            if (!options.nobtn) {
                that.$element.parent('.wrap_bjui_btn_box').find('[data-toggle="datepickerbtn"]').off('click.bjui.datepicker.data-api').on('click.bjui.datepicker.data-api', $.proxy(function(e) {
                    that.create()
                }, that))
            }
        }
    }
    
    Datepicker.prototype.create = function(dw, minDate, maxDate) {
        if (!dw) {
            this.init()
            
            dw = this.dw
            minDate = this.minDate
            maxDate = this.maxDate
        }
        
        var that       = this,
            options    = this.options,
            tools      = this.tools,
            monthStart = new Date(dw.year, dw.month - 1, 1),
            startDay   = monthStart.getDay(),
            dayStr     = ''
        
        if (startDay > 0) {
            monthStart.setMonth(monthStart.getMonth() - 1)
            var prevDateWrap = tools.getDateWrap(monthStart)
            
            for (var t = prevDateWrap.days - startDay + 1; t <= prevDateWrap.days; t++) {
                var _date     = new Date(dw.year, dw.month - 2, t),
                    _ctrClass = (_date >= minDate && _date <= maxDate) ? '' : ' disabled'
                
                dayStr += '<dd class="other'+ _ctrClass +'" data-month="-1" data-day="'+ t +'">'+ t +'</dd>'
            }
        }
        for (var t = 1; t <= dw.days; t++) {
            var _date     = new Date(dw.year, dw.month - 1, t),
                _ctrClass = (_date >= minDate && _date <= maxDate) ? '' : 'disabled'
            
            if (t == dw.day)
                _ctrClass += ' slt'
            dayStr += '<dd class="'+ _ctrClass +'" data-day="'+ t +'">'+ t +'</dd>'
        }
        for (var t = 1; t <= 42 - startDay - dw.days; t++) {
            var _date     = new Date(dw.year, dw.month, t),
                _ctrClass = (_date >= minDate && _date <= maxDate) ? '' : ' disabled'
            
            dayStr += '<dd class="other'+ _ctrClass +'" data-month="1" data-day="'+ t +'">'+ t +'</dd>'
        }
        
        var $alldays = $days.html(dayStr).find('dd')
        
        $alldays.not('.disabled').off(this.events.click_days).on(this.events.click_days, function() {
            var $day = $(this)
            
            if (!tools.hasTime()) {
                var date = tools.changeDay($day.data('day'), $day.data('month'))
                
                tools.closeCalendar(true)
                that.$element.val(tools.formatDate(date)).focus()
                
                //changedEvent
                tools.afterChange(date)
            } else {
                $alldays.removeClass('slt')
                $day.addClass('slt')
            }
        })
        
        if (!tools.hasDate()) {
            $main.addClass('nodate') // only time
        } else {
            $main.removeClass('nodate')
        }
        if (tools.hasTime()) {
            $time.show()
            $hh.val(dw.hour < 10 ? ('0'+ dw.hour) : dw.hour).off(this.events.focus_time).on(this.events.focus_time, function() {
                tools.changeTmMenu('hh')
            })
            
            var iMinute = parseInt(dw.minute / options.mmStep) * options.mmStep
            
            $mm.val(iMinute < 10 ? ('0'+ iMinute) : iMinute).data('step', options.mmStep).off(this.events.focus_time).on(this.events.focus_time, function() {
                tools.changeTmMenu('mm')
            })
            $ss.val(tools.hasSecond() ? (dw.second < 10 ? ('0'+ dw.second) : dw.second) : '00').data('step', options.ssStep).off(this.events.focus_time).on(this.events.focus_time, function() {
                tools.changeTmMenu('ss')
            })
            $box.off('click').on('click', function(e) {
                if ($(e.target).closest('.time').length) return
                $tm.find('> ul').hide()
                tools.changeTmMenu()
            })
            $timeinps.off(this.events.keydown_time).on(this.events.keydown_time, tools.keydownInt).each(function() {
                var $input = $(this)
                
                $input.off(that.events.keyup_time).on(that.events.keyup_time, function() {
                    tools.changeTm($input)
                })
            }).off(this.events.click_time).on(this.events.click_time, function() {
                tools.clickTmMenu($(this), $(this).data('type'))
            })
            
            var timer = null
            
            $spinner.off(this.events.click_spinner).on(this.events.click_spinner, function(e) {
                var $btn = $(this)
                
                $timeinps.filter('.slt').each(function() {
                    tools.changeTm($(this), $btn)
                })
                
                e.preventDefault()
            }).off(this.events.mousedown_sp).on(this.events.mousedown_sp, function(e) {
                var $btn = $(this)
                
                timer = setInterval(function() {
                    $timeinps.filter('.slt').each(function() {
                        tools.changeTm($(this), $btn)
                    })
                }, 150)
            }).off(this.events.mouseup_sp).on(this.events.mouseup_sp, function(e) {
                clearTimeout(timer)
            })
            
            $hh.prop('disabled', !tools.hasHour())
            $mm.prop('disabled', !tools.hasMinute())
            $ss.prop('disabled', !tools.hasSecond())
        } else {
            $time.hide()
        }
        this.show()
    }
    
    Datepicker.prototype.show = function() {
        var offset = this.$element.offset(),
            iTop   = offset.top + this.$element.get(0).offsetHeight,
            iLeft  = offset.left, 
            iBoxH  = $box.outerHeight(true),// fix top
            iBoxW  = $box.outerWidth(true), // fix left
            css    = {}
        
        if (iTop > iBoxH && iTop > $(window).height() - iBoxH)
            iTop = offset.top - iBoxH
        
        css.left = iLeft
        css.top  = iTop
            
        if (iLeft > $(window).width() - iBoxW) {
            $box.css('left', '')
            
            delete css.left
            css.right = 10
        }
        
        $box.css(css).show().click(function(e) {
            e.stopPropagation()
        })
    }
    
    // DATEPICKER PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments
        var property = option
        
        return this.each(function () {
            var $this   = $(this)
            var options = $.extend({}, Datepicker.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data    = $this.data('bjui.datepicker')
            
            if (!data) $this.data('bjui.datepicker', (data = new Datepicker(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.datepicker
    
    $.fn.datepicker             = Plugin
    $.fn.datepicker.Constructor = Datepicker
    
    // DATEPICKER NO CONFLICT
    // =================
    
    $.fn.datepicker.noConflict = function () {
        $.fn.datepicker = old
        return this
    }
    
    // DATEPICKER DATA-API
    // ==============
    
    $(document).on(BJUI.eventType.initUI, function(e) {
        $(e.target).find('[data-toggle="datepicker"]').each(function() {
            var $this = $(this)
            
            if (!$this.length) return
            if ($this.data('nobtn')) return
            
            Plugin.call($this, 'addBtn')
        })
    })
    
    $(document).on('click.bjui.datepicker.data-api', '[data-toggle="datepickerbtn"]', function(e) {
        var $date = $(this).prevAll('[data-toggle="datepicker"]')
        
        if (!$date || !$date.is(':text')) return
        Plugin.call($date, 'create')
        
        e.preventDefault()
    })
    
    $(document).on('click.bjui.datepicker.data-api', '[data-toggle="datepicker"]', function(e) {
        var $this = $(this)
        
        if ($this.data('onlybtn')) return
        if (!$this.is(':text')) return
        Plugin.call($this, 'create')
        
        e.preventDefault()
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-ajaxtab.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-ajaxtab.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // AJAXTAB CLASS DEFINITION
    // ======================
    
    var Ajaxtab = function(element, options) {
        this.$element = $(element)
        this.options  = options
    }
    
    Ajaxtab.DEFAULTS = {
        url    : undefined,
        target : undefined,
        reload : false
    }
    
    Ajaxtab.prototype.init = function() {
        var options = this.options
        
        if (!(options.url)) {
            BJUI.debug('Ajaxtab Plugin: Error trying to open a tab, url is undefined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh(this.$element.closest('.unitBox'))
            
            if (!options.url.isFinishedTm()) {
                this.$element.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('Ajaxtab Plugin: The new ajaxtab\'s url is incorrect, url: '+ options.url)
                return
            }
            
            options.url = encodeURI(options.url)
        }
        if (!options.target) {
            BJUI.debug('Ajaxtab Plugin: Attribute \'target\' is not defined!')
            return
        }
        if (options.reload) {
            this.load()
        } else {
            var reload = this.$element.data('bjui.ajaxtab.reload')
            
            if (!reload) this.load()
            else this.$element.tab('show')
        }
    }
    
    Ajaxtab.prototype.load = function() {
        var $element = this.$element
        var options  = this.options
        
        $(options.target).ajaxUrl({
            url      : options.url,
            data     : {},
            callback : function() {
                $element.data('bjui.ajaxtab.reload', true).tab('show')
            }
        })
    }
    
    // AJAXTAB PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments
        var property = option
        
        return this.each(function () {
            var $this   = $(this)
            var options = $.extend({}, Ajaxtab.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data    = $this.data('bjui.ajaxtab')
            
            if (!data) $this.data('bjui.ajaxtab', (data = new Ajaxtab(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.ajaxtab

    $.fn.ajaxtab             = Plugin
    $.fn.ajaxtab.Constructor = Ajaxtab
    
    // AJAXTAB NO CONFLICT
    // =================
    
    $.fn.ajaxtab.noConflict = function () {
        $.fn.ajaxtab = old
        return this
    }
    
    // AJAXTAB DATA-API
    // ==============

    $(document).on('click.bjui.ajaxtab.data-api', '[data-toggle="ajaxtab"]', function(e) {
        var $this   = $(this)
        var options = $this.data()
        
        if (!options.url) options.url = $this.attr('href')
        Plugin.call($this, options)
        
        e.preventDefault()
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-datagrid.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-datagrid.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // DATAGRID CLASS DEFINITION
    // ======================
    
    var Datagrid = function(element, options) {
        this.$element = $(element)
        this.options  = options
        this.tools    = this.TOOLS()
        
        this.datanames = {
            tbody      : 'bjui.datagrid.tbody.dom',
            td_html    : 'bjui.datagrid.td.html',
            changeData : 'bjui.datagrid.tr.changeData'
        }
        
        this.classnames = {
            s_checkbox    : 'datagrid-checkbox',
            s_linenumber  : 'datagrid-linenumber',
            s_edit        : 'datagrid-column-edit',
            s_lock        : 'datagrid-lock',
            s_menu        : 'datagrid-menu-box',
            s_filter      : 'datagrid-filter-box',
            s_showhide    : 'datagrid-showhide-box',
            th_cell       : 'datagrid-cell',
            th_menu       : 'datagrid-column-menu',
            btn_menu      : 'datagrid-column-menu-btn',
            th_col        : 'datagrid-col',
            th_field      : 'datagrid-col-field',
            th_sort       : 'datagrid-sortable',
            th_resize     : 'datagrid-resize-head',
            th_resizemark : 'datagrid-column-resizemark',
            tr_child      : 'datagrid-child-tr',
            tr_edit       : 'datagrid-edit-tr',
            tr_add        : 'datagrid-add-tr',
            tr_selected   : 'datagrid-selected-tr',
            td_edit       : 'datagrid-edit-td',
            td_changed    : 'datagrid-changed',
            td_child      : 'datagrid-child-td',
            td_linenumber : 'datagrid-linenumber-td',
            td_checkbox   : 'datagrid-checkbox-td',
            li_asc        : 'datagrid-li-asc',
            li_desc       : 'datagrid-li-desc',
            li_filter     : 'datagrid-li-filter',
            li_showhide   : 'datagrid-li-showhide',
            li_lock       : 'datagrid-li-lock',
            li_unlock     : 'datagrid-li-unlock'
        }
    }
    
    Datagrid.DEFAULTS = {
        gridTitle       : '',
        columns         : null,     // Thead column module
        data            : null,     // Data source
        dataUrl         : null,     // Request data source URL, for processing (filtering / sorting / paging) results
        updateRowUrl    : null,     // Update row data URL, return JSON data
        postData        : null,     // Send other data to server
        loadType        : 'POST',   // Ajax load request type
        dataType        : 'json',   // Data type of data source
        local           : 'remote', // Optional 'local' | 'remote'
        fieldSortable   : true,     // Click the field to sort
        filterThead     : true,     // Filter in the thead
        sortAll         : true,     // Sort scope, false = this page, true = all
        filterAll       : true,     // Filter scope, false = this page, true = all
        filterMult      : true,     // Filter multiple fileds, false = single, true = multiple
        initFilter      : {},       // 
        selectMult      : false,    // When clicked tr, multiple selected (Default need to press Ctrl or Shift or used checkbox)
        linenumberAll   : false,    // All data together numbers
        showLinenumber  : true,     // Display linenumber column, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean)
        showCheckboxcol : false,    // Display checkbox column, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean)
        showChildcol    : undefined,// Display child button column, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean). If not set false and 'hasChild == true', showChildcol = true
        showEditbtnscol : false,    // Display edit buttons column, Optional 'true' | 'false' | 'edit', (Optional 'true, false' is a boolean, Optional 'edit' is custom column label)
        customEditbtns  : {         // Custom edit column buttons
            position    : 'after',  // Button inserted position, Optional 'after' | 'before' | 'replace'
            buttons     : null,     // Custom buttons, Set string or function
            width       : 0         // The edit column width, 0 = 'auto'
        },
        showTfoot       : false,    // Display the tfoot, Optional 'true' | 'false' | 'lock', (Optional 'true, false' is a boolean)
        showToolbar     : false,    // Display datagrid toolbar
        showNoDataTip   : true,     // Display 'no data' tips, Optional 'true' | 'false' | 'string', (Optional 'true, false' is a boolean, Optional 'string' is custom tips)
        toolbarItem     : '',       // Displayed on the toolbar elements, Optional 'all, add, edit, cancel, save, del, import, export, exportf |'
        toolbarCustom   : '',       // Html code || function || jQuery dom object, custom elements, displayed on the toolbar
        columnResize    : true,     // Allow adjust the column width
        columnMenu      : true,     // Display the menu button on the column
        columnShowhide  : true,     // On the menu display (show / hide columns)
        columnFilter    : false,    // On the menu display (filter form)
        columnLock      : true,     // On the menu display (lock / unlock columns)
        paging          : true,     // Display pagination component
        pagingAlign     : 'center', // The pagination component alignment
        keys            : {
            gridChild   : 'gridChild',
            gridNumber  : 'gridNumber',
            gridCheckbox: 'gridCheckbox',
            gridEdit    : 'gridEdit',
            gridIndex   : 'gridIndex',
            dropFlag    : 'dropFlag',
            treePTr     : 'datagrid.tree.parentTr',
            treePData   : 'datagrid.tree.parentData',
            childlen    : 'childlen',
            isExpand    : 'isExpand',
            isParent    : 'isparent'
        },
        hasChild        : false,    // It contains child data, Optional (true | false)
        childOptions    : {         // Child grid options
            width       : '100%',
            height      : 'auto',
            paging      : false,
            columnMenu  : false,
            filterThead : false,
            childUpdate : false     // This options only for childOptions, when the child grid data changes, update the parent row(!! The parent grid neeed option 'updateRowUrl' !!), Optional (Boolean: true | false, true = 'all') OR (String: 'all, add, edit, del')
        },
        isTree          : false,
        treeOptions     : {
            keys        : {
                key       : 'id',
                parentKey : 'pid',
                childKey  : 'children',
                childLen  : 'childlen',
                isParent  : 'isparent',
                level     : 'level',
                order     : 'order',
                isExpand  : 'isExpand'
            },
            simpleData  : true,
            expandAll   : false,
            add         : true
        },
        dropOptions     : {
            drop        : false,
            dropUrl     : null,
            paging      : true,     // send paging data
            scope       : 'drop',   // send data scope, Optional 'drop' | 'all'
            beforeDrag  : null,     // Function - before drag, return true can drag
            beforeDrop  : null,     // Function - before drop, return false cancel drop
            afterDrop   : 'POST'    // Post(Ajax) the current page data to server, request type = editType. Optional ('POST' | function), If set a function, doesn't post
        },
        tdTemplate      : '',
        editUrl         : null,     // An action URL, for processing (update / save), return results (json)
        editCallback    : null,     // Callback for save
        editMode        : 'inline', // Editing mode, Optional 'false' | 'inline' | 'dialog', (Optional 'false' is a boolean)
        editDialogOp    : null,     // For dialog edit, the dialog init options
        editType        : 'POST',   // Ajax request method of edit, Optional 'POST' | 'GET' | 'raw', (Optional 'raw' is post raw data)
        inlineEditMult  : true,     // Can inline edit multiple rows
        saveAll         : true,     // For inline edit, true = save current row, false = save all editing rows
        addLocation     : 'first',  // Add rows to datagrid location, Optional 'first' | 'last' | 'prev' | 'next'
        delUrl          : null,     // The delete URL, return delete tr results (json)
        delType         : 'POST',   // Delete URL of ajax request method
        delPK           : null,     // Ajax delete request to send only the primary key
        delConfirm      : true,     // Delete confirmation message, Optional 'true' | 'false' | 'message', (Optional 'true, false' is a boolean)
        delCallback     : null,     // Callback for delete
        jsonPrefix      : '',       // JSON object key prefix, for post data
        contextMenuH    : true,     // Right-click on the thead, display the context menu
        contextMenuB    : false,    // Right-click on the tbody tr, display the context menu
        templateWidth   : 600,      // 
        flowLayoutWidth : 0,        // 
        dialogFilterW   : 360,      // 
        hScrollbar      : false,    // Allowed horizontal scroll bar
        fullGrid        : false,    // If the table width below gridbox width, stretching table width
        importOption    : null,     // Import btn options
        exportOption    : null,     // Export btn options
        beforeEdit      : null,     // Function - before edit method, return true execute edit method
        beforeDelete    : null,     // Function - before delete method, return true execute delete method
        beforeSave      : null,     // Function - before save method, arguments($trs, datas)
        afterSave       : null,     // Function - after save method, arguments($trs, datas)
        afterDelete     : null      // Function - after delete method
    }
    
    Datagrid.renderItem = function(value, data, items, itemattr) {
        if (!items || !items.length) return ''
        var label = ''
        
        $.each(items, function(i, n) {
            if (itemattr && itemattr.value) {
                if (n[itemattr.value] == value) {
                    label = n[itemattr.label]
                    return false
                }
            } else {
                if (typeof n[value] !== 'undefined') {
                    label = n[value]
                    return false
                }
            }
        })
        
        return label
    }
    
    Datagrid.renderItemMulti = function(value, data, items, itemattr) {
        if (!items || !items.length) return ''
        var label = [], val = $.type(value) === 'array' ? value : String(value).split(',')
        
        $.each(items, function(i, n) {
            $.each(val, function(k, v) {
                if (itemattr && itemattr.value) {
                    if (n[itemattr.value] == v) {
                        label.push(n[itemattr.label])
                        return false
                    }
                } else {
                    if (typeof n[v] !== 'undefined') {
                        label.push(n[v])
                        return false
                    }
                }
            })
        })
        
        return label.join(',')
    }
    
    Datagrid.prototype.TOOLS = function() {
        var that  = this, options = that.options
        var tools = {
            getPageCount: function(pageSize, total) {
                return Math.ceil(total / pageSize)
            },
            getPageInterval: function(count, pageCurrent, showPageNum) {
                var half  = Math.ceil(showPageNum / 2), limit = count - showPageNum,
                    start = pageCurrent > half ? Math.max(Math.min(pageCurrent - half, limit), 0) : 0,
                    end   = pageCurrent > half ? Math.min((pageCurrent + half), count) : Math.min(showPageNum, count)
                
                if (end - start == showPageNum) end = end + 1
                if (end < showPageNum) end = end + 1
                if (start + 1 == end) end = end + 1
                
                return {start:start + 1, end:end}
            },
            getRight: function($obj) {
                var width = 0, index = $obj.data('index'), model = that.columnModel
                
                for (var i = index; i >= 0; i--) {
                    width += model[i].th.outerWidth()
                }
                
                return width
            },
            getRight4Lock: function(index) {
                var width = 0, $td = that.$lockTbody.find('> tr:first > td:eq('+ index +')'), $firstTds = $td && $td.prevAll().add($td)
                
                if (!$firstTds || !$firstTds.length) $firstTds = that.$lockColgroupH.filter(':lt('+ (index + 1) +')')
                $firstTds.each(function() {
                    var $td = $(this), w = $td.is(':hidden') ? 0 : $td.outerWidth()
                    
                    width += w
                })
                
                return width
            },
            beforeEdit: function($trs, datas) {
                var beforeEdit = options.beforeEdit
                
                if (beforeEdit) {
                    if (typeof beforeEdit === 'string') beforeEdit = beforeEdit.toFunc()
                    if (typeof beforeEdit === 'function') {
                        return beforeEdit.call(that, $trs, datas)
                    }
                }
                
                return true
            },
            beforeSave: function($trs, data) {
                var beforeSave = options.beforeSave
                
                if (beforeSave) {
                    if (typeof beforeSave === 'string') beforeSave = beforeSave.toFunc()
                    if (typeof beforeSave === 'function') {
                        return beforeSave.call(that, $trs, data)
                    }
                }
                
                return true
            },
            afterSave: function($trs, data) {
                var afterSave = options.afterSave, childUpdate = that.options.childUpdate, $parent = that.$element.data('bjui.datagrid.parent'),
                    updateParent = function($tr) {
                        $tr.closest('table').datagrid('updateRow', $tr)
                    }
                
                if (afterSave) {
                    if (typeof afterSave === 'string') afterSave = afterSave.toFunc()
                    if (typeof afterSave === 'function') {
                        afterSave.call(that, $trs, data)
                    }
                }
                
                // remove add data
                $trs.each(function() {
                    $(this).removeData('datagrid.addData')
                })
                
                // update child parent
                if ($parent && childUpdate) {
                    if (typeof childUpdate === 'string' && childUpdate.indexOf('all') === -1) {
                        if (data.addFlag && childUpdate.indexOf('add') !== -1)
                            updateParent($parent)
                        else if (childUpdate.indexOf('edit') !== -1)
                            updateParent($parent)
                    } else {
                        updateParent($parent)
                    }
                }
                
                if (that.needfixedWidth) {
                    that.fixedWidth()
                    that.needfixedWidth = false
                }
                
                // fixedH
                if (options.height === 'auto') {
                    var scrollTop = that.$boxB.scrollTop()
                    
                    that.$boxB.scrollTop(5)
                    if (that.$boxB.scrollTop()) {
                        that.fixedHeight()
                        that.$boxB.scrollTop(scrollTop)
                    }
                }
                
                that.$element.data('allData', that.allData)
            },
            afterDelete: function() {
                var afterDelete = options.afterDelete
                
                if (afterDelete) {
                    if (typeof afterDelete === 'string') afterDelete = afterDelete.toFunc()
                    if (typeof afterDelete === 'function') {
                        afterDelete.call(that)
                    }
                }
                
                that.$element.data('allData', that.allData)
            },
            // Correct colspan
            setColspan: function(column, colspanNum) {
                if (column.colspan) column.colspan = column.colspan + colspanNum - 1
                column.index = column.index + colspanNum - 1
                if (column.parent) this.setColspan(column.parent, colspanNum)
            },
            // set columns options
            setOp: function(op) {
                if (!op.name) {
                    op.menu = op.lock = op.edit = op.add = op.quickSort = op.quickfilter = false
                } else {
                    op.menu        = (typeof op.menu        === 'undefined') ? true  : op.menu
                    op.lock        = (typeof op.lock        === 'undefined') ? true  : op.lock
                    op.edit        = (typeof op.edit        === 'undefined') ? true  : op.edit
                    op.add         = (typeof op.add         === 'undefined') ? true  : op.add
                    op.quicksort   = (typeof op.quicksort   === 'undefined') ? true  : op.quicksort
                    op.quickfilter = (typeof op.quickfilter === 'undefined') ? true  : op.quickfilter
                    op.finalWidth  = (typeof op.finalWidth  === 'undefined') ? false : op.finalWidth
                }
                op.hide = (typeof op.hide === 'undefined') ? false : op.hide
                
                return op
            },
            json2Array4Tree: function (options, datas, level, pid) {
                if (!datas) return []
                if (!level) level = 0
                
                var k = options.key, pk = options.parentKey, childKey = options.childKey, childLen = options.childLen, levelKey = options.level, isParent = options.isParent, r = [], data
                
                if (datas) {
                    datas.sort(function(a, b) {
                        return a[options.order] - b[options.order]
                    })
                }
                
                if ($.isArray(datas)) {
                    for (var i = 0, l = datas.length; i < l; i++) {
                        data = $.extend({}, datas[i])
                        
                        data[levelKey] = level
                        r.push(data)
                        
                        if (data[childKey]) {
                            delete data[childKey]
                            data[isParent] = true
                            data[childLen] = datas[i][childKey].length
                            pid = data[k]
                            
                            r = r.concat(this.json2Array4Tree(options, datas[i][childKey], data[levelKey] + 1, pid))
                            
                            pid = null
                        }
                    }
                } else {
                    data = $.extend({}, datas)
                    
                    data[levelKey] = level
                    r.push(data)
                    
                    if (data[childKey]) {
                        delete data[childKey]
                        data[isParent] = true
                        data[childLen] = datas[childKey].length
                        pid = data[k]
                        
                        r = r.concat(this.json2Array4Tree(options, datas[childKey], data[levelKey] + 1))
                        
                        pid = null
                    }
                }
                
                return r
            },
            array2Json4Tree: function(options, datas) {
                var r = [], temp = [], keys = options.keys, key = keys.key, parentKey = keys.parentKey, childKey = keys.childKey
                
                if (!$.isArray(datas)) return [datas]
                
                for (var i = 0, l = datas.length; i < l; i++) {
                    temp[datas[i][key]] = datas[i]
                }
                for (var i = 0, l = datas.length; i < l; i++) {
                    if (datas[i][parentKey] && datas[i][key] !== datas[i][parentKey]) {
                        if (!temp[datas[i][parentKey]])
                            temp[datas[i][parentKey]] = []
                        if (!temp[datas[i][parentKey]][childKey])
                            temp[datas[i][parentKey]][childKey] = []
                        
                        temp[datas[i][parentKey]][childKey].push(datas[i])
                    } else {
                        r.push(datas[i])
                    }
                }
                
                return r
            },
            // create trs by data source
            createTrsByData: function(data, refreshFlag) {
                var list
                
                if (!that.$tbody) that.$tbody = $('<tbody></tbody>')
                if (data) {
                    if (data.list) list = data.list
                    else list = data
                    
                    that.paging.total = list.length || 0
                    
                    // for tree
                    if (that.options.isTree) {
                        if (that.options.treeOptions.simpleData) {
                            list = this.array2Json4Tree(that.options.treeOptions, list)
                            list = this.json2Array4Tree(that.options.treeOptions.keys, list)
                        } else {
                            list = this.json2Array4Tree(that.options.treeOptions.keys, list)
                        }
                    }
                    
                    if (list && !$.isArray(list))
                        list = [list]
                    
                    that.paging.total = (list && list.length) || 0
                    
                    if (!that.paging.total)
                        list = []
                    
                    if (typeof data === 'object' && that.paging.total) {
                        if (data[BJUI.pageInfo.total]) that.paging.total = parseInt(data[BJUI.pageInfo.total], 10)
                        if (data[BJUI.pageInfo.pageSize]) {
                            if (refreshFlag && that.paging.pageSize != data[BJUI.pageInfo.pageSize]) {
                                that.$boxP && that.$boxP.trigger('bjui.datagrid.paging.pageSize', data[BJUI.pageInfo.pageSize])
                            }
                            that.paging.pageSize = parseInt(data[BJUI.pageInfo.pageSize], 10)
                        }
                    }
                    
                    that.paging.pageCount = tools.getPageCount(that.paging.pageSize, that.paging.total)
                    
                    if (that.paging.pageCurrent > that.paging.pageCount) that.paging.pageCurrent = that.paging.pageCount
                    if (!that.paging.pageCurrent) that.paging.pageCurrent = 1
                    
                    this.initTbody(list, refreshFlag)
                }
                
                if (!that.init_tbody) that.$tbody.appendTo(that.$tableB)
                if (!that.init_thead) that.initThead()
            },
            // initTbody
            initTbody: function(data, refreshFlag) {
                var tools  = this, allData = that.allData, type = options.dataType || 'json', model = that.columnModel, hiddenFields = that.hiddenFields, regional = that.regional, newData = [], attach = that.attach, json
                var paging = that.paging, start = 0, end = paging.pageSize, keys = that.options.keys
                var doInit = function() {
                    type = type.toLowerCase()
                    if (data) allData = that.allData = data
                    
                    that.$element.data('allData', that.allData)
                    
                    if (!allData.length) {
                        end = 0
                    } else {
                        if (options.local === 'local') {
                            start = (paging.pageSize * (paging.pageCurrent - 1))
                            end   = start + paging.pageSize
                            if (paging.total != allData.length) paging.total = allData.length
                            if (start > allData.length) start = paging.pageSize * (paging.pageCount - 1)
                        } else {
                            if (allData.length > paging.pageSize) end = paging.pageSize
                        }
                    }
                    if (end > allData.length) end = allData.length
                    
                    // array to json
                    if (type === 'array' && data && data.length && $.type(data[0]) === 'array') {
                        var a1 = start, a2 = end, arrData = [], _index
                        
                        if (options.local === 'local') {
                            a1 = 0
                            a2 = allData.length
                        }
                        for (var i = a1; i < a2; i++) {
                            json   = {}
                            _index = 0
                            $.each(allData[i], function(k, v) {
                                var obj, val = v
                                
                                if (model[_index] && model[_index][keys.gridChild])    _index ++
                                if (model[_index] && model[_index][keys.gridNumber])   _index ++
                                if (model[_index] && model[_index][keys.gridCheckbox]) _index ++
                                if (typeof val === 'string') val = '"'+ val +'"'
                                
                                if (model[_index] && !model[_index][keys.gridEdit]) {
                                    obj = '{"'+ model[_index].name +'":'+ val +'}'
                                    $.extend(json, JSON.parse(obj))
                                } else { // init hidden fields
                                    if (model[_index] && model[_index][keys.gridEdit]) _index ++
                                    if (_index >= model.length && hiddenFields) {
                                        if (hiddenFields[k - model.length]) {
                                            obj = '{"'+ hiddenFields[k - model.length] +'":'+ val +'}'
                                            $.extend(json, JSON.parse(obj))
                                        }
                                    }
                                }
                                _index ++
                            })
                            
                            arrData.push(json)
                        }
                        
                        allData = that.allData = arrData
                    }
                    
                    // create cuttent page data
                    for (var i = start; i < end; i++) {
                        json = $.extend({}, that.allData[i], attach)
                        
                        /* for quickSort */
                        if (options.local === 'local' && !refreshFlag) {
                            json['bjui_local_index'] = i
                            that.allData[i]['bjui_local_index'] = i
                        }
                        
                        newData.push(json)
                    }
                    
                    tools.createTrs(newData, refreshFlag)
                    
                    that.data = newData
                    
                    that.$element.trigger('afterLoad.bjui.datagrid', {datas:newData})
                }
                
                if (refreshFlag && that.$boxM) {
                    that.$boxM.show().trigger('bjui.ajaxStop').trigger('bjui.ajaxStart', [50, doInit])
                } else {
                    doInit()
                }
            },
            // create tbody - tr
            createTrs: function(datas, refreshFlag) {
                var tools = this, keys = that.options.keys, model = that.columnModel, paging = that.paging, trs = [], editFrag = BJUI.doRegional(FRAG.gridEditBtn, that.regional), childFrag = BJUI.doRegional(FRAG.gridExpandBtn, that.regional), lockedCols = [], isRenderOnly = false,
                    customEditbtns = that.options.customEditbtns, editFrag2 = customEditbtns.buttons
                    
                // custom edit column buttons
                if (editFrag2) {
                    if (typeof editFrag2 === 'function')
                        editFrag2 = editFrag2.apply()
                    
                    if (editFrag2) {
                        if (customEditbtns.position === 'before')
                            editFrag = editFrag2 + editFrag
                        else if (customEditbtns.position === 'replace')
                            editFrag = editFrag2
                        else
                            editFrag += editFrag2
                    }
                }
                
                if (refreshFlag) {
                    // remebered lock columns
                    $.each(model, function(i, n) {
                        if (n.locked) {
                            that.colLock(n.th, false)
                            n.lock_refresh = true
                        }
                    })
                    
                    if (that.$tbody.find('> tr.datagrid-nodata').length) {
                        that.needfixedWidth = true
                    }
                    
                    that.$tbody.empty()
                    that.$lockTableH && that.$lockTableH.empty()
                    that.$lockTableB && that.$lockTableB.empty()
                }
                
                if (!datas.length && !refreshFlag) {
                    var emptyObj = {}
                    
                    emptyObj[model[0].name] = '0'
                    isRenderOnly = true
                    datas = []
                    datas.push(emptyObj)
                }
                
                for (var i = 0, l = datas.length; i < l; i++) {
                    var trData = datas[i], modellength = model.length, tempcolspan = modellength, linenumber = options.linenumberAll ? ((paging.pageCurrent - 1) * paging.pageSize + (i + 1)) : (i + 1),
                        tds = [], n, tdHtml = BJUI.StrBuilder(), $td, name, label, _label, render_label, align, cls, display, tree, hasTree = false, treeattr = '', tdTemplate = options.tdTemplate
                    
                    trData[keys.gridNumber] = linenumber
                    trData[keys.gridIndex]  = i
                    
                    if (that.options.hasChild && that.options.childOptions)
                        tempcolspan = tempcolspan - 1
                    if (that.options.showLinenumber)
                        tempcolspan = tempcolspan - 1
                    if (that.options.showCheckboxcol)
                        tempcolspan = tempcolspan - 1
                    
                    var tempData = $.extend({}, trData)
                    
                    for (var j = 0; j < modellength; j++) {
                        n       = model[j] 
                        name    = n.name || 'datagrid-noname'
                        label   = trData[name]
                        align   = ''
                        cls     = []
                        _label  = ''
                        display = ''
                        tree    = ''
                        render_label = undefined
                        
                        if (typeof label === 'undefined' || label === 'null' || label === null) label = ''
                        _label = label
                        
                        if (n.align) align = ' align="'+ n.align +'"'
                        if (n[keys.gridChild]) label = childFrag
                        if (n[keys.gridCheckbox]) label = '<div><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox" value="true"></div>'
                        if (n[keys.gridEdit]) {
                            label = editFrag
                            cls.push(that.classnames.s_edit)
                        }
                        if (options.hScrollbar) {
                            if (!n.width || n.width === 'auto')
                                cls.push('nowrap')
                        }
                        
                        /* for tfoot */
                        /*if (n.calc) {
                            if (!n.calc_count) n.calc_count = datas.length
                            
                            var number = label ? (String(label).isNumber() ? Number(label) : 0) : 0
                            
                            if (n.calc === 'sum' || n.calc === 'avg') n.calc_sum = (n.calc_sum || 0) + number
                            else if (n.calc === 'max') n.calc_max = n.calc_max ? (n.calc_max < number ? number : n.calc_max) : number
                            else if (n.calc === 'min') n.calc_min = n.calc_min ? (n.calc_min > number ? number : n.calc_min) : number
                        }*/
                        
                        if (n[keys.gridChild])    cls.push(that.classnames.td_child)
                        if (n[keys.gridNumber])   cls.push(that.classnames.td_linenumber)
                        if (n[keys.gridCheckbox]) cls.push(that.classnames.td_checkbox)
                        
                        if (cls.length) cls = cls.join(' ')
                        else cls = ''
                        
                        if (refreshFlag && n.hidden) display = ' style="display:none;"'
                        
                        /* render */
                        if (n.items && !n.render) {
                            if (n.attrs && n.attrs['multiple'])
                                n.render = $.datagrid.renderItemMulti
                            else
                                n.render = $.datagrid.renderItem
                        }
                        if (n.render && typeof n.render === 'string') n.render = n.render.toFunc()
                        if (n.render && typeof n.render === 'function') {
                            if (n.items) {
                                if (typeof n.items === 'string') {
                                    if (n.items.trim().startsWith('[')) n.items = n.items.toObj()
                                    else n.items = n.items.toFunc()
                                }
                                
                                if (!that.renderTds) that.renderTds = []
                                
                                var delayRender = function(index, label, trData, n) {
                                    $.when(n.items.call(that)).done(function(item) {
                                        n.items = item
                                        
                                        that.delayRender --
                                    })
                                }
                                
                                if (typeof n.items === 'function') {
                                    if (!i) {
                                        if (!that.delayRender) that.delayRender = 0
                                        that.delayRender ++
                                        delayRender((i * j), _label, trData, n)
                                    }
                                    n.delayRender = true
                                    that.renderTds.push({trindex:i, tdindex:j, label:_label, data:trData, render:n.render})
                                } else {
                                    delete n.delayRender
                                    render_label = n.render.call(that, _label, trData, n.items, n.itemattr)
                                }
                            } else {
                                render_label = n.render.call(that, _label, trData)
                            }
                        }
                        
                        if (options.isTree && !hasTree && (!(n === that.childColumn || n === that.linenumberColumn || n === that.checkboxColumn || n === that.editBtnsColumn) || that.treeColumn)) {
                            if (typeof options.isTree === 'string') {
                                if (n.name && n.name === options.isTree)
                                    hasTree = true
                            } else {
                                hasTree = true
                            }
                            
                            if (hasTree) {
                                !that.treeColumn && (that.treeColumn = n)
                                n.hasTree = true
                                !trData[options.treeOptions.keys.level] && (trData[options.treeOptions.keys.level] = 0)
                                tree  = that.tools.createTreePlaceholder(trData, (render_label || label))
                                align = ' align="left"'
                                
                                if (cls) {
                                    cls += ' datagrid-tree-td'
                                } else {
                                    cls = 'datagrid-tree-td'
                                }
                                
                                treeattr = ' data-child="'+ (trData[options.treeOptions.keys.childLen] || 0) +'" data-level="'+ trData[options.treeOptions.keys.level] +'" class="datagrid-tree-tr datagrid-tree-level-'+ trData[options.treeOptions.keys.level] +'"'
                            }
                        }
                        
                        cls && (cls = ' class="'+ cls +'"')
                        
                        if (that.isTemplate) {
                            if (!n.delayRender) {
                                tempData[n.name] = (typeof render_label === 'undefined' ? label : render_label)
                            } else {
                                delete tempData[n.name]
                            }
                            if (!tds[0] && j == (modellength - 1)) {
                                if (tdTemplate && typeof tdTemplate === 'function')
                                    tdTemplate = tdTemplate.apply(that, [trData])
                                
                                tdTemplate = that.tools.replacePlh4Template(tdTemplate, tempData, true)
                                
                                if (that.options.hasChild && that.options.childOptions) {
                                    display = that.childColumn.hidden ? ' style="display:none;"' : ''
                                    tds[0]  = ['<td data-title="..." align="center" class="datagrid-child-td"'+ display +'><div>'+ childFrag +'</div></td>']
                                }
                                if (that.options.showLinenumber) {
                                    display = that.linenumberColumn.hidden ? ' style="display:none;"' : ''
                                    
                                    var linenumbertd = ['<td data-title="No." align="center" class="datagrid-linenumber-td"'+ display +'>'+ linenumber +'</td>']
                                    
                                    tds[tds.length] = linenumbertd
                                }
                                if (that.options.showCheckboxcol) {
                                    display = that.checkboxColumn.hidden ? ' style="display:none;"' : ''
                                    
                                    var checkboxtd = ['<td data-title="Checkbox" align="center" class="datagrid-checkbox-td"'+ display +'><div><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox" value="true"></div></td>']
                                    
                                    tds[tds.length] = checkboxtd
                                }
                                
                                var temptd = ['<td class="datagrid-template-td" colspan="'+ tempcolspan +'">'+ tdTemplate +'</td>']
                                
                                tds[tds.length] = temptd
                                
                                treeattr = ' class="datagrid-template-tr"'
                            }
                        } else {
                            tdHtml
                                .add('<td data-title="'+ n.label +'"')
                                .add(align)
                                .add(cls)
                                .add(display)
                                .add('>')
                            
                            if (tree) {
                                tdHtml.add(tree)
                            } else {
                                tdHtml
                                    .add('<div>')
                                    .add(typeof render_label === 'undefined' ? label : render_label)
                                    .add('</div>')
                            }
                            
                            tdHtml.add('</td>')
                            tds.push(tdHtml.toString())
                        }
                    }
                    
                    trs.push('<tr'+ treeattr +'>'+ tds.join('') +'</tr>')
                    
                    if (isRenderOnly) {
                        trs = []
                        trs.push(tools.createNoDataTr(true) || '')
                    } else {
                        trs.push(tools.createChildTr(null, trData))
                    }
                }
                
                that.$tbody.html(trs.join(''))
                
                if (refreshFlag) {
                    that.initEvents()
                    if (options.editMode) that.edit()
                    
                    if (!datas.length)
                        tools.createNoDataTr()
                    
                    that.$boxP && that.$boxP.trigger('bjui.datagrid.paging.jump')
                    
                    // locked
                    $.each(model, function(i, n) {
                        if (n.lock_refresh) {
                            that.colLock(n.th, true)
                            delete n.lock_refresh
                        }
                    })
                    
                    setTimeout(function() {
                        that.$tableB.initui()
                        that.$lockTableB && that.$lockTableB.initui()
                        
                        that.fixedHeight()
                        
                        if (that.needfixedWidth) {
                            that.fixedWidth()
                            that.needfixedWidth = null
                        }
                        
                        that.$boxM && that.$boxM.trigger('bjui.ajaxStop').hide()
                        
                        // for initFilter
                        if (that.doInitFilter) {
                            that.tools.initFilter()
                            that.doInitFilter = undefined
                        }
                    }, datas.length + 1)
                }
            },
            coverTemplate: function() {
                var tools = this, options = that.options, datas = that.data, model = that.columnModel, trs
                
                if (that.isTemplate) {
                    trs = that.$grid.data('bjui.datagrid.trs.template')
                    
                    if (!trs)
                        tools.createTrs(datas, true)
                    else {
                        that.$tbody.html(trs)
                        
                        that.initEvents()
                        if (options.editMode) that.edit()
                        
                        if (!datas.length)
                            tools.createNoDataTr()
                        
                        that.$boxP && that.$boxP.trigger('bjui.datagrid.paging.jump')
                        
                        // locked
                        $.each(model, function(i, n) {
                            if (n.lock_refresh) {
                                that.colLock(n.th, true)
                                delete n.lock_refresh
                            }
                        })
                        
                        setTimeout(function() {
                            that.$tableB.initui()
                            that.$lockTableB && that.$lockTableB.initui()
                            
                            that.fixedHeight()
                            
                            that.$boxM && that.$boxM.trigger('bjui.ajaxStop').hide()
                            
                            // for initFilter
                            if (that.doInitFilter) {
                                that.tools.initFilter()
                                that.doInitFilter = undefined
                            }
                        }, datas.length + 1)
                    }
                } else {
                    trs = that.$grid.data('bjui.datagrid.trs.normal')
                    
                    if (!trs)
                        this.createTrs(datas, true)
                    else {
                        that.$tbody.html(trs)
                        
                        that.initEvents()
                        if (options.editMode) that.edit()
                        
                        if (!datas.length)
                            tools.createNoDataTr()
                        
                        that.$boxP && that.$boxP.trigger('bjui.datagrid.paging.jump')
                        
                        // locked
                        $.each(model, function(i, n) {
                            if (n.lock_refresh) {
                                that.colLock(n.th, true)
                                delete n.lock_refresh
                            }
                        })
                        
                        setTimeout(function() {
                            that.$tableB.initui()
                            that.$lockTableB && that.$lockTableB.initui()
                            
                            that.fixedHeight()
                            
                            that.$boxM && that.$boxM.trigger('bjui.ajaxStop').hide()
                            
                            // for initFilter
                            if (that.doInitFilter) {
                                that.tools.initFilter()
                                that.doInitFilter = undefined
                            }
                        }, datas.length + 1)
                    }
                }
                
                setTimeout(function() {
                    that.initFixedW = false
                    that.hasAutoCol = true
                    that.fixedWidth(true)
                }, 300)
            },
            replacePlh: function(url, data) {
                return url.replace(/{\/?[^}]*}/g, function($1) {
                    var key = $1.replace(/[{}]+/g, ''), val = data[key]
                    
                    if (typeof val === 'undefined' || val === 'null' || val === null)
                        val = ''
                    
                    return val
                })
            },
            replacePlh4Template: function(html, data, replaceAll) {
                return html.replace(/{#\/?[^}]*}/g, function($1) {
                    var key = $1.replace(/[{#}]+/g, ''), val = data[key]
                    
                    if (replaceAll && typeof val === 'undefined')
                        return $1
                    
                    if (typeof val === 'undefined' || val === 'null' || val === null)
                        val = ''
                    
                    return val
                })
            },
            createNoDataTr: function(str) {
                if (that.options.showNoDataTip) {
                    if (str) {
                        return '<tr class="datagrid-nodata"><td colspan="'+ that.columnModel.length +'">'+ (typeof that.options.showNoDataTip === 'string' ? that.options.showNoDataTip : BJUI.getRegional('datagrid.noData')) +'</td></tr>'
                    } else if (!that.$tbody.find('> tr').length) {
                        $('<tr class="datagrid-nodata"></tr>').html('<td colspan="'+ that.columnModel.length +'">'+ (typeof that.options.showNoDataTip === 'string' ? that.options.showNoDataTip : BJUI.getRegional('datagrid.noData')) +'</td>').appendTo(that.$tbody)
                    }
                }
            },
            createTreePlaceholder: function(data, label, isExpand) {
                var keys = that.options.treeOptions.keys, str = BJUI.StrBuilder()
                var addIndent = function(level) {
                    var indent = []
                    
                    while (level--) {
                        indent.push('<span class="datagrid-tree-indent"></span>')
                    }
                    
                    return indent.join('')
                }
                
                str.add('<div class="datagrid-tree-box">')
                
                if (data[keys.level])
                    str.add(addIndent(data[keys.level]))
                
                str.add('<span class="datagrid-tree-switch'+ (typeof isExpand !== 'undefined' && !isExpand ? ' collapsed' : '') +'">')
                
                if (data[keys.isParent]) {
                    if (typeof isExpand === 'undefined') {
                        if (typeof data[keys.isExpand] === 'undefined')
                            data[keys.isExpand] = true
                            
                            if (!data[keys.isExpand]) {
                                if (!that.collapseIndex) that.collapseIndex = []
                                that.collapseIndex.push(data.gridIndex)
                            }
                        
                        str.add('<i class="fa fa-minus-square-o"></i>')
                    } else {
                        str.add('<i class="fa fa-'+ (isExpand ? 'minus' : 'plus') +'-square-o"></i>')
                    } 
                }
                
                str
                    .add('</span>')
                    .add('<span class="datagrid-tree-'+ (data[keys.isParent] ? 'branch' : 'leaf') +'"><i class="fa fa-'+ (data[keys.isParent] ? 'folder' : 'file') +'-o"></i></span>')
                    .add('<span class="datagrid-tree-title">')
                    .add(label)
                    .add('</span>')
                    .add('</div>')
                
                return str.toString()
            },
            createChildTr: function($tr, trData) {
                if ($tr && $tr.next() && $tr.next().hasClass(that.classnames.tr_child))
                    return
                
                if (options.hasChild && options.childOptions && options.childOptions.dataUrl) {
                    that.childOptions = $.extend(true, {}, Datagrid.DEFAULTS, options.childOptions)
                    
                    var child = '<tr class="'+ that.classnames.tr_child +'"><td colspan="'+ that.columnModel.length +'" style="width:100%; padding:10px;"><table class="table-child"></table></td></tr>'
                    
                    if ($tr && $tr.length)
                        $tr.after(child)
                    else
                        return child
                }
                
                return ''
            },
            // Parameters can be (jQuery Object || number)
            getNoChildTrIndex: function(row) {
                var index 
                
                if (isNaN(row)) {
                    if (!row || !row.length) return -1
                    index = row.index()
                } else {
                    index = parseInt(row, 10)
                }
                if (that.options.hasChild && that.options.childOptions)
                    index = index * 2
                
                return index
            },
            // Parameters can be (jQuery Object || number)
            getNoChildDataIndex: function(row) {
                var data_index
                
                if (isNaN(row))
                    data_index = this.getNoChildTrIndex(row)
                else
                    data_index = parseInt(row, 10)
                
                if (data_index === -1) return data_index
                if (that.options.hasChild && that.options.childOptions)
                    data_index = data_index / 2
                
                return data_index
            },
            // ajax load data by url
            loadData: function(data, refreshFlag) {
                var tools = this, url = options.dataUrl, dataType = options.dataType || 'json', model = that.columnModel
                
                that.$boxM && that.$boxM.show().trigger('bjui.ajaxStart')
                
                dataType = dataType.toLowerCase()
                if (dataType === 'array') dataType = 'text'
                
                if (options.postData) {
                    if (typeof options.postData === 'string') {
                        if (options.postData.trim().startsWith('{')) options.postData = options.postData.toObj()
                        else options.postData = options.postData.toFunc()
                    }
                    if (typeof options.postData === 'function') {
                        options.postData = options.postData.apply()
                    }
                    if (typeof options.postData === 'object') {
                        if (!data) data = options.postData
                        else data = $.extend({}, options.postData, data)
                    }
                }
                
                data = data || {}
                if (!data.pageSize && options.paging) {
                    data.pageSize = that.paging.pageSize
                    data.pageCurrent = that.paging.pageCurrent
                }
                
                if (!options.paging) {
                    delete data.pageSize
                    delete data.pageCurrent
                }
                
                BJUI.ajax('doajax', {
                    url       : url,
                    data      : data,
                    type      : options.loadType,
                    cache     : options.cache || false,
                    dataType  : dataType,
                    okCallback: function(response) {
                        if (dataType === 'json' || dataType === 'jsonp') {
                            tools.createTrsByData(response, refreshFlag)
                        } else if (dataType === 'text') {
                            if ($.type(response) !== 'array')
                                response = []
                            
                            tools.createTrsByData(response, refreshFlag)
                        } else if (dataType === 'xml') {
                            var xmlData = [], obj
                            
                            $(response).find('row').each(function() {
                                obj = {}
                                
                                $(this).find('cell').each(function(i) {
                                    var $cell = $(this), label = $cell.text(), name = $cell.attr('name')
                                    
                                    obj[name] = label
                                })
                                
                                xmlData.push(obj)
                            })
                            
                            if (xmlData.length) tools.createTrsByData(xmlData, refreshFlag)
                        } else {
                            BJUI.debug('BJUI.Datagrid: The options \'dataType\' is incorrect!')
                        } 
                    },
                    errCallback: function(json) {
                        if (json && json[BJUI.keys.statusCode]) {
                            BJUI.alertmsg('error', json[BJUI.keys.message] || BJUI.getRegional('datagrid.errorData'))
                        } else {
                            BJUI.alertmsg('warn', BJUI.getRegional('datagrid.errorData'))
                        }
                        
                        tools.createTrsByData([], refreshFlag)
                    },
                    failCallback: function(msg) {
                        BJUI.alertmsg('warn', BJUI.getRegional('datagrid.failData'))
                        
                        tools.createTrsByData([], refreshFlag)
                    }
                })
            },
            // append columns
            appendColumns: function() {
                that.childColumn      = {name:that.options.keys.gridChild, gridChild:true, width:30, minWidth:30, label:'...', align:'center', menu:false, edit:false, quicksort:false}
                that.linenumberColumn = {name:that.options.keys.gridNumber, gridNumber:true, width:'auto', minWidth:80, label:'No.', align:'center', menu:false, edit:false, quicksort:false}
                that.checkboxColumn   = {name:that.options.keys.gridCheckbox, gridCheckbox:true, width:30, minWidth:30, label:'Checkbox', align:'center', menu:false, edit:false, quicksort:false}
                that.editBtnsColumn   = {name:that.options.keys.gridEdit, gridEdit:true, width:that.options.customEditbtns.width, minWidth:110, label:(typeof options.showEditbtnscol === 'string' ? options.showEditbtnscol : 'Edit'), align:'center', menu:false, edit:false, hide:false, quicksort:false}
            },
            // setBoxb - height
            setBoxbH: function(height) {
                var boxH = height || that.boxH || options.height, topM = 0, h, $boxB, bH
                
                if (boxH < 100) return
                if (isNaN(boxH)) {
                    if (boxH === 'auto') {
                        $boxB = that.$boxB.clone().height('').prependTo('body').find('> colgroup').remove().end()
                        bH = $boxB.outerHeight()
                        $boxB.remove()
                        
                        boxH = (that.$boxT ? that.$boxT.outerHeight() : 0)
                             + (that.$boxH ? that.$boxH.outerHeight() : 0)
                             + (that.$toolbar ? that.$toolbar.outerHeight() : 0) 
                             + (that.$boxP ? that.$boxP.outerHeight() : 0)
                             + bH
                             + (that.$boxF ? that.$boxF.outerHeight() : 0)
                        
                        if (options.maxHeight && Number(options.maxHeight) < boxH) {
                            boxH = Number(options.maxHeight)
                        }
                        
                        that.$grid.height(boxH)
                        that.boxH = boxH
                    } else {
                        boxH = that.$grid.height()
                    }
                }
                
                if (that.$boxT) {
                    h     = that.$boxT.outerHeight()
                    boxH -= h
                    topM += h
                }
                if (that.$toolbar) {
                    h     = that.$toolbar.outerHeight()
                    boxH -= h
                    topM += h
                }
                if (that.$boxP)
                    boxH -= that.$boxP.outerHeight()
                if (that.$boxF)
                    boxH -= that.$boxF.outerHeight()
                
                topM += that.$tableH.outerHeight()
                boxH -= that.$boxH.outerHeight()
                
                if (boxH < 0) boxH = 0
                
                that.$boxB.height(boxH)
                that.$boxM.height(boxH).css({top:topM})
                that.$lockB && that.$lockB.height(boxH)
                
                if (that.$element.data('bjui.datagrid.parent'))
                    that.$element.data('bjui.datagrid.parent').closest('table').datagrid('fixedHeight')
            },
            // column menu - toggle show submenu
            showSubMenu: function($li, $menu, $submenu) {
                var left, width = $menu.outerWidth(), submenu_width = $submenu.data('width') || $submenu.outerWidth(), wh = that.$grid.height(), mt, submenu_height = $submenu.data('height'), animate_op, boxWidth = that.$boxH.width()
                var hidesubmenu = function($li, $menu, $submenu) {
                    left       = $menu.offset().left - that.$grid.offset().left - 1
                    animate_op = {left:'50%'}
                    
                    $li.removeClass('active')
                    
                    if ($menu.hasClass('position-right') || (boxWidth - left < width + submenu_width)) {
                        $submenu.css({left:'auto', right:'100%'})
                        animate_op = {right:'50%'}
                    } else {
                        $submenu.css({left:'100%', right:'auto'})
                    }
                    animate_op.opacity = 0.2
                    
                    $submenu.stop().animate(animate_op, 'fast', function() {
                        $(this).hide()
                    })
                }
                
                $li.hover(function() {
                    $submenu.appendTo($li)
                    if ($li.hasClass(that.classnames.li_filter) && $submenu.is(':visible')) {
                        return false
                    } else {
                        var $filterli = $li.siblings('.'+ that.classnames.li_filter)
                        
                        if ($filterli.length && $filterli.hasClass('active')) {
                            hidesubmenu($filterli, $menu, $filterli.find('> .'+ that.classnames.s_filter))
                        }
                    }
                    
                    if ($li.hasClass(that.classnames.li_showhide)) {
                        mt = $li.position().top + $menu.position().top
                        
                        if ((wh - mt) < submenu_height) {
                            if (submenu_height > wh) {
                                submenu_height = wh
                                $submenu.css('height', wh)
                            }
                            $submenu.css({top:(- (submenu_height - (wh - mt)))})
                        }
                    }
                    
                    left       = $menu.offset().left - that.$grid.offset().left - 1
                    animate_op = {left:'100%'}
                    
                    if ($menu.hasClass('position-right') || (boxWidth - left < width + submenu_width)) {
                        $submenu.css({left:'auto', right:'50%'})
                        animate_op = {right:'100%'}
                    } else {
                        $submenu.css({left:'50%', right:'auto'})
                    }
                    animate_op.opacity = 1
                    
                    $li.addClass('active')
                    $submenu.show().stop().animate(animate_op, 'fast')
                }, function() {
                    if ($li.hasClass(that.classnames.li_filter)) {
                        return false
                    }
                    hidesubmenu($li, $menu, $submenu)
                })
                
                $li.on('hidesubmenu.bjui.datagrid.th', function(e, menu, submenu) {
                    hidesubmenu($(this), menu, submenu)
                })
            },
            // column menu - lock/unlock
            locking: function($th) {
                var index= $th.data('index'), columnModel = that.columnModel[index], lockFlag = columnModel.lock, locked = columnModel.locked, $menu = that.$menu, $ul = $menu.find('> ul'), $lockli = $ul.find('> li.'+ that.classnames.li_lock), $unlockli = $lockli.next()
                    
                if (locked) {
                    $lockli.addClass('disable')
                    $unlockli.removeClass('disable')
                } else {
                    $unlockli.addClass('disable')
                    $lockli.removeClass('disable')
                }
                
                if (lockFlag) {
                    $lockli.show().off('click').on('click', function() {
                        if ($lockli.hasClass('disable')) return
                        
                        $menu.hide().data('bjui.datagrid.menu.btn').removeClass('active')
                        that.colLock($th, true)
                    })
                    
                    $unlockli.show().off('click').on('click', function() {
                        if ($unlockli.hasClass('disable')) return
                        
                        $menu.hide().data('bjui.datagrid.menu.btn').removeClass('active')
                        that.colLock($th, false)
                    })
                } else {
                    $lockli.hide().off('click')
                    $unlockli.hide().off('click')
                }
            },
            // create show/hide column panel
            createShowhide: function() {
                var $showhide, keys = that.options.keys
                
                if (!that.$showhide) {
                    that.col_showhide_count = that.columnModel.length
                    $showhide = $('<ul class="'+ that.classnames.s_showhide +'" role="menu"></ul>')
                    
                    $.each(that.columnModel, function(i, n) {
                        if (that.tools.isGridModel(n))
                            that.col_showhide_count --
                        
                        var $col = $(FRAG.gridShowhide.replaceAll('#index#', n.index).replaceAll('#label#', (n.label || ''))).attr('title', (n.label || '')).toggleClass('nodisable', !!(that.tools.isGridModel(n)))
                        var colClick = function(n) {
                            $col.click(function() {
                                if ($(this).hasClass('disable')) return false
                                
                                var $this = $(this), check = !$this.find('i').hasClass('fa-check-square-o'), index = n.index
                                
                                $this.toggleClass('datagrid-col-check')
                                    .find('i').attr('class', 'fa fa'+ (check ? '-check' : '') +'-square-o')
                                
                                that.showhideColumn(n.th, check)
                                
                                if (!(that.tools.isGridModel(n))) {
                                    that.col_showhide_count = check ? that.col_showhide_count + 1 : that.col_showhide_count - 1
                                }
                                
                                if (that.col_showhide_count == 1) $showhide.find('> li.datagrid-col-check').addClass('disable')
                                else $showhide.find('> li.disable').removeClass('disable')
                                
                                $showhide.find('> li.nodisable').removeClass('disable')
                            })
                        }
                        
                        colClick(n)
                        $col.appendTo($showhide)
                        
                        if (n.hide) $col.trigger('click')
                    })
                    
                    $showhide.appendTo(that.$grid)
                    $showhide.data('width', $showhide.outerWidth()).data('height', $showhide.outerHeight())
                    that.$showhide = $showhide
                }
            },
            // column - display/hide
            showhide: function(model, showFlag) {
                var keys = that.options.keys, index = model.index, $th = model.th, $trs = that.$tbody.find('> tr'), display = showFlag ? '' : 'none'
                var setColspan = function(column) {
                    var _colspan = column.colspan
                    
                    if (showFlag) _colspan ++
                    else _colspan --
                    
                    if (!_colspan) column.th.css('display', 'none')
                    else column.th.css('display', '')
                    
                    column.th.attr('colspan', _colspan)
                    column.colspan = _colspan
                    
                    if (column.parent) setColspan(column.parent)
                }
                
                if (typeof model.hidden === 'undefined') model.hidden = false
                if (model.hidden === !showFlag) return
                
                model.hidden = !showFlag
                
                $th.css('display', display)
                
                if (that.options.tdTemplate && !this.isGridModel(model)) {
                    var colspan = $trs.eq(0).find('> td.datagrid-template-td').attr('colspan')
                    
                    if (colspan) {
                        $trs.find('> td.datagrid-template-td').attr('colspan', parseInt(colspan, 10) + (model.hidden ? -1 : 1))
                    }
                } else {
                    $trs.find('> td:eq('+ index +')').css('display', display)
                }
                that.$colgroupH.find('> col').eq(index).css('display', display)
                that.$colgroupB.find('> col').eq(index).css('display', display)
                that.$thead.find('> tr.datagrid-filter > th:eq('+ index +')').css('display', display)
                if (that.$boxF) {
                    that.$tableF.find('> thead > tr > th:eq('+ index +')').css('display', display)
                    that.$colgroupF.find('> col').eq(index).css('display', display)
                }
                
                if (model.calc) {
                    that.$tfoot && that.$tfoot.trigger('resizeH.bjui.datagrid.tfoot')
                }
                
                if (model.parent) setColspan(model.parent)
                
                // fixed width && height for child datagrid
                that.$tbody.find('> tr.'+ that.classnames.tr_child +':visible > td table').trigger('bjui.datagrid.child.resize')
            },
            isGridModel: function(model) {
                var keys = that.options.keys
                
                if (!model || typeof model !== 'object') return false
                return (model[keys.gridChild] || model[keys.gridNumber] || model[keys.gridCheckbox] || model[keys.gridEdit])
            },
            isGridData: function(name) {
                var keys = that.options.keys, b = false
                
                for (var key in keys) {
                    if (keys[key] === name) {
                        b = true
                        
                        break
                    }
                }
                
                return b
            },
            // jump to page
            jumpPage: function(pageCurrent, pageSize) {
                var allData = that.allData, filterDatas
                
                if (pageCurrent) {
                    that.paging.oldPageCurrent = that.paging.pageCurrent
                    that.paging.pageCurrent    = pageCurrent
                }
                if (pageSize) {
                    that.paging.oldPageSize = that.paging.pageSize
                    
                    that.paging.pageSize  = pageSize
                    that.paging.pageCount = this.getPageCount(pageSize, that.paging.total)
                    
                    if (that.paging.pageCurrent > that.paging.pageCount)
                        that.paging.pageCurrent = that.paging.pageCount
                }
                
                if (options.local === 'remote') {
                    filterDatas = this.getRemoteFilterData(true)
                    this.loadData(filterDatas, true)
                } else {
                    this.initTbody(allData, true)
                }
            },
            // column - quicksort
            quickSort: function(model) {
                if (that.isDom) {
                    if (options.local === 'local') return
                    options.sortAll = true
                }
                if (!that.sortData) {
                    that.sortData = {}
                }
                if (that.$tbody.find('> tr.'+ that.classnames.tr_edit).length) {
                    that.$tbody.alertmsg('info', BJUI.getRegional('datagrid.editMsg'))
                    return
                }
                
                var $th = model.th, data = that.data, allData = that.allData, postData, direction, name = model.name, type = model.type, $ths = that.$thead.find('> tr > th.datagrid-quicksort-th')
                
                if (!name) name = 'datagrid-noname'
                
                var getOrders = function() {
                    var orders = []
                    
                    $.each(that.sortData, function(k, v) {
                        orders.push(k +' '+ v.direction)
                    })
                    
                    return orders
                }
                var sortData = function(data, orders) {
                    var localSort = function(a, b) {
                        var key_index = 0
                        
                        function doCompare() {
                            var keys = orders[key_index].split(' '), name = keys[0], direction = keys[1], typeA = (typeof a[name]), typeB = (typeof b[name])
                            
                            if (a[name] == b[name] && key_index < orders.length - 1) {
                                key_index ++
                                return doCompare()
                            }
                            
                            if ((typeA = typeB === 'number') || (typeA = typeB === 'boolean')) {
                                return direction === 'asc' ? (a[name] - b[name]) : (b[name] - a[name])
                            } else {
                                return direction === 'asc' ? String(a[name]).localeCompare(b[name]) : String(b[name]).localeCompare(a[name])
                            }
                        }
                        
                        if (!orders || !orders.length) {
                            return (a['bjui_local_index'] - b['bjui_local_index'])
                        }
                        return doCompare()
                    }
                    
                    data.sort(localSort)
                }
                
                if (options.fieldSortable)
                    $th.find('> div > .datagrid-label > i').remove()
                else
                    that.$thead.find('> tr:not(.datagrid-filter) > th > div > .datagrid-label > i').remove()
                
                if (model.sortDesc) {
                    model.sortDesc = false
                } else {
                    if (model.sortAsc) {
                        direction = 'desc'
                        model.sortAsc  = false
                        model.sortDesc = true
                    } else {
                        direction = 'asc'
                        model.sortAsc = true
                    }
                    // for thead
                    $th.find('> div > .datagrid-label').prepend('<i class="datagrid-sort-i fa fa-long-arrow-'+ (model.sortAsc ? 'up' : 'down') +'"></i>')
                }
                
                if (direction) {
                    if (that.sortData[name]) {
                        that.sortData[name]['direction'] = direction
                    } else {
                        that.sortData[name] = {index:(Object.keys(that.sortData)).length, direction:direction}
                    }
                } else {
                    delete that.sortData[name]
                }
                if (options.sortAll) {
                    if (options.local === 'remote') {
                        postData = that.$element.data('filterDatas') || {}
                        postData[BJUI.pageInfo.pageSize]    = that.paging.pageSize
                        postData[BJUI.pageInfo.pageCurrent] = that.paging.pageCurrent
                        
                        if (direction) {
                            postData[BJUI.pageInfo.orderField]     = name
                            postData[BJUI.pageInfo.orderDirection] = direction
                        } else {
                            postData[BJUI.pageInfo.orderField]     = ''
                            postData[BJUI.pageInfo.orderDirection] = ''
                        }
                        
                        postData['orders'] = getOrders().join(',')
                        that.orders = {orders : postData['orders']}
                        
                        this.loadData(that.tools.getRemoteFilterData(true), true)
                    } else {
                        sortData(allData, getOrders())
                        this.initTbody(allData, true)
                    }
                } else {
                    sortData(data, getOrders())
                    this.createTrs(data, true)
                }
            },
            showFilterMsg: function(msgs) {
                var $msg = that.$boxH.find('.datagrid-thead-dialog-filter-msg'), $filter = $msg.find('> .msg-filter'), html = BJUI.StrBuilder()
                
                $filter.html('')
                
                if (!msgs.length) return
                
                $.each(msgs, function(i, n) {
                    if (!n) return true
                    
                    var label = n.label, val = n.data.valA
                    
                    if (n.model.type === 'select' || n.model.type === 'boolean') {
                        val = n.model.render.call(that, val, {}, n.model.items, n.model.itemattr)
                    }
                    
                    html.add('；')
                        .add(n.model.label +':'+ val)
                })
                
                html = html.toString()
                html.length > 1 && (html = html.substr(1))
                
                $filter.html(html)
            },
            quickFilter: function(model, filterDatas) {
                if (that.isDom) {
                    if (options.local != 'remote') {
                        BJUI.debug('BJUI.Datagrid: Please change the local option is remote!')
                        return
                    }
                    if (!options.dataUrl) {
                        BJUI.debug('BJUI.Datagrid: Not Set the dataUrl option!')
                        return
                    }
                }
                
                var tools = this, $th = model.th, data = that.data, allData = that.allData, name = model.name, postData, fDatas, msgs = []
                var switchOperator = function(operator, val1, val2, model) {
                    var compare = false
                    
                    switch (operator) {
                    case '=':
                        compare = String(val1) === String(val2)
                        break
                    case '!=':
                        compare = String(val1) !== String(val2)
                        break
                    case '>':
                        compare = parseFloat(val2) > parseFloat(val1)
                        break
                    case '<':
                        compare = parseFloat(val2) < parseFloat(val1)
                        break
                    case 'like':
                        if (model && model.type === 'select') {
                            compare = String(val1) === String(val2)
                            if (model.attrs && model.attrs.multiple) {
                                if ($.isArray(val1) && String(val2)) {
                                    $.each(String(val2).split(','), function(i, n) {
                                        compare = $.inArray(n, val1) != -1
                                        if (compare) return false
                                    })
                                }
                            }
                        } else {
                            compare = String(val2).indexOf(String(val1)) >= 0
                        }
                        break
                    default:
                        break
                    }
                    
                    return compare
                }
                var filterData = function(data, filterDatas) {
                    var grepFun = function(n) {
                        var count = 0
                        
                        $.each(filterDatas, function(name, v) {
                            var op = v.datas
                            
                            count ++
                            if (!op) {
                                count --
                                v.model.isFiltered = false
                                v.model.th.trigger('filter.bjui.datagrid.th', [false])
                                if (v.model.$quickfilter) v.model.$quickfilter.trigger('clearfilter.bjui.datagrid.thead')
                                
                                return true
                            }
                            
                            v.model.isFiltered = true
                            v.model.th.trigger('filter.bjui.datagrid.th', [true])
                            
                            if (!msgs[v.model.index])
                                msgs[v.model.index] = {model:v.model, data:v.datas}
                            
                            if (op.andor) {
                                if (op.andor === 'and') {
                                    if (switchOperator(op.operatorA, op.valA, n[name], v.model) && switchOperator(op.operatorB, op.valB, n[name], v.model)) {
                                        count --
                                    }
                                } else if (op.andor === 'or') {
                                    if (switchOperator(op.operatorA, op.valA, n[name], v.model) || switchOperator(op.operatorB, op.valB, n[name], v.model)) {
                                        count --
                                    }
                                }
                            } else {
                                if (op.operatorB) {
                                    if (switchOperator(op.operatorB, op.valB, n[name], v.model)) {
                                        count --
                                    }
                                } else {
                                    if (switchOperator(op.operatorA, op.valA, n[name], v.model)) {
                                        count --
                                    }
                                }
                            }
                        })
                        
                        return !count ? true : false
                    }
                    
                    return $.grep(data, function(n, i) {
                        return grepFun(n)
                    })
                }
                
                if (!that.filterDatas) that.filterDatas = {}
                if (options.filterMult) {
                    that.filterDatas[name] = {datas:filterDatas, model:model}
                } else {
                    that.filterDatas = {}
                    that.filterDatas[name] = {datas:filterDatas, model:model}
                }
                
                if (options.local !== 'remote' && allData) {
                    if (!that.oldAllData) that.oldAllData = allData.concat()
                    else allData = that.oldAllData.concat()
                }
                
                if (options.filterAll) {
                    if (options.local === 'remote') {
                        tools.loadData(tools.getRemoteFilterData(false, msgs), true)
                    } else {
                        fDatas = filterData(allData, that.filterDatas)
                        
                        that.tools.showFilterMsg(msgs)
                        
                        that.paging.pageCurrent = 1
                        that.paging.pageCount   = this.getPageCount(that.paging.pageSize, fDatas.length)
                        
                        this.initTbody(fDatas, true)
                    }
                } else {
                    if (that.isDom) {
                        tools.loadData(tools.getRemoteFilterData(false, msgs), true)
                    } else {
                        if (!that.oldData) that.oldData = data.concat()
                        else data = that.oldData.concat()
                        
                        fDatas = filterData(data, that.filterDatas)
                        
                        that.tools.showFilterMsg(msgs)
                        
                        this.createTrs(fDatas, true)
                    }
                }
            },
            getRemoteFilterData: function(isPaging, msgs) {
                var filterDatas = {}
                
                if (that.filterDatas && !$.isEmptyObject(that.filterDatas)) {
                    $.each(that.filterDatas, function(name, v) {
                        if (!v.datas) {
                            v.model.isFiltered = false
                            v.model.th.trigger('filter.bjui.datagrid.th', [false])
                            if (v.model.$quickfilter) v.model.$quickfilter.trigger('clearfilter.bjui.datagrid.thead')
                            msgs && msgs[v.model.index] && (msgs[v.model.index] = false)
                            return true
                        }
                        
                        v.model.isFiltered = true
                        v.model.th.trigger('filter.bjui.datagrid.th', [true])
                        
                        if (options.jsonPrefix)
                            name = options.jsonPrefix +'.'+ name
                        
                        if (v.datas.andor)
                            filterDatas['andor'] = v.datas.andor
                        if (v.datas.operatorA) {
                            filterDatas[name] = v.datas.valA
                            filterDatas[name +'.operator'] = v.datas.operatorA
                        }
                        if (v.datas.operatorB) {
                            if (filterDatas[name]) {
                                filterDatas[name] = [filterDatas[name], v.datas.valB]
                                filterDatas[name +'.operator'] = [filterDatas[name +'.operator'], v.datas.operatorB]
                            } else {
                                filterDatas[name] = v.datas.valB
                                filterDatas[name +'.operator'] = v.datas.operatorB
                            }
                        }
                    })
                    
                    if (!isPaging) that.paging.pageCurrent = 1
                }
                
                if (that.initFilter) {
                    var name = options.jsonPrefix ? options.jsonPrefix +'.' : ''
                    
                    $.each(that.initFilter, function(k, v) {
                        if (!that.filterDatas || typeof that.filterDatas[k] === 'undefined')
                            filterDatas[name + k] = v.value
                    })
                }
                
                // paging
                filterDatas[BJUI.pageInfo.pageSize]    = that.paging.pageSize
                filterDatas[BJUI.pageInfo.pageCurrent] = that.paging.pageCurrent || 1
                
                $.extend(filterDatas, that.orders || {})
                
                that.$element.data('filterDatas', filterDatas)
                
                return filterDatas
            },
            // set data for Dom
            setDomData: function(tr) {
                var columnModel = that.columnModel, data = {}, hideDatas = tr.attr('data-hidden-datas'), attach = that.attach
                    
                tr.find('> td').each(function(i) {
                    var $td = $(this), model = columnModel[i], val = $td.attr('data-val') || $td.text()
                    
                    if (!model.name) data['datagrid-noname'+ i] = val
                    else data[model.name] = val
                })
                
                if (hideDatas) hideDatas = hideDatas.toObj()
                
                attach[that.options.keys.gridNumber] = (tr.index() + 1)
                $.extend(data, attach, hideDatas)
                
                tr.data('initData', data)
                
                return data
            },
            // update linenumber
            updateLinenumber: function() {
                if ($.inArray(that.linenumberColumn, that.columnModel) == -1)
                    return
                
                var lock = false
                
                if (that.linenumberColumn.locked) {
                    that.colLock(that.linenumberColumn.th, false)
                    lock = true
                }
                
                that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')').each(function(i) {
                    var $tr = $(this)
                    
                    $tr.find('> td.'+ that.classnames.td_linenumber).text(i + 1)
                })
                
                if (lock)
                    that.colLock(that.linenumberColumn.th, true)
            },
            // update that.data gridIndex && gridNumber
            updateGridIndex: function() {
                var paging = that.paging, options = that.options, startNumber = (paging.pageCurrent - 1) * paging.pageSize
                
                $.each(that.data, function(i, data) {
                    var linenumber = options.linenumberAll ? (startNumber + (i + 1)) : (i + 1)
                    
                    data[options.keys.gridNumber] = linenumber
                    data[options.keys.gridIndex]  = i
                })
            },
            // init filter
            initFilter: function($input, model) {
                var initFilter = that.initFilter
                var doFilter = function($input, model) {
                    $input.val(String(that.options.initFilter[model.name]))
                    
                    if (that.$headFilterUl) {
                        var $headinput = that.$headFilterUl.find('> li.li-'+ model.index +'> '+ $input[0].tagName)
                        
                        if ($headinput.length) {
                            $headinput.val($input.val())
                            if ($headinput.isTag('select'))
                                $headinput.selectpicker('refresh')
                        }
                    }
                    
                    if (that.options.local === 'remote') {
                        model.isFiltered = true
                        model.th.trigger('filter.bjui.datagrid.th', [true])
                    } else {
                        that.tools.quickFilter(model, {operatorA:'like', valA:that.options.initFilter[model.name]})
                    }
                }
                
                if (!$input && !model) {
                    if (initFilter) {
                        $.each(initFilter, function(i, n) {
                            doFilter(n.input, n.model)
                        })
                    }
                    return
                }
                
                if (!initFilter) initFilter = {}
                
                if (!model) return
                
                doFilter($input, model)
                
                if (!initFilter[model.name]) {
                    initFilter[model.name] = {}
                    initFilter[model.name].input = $input
                    initFilter[model.name].model = model
                    initFilter[model.name].value = that.options.initFilter[model.name]
                }
                
                that.initFilter = initFilter
            },
            // init inputs array for edit
            initEditInputs: function() {
                var columnModel = that.columnModel
                
                that.inputs = []
                
                $.each(columnModel, function(i, op) {
                    var name = op.name, rule = '', pattern = '', selectoptions = [], attrs = ''
                    
                    if (!op) return
                    if (op.attrs && typeof op.attrs === 'object') {
                        $.each(op.attrs, function(i, n) {
                            if (typeof n === 'object') n = JSON.stringify(n).replaceAll('\"', '\'')
                            attrs += ' '+ i +'='+ n
                        })
                    }
                    
                    if (op === that.childColumn || op === that.linenumberColumn || op === that.checkboxColumn || op === that.editBtnsColumn) {
                        that.inputs.push('')
                    } else if (name) {
                        if (op.rule) rule = ' data-rule="'+ op.label +'：'+ op.rule +'"'
                        else if (op.type === 'date') rule = ' data-rule="pattern('+ (op.pattern || 'yyyy-MM-dd') +')"';
                        if (op.type) {
                            switch (op.type) {
                            case 'date':
                                if (!op.pattern) op.pattern = 'yyyy-MM-dd'
                                pattern = ' data-pattern="'+ op.pattern +'"'
                                that.inputs.push('<input type="text" name="'+ name +'" data-toggle="datepicker"'+ pattern + rule + attrs +'>')
                                
                                break
                            case 'select':
                                if (!op.items) return
                                
                                $.each(op.items, function(i, n) {
                                    if (op.itemattr) {
                                        selectoptions.push('<option value="'+ n[op.itemattr.value] +'">'+ n[op.itemattr.label] +'</option>')
                                    } else {
                                        $.each(n, function(key, value) {
                                            selectoptions.push('<option value="'+ key +'">'+ value +'</option>')
                                        })
                                    }
                                })
                                
                                that.inputs.push('<select name="'+ name +'" data-toggle="selectpicker"'+ rule + attrs +' data-width="100%">'+ selectoptions.join('') +'</select>')
                                
                                break
                            case 'boolean':
                                that.inputs.push('<input type="checkbox" name="'+ name +'" data-toggle="icheck"'+ rule + attrs +' value="true">')
                                
                                break
                            case 'findgrid':
                                that.inputs.push('<input type="text" name="'+ name +'" data-toggle="findgrid" data-custom-event="true"'+ rule + attrs +'>')
                                
                                break
                            case 'tags':
                                that.inputs.push('<input type="text" name="'+ name +'" data-toggle="tags"'+ attrs +'>')
                                
                                break
                            case 'spinner':
                                that.inputs.push('<input type="text" name="'+ name +'" data-toggle="spinner"'+ rule + attrs +'>')
                                
                                break
                            case 'textarea':
                                that.inputs.push('<textarea data-toggle="autoheight" rows="1"'+ rule + attrs +'></textarea>')
                                
                                break
                            default:
                                that.inputs.push('<input type="text" name="'+ name +'"'+ rule + attrs +'>')
                                break
                            }
                        } else {
                            that.inputs.push('<input type="text" name="'+ name +'"'+ rule + attrs +'>')
                        }
                    } else {
                        that.inputs.push('')
                    }
                })
                
                return that.inputs
            },
            contextmenuH: function() {
                var tools = this
                
                that.$tableH.on('contextmenu', 'tr:not(.datagrid-filter)', function(e) {
                    if (!that.$showhide) tools.createShowhide()
                    
                    var posX = e.pageX, posY = e.pageY, wh = $(window).height(), mh = that.$showhide.data('height')
                    
                    if ($(window).width()  < posX + that.$showhide.width())  posX -= that.$showhide.width()
                    if (posY < 0 || (posY + mh) > wh)
                        posY = 0
                    if (mh > wh)
                        that.$showhide.css({height:wh})
                    else
                        that.$showhide.css({height:''})
                    if (that.$menu) {
                        that.$grid.trigger('click.bjui.datagrid.filter')
                    }
                    
                    that.$showhide
                        .appendTo('body')
                        .css({left:posX, top:posY, opacity:1, 'z-index':9999}).show()
                    
                    $(document).on('click', function(e) {
                        var $showhide = $(e.target).closest('.'+ that.classnames.s_showhide)
                        
                        if (!$showhide.length)
                            that.$showhide.css({left:'50%', top:0, opacity:0.2, 'z-index':''}).hide().appendTo(that.$grid)
                    })
                    
                    e.preventDefault()
                    e.stopPropagation()
                })
            },
            contextmenuB: function($tr, isLock) {
                $tr.contextmenu('show', 
                        {
                            exclude : 'input, .bootstrap-select',
                            items:[
                                {
                                    icon  : 'refresh',
                                    title : BJUI.getRegional('datagrid.refresh'),
                                    func  : function(parent, menu) {
                                        that.refresh()
                                    }
                                },
                                {
                                    title : 'diver'
                                },
                                {
                                    icon  : 'plus',
                                    title : BJUI.getRegional('datagrid.add'),
                                    func  : function(parent, menu) {
                                        that.add()
                                    }
                                },
                                {
                                    icon  : 'edit',
                                    title : BJUI.getRegional('datagrid.edit'),
                                    func  : function(parent, menu) {
                                        var $tr = parent
                                        
                                        if (isLock) $tr = that.$tbody.find('> tr:eq('+ $tr.index() +')')
                                        that.doEditRow($tr)
                                    }
                                },
                                {
                                    icon  : 'undo',
                                    title : BJUI.getRegional('datagrid.cancel'),
                                    func  : function(parent, menu) {
                                        var $tr = parent
                                        
                                        if (isLock) $tr = that.$tbody.find('> tr:eq('+ $tr.index() +')')
                                        
                                        if (!$tr.hasClass(that.classnames.tr_edit)) {
                                            $tr = that.$tbody.find('> tr.'+ that.classnames.tr_edit)
                                        }
                                        that.doCancelEditRow($tr)
                                    }
                                },
                                {
                                    icon  : 'remove',
                                    title : BJUI.getRegional('datagrid.del'),
                                    func  : function(parent, menu) {
                                        var $tr = parent
                                        
                                        if (isLock) $tr = that.$tbody.find('> tr:eq('+ $tr.index() +')')
                                        that.delRows($tr)
                                    }
                                }
                            ]
                        }
                    )
            }
        }
        
        return tools
    }
    
    Datagrid.prototype.init = function() {
        if (!this.$element.isTag('table')) return
        if (this.$element.data('bjui.datagrid.init')) return
        
        this.$element.data('bjui.datagrid.init', true)
        
        var that = this, options = that.options, keys = options.keys, tools = that.tools, $parent = that.$element.parent(), gridHtml = BJUI.StrBuilder()
        
        options.tableWidth = options.tableWidth || ''
        options.width      = options.width || '100%'
        options.height     = options.height || 'auto'
        that.isDom         = false
        that.columnModel   = []
        that.inputs        = []
        that.regional      = BJUI.regional.datagrid
        
        gridHtml
            .add('<div class="bjui-datagrid">')
            .add(options.gridTitle ? '<div class="datagrid-title">'+ options.gridTitle +'</div>' : '')
            .add(options.showToolbar ? '<div class="datagrid-toolbar"></div>' : '')
            .add('<div class="datagrid-box-h"><div class="datagrid-wrap-h"><table class="table table-bordered"><colgroup></colgroup></table></div></div>')
            .add('<div class="datagrid-box-b"><div class="datagrid-wrap-b"></div></div>')
            .add('<div class="datagrid-box-m"></div>')
            .add(options.paging ? '<div class="datagrid-paging-box bjui-paging-box"></div>' : '')
            .add('</div>')
        
        that.$grid    = $(gridHtml.toString()).insertAfter(that.$element).css('height', options.height)
        that.$boxH    = that.$grid.find('> div.datagrid-box-h')
        that.$boxB    = that.$boxH.next()
        that.$boxM    = that.$boxB.next().css('height', options.height)
        that.$boxP    = options.paging ? that.$boxM.next() : null
        that.$boxT    = options.gridTitle ? that.$grid.find('> div.datagrid-title') : null
        that.$toolbar = options.showToolbar ? that.$boxH.prev() : null
        that.$tableH  = that.$boxH.find('> div > table')
        that.$tableB  = that.$element
        // tdtemplate
        that.isTemplate = (options.tdTemplate && options.templateWidth) && options.templateWidth > that.$grid.width() || (options.tdTemplate && !options.templateWidth)
        
        that.$grid.data('bjui.datagrid.table', that.$element.clone())
        that.$boxB.find('> div').append(that.$element)
        
        that.initTop()
        
        if (typeof options.paging === 'string') options.paging = options.paging.toObj()
        that.paging = $.extend({}, {pageSize:30, selectPageSize:'30,60,90', pageCurrent:1, total:0, showPagenum:5}, (typeof options.paging === 'object') && options.paging)
        that.$thead = that.$element.find('> thead')
        that.$tbody = that.$element.find('> tbody')
        that.attach = {}
        
        that.attach[keys.gridNumber]   = 0
        that.attach[keys.gridCheckbox] = '#checkbox#'
        that.attach[keys.gridEdit]     = '#edit#'
        
        if (that.$tbody && that.$tbody.find('> tr').length) {
            that.isDom = true
            
            that.setColumnModel()
            
            that.$tbody.find('> tr > td').each(function() {
                var $td = $(this), html = $td.html()
                
                $td.html('<div>'+ html +'</div>')
            })
            
            if (!that.paging.total) {
                that.paging.total = that.$tbody.find('> tr').length
                that.paging.pageCount = 1
            } else {
                that.paging.pageCount = tools.getPageCount(that.paging.pageSize, that.paging.total)
            }
            
            that.paging.pageCurrent = 1
            that.initThead()
        } else {
            that.$tbody = null
            that.$element.find('> tbody').remove()
            
            if (options.columns) {
                if (typeof options.columns === 'string') {
                    if (options.columns.trim().startsWith('[')) {
                        options.columns = options.columns.toObj()
                    } else {
                        options.columns = options.columns.toFunc()
                    }
                }
                if (typeof options.columns === 'function') {
                    options.columns = options.columns.call()
                }
                
                that.$thead = null
                that.$element.find('> thead').remove()
                that.createThead()
            } else {
                if (that.$thead && that.$thead.length && that.$thead.find('> tr').length) {
                    that.setColumnModel()
                } else {
                    BJUI.debug('BJUI.Datagrid: No set options \'columns\' !')
                    that.destroy()
                    return
                }
            }
            if (options.data || options.dataUrl) {
                that.createTbody()
            } else {
                BJUI.debug('BJUI.Datagrid: No options \'data\' or \'dataUrl\'!')
                that.destroy()
            }
        }
    }
    
    // DOM to datagrid - setColumnModel
    Datagrid.prototype.setColumnModel = function() {
        var that = this, options = that.options, $trs = that.$thead.find('> tr'), rows = [], ths = [], trLen = $trs.length
        
        if (!that.isDom) {
            that.tools.appendColumns()
            
            var $th, _rowspan = trLen > 1 ? ' rowspan="'+ trLen +'"' : ''
            
            if (options.showCheckboxcol) {
                that.columnModel.push(that.checkboxColumn)
                if (options.showCheckboxcol === 'lock') that.checkboxColumn.initLock = true
                
                $th = $('<th class="'+ that.classnames.td_checkbox +'"'+ _rowspan +'><input type="checkbox" data-toggle="icheck"></th>')
                $th.prependTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.checkboxColumn)
            }
            if (options.showLinenumber) {
                that.columnModel.unshift(that.linenumberColumn)
                if (options.showLinenumber === 'lock') that.linenumberColumn.initLock = true
                
                $th = $('<th class="'+ that.classnames.td_linenumber +'"'+ _rowspan +'>No.</th>')
                $th.prependTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.linenumberColumn)
            }
            if (options.showChildcol || (options.showChildcol === undefined && options.hasChild && options.childOptions)) {
                that.columnModel.unshift(that.childColumn)
                
                $th = $('<th class="'+ that.classnames.td_child +'"'+ _rowspan +'>...</th>')
                $th.prependTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.childColumn)
            }
            if (options.showEditbtnscol) {
                $th = $('<th'+ _rowspan +'>'+ that.editBtnsColumn.label +'</th>')
                $th.appendTo($trs.first())
                if (_rowspan) $th.data('datagrid.column', that.editBtnsColumn)
                that.columnModel[$th.index()] = that.editBtnsColumn
            }
        }
        
        if ($trs.length && trLen == 1) {
            $trs.find('> th').each(function(i) {
                var $th = $(this).addClass('single-row').data('index', i), op = $th.data('options'), oW = $th.attr('width') || 'auto', label = $th.html()
                
                if (that.columnModel.length && that.columnModel[i]) {
                    op = that.columnModel[i]
                    op.index = i
                } else {
                    if (op && typeof op === 'string') op = op.toObj()
                    if (typeof op !== 'object') op = {}
                    
                    op.index = i
                    op.label = label
                    op.width = (typeof op.width === 'undefined') ? oW : op.width
                    
                    op = that.tools.setOp(op)
                    
                    that.columnModel[i] = op
                }
                
                that.columnModel[i].th = $th
                
                $th.html('<div><div class="datagrid-space"></div><div class="datagrid-label">'+ label +'</div><div class="'+ that.classnames.th_cell +'"><div class="'+ that.classnames.th_resizemark +'"></div></div></div>')
                if (op.menu && options.columnMenu) $th.addClass(that.classnames.th_menu)
                if (options.fieldSortable && op.quicksort) $th.addClass('datagrid-quicksort-th')
                if (op.align) $th.attr('align', op.align)
            })
        } else { // multi headers
            $trs.each(function(len) {
                var next_rows = [], next_ths = [], index = -1, next_index = 0
                
                if (rows.length) {
                    next_rows = rows.concat()
                    next_ths  = ths.concat()
                }
                rows = []
                ths  = []
                
                $(this).find('> th').each(function(i) {
                    var $th = $(this), op = $th.data('options') || $th.data('datagrid.column') || {}, colspan = parseInt(($th.attr('colspan') || 0), 10), rowspan = parseInt(($th.attr('rowspan') || 0), 10), oW = $th.attr('width') || 'auto', label = $th.html()
                    
                    if (op && typeof op === 'string') op = op.toObj()
                    if (typeof op !== 'object') op = {}
                    if (BJUI.isIE(8) && colspan === 1) colspan = 0
                    
                    op.label = label
                    op.th    = $th
                    if (op[options.keys.gridCheckbox]) op.label = 'Checkbox'
                    
                    index++
                    if (colspan) {
                        op.colspan = colspan
                        for (var start_index = (next_rows.length ? next_rows[index] : index), k = start_index; k < (start_index + colspan); k++) {
                            rows[next_index++]  = k
                            ths[next_index - 1] = op
                        }
                        index += (colspan - 1)
                        
                        $th.data('index', index)
                        
                        if (next_rows.length) {
                            op.parent = next_ths[index]
                        }
                    }
                    if (!rowspan || rowspan == 1) $th.addClass('single-row')
                    if (!colspan) {
                        op.width = (typeof op.width === 'undefined') ? oW : op.width
                        
                        op = that.tools.setOp(op)
                        $th.html('<div><div class="datagrid-space"></div><div class="datagrid-label">'+ label +'</div><div class="'+ that.classnames.th_cell +'"><div class="'+ that.classnames.th_resizemark +'"></div></div></div>')
                        
                        if (op.menu && options.columnMenu) $th.addClass(that.classnames.th_menu)
                        if (options.fieldSortable && op.quicksort) $th.addClass('datagrid-quicksort-th')
                        if (op.align) $th.attr('align', op.align)
                        if (!next_rows.length) {
                            op.index = index
                            that.columnModel[index] = op
                        } else {
                            op.index  = next_rows[index]
                            op.parent = next_ths[index]
                            that.columnModel[next_rows[index]] = op
                        }
                        
                        $th.data('index', op.index)
                    } else {
                        $th.html('<div><div class="datagrid-space"></div><div class="datagrid-label">'+ label +'</div><div class="'+ that.classnames.th_cell +'"><div class="'+ that.classnames.th_resizemark +'"></div></div></div>')
                    }
                })
            })
        }
    }
    
    // create thead by columns
    Datagrid.prototype.createThead = function() {
        var that = this, options = that.options, keys = options.keys, columns = options.columns.concat(), rowArr = [], rows = [], label, align, width, colspan, rowspan, resize, menu,
            columns2Arr = function(columns, rowArr, index, parent) {
                if (!rowArr) rowArr = []
                if (!index)   index = 0
                if (!rowArr[index]) rowArr[index] = []
                
                $.each(columns, function(i, n) {
                    var len = rowArr[index].length, colspan
                    
                    if (parent) n.parent = parent
                    if (n.columns) {
                        colspan = n.columns.length
                        if (index && n.parent) {
                            that.tools.setColspan(n.parent, colspan)
                        }
                        
                        n.index     = that.columnModel.length + colspan - 1
                        n.colspan   = colspan
                        n.quicksort = false
                        rowArr[index][len++] = n
                        
                        return columns2Arr(n.columns, rowArr, index + 1, n)
                    } else {
                        n.rowspan = index
                        n.index   = that.columnModel.length
                        
                        n = that.tools.setOp(n)
                        
                        rowArr[index][len++] = n
                        that.columnModel.push(n)
                    }
                })
                
                return rowArr
            }
        
        that.tools.appendColumns()
        
        if (options.showCheckboxcol) {
            columns.unshift(that.checkboxColumn)
            if (options.showCheckboxcol === 'lock') that.checkboxColumn.initLock = true
        }
        if (options.showLinenumber) {
            columns.unshift(that.linenumberColumn)
            if (options.showLinenumber === 'lock') that.linenumberColumn.initLock = true
        }
        if (options.showChildcol || (options.showChildcol === undefined && options.hasChild && options.childOptions)) {
            columns.unshift(that.childColumn)
        }
        if (options.showEditbtnscol) columns.push(that.editBtnsColumn)
        
        rowArr = columns2Arr(columns, rowArr)
        // the last model can't lock
        that.columnModel[that.columnModel.length - (options.showEditbtnscol ? 2 : 1)].lock = false
        // hidden fields
        if (options.hiddenFields) that.hiddenFields = options.hiddenFields
        // create thead
        that.$thead  = $('<thead></thead>')
        $.each(rowArr, function(i, arr) {
            var $tr = $('<tr style="height:25px;"></tr>'), $num = '<th class="datagrid-number"></th>', $th
            
            $.each(arr, function(k, n) {
                label   = n.label || n.name
                align   = n.align ? (' align="'+ n.align +'"') : ''
                width   = n.width ? (' width="'+ n.width +'"') : ''
                colspan = n.colspan ? ' colspan="'+ n.colspan +'"' : ''
                rowspan = (rowArr.length - n.rowspan > 1) ? ' rowspan="'+ (rowArr.length - n.rowspan) +'"' : ''
                resize  = '<div class="'+ that.classnames.th_resizemark +'"></div>'
                menu    = ''
                
                if (n[keys.gridCheckbox]) label = '<input type="checkbox" data-toggle="icheck">'
                if (n.colspan) align = ' align="center"'
                if (n.thalign) align = ' align="'+ n.thalign +'"'
                if (n.menu && options.columnMenu) menu = ' class="'+ that.classnames.th_menu +'"'
                
                $th = $('<th'+ menu + width + align + colspan + rowspan +'><div><div class="datagrid-space"></div><div class="datagrid-label">'+ label +'</div><div class="'+ that.classnames.th_cell +'">'+ resize +'</div></div></th>')
                $th.data('index', n.index).appendTo($tr)
                
                if (!rowspan) $th.addClass('single-row')
                if (n[keys.gridChild]) $th.addClass(that.classnames.td_child)
                if (n[keys.gridNumber]) $th.addClass(that.classnames.td_linenumber)
                if (n[keys.gridCheckbox]) $th.addClass(that.classnames.td_checkbox)
                if (options.fieldSortable && n.quicksort) $th.addClass('datagrid-quicksort-th')
                
                n.th = $th
            })
            
            $tr.appendTo(that.$thead)
        })
        
        that.$thead.appendTo(that.$element).initui()
    }
    
    Datagrid.prototype.createTbody = function() {
        var that = this, options = that.options, data = options.data, model = that.columnModel, cols = []
        
        if (data) {
            if (typeof data === 'string') {
                if (data.trim().startsWith('[') || data.trim().startsWith('{')) {
                    data = data.toObj()
                } else {
                    data = data.toFunc()
                }
            }
            if (typeof data === 'function') {
                data = data.call()
            }
            
            options.data = data
            that.tools.createTrsByData(data)
        } else if (options.dataUrl) {
            var data = {}
            
            if (typeof options.initFilter === 'object' && options.initFilter && options.jsonPrefix) {
                $.each(options.initFilter, function(k, v) {
                    data[options.jsonPrefix +'.'+ k] = v
                })
            }
            
            that.$element.data('filterDatas', data)
            
            that.tools.loadData(data)
        }
    }
    
    Datagrid.prototype.refresh = function(filterFlag) {
        var that = this, options = that.options, tools = that.tools, isDom = that.isDom, pageInfo = BJUI.pageInfo, paging = that.paging, postData = {}
        
        if (!options.dataUrl) {
            if (options.data && options.data.length) {
                tools.initTbody(that.allData, true)
                return
            }
            
            BJUI.debug('BJUI.Datagrid: Not Set the dataUrl option!')
            return
        }
        
        if (!options.postData || !options.postData[pageInfo.pageSize]) {
            postData[pageInfo.pageSize]    = paging.pageSize
            postData[pageInfo.pageCurrent] = paging.pageCurrent
        }
        if (options.initFilter && typeof options.initFilter === 'object') {
            if (typeof options.initFilter === 'object' && options.initFilter && options.jsonPrefix) {
                $.each(options.initFilter, function(k, v) {
                    postData[options.jsonPrefix +'.'+ k] = v
                })
            }
            that.doInitFilter = true
        }
        if (filterFlag)
            $.extend(postData, that.$element.data('filterDatas') || {})
        
        tools.loadData(postData, true)
        
        // clear fiter
        if (!filterFlag) {
            that.filterDatas = null
            $.each(that.columnModel, function(i, n) {
                n.th.trigger('filter.bjui.datagrid.th', [false])
                n.isFiltered = false
                if (n.$quickfilter) n.$quickfilter.trigger('clearfilter.bjui.datagrid.thead')
            })
            // clear sort
            that.orders = {}
            that.$thead.find('> tr > th.datagrid-quicksort-th').find('> div > .datagrid-label > i').remove()
        }
    }
    
    Datagrid.prototype.refreshParent = function(filterFlag) {
        var that = this, $parent = that.$element.data('bjui.datagrid.parent')
        
        if ($parent && $parent.length)
            $parent.closest('table').datagrid('refresh', filterFlag)
    }
    
    Datagrid.prototype.refreshChild = function(row, filterFlag) {
        var that = this, $trs = that.$tbody.find('> tr'), $table,
            refresh = function(obj) {
                $table = obj.data('bjui.datagrid.child')
                
                if ($table && $table.length && $table.isTag('table'))
                    $table.datagrid('refresh', filterFlag)
            }
        
        if (row instanceof jQuery) {
            row.each(function() {
                refresh($(this))
            })
        } else if (isNaN(row)) {
            $.each(row.split(','), function(i, n) {
                if (n * 2 < $trs.length)
                    refresh($trs.eq(n * 2))
            })
        } else {
            if (row * 2 < $trs.length)
                refresh($trs.eq(row * 2))
        }
    }
    
    // api
    Datagrid.prototype.showChild = function(row, flag, func) {
        if (typeof flag === 'undefined')
            flag = 'toggle'
        
        var that   = this, options = that.options, $child, $td, $table, index, trData, childOptions, dataUrl, $trs = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')'),
            fixedH = function() {
                //if (!options.fullGrid)
                    that.fixedWidth()
                
                that.fixedHeight()
            },
            fixedL = function(index, showorhide, height) {
                if (that.$lockTableB) {
                    var $locktr = that.$lockTableB.find('> tbody > tr:eq('+ index +')')
                    
                    $locktr.toggle(showorhide)
                    
                    if (height)
                        $locktr.height(height)
                    else if (!$locktr.height())
                        $locktr.height($child.height())
                }
            },
            show = function($tr, $child, $table) {
                $td.find('> div').html(BJUI.doRegional(FRAG.gridShrinkBtn, that.regional))
                
                $child.fadeIn('normal', function() {
                    if (!$table.data('bjui.datagrid.init')) {
                        childOptions = $.extend(true, {}, that.childOptions), dataUrl = childOptions.dataUrl
                        
                        if (that.isDom) trData = $tr.data('initData') || that.tools.setDomData($tr)
                        else {
                            trData = that.data[that.tools.getNoChildDataIndex($tr.index())]
                        }
                        
                        if (dataUrl && !dataUrl.isFinishedTm()) {
                            dataUrl = that.tools.replacePlh(dataUrl, trData)
                            
                            if (!dataUrl.isFinishedTm()) {
                                BJUI.debug('BJUI.Datagrid: The datagrid options \'childOptions\' in the \'dataUrl\' options is incorrect: '+ dataUrl)
                            } else {
                                childOptions.dataUrl = dataUrl
                            }
                        }
                        
                        $table
                            .datagrid(childOptions)
                            .data('bjui.datagrid.parent', $tr)
                            .on('completed.bjui.datagrid', $.proxy(function() {
                                fixedH()
                                fixedL(index, true, $child.outerHeight())
                                
                                if (func)
                                    func.apply(that, [$table])
                            }, that))
                            .on('bjui.datagrid.child.resize', function() {
                                $table
                                    .datagrid('fixedWidth')
                                    .datagrid('fixedHeight')
                            })
                        
                        $tr.data('bjui.datagrid.child', $table)
                    } else {
                        fixedH()
                        fixedL(index, true)
                        
                        if (func)
                            func.apply(that, [$table])
                    }
                })
            },
            hide = function($tr, $child, $table) {
                $child.fadeOut('normal', function() {
                    $td.find('> div').html(BJUI.doRegional(FRAG.gridExpandBtn, that.regional))
                    
                    fixedH()
                    fixedL(index, false)
                    
                    if (func)
                        func.apply(that, [$table])
                })
            },
            showhide = function($tr) {
                $child = $tr.next('.'+ that.classnames.tr_child)
                
                if ($child && $child.length) {
                    $td    = $tr.find('> td.'+ that.classnames.td_child)
                    index  = $child.index()
                    $table = $child.find('> td').find('table.table-child')
                    
                    if ($table && $table.length) {
                        if (flag && flag === 'toggle') {
                            $child.is(':hidden') ? show($tr, $child, $table) : hide($tr, $child, $table)
                        } else {
                            flag ? show($tr, $child, $table) : hide($tr, $child, $table)
                        }
                    }
                }
            }
        
        if (func) {
            if (typeof func === 'string')
                func = func.toFunc()
            if (typeof func !== 'function')
                func = false
        }
        
        if (row instanceof jQuery) {
            row.each(function() {
                showhide($(this))
            })
        } else if (isNaN(row)) {
            $.each(row.split(','), function(i, n) {
                var tr = $trs.eq(parseInt(n.trim(), 10))
                
                if (tr && tr.length)
                    showhide(tr)
            })
        } else if (!isNaN(row)) {
            var tr = $trs.eq(row)
            
            if (tr && tr.length)
                showhide(tr)
        }
    }
    
    // api
    Datagrid.prototype.updateRow = function(row, updateData) {
        var that = this, options = that.options, data_index, data, url, $tr,
            doUpdate = function(tr, updateData) {
                $.extend(data, typeof updateData === 'object' && updateData)
                
                that.dialogEditComplete(tr, data)
                
                // refresh child
                if (data['refresh.datagrid.child']) {
                    var $child = tr.data('bjui.datagrid.child')
                    
                    if ($child && $child.length)
                        $child.datagrid('refresh')
                    else
                        that.showChild(row)
                }
                
                // fixedH
                if (options.height === 'auto') {
                    var scrollTop = that.$boxB.scrollTop()
                    
                    that.$boxB.scrollTop(5)
                    if (that.$boxB.scrollTop()) {
                        that.fixedHeight()
                        that.$boxB.scrollTop(scrollTop)
                    }
                }
            }
        
        if (row instanceof jQuery) {
            $tr = row
        } else if (!isNaN(row)) {
            $tr = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')').eq(parseInt(row, 10))
        } else {
            BJUI.debug('BJUI.Datagrid: Func \'updateRow\', Parameter \'row\' is incorrect!')
            return
        }
        
        if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
        else {
            data_index = that.tools.getNoChildDataIndex($tr.index())
            data = that.data[data_index]
        }
        
        if (updateData) {
            if (typeof updateData === 'string') {
                if (updateData.trim().startsWith('{'))
                    updateData = updateData.toObj()
                else
                    updateData = updateData.toFunc()
            }
            if (typeof updateData === 'function')
                updateData = updateData.apply()
                
            if (typeof updateData !== 'object' && !options.updateRowUrl) {
                BJUI.debug('BJUI.Datagrid: Func \'updateRow\', Parameter \'updateData\' is incorrect!')
                return
            }
            
            doUpdate($tr, updateData)
        } else if (options.updateRowUrl) {
            url = that.tools.replacePlh(options.updateRowUrl, data)
            
            if (!url.isFinishedTm()) {
                BJUI.debug('BJUI.Datagrid: The datagrid options \'updateRowUrl\' is incorrect!')
            } else {
                BJUI.ajax('doajax', {
                    url       : url,
                    type      : options.loadType,
                    okCallback: function(json) {
                        doUpdate($tr, json)
                    }
                })
            }
        } else {
            BJUI.debug('BJUI.Datagrid: The datagrid options \'updateRowUrl\' is not set!')
        }
    }
    
    // api
    Datagrid.prototype.doAjaxRow = function(row, opts) {
        var that = this, options = that.options, $tr, data, data_index, url
        
        if (row instanceof jQuery) {
            $tr = row
        } else if (!isNaN(row)) {
            $tr = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')').eq(parseInt(row, 10))
        } else {
            BJUI.debug('BJUI.Datagrid: Func \'doAjaxRow\', Parameter \'row\' is incorrect!')
            return
        }
        if (typeof opts === 'object' && opts.url) {
            if (typeof opts.reload === 'undefined')
                opts.reload = false
            if (!opts.callback && !opts.okCallback) {
                opts.okCallback = function(json, options) {
                    that.updateRow($tr, typeof json === 'object' && !json[BJUI.keys.statusCode] && json)
                }
            }
            
            if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
            else {
                data_index = that.tools.getNoChildDataIndex($tr.index())
                data = that.data[data_index]
            }
            
            url = opts.url
            
            if (!data || data.addFlag)
                url = url.replace(/{\/?[^}]*}/g, '')
            else
                url = that.tools.replacePlh(url, data)
            
            if (!url.isFinishedTm()) {
                BJUI.debug('BJUI.Datagrid: Func \'doAjaxRow\', options \'url\' is incorrect: '+ url)
            }
            
            opts.url = url
            opts.loadingmask = true
            opts.target = that.$boxB
            
            BJUI.ajax('doajax', opts)
        } else {
            BJUI.debug('BJUI.Datagrid: Func \'doAjaxRow\', options is incorrect or the property \'url\' is not set!')
        }
    }
    
    Datagrid.prototype.filter = function(data) {
        var that = this, options = that.options, nodatatr = that.$tbody.find('> tr.datagrid-nodata').length
        
        if (data && typeof data === 'object') {
            if (data.clearOldPostData || !options.postData)
                options.postData = {}
            
            $.extend(options.postData, data)
        }
        
        // reset pageCurrent
        that.paging.pageCurrent = 1
        
        that.refresh()
        
        if (nodatatr) {
            that.$element.one('afterLoad.bjui.datagrid', function() {
                that.needfixedWidth = true
                that.fixedWidth()
                that.needfixedWidth = null
            })
        }
    }
    
    Datagrid.prototype.reload = function(option) {
        var that = this, options = that.options, data, $element
        
        if (option && typeof option === 'object') {
            if (option.clearOldPostData)
                delete options.postData
            
            if (option.columns)
                delete options.columns
            
            $.extend(true, options, option)
        }
        
        that.destroy()
        
        $element = that.$element
        $element.data('bjui.datagrid', (data = new Datagrid($element, options)))
        
        if (data)
            data.init()
    }
    
    Datagrid.prototype.destroy = function() {
        var that = this, $element = that.$grid.data('bjui.datagrid.table')
        
        if ($element) {
            that.$element
                .html($element.html())
                .removeData()
                .insertBefore(that.$grid)
            
            that.$grid.remove()
        }
    }
    
    Datagrid.prototype.initTop = function() {
        var that = this, options = that.options, regional = that.regional, hastoolbaritem = false, $group, groupHtml = '<div class="btn-group" role="group"></div>', btnHtml = '<button type="button" class="btn" data-icon=""></button>'
        
        if (options.showToolbar) {
            if (options.toolbarItem || options.toolbarCustom) {
                if (options.toolbarItem) {
                    var itemFunc = options.toolbarItem.toFunc()
                    
                    if (typeof itemFunc === 'function') {
                        options.toolbarItem = itemFunc.apply()
                    }
                    
                    hastoolbaritem = true
                    if (options.toolbarItem.indexOf('all') >= 0) options.toolbarItem = 'add, edit, cancel, save, |, del, |, refresh, |, import, export, exportf'
                    $.each(options.toolbarItem.split(','), function(i, n) {
                        n = n.trim().toLocaleLowerCase()
                        if (!$group || n === '|') {
                            $group = $(groupHtml).appendTo(that.$toolbar)
                            if (n === '|') return true
                        }
                        
                        if (n === 'add') {
                            that.$toolbar_add = $(btnHtml).attr('data-icon', 'plus').addClass('btn-blue').text(options.addName || BJUI.getRegional('datagrid.add'))
                                .appendTo($group)
                                .on('click', function(e) {
                                    that.add()
                                })
                        } else if (n === 'edit') {
                            that.$toolbar_edit = $(btnHtml).attr('data-icon', 'edit').addClass('btn-green').text(options.editName || BJUI.getRegional('datagrid.edit'))
                                .appendTo($group)
                                .on('click', function(e) {
                                    var $selectTrs = that.$tbody.find('> tr.'+ that.classnames.tr_selected)
                                    
                                    if (!options.editMode) return false
                                    if (!$selectTrs.length) {
                                        $(this).alertmsg('info', BJUI.getRegional('datagrid.selectMsg'))
                                    } else {
                                        if (options.inlineEditMult) {
                                            that.doEditRow($selectTrs)
                                        } else {
                                            if (that.$lastSelect) that.doEditRow(that.$lastSelect)
                                            else that.doEditRow($selectTrs.first())
                                        }
                                    }
                                })
                        } else if (n === 'cancel') {
                            that.$toolbar_calcel = $(btnHtml).attr('data-icon', 'undo').addClass('btn-orange').text(options.cancelName || BJUI.getRegional('datagrid.cancel'))
                                .appendTo($group)
                                .on('click', function(e) {
                                    that.doCancelEditRow(that.$tbody.find('> tr.'+ that.classnames.tr_edit))
                                })
                        } else if (n === 'save') {
                            that.$toolbar_save = $(btnHtml).attr('data-icon', 'save').addClass('btn-default').text(options.saveName || BJUI.getRegional('datagrid.save'))
                                .appendTo($group)
                                .on('click', function(e) {
                                    that.doSaveEditRow()
                                })
                        } else if (n === 'del') {
                            that.$toolbar_del = $(btnHtml).attr('data-icon', 'times').addClass('btn-red').text(options.delName || BJUI.getRegional('datagrid.del'))
                                .appendTo($group)
                                .on('click', function(e) {
                                    var $selectTrs = that.$tbody.find('> tr.'+ that.classnames.tr_selected)
                                    
                                    if ($selectTrs.length) {
                                        that.delRows($selectTrs)
                                    } else {
                                        $(this).alertmsg('info', BJUI.getRegional('datagrid.selectMsg'))
                                    }
                                })
                        } else if (n === 'refresh') {
                            that.$toolbar_refresh = $(btnHtml).attr('data-icon', 'refresh').addClass('btn-green').text(options.refreshName || BJUI.getRegional('datagrid.refresh'))
                                .appendTo($group)
                                .on('click', function(e) {
                                    that.refresh()
                                })
                        } else if (n === 'import') {
                            that.$toolbar_add = $(btnHtml).attr('data-icon', 'sign-in').addClass('btn-blue').text(options.importName || BJUI.getRegional('datagrid.import'))
                                .appendTo($group)
                                .on('click', function(e) {
                                    if (options.importOption) {
                                        var opts = options.importOption
                                        
                                        if (typeof opts === 'string')
                                            opts = opts.toObj()
                                        
                                        if (opts.options && opts.options.url) {
                                            if (opts.type === 'dialog') {
                                                that.$grid.dialog(opts.options)
                                            } else if (opts.type === 'navtab') {
                                                that.$grid.navtab(opts.options)
                                            } else {
                                                that.$grid.bjuiajax('doajax', opts.options)
                                            }
                                        }
                                    }
                                })
                        } else if (n === 'export') {
                            that.$toolbar_add = $(btnHtml).attr('data-icon', 'sign-out').addClass('btn-green').text(options.exportName || BJUI.getRegional('datagrid.export'))
                                .appendTo($group)
                                .on('click', function(e) {
                                    if (options.exportOption) {
                                        var opts = options.exportOption
                                        
                                        if (typeof opts === 'string')
                                            opts = opts.toObj()
                                        
                                        if (opts.options && opts.options.url) {
                                            if (!opts.options.data)
                                                opts.options.data = {}
                                            
                                            $.extend(opts.options.data, that.$element.data('filterDatas') || {}, that.sortData || {})
                                            opts.options.type = 'POST'
                                            
                                            if (opts.type === 'dialog') {
                                                BJUI.dialog(opts.options)
                                            } else if (opts.type === 'navtab') {
                                                BJUI.navtab(opts.options)
                                            } else if (opts.type === 'file') {
                                                opts.options.target = that.$boxB
                                                BJUI.ajax('ajaxdownload', opts.options)
                                            } else {
                                                BJUI.ajax('doajax', opts.options)
                                            }
                                        }
                                    }
                                })
                        } else if (n === 'exportf') {
                            that.$toolbar_add = $(btnHtml).attr('data-icon', 'filter').addClass('btn-green').text(options.exportfName || BJUI.getRegional('datagrid.exportf'))
                                .appendTo($group)
                                .on('click', function(e) {
                                    if (options.exportOption) {
                                        var opts = options.exportOption, filterDatas = that.tools.getRemoteFilterData(true)
                                        
                                        if (typeof opts === 'string')
                                            opts = opts.toObj()
                                        
                                        if (opts.options && opts.options.url) {
                                            if (!opts.options.data)
                                                opts.options.data = {}
                                            
                                            $.extend(opts.options.data, filterDatas, that.sortData || {})
                                            
                                            opts.options.type = 'POST'
                                            
                                            if (opts.type === 'dialog') {
                                                BJUI.dialog(opts.options)
                                            } else if (opts.type === 'navtab') {
                                                BJUI.navtab(opts.options)
                                            } else if (opts.type === 'file') {
                                                opts.options.target = that.$boxB
                                                BJUI.ajax('ajaxdownload', opts.options)
                                            } else {
                                                BJUI.ajax('doajax', opts.options)
                                            }
                                        }
                                    }
                                })
                        }
                    })
                }
                
                if (options.toolbarCustom) {
                    var $custom, $custombox = $('<div style="display:inline-block;"></div>')
                    
                    if (typeof options.toolbarCustom === 'string') {
                        var custom = $(options.toolbarCustom)
                        
                        if (custom && custom.length) {
                            $custom = custom
                        } else {
                            custom = custom.toFunc()
                            if (custom) {
                                $custom = custom.call(that)
                                if (typeof $custom === 'string') $custom = $($custom)
                            }
                        }
                    } else if (typeof options.toolbarCustom === 'function') {
                        $custom = options.toolbarCustom.call(that)
                        if (typeof $custom === 'string') $custom = $($custom)
                    } else {
                        $custom = options.toolbarCustom
                    }
                    
                    if ($custom && $custom.length && typeof $custom !== 'string') {
                        if (hastoolbaritem) {
                            $custombox.css('margin-left', '5px')
                        }
                        $custombox.appendTo(that.$toolbar)
                        $custom.appendTo($custombox)
                    }
                }
                
                that.$toolbar.initui()
            }
        }
    }
    
    Datagrid.prototype.initThead = function() {
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, width, cols = []
        
        that.$tableH.append(that.$thead)
        
        that.init_thead    = true
        that.$trsH         = that.$thead.find('> tr')
        that.table_width   = 0
        that.$colgroupH    = that.$tableH.find('> colgroup')
        that.fixtedColumnWidthCount = 0
        
        $.each(that.columnModel, function(i, n) {
            var lockWidth = ''
            
            if (n === that.checkboxColumn || n === that.childColumn || n === that.editBtnsColumn || n.finalWidth)
                lockWidth = ' class="datagrid_col_lockwidth"'
            
            width = n.width
            
            if ((!width || width === 'auto') && !that.hasAutoCol) {
                that.hasAutoCol = true
            }
            
            cols.push('<col style="width:'+ (width && width !== 'auto' ? width +'px' : 'auto') +'"'+ lockWidth +'>')
            n.width = width
        })
        
        that.table_width = that.$grid.width()
        
        that.$colgroupH.html(cols.join(''))
        
        // thead - events
        var $ths = that.$trsH.find('> th')
        // events - quicksort
        $ths.filter('.datagrid-quicksort-th')
            .on('click.bjui.datagrid.quicksort', function(e) {
                var $target = $(e.target)
                
                if (!$target.closest('.'+ that.classnames.th_cell).length && !that.isResize)
                    tools.quickSort(columnModel[$(this).data('index')])
            })
        
        // events - filter
        $ths.filter('.datagrid-column-menu')
            .on('filter.bjui.datagrid.th', function(e, flag) {
                var $th = $(this), $btn = $th.find('> div > .'+ that.classnames.th_cell +'> .'+ that.classnames.btn_menu +'> .btn')
                
                if (flag) {
                    $th.addClass('filter-active')
                    $btn.find('> i').attr('class', 'fa fa-filter')
                } else {
                    $th.removeClass('filter-active')
                    $btn.find('> i').attr('class', 'fa fa-bars')
                }
            })
        
        // events - contextmenu
        if (options.contextMenuH) {
            tools.contextmenuH()
        }
        
        that.initTbody()
        
        if (options.columnResize) that.colResize()
        if (options.columnMenu)   that.colMenu()
        if (options.paging)       that.initPaging()
        if (options.editMode)     that.edit()
        
        var delayFunc = function() {
            if (options.showTfoot) that.initTfoot()
            
            /* render */
            if (that.renderTds && that.renderTds.length) {
                var $trs = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +', .datagrid-nodata)'), j = that.renderTds && that.renderTds.length, fixedWidthModel = {}
                var $tempTrs = $(), tempDatas = []
                
                for (var i = 0; i < j; i++) {
                    var obj = that.renderTds[i], label = obj.render.call(that, obj.label, obj.data, that.columnModel[obj.tdindex].items, that.columnModel[obj.tdindex].itemattr)
                    var tempData = $.extend({}, obj.data)
                    
                    if (!fixedWidthModel[obj.tdindex] && that.columnModel[obj.tdindex].fixedWidth)
                        fixedWidthModel[obj.tdindex] = that.columnModel[obj.tdindex]
                    
                    if (that.isTemplate) {
                        var tempData = {}, tempIndex = $.inArray($trs.eq(obj.trindex)[0], $tempTrs)
                        
                        if (tempIndex == -1) {
                            $tempTrs = $tempTrs.add($trs.eq(obj.trindex))
                            
                            tempData = $.extend({}, obj.data)
                            tempData[that.columnModel[obj.tdindex]['name']] = label
                            
                            tempDatas.push(tempData)
                        } else {
                            tempDatas[tempIndex][that.columnModel[obj.tdindex]['name']] = label
                        }
                    } else {
                        $trs.eq(obj.trindex).find('> td:eq('+ obj.tdindex +') > div:last').html(label)
                    }
                }
                
                // for tdTemplate
                $.each($tempTrs, function(i) {
                    var $td = $(this).find('> td.datagrid-template-td'), html = $td.html()
                    
                    if ($td.length) {
                        $td.html(tools.replacePlh4Template(html, tempDatas[i]))
                    }
                })
            }
            
            if (that.isTemplate)
                that.$grid.data('bjui.datagrid.trs.template', that.$tbody.html())
            else
                that.$grid.data('bjui.datagrid.trs.normal', that.$tbody.html())
            
            that.initEvents()
            that.$tableB.initui()
            that.$lockTableB && that.$lockTableB.initui()
            
            tools.setBoxbH()
            
            /* tree - collapse */
            if (that.collapseIndex && that.collapseIndex.length) {
                that.expand(that.collapseIndex.join(','), false)
                that.collapseIndex = null
            }
            
            setTimeout(function() {
                that.fixedWidth('init')
                that.initLock()
                that.$element.trigger('completed.bjui.datagrid', {datas:that.data})
                that.$boxM && that.$boxM.trigger('bjui.ajaxStop').hide()
                
                if ((that.options.width.endsWith('%') && 
                    (that.options.flowLayoutWidth && that.$grid.width() < that.options.flowLayoutWidth)
                        || that.options.dialogFilterW && that.$grid.width() < that.options.dialogFilterW)
                        || that.options.dialogFilterW === 0) {
                    $(window).resize()
                }
                
                that.resizeGrid()
            }, 50)
            
            that.delayFilterTimeout && clearInterval(that.delayFilterTimeout)
        }
        
        if (options.filterThead) {
            if (that.delayRender) {
                that.delayFilterTimeout = setInterval(function() {
                    if (!that.delayRender) {
                        that.filterInThead()
                        delayFunc()
                    }
                }, 100)
            } else {
                that.filterInThead()
                delayFunc()
            }
        } else {
            delayFunc()
        }
    }
    
    Datagrid.prototype.fixedWidth = function(isInit) {
        var that = this, options = that.options, bW, excludeW = 0, fixedW, columnModel = that.columnModel, length = columnModel.length
        
        if (isInit && that.initFixedW) return
        that.initFixedW = true
        
        var setNewWidth = function() {
            if (String(that.options.width).endsWith('%'))
                that.$grid.css('width', '')
            
            that.$boxH.find('> div').css('width', '')
            that.$boxB.find('> div').css('width', '')
            that.$boxF && that.$boxF.find('> div').css('width', '')
            that.$boxP && that.$boxP.find('> div.paging-content').css('width', '')
            
            bW = (that.$boxB.find('> div'))[0].clientWidth
            
            if (that.options.hasChild) {
                var scrollTop = that.$boxB.scrollTop()
                
                that.$boxB.scrollTop(1)
                
                if (that.$boxB.scrollTop()) {
                    that.$boxB.scrollTop(scrollTop)
                }
            }
            if (that.$element.hasClass('table-child'))
                bW = bW - 18
            
            that.$boxB.find('> div').width(bW)
            that.$boxH.find('> div').width(bW)
            that.$boxF && that.$boxF.find('> div').width(bW)
            
            if (that.table_width > bW || options.fullGrid)
                that.$boxP && that.$boxP.find('> div.paging-content').width(bW)
            else
                that.$boxP && that.$boxP.find('> div.paging-content').width(that.table_width)
            
            if (options.fullGrid || that.isTemplate || that.needfixedWidth || (that.hasAutoCol && isInit)) {
                $('body').find('> .bjui-datagrid').remove()
                
                var oldTheight = that.$tableH.height(),
                    $grid = $('<div class="bjui-datagrid forwidth"></div>'),
                    $tableB = that.$element.clone().addClass('table table-bordered').appendTo($grid)
                
                $tableB.find('> tbody').before(that.$thead.clone())
                
                $tableB.find('> colgroup > col').each(function(i) {
                    var $col = $(this), dataLength = that.data.length ? String(that.data[that.data.length - 1].gridNumber).length : 2
                    
                    if ($col.hasClass('datagrid_col_lockwidth')) return true
                    if (columnModel[i] === that.linenumberColumn) {
                        dataLength == 1 && (dataLength = 2)
                        $col.width(dataLength * 15)
                    } else if (!columnModel[i].width || columnModel[i].width === 'auto' || that.isTemplate)
                        $col.width('')
                })
                
                $grid.width(bW + 17).height(that.$boxB.height()).prependTo($('body'))
                
                if (!(options.fullGrid || that.isTemplate) && options.hScrollbar) {
                    $grid.width('')
                }
                
                if (that.isTemplate)
                    options.tableWidth = '100%'
                
                $tableB.attr('style', 'width:'+ (options.tableWidth || 'auto') +' !important;')
                $tableB.find('thead th > div').height('')
                
                var $tr = $tableB.find('> tbody > tr:first')
                
                if (!$tr.length || that.isTemplate || $tr.hasClass('datagrid-nodata')) {
                    var trHtml = BJUI.StrBuilder()
                    
                    trHtml.add('<tr>')
                    
                    for (var i = 0, j = $tableB.find('colgroup > col').length; i < j; i++) {
                        trHtml.add('<td></td>')
                    }
                    
                    trHtml.add('</tr>')
                    
                    $tableB.find('> tbody').prepend(trHtml.toString())
                    $tr = $tableB.find('> tbody > tr:first')
                }
                
                if (that.isTemplate) {
                    that.$boxP && that.$boxP.find('> div.paging-content').width('100%')
                } 
                
                var $ths = that.$thead.find('th')
                
                $tableB.find('thead > tr:not(.datagrid-filter) > th').each(function(i) {
                    var $this = $(this), v = $this.is(':hidden')
                    
                    if (v) $this.show()
                    $ths.eq(i).find('> div').height($this.find('> div').height())
                    if (v) $this.hide()
                })
                
                that.$boxB.height(that.$boxB.height() + (oldTheight - $tableB.find('> thead').height()))
                that.$boxM && that.$boxM.height(that.$boxB.height()).css('top', that.$boxB.position().top)
                that.$lockB && that.$lockB.height(that.$boxB.height())
                
                $tr.find('> td').each(function(i) {
                    var $col = that.$colgroupB.find('> col:eq('+ i +')')
                    
                    !($col.hasClass('datagrid_col_lockwidth')) && $col.width($(this).outerWidth())
                })
                
                that.$colgroupH.html(that.$colgroupB.html())
                
                if (!that.options.noremove)
                    $grid.remove()
            }
        }
        
        setNewWidth()
    }
    
    Datagrid.prototype.fixedHeight = function(height) {
        var that = this, options = that.options
        
        if (options.height === 'auto') {
            that.boxH = 'auto'
            that.$grid.css('height', '')
            that.$boxB.css('height', '')
            that.tools.setBoxbH()
        } else
            that.tools.setBoxbH(height)
        
        // if scrollLeft
        var scrollLeft = that.$boxB.scrollLeft()
        
        that.$boxB.scrollLeft(5)
        
        if (that.$boxB.scrollLeft()) {
            that.fixedWidth()
            that.$boxB.scrollLeft(scrollLeft)
        }
    }
    
    Datagrid.prototype.initTbody = function() {
        var that = this, options = that.options, tools = that.tools, $trs = that.$tbody.find('> tr'), $tds = $trs.find('> td'), width = options.tableWidth || '0'
        
        that.init_tbody = true
        that.$colgroupB = that.$colgroupH.clone()
        
        that.$tableB.prepend(that.$colgroupB)
        
        if (options.fullGrid || that.isTemplate) width = '100%'
        
        that.$tableH.css('width', width)
        that.$tableB.css('width', width)
        
        // add class
        that.$tableB.removeAttr('data-toggle width').addClass('table table-bordered').removeClass('table-hover')
        
        that.$boxB
            .scroll(function() {
                that.$boxH.find('> div').prop('scrollLeft', this.scrollLeft)
                that.$boxF && that.$boxF.find('> div').prop('scrollLeft', this.scrollLeft)
                that.$lockB && that.$lockB.prop('scrollTop', this.scrollTop)
            })
        
        // if DOM to datagrid
        if (that.isDom) {
            if (options.showLinenumber) {
                that.showLinenumber(options.showLinenumber)
            }
            if (options.showCheckboxcol) {
                that.showCheckboxcol(options.showCheckboxcol)
            }
            if (options.showEditbtnscol) {
                that.showEditCol(options.showEditbtnscol)
            }
            
            that.$grid.data(that.datanames.tbody, that.$tbody.clone())
        }
    }
    
    // init events(only tbody)
    Datagrid.prototype.initEvents = function($trs) {
        var that = this, options = that.options, trs = that.$tbody.find('> tr')
        
        if (!$trs) $trs = trs
        
        $trs.on('click.bjui.datagrid.tr', function(e, checkbox) {
            var $tr = $(this), index = $tr.index(), data, $selectedTrs = that.$tbody.find('> tr.'+ that.classnames.tr_selected), $last = that.$lastSelect, checked, $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr')
            
            if (checkbox) {
                checked = checkbox.is(':checked')
                if (!checked) that.$lastSelect = $tr
                that.selectedRows($tr, !checked)
            } else {
                if ($tr.hasClass(that.classnames.tr_edit)) return
                if (options.selectMult) {
                    that.selectedRows($tr)
                } else {
                    if (!BJUI.KeyPressed.ctrl && !BJUI.KeyPressed.shift) {
                        if ($selectedTrs.length > 1 && $tr.hasClass(that.classnames.tr_selected)) {
                            that.selectedRows($selectedTrs.not($tr))
                            that.$lastSelect = $tr
                        } else {
                            if ($selectedTrs.length && $selectedTrs[0] != this) that.selectedRows(null)
                            if (!$tr.hasClass(that.classnames.tr_selected)) that.$lastSelect = $tr
                            that.selectedRows($tr)
                        }
                    } else {
                        //window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty() //clear selection
                        
                        if (BJUI.KeyPressed.ctrl) {
                            if (!$tr.hasClass(that.classnames.tr_selected)) that.$lastSelect = $tr
                            that.selectedRows($tr)
                        } else if (BJUI.KeyPressed.shift) {
                            if (!$last) $last = that.$lastSelect = $tr
                            if ($last.length) {
                                that.selectedRows(null)
                                if ($last.index() != index) {
                                    if ($last.index() > index) {
                                        that.selectedRows($tr.nextUntil($last).add($tr).add($last), true)
                                    } else {
                                        that.selectedRows($tr.prevUntil($last).add($tr).add($last), true)
                                    }
                                } else {
                                    that.selectedRows(index)
                                }
                            }
                        }
                    }
                }
            }
            
            if (that.isDom)
                data = that.tools.setDomData($tr)
            else
                data = that.data[that.tools.getNoChildDataIndex(index)]
            
            that.$element.trigger('clicked.bjui.datagrid.tr', {target:e.target, tr:$tr, data:data})
        })
        .on('mouseenter.bjui.datagrid', function(e) {
            var $tr = $(this), index = $tr.index()
            
            if (that.isDrag) return false
            
            $tr.addClass('datagrid-hover')
            that.$lockTbody && that.$lockTbody.find('> tr:eq('+ index +')').addClass('datagrid-hover')
        })
        .on('mouseleave.bjui.datagrid', function(e) {
            var $tr = $(this), index = $tr.index()
            
            if (that.isDrag) return false
            
            $tr.removeClass('datagrid-hover')
            that.$lockTbody && that.$lockTbody.find('> tr:eq('+ index +')').removeClass('datagrid-hover')
        })
        // custom event - delete 
        .on('delete.bjui.datagrid.tr', function(e) {
            e.stopPropagation()
            
            var $tr = $(this), tr_index = $tr.index(), data_index = tr_index, data = that.data, gridIndex, allData = that.allData, $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr')
            
            if ($tr.hasClass('datagrid-nodata')) return false
            
            if (that.options.hasChild && that.options.childOptions) {
                data_index = data_index / 2
                
                // remove child dom
                $tr.next().remove()
                $lockTrs && $lockTrs.eq(tr_index).next().remove()
            }
            
            /* remove tree - child */
            if (that.options.isTree) {
                var $childrens = that.getChildrens($tr, null, true), len = $childrens.length
                
                if (that.options.hasChild && that.options.childOptions)
                    len = len / 2
                
                $childrens.remove()
                
                if (!that.isDom) {
                    gridIndex    = data[data_index].gridIndex
                    
                    that.data.splice(data_index + 1, len)
                    that.allData.splice(gridIndex + 1, len)
                }
            }
            
            if (!that.isDom) {
                gridIndex    = data[data_index].gridIndex
                that.data    = data.remove(data_index)     // remove data in the current page data
                that.allData = allData.remove(gridIndex)   // remove data in allData
                
                that.tools.updateGridIndex()
            }
            
            /* update gridNumber */
            if ($.inArray(that.linenumberColumn, that.columnModel) != -1) {
                $tr.nextAll(':not(.'+ that.classnames.tr_child +')').each(function() {
                    var $td = $(this).find('> td.'+ that.classnames.td_linenumber), num = parseInt($td.text(), 10)
                    
                    $td.text(num - 1)
                })
                
                $lockTrs && $lockTrs.eq(tr_index).trigger('delete.bjui.datagrid.tr', [tr_index])
            }
            
            // remove dom
            $tr.remove()
            $lockTrs && $lockTrs.eq(tr_index).remove()
            
            // no data
            that.tools.createNoDataTr()
        })
        
        // child
        that.$grid.off('click.bjui.datagrid.tr.child').on('click.bjui.datagrid.tr.child', 'td.'+ that.classnames.td_child, function(e) {
            e.stopPropagation()
            
            var $this = $(this), $tr = $this.closest('tr'), $child = $tr.next('.'+ that.classnames.tr_child)
            
            if ($child && $child.length)
                that.showChild($tr, !$child.is(':visible'))
        })
        // td checkbox
        .off('ifClicked').on('ifClicked', 'td.'+ that.classnames.td_checkbox +' input', function(e) {
            e.stopPropagation()
            
            var $this = $(this), $tr = $this.closest('tr'), tr_index = $tr.index()
            
            that.$tbody.find('> tr:eq('+ tr_index +')').trigger('click.bjui.datagrid.tr', [$this])
        })
        // th checkbox - check all
        .off('ifChanged').on('ifChanged', 'th.'+ that.classnames.td_checkbox +' input', function(e) {
            e.stopPropagation()
            
            var checked = $(this).is(':checked'), $trs = that.$tbody.find('> tr:not(".'+ that.classnames.tr_child +'")')
            
            that.selectedRows($trs, checked)
        })
        
        
        //contextmenu
        if (options.contextMenuB) {
            $trs.each(function() {
                that.tools.contextmenuB($(this))
            })
        }
        
        // custom update
        that.$tableB.off('click.bjui.datagrid.refresh').on('click.bjui.datagrid.refresh', 'tbody > tr > td [data-toggle="update.datagrid.tr"]', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            that.updateRow($(this).closest('tr'))
        })
        // custom edit
        .off('click.bjui.datagrid.edit').on('click.bjui.datagrid.edit', 'tbody > tr > td [data-toggle="edit.datagrid.tr"]', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            var $this = $(this), opts = $this.data('options'), $tr = $this.closest('tr')
            
            if (opts && typeof opts === 'string') opts = opts.toObj()
            
            if (typeof opts === 'object')
                that.externalEdit($tr, opts)
            else
                that.doEditRow($tr)
        })
        // custom ajax
        .off('click.bjui.datagrid.ajax').on('click.bjui.datagrid.ajax', 'tbody > tr > td [data-toggle="ajax.datagrid.tr"]', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            var $this = $(this), opts = $this.data()
            
            if (opts.options && typeof opts.options === 'string') opts.options = opts.options.toObj()
            $.extend(opts, typeof opts.options === 'object' && opts.options)
            
            if (!opts.url && $this.attr('href'))
                opts.url = $this.attr('href')
            
            that.doAjaxRow($this.closest('tr'), opts)
        })
        // custom addChild
        .off('click.bjui.datagrid.addchild').on('click.bjui.datagrid.addchild', 'tbody > tr > td [data-toggle="addchild.datagrid.tr"]', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            var $this = $(this), $tr = $this.closest('tr'), $table = $tr.data('bjui.datagrid.child'), $child = $tr.next('.'+ that.classnames.tr_child), trData, data = $this.data('addData'), addData,
                replaceData = function(data) {
                    return data.replace(/#\/?[^#]*#/g, function($1) {
                        var key = $1.replace(/[##]+/g, ''), val = trData[key]
                        
                        if (typeof val === 'undefined' || val === 'null' || val === null)
                            val = ''
                        
                        return val
                    })
                },
                doAdd = function($table) {
                    if (data) {
                        if (typeof data === 'string') {
                            data = replaceData(data)
                            
                            if (data.trim().startsWith('{'))
                                data = data.toObj()
                            else
                                data = data.toFunc()
                        } else {
                            if (typeof data === 'function') {
                                data = data.toFunc()
                            }
                            if (typeof data === 'object') {
                                data = JSON.stringify(data)
                            }
                            
                            data = replaceData(data).toObj()
                        } 
                        
                        if (typeof data === 'object') {
                            addData = data
                        }
                    }
                    
                    $table.datagrid('add', false, addData)
                }
            
            if ($child && $child.length) {
                if (that.isDom) trData = $tr.data('initData') || tools.setDomData($tr)
                else trData = that.data[that.tools.getNoChildDataIndex($tr.index())]
                
                if ($table && $table.length) {
                    doAdd($table)
                } else {
                    that.showChild($tr, true, doAdd)
                }
            }
        })
        // custom delete
        .off('click.bjui.datagrid.del').on('click.bjui.datagrid.del', 'tbody > tr > td [data-toggle="del.datagrid.tr"]', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            that.delRows($(this).closest('tr'))
        })
        // tree expand || collapse
        .off('click.bjui.datagrid.tree').on('click.bjui.datagrid.tree', 'tbody > tr > td .datagrid-tree-switch', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            var $t = $(this), $tr = $t.closest('tr'), childLen = $tr.data('child'), level = $tr.data('level'), isExpand = !$t.hasClass('collapsed')
            
            if (childLen) {
                $tr.data('isExpand', !isExpand)
                    .nextAll('.datagrid-tree-level-'+ (level + 1)).slice(0, childLen).toggleClass('collapsed', isExpand).trigger('switch.child.bjui.datagrid.tree', {isExpand:isExpand})
                $t.toggleClass('collapsed', isExpand).find('> i').attr('class', 'fa '+ (isExpand ? 'fa-plus-square-o' : 'fa-minus-square-o'))
            }
            
            if (options.height === 'auto') {
                that.fixedHeight()
            }
        })
        .off('switch.child.bjui.datagrid.tree').on('switch.child.bjui.datagrid.tree', 'tbody > tr', function(e, data) {
            e.stopPropagation()
            e.preventDefault()
            
            var $tr = $(this), childLen = $tr.data('child'), level = $tr.data('level')
            
            if (childLen) {
                $tr.nextAll('.datagrid-tree-level-'+ (level + 1)).slice(0, childLen)
                    .toggleClass('collapsed-child', data[that.options.treeOptions.isExpand])
                    .trigger('switch.child.bjui.datagrid.tree', {isExpand:data[that.options.treeOptions.isExpand]})
            }
        })
        .off('expand.bjui.datagrid.tree').on('expand.bjui.datagrid.tree', 'tbody > tr', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            var $tr = $(this).data('isExpand', true), $t = $tr.find('> td.datagrid-tree-td .datagrid-tree-switch')
            
            if (!$t || !$t.length || !$t.hasClass('collapsed')) return false
            
            $t.trigger('click.bjui.datagrid.tree')
            
            if (options.height === 'auto') {
                that.fixedHeight()
            }
        })
        .off('collapse.bjui.datagrid.tree').on('collapse.bjui.datagrid.tree', 'tbody > tr', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            var $tr = $(this).data('isExpand', false), $t = $tr.find('> td.datagrid-tree-td .datagrid-tree-switch')
            
            if (!$t || !$t.length || $t.hasClass('collapsed')) return false
            
            $t.trigger('click.bjui.datagrid.tree')
            
            if (options.height === 'auto') {
                that.fixedHeight()
            }
        })
        // tree add btn
        .off('mouseover.bjui.datagrid.tree').on('mouseover.bjui.datagrid.tree', 'tbody > tr > td.datagrid-tree-td', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            if (!options.treeOptions.add || that.isDrag)
                return false
            
            var $td = $(this), $tr = $td.closest('tr'), $add = $td.find('.datagrid-tree-add')
            
            if (!$add || !$add.length)
                $td.find('> div').append('<a href="javascript:;" class="datagrid-tree-add" data-toggle="add.datagrid.tree" title="添加"><i class="fa fa-plus-circle"></i></a>')
            else
                $add.show()
        })
        .off('mouseout.bjui.datagrid.tree').on('mouseout.bjui.datagrid.tree', 'tbody > tr > td.datagrid-tree-td', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            if (!options.treeOptions.add || that.isDrag)
                return false
            
            var $td = $(this), $add = $td.find('.datagrid-tree-add')
            
            if ($add && $add.length)
                $add.hide()
        })
        // tree add
        .off('click.bjui.datagrid.tree.add').on('click.bjui.datagrid.tree.add', 'tbody > tr > td.datagrid-tree-td [data-toggle="add.datagrid.tree"]', function(e) {
            e.stopPropagation()
            e.preventDefault()
            
            var $add = $(this), $tr = $add.closest('tr'), opts = $add.data()
            
            if (opts.addData) {
                if (typeof opts.addData === 'string')
                    opts.addData = opts.addData.toObj()
            }
            
            that.addTree($tr, opts.addLocation || '', opts.addData || {})
        })
        
        if (options.dropOptions.drop) {
            var $trs = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')'), beforeDrag = options.dropOptions.beforeDrag
            
            if (typeof beforeDrag === 'string')
                beforeDrag = beforeDrag.toFunc()
            
            if (typeof beforeDrag !== 'function')
                beforeDrag = false
            
            $trs
                .basedrag('destroy')
                .basedrag({
                    exclude: 'input, button, a, .bjui-lookup, .datagrid-tree-switch, .datagrid-tree-add, .datagrid-child-td',
                    drop: $trs,
                    container : that.$boxB,
                    beforeDrag: beforeDrag,
                    treeData: that.data,
                    drag: function() {
                        that.isDrag = true
                    },
                    stop: function() {
                        that.isDrag = false
                        
                        $(this).find('> td.datagrid-tree-td').trigger('mouseout.bjui.datagrid.tree')
                    }
                })
                .off('dropover.bjui.basedrag').on('dropover.bjui.basedrag', function(e, x, y, target) {
                    trs.removeClass('datagrid-hover datagrid-drop-over-after datagrid-drop-over-before').find('.datagrid-tree-box').removeClass('datagrid-drop-over-append')
                    
                    if (this === target[0] || target.hasClass(that.classnames.tr_edit)) {
                        return false
                    }
                    if (that.options.isTree) {
                        var $childrens = that.getChildrens(target)
                        
                        if ($.inArray(this, $childrens) !== -1)
                            return false
                    }
                    
                    var $this = $(this).addClass('datagrid-hover'), height = $this.outerHeight() / 2, top = $this.offset().top
                    
                    if (that.options.isTree)
                        height = 5
                    
                    if (y < (top + height)) {
                        $this.addClass('datagrid-drop-over-before').prevAll(':visible:first').addClass('datagrid-drop-over-after')
                    } else {
                        if (!that.options.isTree || (that.options.isTree && y > (top + $this.outerHeight() - height)))
                            $this.addClass('datagrid-drop-over-after')
                        else if (that.options.isTree) {
                            $this.find('.datagrid-tree-box').addClass('datagrid-drop-over-append')
                        }
                    }
                })
                .off('dropout.bjui.basedrag').on('dropout.bjui.basedrag', function(e, data) {
                    $trs.removeClass('datagrid-hover datagrid-drop-over-after datagrid-drop-over-before')
                    $trs.find('div.datagrid-drop-over-append').removeClass('datagrid-drop-over-append')
                })
                .off('drop.bjui.basedrag').on('drop.bjui.basedrag', function(e, target) {
                    var $this = $(this), position, beforeDrop = options.dropOptions.beforeDrop, afterDrop = options.dropOptions.afterDrop
                    
                    that.afterDrop = false
                    
                    if ($this.hasClass('datagrid-drop-over-before'))
                        position = 'top'
                    else if ($this.hasClass('datagrid-drop-over-after'))
                        position = 'bottom'
                    else if (that.options.isTree && $this.find('.datagrid-tree-box').hasClass('datagrid-drop-over-append'))
                        position = 'append'
                    
                    if (position) {
                        if (position != 'append')
                            that.insertTr(target, $this, position)
                        else {
                            var $parents = that.getParents(target, null, true)
                            
                            if ($.inArray(this, $parents) !== -1) {
                                $this.find('.datagrid-tree-box').removeClass('datagrid-drop-over-append')
                                
                                return false
                            }
                            
                            that.appendTr(target, $this)
                        }
                    }
                    
                    $trs.removeClass('datagrid-drop-over-after datagrid-drop-over-before').find('.datagrid-tree-box').removeClass('datagrid-drop-over-append')
                    
                    if (afterDrop && that.afterDrop) {
                        if (typeof afterDrop === 'function') {
                            afterDrop.apply(that, [target, $this])
                        } else if (options.dropOptions.dropUrl) {
                            var postData = {pageSize:that.paging.pageSize, pageCurrent:that.paging.pageCurrent}, type = options.editType, opts = {url:options.dropOptions.dropUrl, type:'POST', reload:false}
                            
                            postData['list'] = []
                            
                            $.each(that.data, function(i, data) {
                                var _data = $.extend({}, data)
                                
                                if (options.dropOptions.scope !== 'drop' || data[options.keys.dropFlag]) {
                                    for (var key in options.keys)
                                        delete _data[options.keys[key]]
                                }
                                if (options.dropOptions.scope === 'drop') {
                                    if (data[options.keys.dropFlag]) {
                                        postData['list'].push(_data)
                                    }
                                } else {
                                    postData['list'].push(_data)
                                }
                            })
                            
                            if (postData) {
                                postData = JSON.stringify(options.dropOptions.paging && options.paging ? postData : postData['list'])
                                
                                if (type && type === 'raw') {
                                    opts.data = postData
                                    opts.contentType = 'application/json'
                                } else {
                                    opts.data = {json:postData}
                                    type && (opts.type = type)
                                }
                                
                                BJUI.ajax('doajax', opts)
                            }
                        }
                    }
                })
        }
    }
    
    Datagrid.prototype.getChildrens = function(tr, childrens, hasChild, maxLevel) {
        var that = this, childLen = tr.data('child'), level = tr.data('level')
        
        if (maxLevel) maxLevel --
        if (!childrens) childrens = $()
        if (childLen) {
            tr.nextAll('.datagrid-tree-level-'+ (level + 1)).slice(0, childLen).each(function() {
                childrens = childrens.add(this)
                
                if (hasChild && $(this).next().hasClass(that.classnames.tr_child))
                    childrens = childrens.add($(this).next())
                
                if (maxLevel)
                    childrens = childrens.add(that.getChildrens($(this), childrens, hasChild, maxLevel))
            })
        }
        
        return childrens
    }
    
    Datagrid.prototype.getParents = function(tr, parents, onlyParent, hasChild) {
        var that = this, level = tr.data('level'), prev
        
        if (!parents) parents = $()
        if (level) {
            prev = tr.prevAll('.datagrid-tree-level-'+ (level - 1) +':first')
            if (prev.length) {
                parents = parents.add(prev)
                
                if (hasChild && prev.next().hasClass(that.classnames.tr_child))
                    parents = parents.add(prev.next())
                
                if (!onlyParent)
                    parents = parents.add(that.getParents(prev, parents, hasChild))
            }
        }
        
        return parents
    }
    
    // only for tree
    Datagrid.prototype.appendTr = function(sTr, dTr, option) {
        if (!this.options.isTree)
            return
        
        var that = this, options = that.options, sData, dData, sIndex = that.tools.getNoChildDataIndex(sTr.index()), dIndex = that.tools.getNoChildDataIndex(dTr.index()), hasChild = sTr.next().hasClass(that.classnames.tr_child),
            keys = options.treeOptions.keys,
            sParent, sParentData, sLevel = sTr.data('level'), dLevel = (dTr.data('level') + 1) || 0, dChildLen = dTr.data('child') + 1,
            dParent, dTd = dTr.find('> td.datagrid-tree-td'), moveIndex, len,
            _lastDTr
        var beforeDrop = options.dropOptions.beforeDrop
        
        if (hasChild) {
            sTr = sTr.add(sTr.next())
            dTr = dTr.add(dTr.next())
        }
        
        sData = that.data[sIndex]
        dData = that.data[dIndex]
        /* beforeDrop */
        if (beforeDrop && typeof beforeDrop === 'string')
            beforeDrop = beforeDrop.toFunc()
        if (beforeDrop && typeof beforeDrop === 'function') {
            if (!beforeDrop.apply(this, [sData, dData, 'append'])) {
                return
            }
        }
        
        sParent = that.getParents(sTr, null, true)
        if (sParent.length) {
            sParentData = that.data[that.tools.getNoChildDataIndex(sParent.index())]
            
            sParentData[keys.childLen] = sParentData[keys.childLen] - 1
            sParent.data('child', sParentData[keys.childLen]).attr('data-child', sParentData[keys.childLen])
            
            if (!sParentData[keys.childLen]) {
                delete sParentData[keys.isParent]
                delete sParentData[keys.isExpand]
                
                var sParentTd = sParent.find('> td.datagrid-tree-td')
                
                sParentTd.html(that.tools.createTreePlaceholder(sParentData, sParentTd.find('.datagrid-tree-title').html()), sParent.data('isExpand'))
            }
        }
        
        if (dTr.length) {
            dTr   = that.getChildrens(dTr, dTr, true)
            _lastDTr = that.getChildrens(dTr.last(), dTr.last(), true, true).last()
            //dData = that.data[dIndex]
            that.expand(dTr.first(), true)
            
            dData[keys.isParent] = true
            dData[keys.childLen] = dChildLen
            
            dTr.first().data('child', dChildLen).attr('data-child', dChildLen)
            dTd.html(that.tools.createTreePlaceholder(dData, dTd.find('.datagrid-tree-title').html(), dTr.first().data('isExpand')))
        }
        
        sTr = that.getChildrens(sTr, sTr, true)
        sTr.filter(':not(.'+ that.classnames.tr_child +')').each(function() {
            var $tr = $(this), level = $tr.data('level'), newLevel = level + (dLevel - sLevel), $td = $tr.find('> td.datagrid-tree-td'), label = $td.find('.datagrid-tree-title').html(),
                trIndex = $tr.index(), trData = that.data[that.tools.getNoChildDataIndex(trIndex)]
            
            if (option && option.isUp) {
                newLevel = dData[keys.level]
            }
            trData[keys.level] = newLevel
            
            $tr.removeClass('datagrid-tree-level-'+ level).addClass('datagrid-tree-level-'+ newLevel)
                .data('level', newLevel).attr('data-level', newLevel)
            
            $td.html(that.tools.createTreePlaceholder(trData, label, $tr.data('isExpand')))
        })
        
        if (option && option.dIndex)
            moveIndex = option.dIndex
        else {
            moveIndex = dTr.last().index() + 1
            if (hasChild)
                moveIndex = moveIndex / 2
        }
        
        //sData = that.data[sIndex]
        len   = (hasChild ? sTr.length / 2 : sTr.length)
        
        that.data.moveItems(sIndex, moveIndex, len)
        that.allData.moveItems(sData.gridIndex, (sData.gridIndex + (moveIndex - sIndex)), len)
        
        that.tools.updateGridIndex()
        
        if (option && option._dTr) {
            dParent = that.getParents(option._dTr, null, true, false)
            if (option.isBefore)
                sTr.insertBefore(option._dTr)
            else if (option.isUp) {
                sTr.insertAfter(_lastDTr)
            } else {
                sTr.insertAfter(option._dTr)
            }
        } else {
            sTr.insertAfter(_lastDTr)
        }
        
        // update the source seq
        if (sParent) {
            var _childs = that.getChildrens(sParent, null, false)
            
            _childs.each(function(i) {
                var _trData = that.data[that.tools.getNoChildDataIndex($(this).index())]
                
                if (_trData) {
                    _trData[keys.order] = i
                    _trData[options.keys.dropFlag] = true
                }
            })
        }
        
        // update this seq and parentid
        if (!dParent)
            dParent = dTr.first()
        
        if (dParent && dParent.length) {
            var _childs = that.getChildrens(dParent, null, false), dParentData = that.data[that.tools.getNoChildDataIndex(dParent.index())]
            
            sData[keys.parentKey] = dParentData[keys.key]
            
            _childs.each(function(i) {
                var _trData = that.data[that.tools.getNoChildDataIndex($(this).index())]
                
                if (_trData) {
                    _trData[keys.order] = i
                    _trData[options.keys.dropFlag] = true
                }
            })
        } else {
            var _trs = sTr.siblings('.datagrid-tree-level-'+ sData[keys.level]).addBack()
            
            sData[keys.parentKey] = null
            
            _trs.each(function(i) {
                var _trData = that.data[that.tools.getNoChildDataIndex($(this).index())]
                
                if (_trData) {
                    _trData[keys.order] = i
                    _trData[options.keys.dropFlag] = true
                }
            })
        }
        
        that.tools.updateLinenumber()
        
        that.afterDrop = true
    }
    
    Datagrid.prototype.insertTr = function(sTr, dTr, position) {
        var that = this, point, options = that.options, keys = options.treeOptions.keys, sData, dData, moveIndex,
            sIndex = that.tools.getNoChildDataIndex(sTr.index()), dIndex = that.tools.getNoChildDataIndex(dTr.index()), hasChild = sTr.next().hasClass(that.classnames.tr_child),
            _start, _end
        var beforeDrop = options.dropOptions.beforeDrop
        var insertTr = function(sTr, dTr, point) {
            if (position === 'bottom') {
                if (hasChild)
                    dTr = dTr.next()
                sTr.insertAfter(dTr)
            } else {
                sTr.insertBefore(dTr)
            }
        }
        
        sData = that.data[sIndex]
        dData = that.data[dIndex]
        
        /* beforeDrop */
        if (beforeDrop && typeof beforeDrop === 'string')
            beforeDrop = beforeDrop.toFunc()
        if (beforeDrop && typeof beforeDrop === 'function') {
            if (!beforeDrop.apply(this, [sData, dData, position])) {
                return
            }
        }
        
        if (sIndex > dIndex) {
            point = position === 'top' ? 0 : 1
        } else {
            point = position === 'top' ? -1 : 0
        }
        
        moveIndex = dIndex + point
        
        if (moveIndex == sIndex && !options.isTree) return
        
        if (hasChild)
            sTr = sTr.add(sTr.next())
        
        if (that.isDom) {
            insertTr(sTr, dTr, point)
        } else {
            sData = that.data[sIndex]
            dData = that.data[dIndex]
            
            if (options.isTree) {
                var len, option = {}, append = false, sParent = that.getParents(sTr.first(), null, true), dParent = that.getParents(dTr, null, true)
                
                if (dData[keys.level] === sData[keys.level] && dData[keys.order] == sData[keys.order] + (position === 'bottom' ? -1 : 1)) {
                    return
                }
                
                /* to append */
                // if bottom
                if (position === 'bottom' && dData[keys.isParent]) {
                    append        = true
                    option._dTr   = dTr
                    option.dIndex = dIndex + 1
                    option.isUp   = true
                }
                // if different level or not the same parent
                else if (sTr.data('level') != dTr.data('level') || sParent[0] !== dParent[0]) {
                    append        = true
                    option._dTr   = dTr
                    option.dIndex = moveIndex
                    dTr           = dParent
                    
                    if (position === 'top')
                        option.isBefore = true
                    
                    if (sIndex < dIndex) {
                        option.dIndex ++
                    }
                }
                if (append) {
                    that.appendTr(sTr, dTr, option)
                    
                    return
                }
                
                sTr = that.getChildrens(sTr, sTr, true)
                len = (hasChild ? sTr.length / 2 : sTr.length) 
                
                that.data.moveItems(sIndex, moveIndex, len)
                that.allData.moveItems(sData.gridIndex, (dData.gridIndex + point), len)
                
                _start = (sIndex > dIndex ? dIndex : sIndex)
                _end = (sIndex > dIndex ? sIndex : dIndex) + (len - 1)
            } else {
                that.data.move(sIndex, moveIndex)
                that.allData.move(sData.gridIndex, (dData.gridIndex + point))
                
                _start = sIndex > dIndex ? dIndex : sIndex
                _end = sIndex > dIndex ? sIndex : dIndex
            }
            
            that.tools.updateGridIndex()
            
            insertTr(sTr, dTr, point)
            
            // update the seqs
            if (sParent) {
                var _childs = that.getChildrens(sParent, null, false, 1)
                
                _childs.each(function(i) {
                    var _trData = that.data[that.tools.getNoChildDataIndex($(this).index())]
                    
                    if (_trData && (_trData[options.keys.gridIndex] >= _start && _trData[options.keys.gridIndex] <= _end)) {
                        _trData[keys.order] = i
                        _trData[options.keys.dropFlag] = true
                    }
                })
            } else {
                for (var i = _start; i <= _end; i++) {
                    var _trData = that.data[i]
                    
                    _trData[options.keys.dropFlag] = true
                }
            }
            
            that.tools.updateLinenumber()
            
            that.afterDrop = true
        }
    }
    
    Datagrid.prototype.addTree = function(row, addLocation, addData) {
        var that = this, options = that.options, $tr, data, tr_index, index
        
        if (options.isTree) {
            if (!addData || typeof addData !== 'object') {
                addData = {}
            }
            
            if (row instanceof jQuery) {
                $tr = row.first()
            } else {
                $tr = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')').eq(parseInt(row, 10))
            }
            
            if ($tr && $tr.length) {
                that.$lastSelect = $tr
                that.add(addLocation, addData)
            }
        }
    }
    
    Datagrid.prototype.expand = function(rows, expandFlag) {
        var that = this, options = that.options, $trs = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')'), event
        
        if (typeof expandFlag === 'undefined') expandFlag = true
        
        event = (expandFlag ? 'expand' : 'collapse') +'.bjui.datagrid.tree'
        
        if (options.isTree) {
            if (rows instanceof jQuery) {
                rows.trigger(event)
            } else if (isNaN(rows)) {
                $.each(rows.split(','), function(i, n) {
                    var tr = $trs.eq(parseInt(n.trim(), 10))
                    
                    if (tr && tr.length)
                        tr.trigger(event)
                })
            } else if (!isNaN(rows)) {
                var tr = $trs.eq(rows)
                
                if (tr && tr.length)
                    tr.trigger(event)
            }
        }
    }
    
    Datagrid.prototype.initTfoot = function() {
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, $tr = $('<tr></tr>')
        
        that.$boxF      = $('<div class="datagrid-box-f"></div>')
        that.$colgroupF = that.$colgroupH.clone()
        that.$tableF    = that.$tableH.clone().empty()
        that.$tableF.append(that.$colgroupF)
        that.$boxF.append($('<div class="datagrid-wrap-h"></div>').append(that.$tableF))
        that.$boxF.insertAfter(that.$boxB)
        
        that.$tfoot = $('<thead></thead>')
        $.each(columnModel, function(i, n) {
            var $th = $('<th><div></div></th>')
            
            if (n.calc) {
                var calc_html = '<div><div class="datagrid-calcbox">#tit#</div>#number#</div>'
                
                if (n.calc === 'avg')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'AVG')).replace('#number#', (n.calc_sum / n.calc_count).toFixed(n.calcDecimal || 2)))
                else if (n.calc === 'count')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'COUNT')).replace('#number#', (n.calc_count)))
                else if (n.calc === 'sum')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'SUM')).replace('#number#', (n.calc_sum)))
                else if (n.calc === 'max')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'MAX')).replace('#number#', (n.calc_max)))
                else if (n.calc === 'min')
                    $th.html(calc_html.replace('#tit#', (n.calcTit || 'MIN')).replace('#number#', (n.calc_min)))
            }
            
            if (n.hidden) $th.css('display', 'none')
            
            $th.appendTo($tr)
        })
        
        that.$tfoot.append($tr).appendTo(that.$tableF)
        
        // events
        that.$tfoot.on('resizeH.bjui.datagrid.tfoot', function() {
            tools.setBoxbH(options.height)
        })
    }
    
    // selected row
    Datagrid.prototype.selectedRows = function(rows, selected) {
        var that = this, $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr'), $trs = that.$tbody.find('> tr')
        var selectedTr = function(n) {
            if (typeof selected === 'undefined') selected = true
            if ($trs.eq(n).hasClass(that.classnames.tr_child)) return
            
            $trs.eq(n)
                .toggleClass(that.classnames.tr_selected, selected)
                .find('> td.'+ that.classnames.td_checkbox +' input').iCheck(selected ? 'check' : 'uncheck')
            
            $lockTrs && $lockTrs.eq(n)
                .toggleClass(that.classnames.tr_selected, selected)
                .find('> td.'+ that.classnames.td_checkbox +' input').iCheck(selected ? 'check' : 'uncheck')
        }
        
        if (rows === null) {
            $trs.removeClass(that.classnames.tr_selected)
                .find('> td.'+ that.classnames.td_checkbox +' input').iCheck('uncheck')
                
            $lockTrs && $lockTrs.removeClass(that.classnames.tr_selected)
                .find('> td.'+ that.classnames.td_checkbox +' input').iCheck('uncheck')
        } else if (typeof rows === 'object') {
            rows.each(function() {
                var $row = $(this), index = $row.index()
                
                if ($row.hasClass(that.classnames.tr_child)) return true
                
                if (typeof selected !== 'undefined') {
                    selectedTr(index)
                } else {
                    $row.toggleClass(that.classnames.tr_selected)
                        .trigger('bjui.datagrid.tr.selected')
                        .find('> td.'+ that.classnames.td_checkbox +' input').iCheck(($row.hasClass(that.classnames.tr_selected) ? 'check' : 'uncheck'))
                    
                    $lockTrs && $lockTrs.eq(index)
                        .toggleClass(that.classnames.tr_selected)
                        .trigger('bjui.datagrid.tr.selected')
                        .find('> td.'+ that.classnames.td_checkbox +' input').iCheck(($row.hasClass(that.classnames.tr_selected) ? 'check' : 'uncheck'))
                }
            })
        } else if (isNaN(rows)) {
            $.each(rows.split(','), function(i, n) {
                selectedTr(parseInt(n.trim(), 10))
            })
        } else if (!isNaN(rows)) {
            selectedTr(parseInt(rows, 10))
        }
        
        if (that.$lastSelect && !that.$lastSelect.hasClass(that.classnames.tr_selected)) {
            that.$lastSelect = null
        }
        
        // selectedTrs
        var $selectedTrs = that.$tbody.find('> tr.'+ that.classnames.tr_selected), datas = []
        
        $selectedTrs.each(function() {
            var $tr = $(this), data_index = $tr.index(), data
            
            data_index = that.tools.getNoChildDataIndex(data_index)
            
            if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
            else data = that.data[data_index]
            
            datas.push(data)
        })
        
        that.$element.data('selectedTrs', $selectedTrs).data('selectedDatas', datas)
    }
    
    //lock
    Datagrid.prototype.initLock = function() {
        var that = this, columnModel = that.columnModel
        
        that.col_lock_count = 0
        $.each(that.columnModel, function(i, n) {
            if (n.initLock) that.col_lock_count ++
        })
        
        if (that.col_lock_count) that.doLock()
    }
    
    //api - colLock
    Datagrid.prototype.colLock = function(column, lockFlag) {
        var that = this, $th, index, columnModel 
        
        if (that.isTemplate)
            return
        if ($.type(column) === 'number') {
            index = parseInt(column, 10)
            if (index < 0 || index > that.columnModel.length - 1) return
            columnModel = that.columnModel[index]
            $th         = columnModel.th
        } else {
            $th         = column
            index       = $th.data('index')
            columnModel = that.columnModel[index]
        }
        
        if (columnModel === that.editBtnsColumn) return // edit btn column
        else if (columnModel.index === that.columnModel.length - 1) return // last column
        if (typeof columnModel.locked === 'undefined') columnModel.locked = false
        if (columnModel.locked === lockFlag) return
        
        columnModel.initLock = lockFlag
        
        if (lockFlag) {
            that.col_lock_count ++
        } else {
            that.col_lock_count --
        }
        if (that.col_lock_count < 0) that.col_lock_count = 0
        
        that.doLock()
    }
    
    Datagrid.prototype.fixedLockItem = function(type) {
        var that = this, columnModel = that.columnModel, $filterThs = that.$thead.find('> tr.datagrid-filter > th'), $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr')
        
        // out
        if (!type) {
            var fixedTh = function($th, $lockTh) {
                $lockTh.clone().insertAfter($lockTh)
                $lockTh.hide().insertAfter($th)
                $th.remove()
            }
            
            // filterThead
            if ($filterThs && $filterThs.length) {
                that.$lockThead && that.$lockThead.find('> tr.datagrid-filter > th:visible').each(function() {
                    var $lockTh = $(this), index = $lockTh.index(), $th = $filterThs.eq(index)
                    
                    fixedTh($th, $lockTh)
                })
            }
            
            //thead checkbox
            if (that.$lockThead) {
                // checkbox
                if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                    var $lockTh = that.$lockThead.find('> tr:first > th.'+ that.classnames.td_checkbox), index = that.checkboxColumn.index, $th = that.$thead.find('> tr:first > th:eq('+ index +')')
                    
                    fixedTh($th, $lockTh)
                }
            }
            
            // inline edit && checkbox td
            if ($lockTrs && $lockTrs.length) {
                $lockTrs.each(function() {
                    var $lockTr = $(this), tr_index = $lockTr.index(), $tr, $td,
                        fixedTd = function($lockTd, tr_index, td_index) {
                            $tr = that.$tbody.find('> tr:eq('+ tr_index +')')
                            $td = $tr.find('> td:eq('+ td_index +')')
                            $lockTd.clone().insertAfter($lockTd)
                            $lockTd.hide().insertAfter($td)
                            $td.remove()
                        }
                    
                    if ($lockTr.hasClass(that.classnames.tr_edit)) {
                        var $lockTd = $lockTr.find('> td:eq('+ columnModel.lockIndex +')'), td_index = $lockTd.index()
                        
                        if ($lockTd.hasClass(that.classnames.td_edit)) {
                            fixedTd($lockTd, tr_index, td_index)
                        }
                    }
                    
                    if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                        $lockTr.find('> td.'+ that.classnames.td_checkbox).each(function() {
                            var $lockTd = $(this), td_index = that.checkboxColumn.index
                            
                            fixedTd($lockTd, tr_index, td_index)
                        })
                    }
                })
            }
        } else { //in
            var fixedTh = function($th, $lockTh) {
                $th.clone().html('').insertAfter($th)
                $th.show().insertAfter($lockTh)
                $lockTh.remove()
            }
            
            // filterThead
            if ($filterThs && $filterThs.length) {
                that.$lockThead.find('> tr.datagrid-filter > th:visible').each(function() {
                    var $lockTh = $(this), index = $lockTh.index(), $th = $filterThs.eq(index)
                    
                    fixedTh($th, $lockTh)
                })
            }
            
            //thead checkbox
            if (that.$lockThead) {
                // checkbox
                if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                    var $lockTh = that.$lockThead.find('> tr:first > th.'+ that.classnames.td_checkbox), index = that.checkboxColumn.index, $th = that.$thead.find('> tr:first > th:eq('+ index +')')
                    
                    fixedTh($th, $lockTh)
                }
            }
            
            // inline edit && checkbox td
            if ($lockTrs && $lockTrs.length) {
                $lockTrs.each(function() {
                    var $lockTr = $(this), tr_index = $lockTr.index(), $tr, $td,
                        fixedTd = function($lockTd, tr_index, td_index) {
                            $tr = that.$tbody.find('> tr:eq('+ tr_index +')')
                            $td = $tr.find('> td:eq('+ td_index +')')
                            $td.clone().insertAfter($td)
                            $td.show().insertAfter($lockTd)
                            $lockTd.remove()
                        }
                    
                    if ($lockTr.hasClass(that.classnames.tr_edit)) {
                        $lockTr.find('> td.'+ that.classnames.td_edit +':visible').each(function() {
                            var $lockTd = $(this), td_index = $lockTd.index(), model = columnModel[td_index]
                            
                            if (model.locked) {
                                fixedTd($lockTd, tr_index, td_index)
                            }
                        })
                    }
                    if ($.inArray(that.checkboxColumn, columnModel) != -1 && that.checkboxColumn.locked) {
                        $lockTr.find('> td.'+ that.classnames.td_checkbox).each(function() {
                            var $lockTd = $(this), td_index = that.checkboxColumn.index
                            
                            fixedTd($lockTd, tr_index, td_index)
                        })
                    }
                })
            }
        }
    }
    
    //locking
    Datagrid.prototype.doLock = function() {
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, tableW = that.$tableH.width(), width = 0, $trs, $lockTrs, lockedLen = 0
        var hasFoot = that.$boxF && true, top = 0
        
        if (!that.$lockBox || !that.$lockBox.length) {
            that.$lockBox = $('<div class="datagrid-box-l"></div>')
            that.$lockH   = $('<div class="datagrid-box-h"></div>')
            that.$lockB   = $('<div class="datagrid-box-b"></div>')
            that.$lockF   = $('<div class="datagrid-box-f"></div>')
            
            that.$lockTableH = $('<table class="table table-bordered"></table>')
            that.$lockTableB = $('<table></table>').addClass(that.$tableB.attr('class'))
            that.$lockTableF = $('<table class="table table-bordered"></table>')
            
            that.$lockH.append(that.$lockTableH)
            that.$lockB.append(that.$lockTableB)
            that.$lockF.append(that.$lockTableF)
            
            that.$lockBox.append(that.$lockH).append(that.$lockB).prependTo(that.$grid)
            if (hasFoot) {
                that.$lockBox.append(that.$lockF)
                that.$lockF.css('margin-top', (that.$boxB.outerHeight() - that.$boxB[0].clientHeight)).height(that.$boxF.outerHeight())
            }
        } else {
            that.fixedLockItem()
            that.$lockTableH.empty()
            that.$lockTableB.empty()
            that.$lockTableF && that.$lockTableF.empty()
        }
        
        if (that.$boxT)    top += that.$boxT.outerHeight()
        if (that.$toolbar) top += that.$toolbar.outerHeight()
        if (top) that.$lockBox.css({top:top})
        
        // display initLock columns, hide other
        $.each(columnModel, function(i, n) {
            n.lockShow = false
            n.lockHide = false
            if (n.initLock) {
                if (n.hidden) tools.showhide(n, true)
                n.lockHide  = true
                n.locked    = true
                n.lockIndex = lockedLen ++
                width      += n.width
            } else {
                n.lockShow = true
                if (!n.hidden) tools.showhide(n, false)
                else n.lockShow = false
                if (n.locked) n.lockShow = true
                n.locked   = false
            }
        })
        
        that.$lockThead     = that.$thead.clone(true)
        that.$lockTbody     = that.$tbody.clone()
        that.$lockColgroupH = that.$colgroupH.clone()
        that.$lockColgroupB = that.$colgroupB.clone()
        
        that.$lockTableH.append(that.$lockColgroupH).append(that.$lockThead).css('width', width)
        that.$lockTableB.append(that.$lockColgroupB).append(that.$lockTbody).css('width', width)
        
        if (hasFoot) {
            that.$lockTfoot     = that.$tableF.find('> thead').clone()
            that.$lockColgroupF = that.$colgroupF.clone()
            that.$lockTableF.append(that.$lockColgroupF).append(that.$lockTfoot).css('width', width)
        }
        
        // display unlock columns, hide locked columns
        $.each(that.columnModel, function(i, n) {
            if (n.lockShow) tools.showhide(n, true)
            if (n.lockHide) tools.showhide(n, false)
        })
        
        that.$boxH.find('> div').css('width', '')
        that.$boxB.find('> div').css('width', '')
        that.$boxF && that.$boxF.find('> div').css('width', '')
        
        setTimeout(function() {
            var bw = that.$boxB.find('> div').width()
            
            that.$boxB.find('> div').width(bw)
            that.$boxH.find('> div').width(bw)
            that.$boxF && that.$boxF.find('> div').width(bw)
        }, 50)
        
        if (!that.col_lock_count) that.$lockBox.hide()
        else that.$lockBox.show()
        
        // colspan for child tr && nodata tr
        that.$tbody.find('> .'+ that.classnames.tr_child +', .datagrid-nodata').each(function() {
            $(this).find('> td').attr('colspan', that.columnModel.length - that.col_lock_count)
        })
        
        if (width > 1) width = width - 1
        that.$boxH.css('margin-left', width)
        that.$boxB.css('margin-left', width)
        if (hasFoot) that.$boxF.css('margin-left', width)
        
        // fixed height
        that.$lockB.height(that.$boxB[0].clientHeight)
        that.$lockB.prop('scrollTop', that.$boxB[0].scrollTop)
        
        var lockH = that.$lockTableH.height(), H = that.$thead.height(), lockFH = that.$lockTableF && that.$lockTableF.height(), HF = that.$tableF && that.$tableF.height()
        
        if (lockH != H) {
            if (lockH < H) that.$lockTableH.height(H)
            else that.$tableH.height(lockH)
        }
        
        if (lockFH && HF && (lockFH != HF)) {
            if (lockFH < HF) that.$lockTableF.find('> thead > tr:first-child > th:visible:first').height(HF)
            else that.$tableF.find('> thead > tr:first-child > th:visible:first').height(lockFH)
        }
        
        $lockTrs = that.$lockTbody.find('> tr')
        $trs     = that.$tbody.find('> tr')
        
        setTimeout(function() {
            var lockBH = that.$lockTableB.height(), HB = that.$tableB.height()
            
            if (lockBH != HB) {
                if (lockBH > HB) {
                    $lockTrs.each(function(tr_index) {
                        var $lockTr = $(this), $lockTd = $lockTr.find('> td:visible:first'), newH = $lockTd.outerHeight()
                        
                        if (newH > 30) {
                            $lockTr.height(newH)
                            $trs.eq(tr_index).height(newH)
                        }
                    })
                } else {
                    $trs.each(function(tr_index) {
                        var $tr = $(this), $td = $tr.find('> td:visible:first'), newH = $td.outerHeight()
                        
                        if (newH > 30) {
                            $tr.height(newH)
                            $lockTrs.eq(tr_index).height(newH)
                        }
                    })
                }
            }
        }, 20)
        
        that.fixedLockItem(1)
        
        // remove hidden tds
        $lockTrs.find('> td:hidden').remove()
        
        // events
        that.initLockEvents($lockTrs)
    }
    
    // init lockTr Events
    Datagrid.prototype.initLockEvents = function($locktrs) {
        if (!this.$lockTbody) return
        
        var that = this, options = that.options
        
        if (!$locktrs) $locktrs = that.$lockTbody.find('> tr')
        
        $locktrs
            .on('click.bjui.datagrid.tr', function(e) {
                var index = $(this).index(), $td = $(e.target).closest('td')
                
                that.$tbody.find('> tr:eq('+ index +')').trigger('click.bjui.datagrid.tr')
            })
            .on('delete.bjui.datagrid.tr', function(e, index) {
                var $tr = $(this)
                
                if (that.linenumberColumn && that.linenumberColumn.locked) {
                    $tr.nextAll(':not(.'+ that.classnames.tr_child +')').each(function() {
                        var $td = $(this).find('> td.'+ that.classnames.td_linenumber), num = parseInt($td.text(), 10)
                        
                        $td.text(num - 1)
                    })
                }
            })
            .on('mouseenter.bjui.datagrid', function(e) {
                var $tr = $(this), index = $tr.index()
                
                $tr.addClass('datagrid-hover')
                that.$tbody.find('> tr:eq('+ index +')').addClass('datagrid-hover')
            })
            .on('mouseleave.bjui.datagrid', function(e) {
                var $tr = $(this), index = $tr.index()
                
                $tr.removeClass('datagrid-hover')
                that.$tbody.find('> tr:eq('+ index +')').removeClass('datagrid-hover')
            })
        
        //contextmenu
        if (options.contextMenuB) {
            $locktrs.each(function() {
                that.tools.contextmenuB($(this), true)
            })
        }
    }
    
    //api - filterInThead
    Datagrid.prototype.filterInThead = function(isDialog) {
        var that = this, options = that.options, tools = that.tools, regional = that.regional, columnModel = that.columnModel, filterData = {}
        var $tr = isDialog ? $('<ul class="datagrid-thead-dialog-view-ul"></ul>') : $('<tr class="datagrid-filter"></tr>')
        var onFilter = function($input, model, $th) {
            var type = model.type, $others
            
            if ($th.isTag('th')) {
                $others = that.$headFilterUl && that.$headFilterUl.find('> li.li-'+ model.index)
            } else if ($th.isTag('li')) {
                $others = that.$thead.find('> tr.datagrid-filter > th:eq('+ model.index +')')
            }
            
            if ($others) {
                $input.val($others.find('input').val())
                if ($input.isTag('select'))
                    $input.val($others.find('select').val()).selectpicker('refresh')
            }
            
            if (type === 'date') {
                $input.change(function() {
                    doFilter(model, $input.val())
                })
            } else if (type === 'findgrid') {
                $input.change(function() {
                    doFilter(model, $input.val())
                })
            } else if (type === 'tags') {
                $th.on('aftercreated.bjui.tags', '[data-toggle="tags"]', function(e, data) {
                    doFilter(model, data.value)
                })
                
                $input.change(function() {
                    doFilter(model, $input.val())
                })
            } else {
                $input.change(function() {
                    doFilter(model, $input.val())
                })
            }
            
            $input.change(function() {
                if ($th.isTag('th') && !$others) {
                    $others = that.$headFilterUl && that.$headFilterUl.find('> li.li-'+ model.index)
                }
                
                $others && $others.find('input').val($input.val())
                if ($input.isTag('select'))
                    $others && $others.find('select').val($input.val()).selectpicker('refresh')
            })
        }
        var doFilter = function(model, val) {
            tools.quickFilter(model, val ? {operatorA:'like', valA:val} : null)
        }
        var initFilter = (typeof options.initFilter === 'object') && options.initFilter
        
        if (!that.inputs || !that.inputs.length) tools.initEditInputs()
        
        $.each(columnModel, function(i, n) {
            if (isDialog) {
                if (n === that.linenumberColumn || n === that.checkboxColumn || n === that.editBtnsColumn || n === that.childColumn)
                    return true
            }
            
            var $input = $(that.inputs[i]), $th = isDialog ? $('<li></li>') : $('<th></th>'), attrs = ''
            
            if (!n.quickfilter) {
                $th.appendTo($tr)
                return true
            }
            else if (n.type === 'findgrid') $input.data('context', $tr)
            else if (n.type === 'spinner') $input = $('<input type="text" name="'+ n.name +'">')
            else if (n.type === 'boolean') $input = $(BJUI.doRegional('<select name="'+ n.name +'" data-toggle="selectpicker"><option value="">#all#</option><option value="true">#true#</option><option value="false">#false#</option></select>', regional))
            else if (n.type === 'select') {
                if (n.attrs && typeof n.attrs === 'object') {
                    $.each(n.attrs, function(i, n) {
                        attrs += ' '+ i +'='+ n
                    })
                }
                if ($input.find('> option:first-child').val() && (!n.attrs || !n.attrs.multiple)) {
                    $input = $('<select name="'+ n.name +'" data-toggle="selectpicker"'+ attrs +'></select>')
                        .append(BJUI.doRegional('<option value="">#all#</option>', regional))
                        .append($input.html())
                        
                    $input.val('') // for IE8
                }
            }
            
            if (isDialog)
                $th.data('index', i).addClass('li-'+ i).append('<a href="javascript:;" class="datagrid-thead-dialog-sort" title="'+ BJUI.getRegional('datagrid.asc') +'/'+ BJUI.getRegional('datagrid.desc') +'">'+ n.label +'</a>')
            
            $th.append($input)
            
            if (n.hidden) $th.hide()
            if (n.type === 'boolean' && !isDialog) $th.attr('align', 'center')
            $th.appendTo($tr)
            
            $input.data('clearFilter', false)
            
            onFilter($input, n, $th)
            
            // events - clear filter
            $input.on('clearfilter.bjui.datagrid.thead', function() {
                $input.val('')
                if (n.type === 'boolean' || n.type === 'select') $input.selectpicker('refresh')
            })
            
            n.$quickfilter = $input
            
            // init filter
            if (initFilter && typeof initFilter[n.name] !== 'undefined') {
                that.tools.initFilter($input, n)
            }
        })
        
        if (isDialog) {
            $tr.prepend('<li><label>'+ BJUI.getRegional('datagrid.asc') +'/'+ BJUI.getRegional('datagrid.desc') +'</label><span class="quicksort-title">'+ BJUI.getRegional('datagrid.filter') +'</span></li>')
            $tr.hide().appendTo(that.$grid).initui()
            
            that.$headFilterUl = $tr
            // for thead
            that.sortModels = !that.sortModels ? [] : that.sortModels
            
            $tr.off('click.datagrid.thead.quicksort').on('click.datagrid.thead.quicksort', 'a.datagrid-thead-dialog-sort', function(e) {
                var $this = $(this), model = that.columnModel[$this.closest('li').data('index')], html = []
                
                $this.find('> i').remove()
                
                that.tools.quickSort(model)
                
                if (model.sortDesc) {
                    $this.append('<i class="fa fa-long-arrow-down"></i>')
                    that.sortModels[model.index] = model
                } else if (model.sortAsc) {
                    $this.append('<i class="fa fa-long-arrow-up"></i>')
                    that.sortModels[model.index] = model
                } else {
                    delete that.sortModels[model.index]
                }
                
                if (!that.sortData || $.isEmptyObject(that.sortData))
                    that.sortModels = []
                
                $.each(that.sortModels, function(i, n) {
                    if (n)
                        html.push('<i class="datagrid-sort-i fa fa-long-arrow-'+ (n.sortAsc ? 'up' : 'down') +'"></i> '+ n.label)
                })
                
                that.$boxH.find('.datagrid-thead-dialog-filter-msg > .msg-sort')
                    .html(html.length > 0 ? html.join('<em>&nbsp;&gt;&nbsp;</em>') : '')
            })
        } else
            $tr.appendTo(that.$thead).initui()
    }
    
    //api - showhide linenumber column
    Datagrid.prototype.showLinenumber = function(flag) {
        var that = this, options = that.options, model = that.columnModel, numberModel, modelOrder = -1, data = that.data, numberModel_index = $.inArray(that.linenumberColumn, model)
        
        if (numberModel_index != -1)
            numberModel = model[numberModel_index]
        
        if (numberModel) {
            if (typeof flag === 'string' && (flag === 'lock' || flag === 'unlock')) {
                that.colLock(numberModel.th, flag === 'lock' ? true : false)
            } else {
                that.showhideColumn(numberModel.th, flag ? true : false)
            }
        } else if (flag) {
            modelOrder = $.inArray(that.childColumn, model)
            
            modelOrder ++
            numberModel = that.linenumberColumn
            numberModel.index = modelOrder
            model.splice(modelOrder, 0, numberModel)
            
            if (that.inputs && that.inputs.length)
                that.inputs = that.inputs.splice(modelOrder, 0, '')
            
            var $trsH = that.$thead.find('> tr'), col = '<col style="width:30px;">', $th, $filterTr = $trsH.filter('.datagrid-filter'), rowspan = $trsH.length - $filterTr.length
            
            $th = $('<th align="center"'+ (rowspan > 1 ? ' rowspan="'+ rowspan +' "' : '') +'class="'+ that.classnames.td_linenumber + (rowspan == 1 ? ' single-row' : '') +'"><div><div class="datagrid-space"></div><div class="datagrid-label">'+ that.linenumberColumn.label +'</div></div></th>')
            
            that.$colgroupH.find('> col:eq('+ modelOrder +')').before(col)
            that.$colgroupB.find('> col:eq('+ modelOrder +')').before(col)
            that.$colgroupF && that.$colgroupF.find('> col:eq('+ modelOrder +')').before(col)
            $filterTr.length && $filterTr.find('> th:eq('+ modelOrder +')').before('<th></th>')
            $trsH.first().find('> th:eq('+ modelOrder +')').before($th)
            that.$tableF && that.$tableF.find('> thead > tr > th:eq('+ modelOrder +')').before('<th></th>')
            
            numberModel.th    = $th
            numberModel.width = 30
            
            that.$tbody.find('> tr').each(function(i) {
                var $tr = $(this), colspan = that.columnModel.length, linenumber = i, paging = that.paging
                
                if ($tr.hasClass(that.classnames.tr_child) || $tr.hasClass('datagrid-nodata'))
                    $tr.find('> td').attr('colspan', colspan)
                else {
                    linenumber = that.tools.getNoChildDataIndex(linenumber)
                    if (options.linenumberAll)
                        linenumber = ((paging.pageCurrent - 1) * paging.pageSize + (linenumber))
                    
                    $tr.find('> td:eq('+ modelOrder +')').before('<td align="center" class="'+ that.classnames.td_linenumber +'">'+ (linenumber + 1) +'</td>')
                }
            })
            
            that.$tableF && that.$tableF.find('> thead > tr > th:eq('+ modelOrder +')').before('<th></th>')
            
            $.each(model, function(i, n) {
                n.index = i
                if (n.th) n.th.data('index', i)
            })
            
            $th.find('> div').height($th.outerHeight())
            
            if (flag === 'lock') {
                that.colLock($th, true)
            }
            if (that.$showhide) {
                that.$showhide.remove()
                that.colShowhide(options.columnShowhide)
            }
        }
    }
    
    //api - showhide checkbox column
    Datagrid.prototype.showCheckboxcol = function(flag) {
        var that = this, options = that.options, model = that.columnModel, numberModel = model[0], checkModel, modelOrder = -1, checkModel_index = $.inArray(that.checkboxColumn, model)
        
        if (checkModel_index != -1)
            checkModel = that.columnModel[checkModel_index]
        
        if (checkModel) {
            if (typeof flag === 'string' && (flag === 'lock' || flag === 'unlock')) {
                that.colLock(checkModel.th, flag === 'lock' ? true : false)
            } else {
                that.showhideColumn(checkModel.th, flag)
            }
        } else if (flag) {
            modelOrder = $.inArray(that.linenumberColumn, model)
            if (modelOrder == -1)
                modelOrder = $.inArray(that.childColumn, model)
            
            modelOrder ++
            checkModel = that.checkboxColumn
            checkModel.index = modelOrder
            model.splice(modelOrder, 0, checkModel)
            
            if (that.inputs && that.inputs.length)
                that.inputs = that.inputs.splice(modelOrder, 0, '')
                
            var $trsH = that.$thead.find('> tr'), col = '<col style="width:30px;">', $th, $td, $filterTr = $trsH.filter('.datagrid-filter'), rowspan = $trsH.length - $filterTr.length
            
            $th = $('<th align="center"'+ (rowspan > 1 ? ' rowspan="'+ rowspan +' "' : '') +'class="'+ that.classnames.td_checkbox + (rowspan == 1 ? ' single-row' : '') +'"><div><div class="datagrid-space"></div><div class="datagrid-label"><input type="checkbox" data-toggle="icheck"></div></th>')
            
            that.$colgroupH.find('> col:eq('+ modelOrder +')').before(col)
            that.$colgroupB.find('> col:eq('+ modelOrder +')').before(col)
            that.$colgroupF && that.$colgroupF.find('> col:eq('+ modelOrder +')').before(col)
            $filterTr.length && $filterTr.find('> th:eq('+ modelOrder +')').before('<th></th>')
            $trsH.first().find('> th:eq('+ modelOrder +')').before($th)
            that.$tableF && that.$tableF.find('> thead > tr > th:eq('+ modelOrder +')').before('<th></th>')
            $th.initui()
            
            checkModel.th    = $th
            checkModel.width = 30
            
            that.$tbody.find('> tr').each(function(i) {
                var $tr = $(this), colspan = that.columnModel.length
                
                if ($tr.hasClass(that.classnames.tr_child) || $tr.hasClass('datagrid-nodata'))
                    $tr.find('> td').attr('colspan', colspan)
                else {
                    $td = $('<td align="center" class="'+ that.classnames.td_checkbox +'"><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox"></td>')
                    $tr.find('> td:eq('+ modelOrder +')').before($td)
                    $td.initui()
                }
            })
            
            $.each(model, function(i, n) {
                n.index = i
                if (n.th) n.th.data('index', i)
            })
            
            $th.find('> div').height($th.outerHeight())
            
            if (flag === 'lock') {
                that.colLock($th, true)
            }
            if (that.$showhide) {
                that.$showhide.remove()
                that.colShowhide(options.columnShowhide)
            }
        }
    }
    
    //api - showhide checkbox column
    Datagrid.prototype.showEditCol = function(flag) {
        var that = this, options = that.options, model = that.columnModel, editModel = model[model.length - 1], data = that.data
        
        if (editModel === that.editBtnsColumn) {
            that.showhideColumn(editModel.th, flag ? true : false)
        } else if (flag) {
            var $trsH = that.$thead.find('> tr'), col = '<col style="width:110px;">', $th, $td, $filterTr = $trsH.filter('.datagrid-filter'), rowspan = $trsH.length - $filterTr.length
            
            editModel = that.editBtnsColumn
            model.push(editModel)
            
            that.$colgroupH.append(col)
            that.$colgroupB.append(col)
            that.$colgroupF && that.$colgroupF.append(col)
            $th = $('<th align="center" rowspan="'+ rowspan +'">'+ that.editBtnsColumn.label +'</th>')
            $trsH.first().append($th)
            $filterTr.length && $filterTr.append('<th></th>')
            $th.initui().data('index', model.length - 1)
            editModel.th    = $th
            editModel.width = 110
            editModel.index = model.length - 1
            
            that.$tbody.find('> tr').each(function(i) {
                var $tr = $(this), colspan = that.columnModel.length
                
                if ($tr.hasClass(that.classnames.tr_child) || $tr.hasClass('datagrid-nodata'))
                    $tr.find('> td').attr('colspan', colspan)
                else {
                    $td = $('<td align="center" class="'+ that.classnames.s_edit +'">'+ BJUI.doRegional(FRAG.gridEditBtn, that.regional) +'</td>')
                    $tr.append($td)
                    $td.initui()
                }
            })
            
            that.edit(that.$tbody.find('> tr'))
            
            that.$tableF && that.$tableF.find('> thead > tr').append('<th></th>')
            
            if (that.$showhide) {
                that.$showhide.remove()
                that.colShowhide(options.columnShowhide)
            }
        }
    }
    
    //resize
    Datagrid.prototype.colResize = function() {
        var that        = this,
            tools       = that.tools,
            columnModel = that.columnModel,
            $thead      = that.$thead,
            $resizeMark = that.$grid.find('> .resizeProxy')
        
        if (!$resizeMark.length) {
            $resizeMark = $('<div class="resizeProxy" style="left:0; display:none;"></div>')
            $resizeMark.appendTo(that.$grid)
        }
        
        $thead.find('> tr > th').each(function(i) {
            var $th = $(this),  $resize = $th.find('> div > .'+ that.classnames.th_cell +'> .'+ that.classnames.th_resizemark)
            
            $resize.on('mousedown.bjui.datagrid.resize', function(e) {
                var ofLeft = that.$boxH.scrollLeft(), marginLeft = parseInt(that.$boxH.css('marginLeft') || 0, 10), left, index = $th.data('index'), model = columnModel[index], width = model.th.width()
                    , $trs = that.$tbody.find('> tr'), $lockTrs = that.$lockTbody && that.$lockTbody.find('> tr'), lockH = that.$lockTableB && that.$lockTableB.height(), H = that.$tableB.height(), lockH_new, H_new
                
                if (isNaN(marginLeft)) marginLeft = 0
                left = tools.getRight($th) - ofLeft + marginLeft
                
                that.isResize = true
                
                if (model.locked) {
                    left = tools.getRight4Lock(model.lockIndex)
                    if (model.lockWidth) width = model.lockWidth
                }
                
                $resizeMark
                    .show()
                    .css({
                        left   : left,
                        bottom : (that.$boxP ? that.$boxP.outerHeight() : 0),
                        cursor : 'col-resize'
                    })
                    .basedrag({
                        scop  : true, cellMinW:20, relObj:$resizeMark,
                        move  : 'horizontal',
                        event : e,
                        stop  : function() {
                            var new_left = $resizeMark.position().left,
                                move     = new_left - left,
                                newWidth = move + width,
                                tableW   = that.$tableH.width() + move,
                                lockW    = that.$lockTableH && that.$lockTableH.width() + move
                            
                            if (newWidth < 5) newWidth = 20
                            if (model.minWidth && newWidth < model.minWidth) newWidth = model.minWidth
                            if (newWidth != width + move) {
                                tableW   = tableW - move + (newWidth - width)
                                lockW    = lockW - move + (newWidth - width)
                            }
                            
                            model.width = newWidth
                            
                            if (model.locked) {
                                if (lockW < (that.$grid.width() * 0.75)) {
                                    model.lockWidth = newWidth
                                    that.$lockColgroupH.find('> col:eq('+ index +')').width(newWidth)
                                    that.$lockColgroupB.find('> col:eq('+ index +')').width(newWidth)
                                    that.$lockColgroupF && that.$lockColgroupF.find('> col:eq('+ index +')').width(newWidth)
                                    
                                    that.$lockTableH.width(lockW)
                                    that.$lockTableB.width(lockW)
                                    that.$lockTableF && that.$lockTableF.width(lockW)
                                    
                                    var margin = that.$lockBox.width()
                                    
                                    that.$boxH.css('margin-left', margin - 1)
                                    that.$boxB.css('margin-left', margin - 1)
                                    that.$boxH.find('> div').width(that.$boxH.width())
                                    that.$boxB.find('> div').width(that.$boxH.width())
                                    that.$boxF && that.$boxF.css('margin-left', margin - 1)
                                    
                                    that.$colgroupH.find('> col:eq('+ index +')').width(newWidth)
                                    that.$colgroupB.find('> col:eq('+ index +')').width(newWidth)
                                    that.$colgroupF && that.$colgroupF.find('> col:eq('+ index +')').width(newWidth)
                                }
                            } else {
                                setTimeout(function() {
                                    that.$colgroupH.find('> col:eq('+ index +')').width(newWidth)
                                    that.$colgroupB.find('> col:eq('+ index +')').width(newWidth)
                                    that.$colgroupF && that.$colgroupF.find('> col:eq('+ index +')').width(newWidth)
                                }, 1) //setTimeout for chrome
                            }
                            
                            /* fixed height */
                            setTimeout(function() {
                                $trs.css('height', '')
                                H_new = that.$tableB.height()
                                
                                if (that.$lockTbody) {
                                    $lockTrs.css('height', '')
                                    lockH_new = that.$lockTableB.height()
                                    if (lockH_new != lockH || H_new != H) {
                                        if (lockH_new > H_new) {
                                            $lockTrs.each(function(tr_index) {
                                                var $lockTr = $(this), $lockTd = $lockTr.find('> td:eq('+ model.lockIndex +')'), newH = $lockTd.outerHeight()
                                                
                                                if (newH > 30) {
                                                    $lockTr.height(newH)
                                                    $trs.eq(tr_index).height(newH)
                                                }
                                            })
                                        } else {
                                            $trs.each(function(tr_index) {
                                                var $tr = $(this), $td = $tr.find('> td:eq('+ index +')'), newH = $td.outerHeight()
                                                
                                                if (newH > 30) {
                                                    $tr.height(newH)
                                                    $lockTrs.eq(tr_index).height(newH)
                                                }
                                            })
                                        }
                                    }
                                    
                                    that.$lockB.height(that.$boxB[0].clientHeight)
                                }
                                
                                if (model.calc) that.$tfoot && that.$tfoot.trigger('resizeH.bjui.datagrid.tfoot')
                                
                                that.isResize = false
                            }, 10)
                            
                            $resizeMark.hide()
                            that.resizeFlag = true
                        }
                    })
            })
        })
    }
    
    //column - add menu button
    Datagrid.prototype.colMenu = function() {
        var that = this, options = that.options, tools = that.tools, regional = that.regional, $ths = that.$trsH.find('> th.'+ that.classnames.th_menu), $menu = that.$grid.find('> .'+ that.classnames.s_menu)
        
        if (!$menu.legnth) {
            $menu = $(BJUI.doRegional(FRAG.gridMenu, regional))
            $menu.hide().appendTo(that.$grid)
            that.$menu = $menu
        }
        that.colShowhide(options.columnShowhide)
        
        $ths.each(function() {
            var $th     = $(this),
                index   = $th.data('index'),
                model   = that.columnModel[index],
                $cell   = $th.find('> div > .'+ that.classnames.th_cell),
                $btnBox = $cell.find('> .'+ that.classnames.btn_menu),
                $btn
            
            if (!$btnBox.length) {
                $btn    = $('<button type="button" class="btn btn-default"><i class="fa fa-bars"></button>'),
                $btnBox = $('<div class="'+ that.classnames.btn_menu +'"></div>').append($btn)
                
                $btnBox.appendTo($cell)
                
                $btn.click(function() {
                    var $tr = $th.closest('tr'), rowspan = parseInt(($th.attr('rowspan') || 1), 10), left = $(this).offset().left - that.$grid.offset().left - 1, top = (that.$trsH.length - rowspan) * 25 + (13 * rowspan) + 11, $showhide = $menu.find('> ul > li.datagrid-li-showhide'), menu_width, submenu_width 
                    var $otherBtn = $menu.data('bjui.datagrid.menu.btn')
                    
                    if ($otherBtn && $otherBtn.length) $otherBtn.removeClass('active')
                    $(this).addClass('active')
                    if ($showhide.length && that.$showhide) {
                        that.$showhide.appendTo($showhide)
                        submenu_width = that.$showhide.data('width')
                    }
                    $menu
                        .css({position:'absolute', top:top, left:left})
                        .show()
                        .data('bjui.datagrid.menu.btn', $btn)
                        .siblings('.'+ that.classnames.s_menu).hide()
                    
                    menu_width = $menu.outerWidth()
                    
                    var position = function(left) {
                        if (that.$boxH.width() - left < menu_width) {
                            $menu.css({left:left - menu_width + 18}).addClass('position-right')
                        } else {
                            $menu.css({left:left}).removeClass('position-right')
                        }
                    }
                    var fixedLeft = function($btn) {
                        that.$boxB.scroll(function() {
                            var left = $btn.offset().left - that.$grid.offset().left - 1
                            
                            position(left)
                        })
                    }
                    
                    position(left)
                    fixedLeft($btn)
                    
                    if (options.columnFilter) that.colFilter($th)
                    else $menu.find('> ul > li.'+ that.classnames.li_filter).hide()
                    
                    tools.locking($th)
                    
                    // quick sort
                    var $asc  = $menu.find('> ul > li.'+ that.classnames.li_asc),
                        $desc = $asc.next()
                    
                    $asc.off('click.bjui.datagrid.sort').on('click.bjui.datagrid.sort', function() {
                        if (model.sortDesc)
                            model.sortDesc = false
                        else {
                            model.sortAsc  = false
                        }
                        tools.quickSort(model)
                    })
                    
                    $desc.off('click.bjui.datagrid.sort').on('click.bjui.datagrid.sort', function() {
                        if (model.sortAsc) {
                            model.sortDesc = false
                        } else {
                            model.sortAsc  = true
                        }
                        tools.quickSort(model)
                    })
                    
                    $menu.on('sort.bjui.datagrid.th', function(e, asc) {
                        $asc.toggleClass('sort-active', asc)
                        $desc.toggleClass('sort-active', !asc)
                    })
                })
            }
        })
        
        /* hide filterbox */
        that.$grid.on('click.bjui.datagrid.filter', function(e) {
            var $target = $(e.target), $menu = that.$grid.find('.'+ that.classnames.s_menu +':visible')
            
            if ($menu.length && !$target.closest('.'+ that.classnames.btn_menu).length) {
                if (!$target.closest('.'+ that.classnames.s_menu).length) {
                    $menu.hide().data('bjui.datagrid.menu.btn').removeClass('active')
                }
            }
        })
    }
    
    // show or hide columns on btn menu
    Datagrid.prototype.colShowhide = function(showFlag) {
        var that = this, options = that.options, tools = that.tools, $menu = that.$menu, $ul = $menu.find('> ul'), $showhideli = $ul.find('> li.'+ that.classnames.li_showhide)
        
        if (showFlag) {
            if (!that.$showhide) {
                tools.createShowhide()
                tools.showSubMenu($showhideli, $menu, that.$showhide)
            }
        } else {
            $showhideli.hide()
        }
    }
    
    // api - show or hide column
    Datagrid.prototype.showhideColumn = function(column, showFlag) {
        var that = this, tools = that.tools, index, model
        
        if ($.type(column) === 'number') {
            index = parseInt(column, 10)
            if (index < 0) return
        } else {
            index = column.data('index')
        }
        
        model = that.columnModel[index]
        
        if (model) {
            if (model.locked) {
                if (showFlag) return
                else that.colLock(model.th, showFlag)
            }
            tools.showhide(model, showFlag)
        }
    }
    
    // filter
    Datagrid.prototype.colFilter = function($th, filterFlag) {
        var that  = this, options = that.options, tools = that.tools, regional = that.regional, $filter = $th.data('bjui.datagrid.filter'), $menu = that.$menu, $filterli = $menu.find('> ul > li.'+ that.classnames.li_filter)
        var model = that.columnModel[$th.data('index')]
        
        if (!that.inputs || !that.inputs.length) tools.initEditInputs()
        if (model.quickfilter) {
            $filterli.show().find('.'+ that.classnames.s_filter).addClass('hide')
            
            if (!$filter || !$filter.length) {
                $filter = $(BJUI.doRegional(FRAG.gridFilter.replaceAll('#label#', $th.text()), regional)).hide().appendTo(that.$grid)
                
                var index = $th.data('index'), model = that.columnModel[index], type = model.type || 'string', operator = model.operator || [],
                    $filterA = $filter.find('span.filter-a'),
                    $filterB = $filter.find('span.filter-b'),
                    $select  = $('<select data-toggle="selectpicker" data-container="true"></select>'),
                    $input   = $(that.inputs[index]),
                    $valA, $valB
                
                $input.removeAttr('data-rule').attr('size', 10).addClass('filter-input')
                
                if (type === 'string' || type === 'findgrid' || type === 'tags') {
                    if (!operator.length) operator = ['=', '!=', 'like']
                    if (type === 'findgrid') {
                        $input.data('context', $filter)
                    }
                } else if (type === 'number' || type === 'int' || type === 'spinner') {
                    if (type === 'spinner') $input.removeAttr('data-toggle')
                    if (!operator.length) operator = ['=', '!=', '>', '<', '>=', '<=']
                } else if (type === 'date') {
                    if (!operator.length) operator = ['=', '!=']
                } else if (type === 'boolean') {
                    if (!operator.length) operator = ['=', '!=']
                    $input = $(BJUI.doRegional('<select name="'+ model.name +'" data-toggle="selectpicker"><option value="">#all#</option><option value="true">#true#</option><option value="false">#false#</option></select>', regional))
                } else if (type === 'select') {
                    if (!operator.length) operator = ['=', '!=']
                    $input.attr('data-width', '80')
                    if ($input.find('> option:first-child').val()) {
                        $input = $('<select name="'+ model.name +'" data-toggle="selectpicker" data-width="80"></select>')
                            .append(BJUI.doRegional('<option value="">#all#</option>', regional))
                            .append($input.html())
                    }
                }
                
                for (var i = 0, l = operator.length; i < l; i++) {
                    $select.append('<option value="'+ (operator[i]) +'">'+ (operator[i]) +'</option>')
                }
                
                $valA = $input
                $valB = $valA.clone()
                
                $filterA.append($select).append($input)
                $filterB.append($select.clone()).append($valB)
                
                $th.data('bjui.datagrid.filter', $filter)
                
                $filter.appendTo($filterli)
                $filter.data('width', $filter.outerWidth()).hide().initui()
                
                /* events */
                var $ok      = $filter.find('button.ok'),
                    $clear   = $filter.find('button.clear'),
                    $selects = $filter.find('select'),
                    $selA    = $selects.first(),
                    $selB    = $selects.last(),
                    $andOr   = $selects.eq(1)
                    
                $ok.click(function() {
                    var operatorA = $selA.val(), valA = $valA.val().trim(), operatorB = $selB.val(), valB = $valB.val().trim(), andor = $andOr.val()
                    var filterDatas = {}
                    
                    if (valA.length) {
                        $.extend(filterDatas, {operatorA:operatorA, valA:valA})
                    }
                    if (valB.length) {
                        if (valA.length) $.extend(filterDatas, {andor:andor})
                        $.extend(filterDatas, {operatorB:operatorB, valB:valB})
                    }
                    if (!$.isEmptyObject(filterDatas)) {
                        tools.quickFilter(that.columnModel[$th.data('index')], filterDatas)
                        that.$grid.trigger('click')
                        $filterli.trigger('hidesubmenu.bjui.datagrid.th', [$menu, $filter])
                    } else {
                        $clear.trigger('click')
                    }
                })
                
                $clear.click(function() {
                    var model = that.columnModel[$th.data('index')]
                    
                    $selects.find('> option:first').prop('selected', true)
                    $selects.selectpicker('refresh')
                    $valA.val('')
                    $valB.val('')
                    if (model.isFiltered) {
                        tools.quickFilter(model, null)
                        that.$grid.trigger('click')
                        $filterli.trigger('hidesubmenu.bjui.datagrid.th', [$menu, $filter])
                    }
                })
            }
            
            tools.showSubMenu($filterli, $menu, $filter.removeClass('hide'))
            
            $menu.find('> ul > li:not(".'+ that.classnames.li_filter +'")').on('mouseover', function() {
                if ($filterli.hasClass('active'))
                    $filterli.trigger('hidesubmenu.bjui.datagrid.th', [$menu, $filter])
            })
        } else {
            $filterli.hide()
        }
    }
    
    // paging
    Datagrid.prototype.initPaging = function() {
        var that = this, tools = that.tools, options = that.options, paging = that.paging, pr = BJUI.regional.pagination, btnpaging = FRAG.gridPaging, pageNums = [], pageCount = paging.pageCount, interval, selectPages = [], pagingHtml = BJUI.StrBuilder()
        
        interval = tools.getPageInterval(pageCount, paging.pageCurrent, paging.showPagenum)
        
        for (var i = interval.start; i < interval.end; i++) {
            pageNums.push(FRAG.gridPageNum.replace('#num#', i).replace('#active#', (paging.pageCurrent == i ? ' active' : '')))
        }
        
        btnpaging = BJUI.doRegional(btnpaging.replaceAll('#pageCurrent#', paging.pageCurrent).replaceAll('#count#', paging.total +'/'+ parseInt((pageCount || 0), 10)), pr)
        
        pagingHtml
            .add('<div class="paging-content" style="width:'+ that.$boxB.width() +'px;">')
            .add('<span></span><div class="paging-pagesize"><button type="button" class="btn-default btn-refresh" title="'+ pr.refresh +'" data-icon="refresh"></button>')
            .add('<select data-toggle="selectpicker"></select>')
            .add('</div>')
            .add('<div class="paging-box">')
            .add(btnpaging.replace('#pageNumFrag#', pageNums.join('')))
            .add('</div>')
            .add('</div>')
        
        that.$boxP.html(pagingHtml.toString())
        that.$boxP.initui()
        
        //events
        var $select    = that.$boxP.find('div.paging-pagesize > select'),
            $pagenum   = that.$boxP.find('ul.pagination'),
            $pagetotal = $pagenum.find('> li.page-total'),
            $jumpto    = $pagetotal.next(),
            $first     = $jumpto.next(),
            $prev      = $first.next(),
            $next      = $prev.nextAll('.page-next'),
            $last      = $next.next()
        
        var disablePrev = function() {
            $first.addClass('disabled')
            $prev.addClass('disabled')
        }
        var enablePrev = function() {
            $first.removeClass('disabled')
            $prev.removeClass('disabled')
        }
        var disableNext = function() {
            $next.addClass('disabled')
            $last.addClass('disabled')
        }
        var enableNext = function() {
            $next.removeClass('disabled')
            $last.removeClass('disabled')
        }
        var pageFirst = function() {
            disablePrev()
            enableNext()
        }
        var pageLast = function() {
            enablePrev()
            disableNext()
        }
        var setPageSize = function(pageSize) {
            $select.empty()
            
            if (!pageSize) pageSize = that.paging.pageSize
            
            selectPages = paging.selectPageSize.split(',')
            selectPages.push(String(pageSize))
            
            $.unique(selectPages).sort(function(a, b) { return a - b })
            
            var opts = []
            
            $.each(selectPages, function(i, n) {
                opts.push('<option value="'+ n +'"'+ (n == paging.pageSize && 'selected') +'>'+ n +'/'+ pr.page +'</option>')
            })
            
            $select.html(opts.join('')).selectpicker('refresh')
        }
        
        if (paging.pageCurrent == 1) pageFirst()
        if (paging.pageCurrent == paging.pageCount) {
            pageLast()
            if (paging.pageCurrent == 1) disablePrev()
        }
        if (!paging.total) disableNext()
        setPageSize()
        
        that.$boxP.on('click.datagrid.pagenum', 'li.page-num', function(e) {
            var $num = $(this)
            
            if (!$num.hasClass('active')) {
                that.jumpPage($num.text())
            }
            
            e.preventDefault()
        }).on('click.datagrid.refresh', 'button.btn-refresh', function() {
            that.refresh()
        }).on('bjui.datagrid.paging.jump', function(e) {
            var pageCurrent = that.paging.pageCurrent, interval = tools.getPageInterval(that.paging.pageCount, pageCurrent, paging.showPagenum), pageNums = []
            
            for (var i = interval.start; i < interval.end; i++) {
                pageNums.push(FRAG.gridPageNum.replace('#num#', i).replace('#active#', (pageCurrent == i ? ' active' : '')))
            }
            
            $pagenum.find('> li.page-num').remove()
            $prev.after(pageNums.join(''))
            
            if (pageCurrent == 1) {
                pageFirst()
                if (pageCurrent == that.paging.pageCount) disableNext()
                if (!that.paging.total) disableNext()
            } else if (pageCurrent == that.paging.pageCount) {
                pageLast()
            } else {
                enablePrev()
                enableNext()
            }
            
            $jumpto.find('input').val(pageCurrent)
            $pagetotal.find('> span').html(that.paging.total +'/'+ that.paging.pageCount)
        }).on('bjui.datagrid.paging.pageSize', function(e, pageSize) {
            setPageSize(pageSize)
        }).on('change', 'div.paging-pagesize > select', function() {
            var pageSize = $(this).val()
            
            that.jumpPage(null, pageSize)
        })
        
        $jumpto.find('input').on('keyup', function(e) {
            if (e.which === BJUI.keyCode.ENTER) {
                var page = $(this).val()
                
                if (page) that.jumpPage(page)
            }
        })
        
        $first.on('click', function() {
            if (that.paging.pageCurrent > 1) 
                that.jumpPage(1)
        })
        
        $prev.on('click', function() {
            if (that.paging.pageCurrent > 1)
                that.jumpPage(that.paging.pageCurrent - 1)
        })
        
        $next.on('click', function() {
            if (that.paging.pageCurrent < that.paging.pageCount)
                that.jumpPage(that.paging.pageCurrent + 1)
        })
        
        $last.on('click', function() {
            if (that.paging.pageCurrent < that.paging.pageCount)
                that.jumpPage(that.paging.pageCount)
        })
    }
    
    Datagrid.prototype.jumpPage = function(pageCurrent, pageSize) {
        var that = this, paging = that.paging, pageCount = paging.pageCount
        
        if (pageCurrent && isNaN(pageCurrent)) return
        if (pageSize && isNaN(pageSize))       return
        if (pageCurrent) {
            pageCurrent = parseInt(pageCurrent, 10)
            
            if (pageCurrent < 1)         pageCurrent = 1
            if (pageCurrent > pageCount) pageCurrent = pageCount
            if (pageCurrent == paging.pageCurrent) return
        }
        if (pageSize) {
            pageSize = parseInt(pageSize, 10)
            
            if (that.options.local != 'remote') {
                if (paging.pageSize > paging.total) return
            }
        }
        
        that.tools.jumpPage(pageCurrent, pageSize)
    }
    
    // api - add
    Datagrid.prototype.add = function(addLocation, addData, noEdit) {
        if (!this.tools.beforeEdit() && !noEdit) return
        
        if (!this.options.editUrl && !noEdit) {
            BJUI.debug('BJUI.Datagrid: Edit url is not set!')
            return
        }
        
        if (!this.options.editMode && !noEdit) return
        if (!this.options.inlineEditMult) {
            this.doCancelEditRow(this.$tbody.find('> tr.'+ this.classnames.tr_edit))
        } else if (!noEdit && this.options.editMode != 'inline' && this.$tbody.find('> tr.'+ this.classnames.tr_add).length) {
            return
        }
        
        if (!addData) {
            addData = {}
        } else {
            if (typeof addData === 'string') {
                addData = addData.toObj()
            }
            if (typeof addData !== 'object') {
                addData = {}
            }
        }
        
        var that = this, options = that.options, keys = options.keys, tools = that.tools, paging = that.paging, trs, obj = {}, data = [], addObj, startNumber = (paging.pageCurrent - 1) * paging.pageSize, linenumber
        
        var addTr = function() {
            var $tr = $('<tr></tr>').addClass(that.classnames.tr_add), $lockTr = $tr.clone()
            
            if (that.isTemplate) {
                var tdTemplate = options.tdTemplate, tempcolspan = that.columnModel.length
                
                if (typeof tdTemplate === 'function')
                    tdTemplate = tdTemplate.apply(that, [{}])
                
                if (that.options.hasChild && that.options.childOptions) {
                    $tr.append('<td data-title="..." align="center" class="datagrid-child-td"><div>'+ BJUI.doRegional(FRAG.gridExpandBtn, that.regional) +'</div></td>')
                    tempcolspan --
                }
                if (that.options.showLinenumber) {
                    $tr.append('<td data-title="No." align="center" class="datagrid-linenumber-td">0</td>')
                    tempcolspan --
                }
                if (that.options.showCheckboxcol) {
                    $tr.append('<td data-title="Checkbox" align="center" class="datagrid-checkbox-td"><div><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox" value="true"></div></td>')
                    tempcolspan --
                }
                
                $tr.append('<td class="datagrid-template-td" colspan="'+ tempcolspan +'">'+ tdTemplate +'</td>')
            } else {
                $.each(that.columnModel, function(i, n) {
                    var label = '', $td = $('<td></td>')
                    
                    if (n[keys.gridChild])
                        $td.addClass(that.classnames.td_child).html('<div>'+ BJUI.doRegional(FRAG.gridExpandBtn, that.regional) +'</div>')
                    else if (n[keys.gridNumber])
                        $td.addClass(that.classnames.td_linenumber).text(0)
                    else if (n[keys.gridCheckbox])
                        $td.addClass(that.classnames.td_checkbox).html('<div><input type="checkbox" data-toggle="icheck" name="datagrid.checkbox" value="true"></div>')
                    else if (n[keys.gridEdit])
                        $td.addClass(that.classnames.s_edit).data('isAdd', true).html(BJUI.doRegional(FRAG.gridEditBtn, that.regional))
                    else $td.text('')
                    
                    if (n.hidden) $td.css('display', 'none')
                    if (n.align)  $td.attr('align', n.align)
                    
                    if (n.name) obj[n.name] = ''
                    
                    if (n.locked) {
                        if (n[keys.gridCheckbox]) {
                            $td.clone().hide().appendTo($tr)
                            $td.show().appendTo($lockTr)
                        } else {
                            $td.clone().show().appendTo($lockTr)
                            $td.hide().appendTo($tr)
                        }
                    } else {
                        $td.appendTo($tr)
                    }
                })
            }
            
            obj = $.extend({}, that.attach, obj)
            if (!that.emptyData) that.emptyData = obj
            
            return {tr:$tr, lockTr:$lockTr.find('> td').length ? $lockTr : null}
        }
        
        if (!addLocation) addLocation = options.addLocation || 'first'
        if (!that.$lastSelect) {
            if (addLocation === 'prev') addLocation = 'first'
            else if (addLocation === 'next') addLocation = 'last'
        }
        if ($.inArray(addLocation, ['first', 'last', 'prev', 'next']) === -1)
            addLocation = 'first'
        
        if (options.isTree && that.$lastSelect) {
            if (!(addLocation === 'first' || addLocation === 'last'))
                addLocation = 'first'
            
            var parentData = that.data[that.tools.getNoChildDataIndex(that.$lastSelect.index())]
            
            if (parentData) {
                addData[options.treeOptions.keys.level]     = parentData[options.treeOptions.keys.level] + 1
                addData[options.treeOptions.keys.parentKey] = parentData[options.treeOptions.keys.key]
                addData[options.keys.treePTr]               = that.$lastSelect
                addData[options.keys.treePData]             = parentData
            } else {
                addData['level'] = 0
            }
        }
        
        if (addLocation === 'first') {
            linenumber = 0
            trs = addTr()
            
            if (options.isTree && that.$lastSelect) {
                linenumber = that.tools.getNoChildDataIndex(that.$lastSelect.index()) + 1
                
                if (that.$lastSelect.next().hasClass(that.classnames.tr_child)) {
                    trs.tr.insertAfter(that.$lastSelect.next())
                    if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq('+ that.$lastSelect.next().index() +')'))
                } else {
                    trs.tr.insertAfter(that.$lastSelect)
                    if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq('+ that.$lastSelect.index() +')'))
                }
            } else {
                trs.tr.prependTo(that.$tbody)
                if (trs.lockTr) trs.lockTr.prependTo(that.$lockTbody)
            }
        } else if (addLocation === 'last') {
            linenumber = that.$tbody.find('> tr').length
            trs = addTr()
            
            if (options.isTree && that.$lastSelect) {
                var child = that.$lastSelect.data('child') || 0, level = that.$lastSelect.data('level'), $lastTr, hasChild = that.$lastSelect.next().hasClass(that.classnames.tr_child)
                
                $lastTr = that.getChildrens(that.$lastSelect, that.$lastSelect).last()
                
                if (hasChild) {
                    trs.tr.insertAfter($lastTr.next())
                    if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq('+ $lastTr.next().index() +')'))
                } else {
                    trs.tr.insertAfter($lastTr)
                    if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq('+ $lastTr.index() +')'))
                }
                
                linenumber = that.tools.getNoChildDataIndex($lastTr.index()) + 1
                
                addData[options.treeOptions.keys.order] = child
            } else {
                trs.tr.appendTo(that.$tbody)
                if (trs.lockTr) trs.lockTr.appendTo(that.$lockTbody)
            }
        } else if (addLocation === 'prev') {
            linenumber = that.tools.getNoChildDataIndex(that.$lastSelect.index())
            trs = addTr()
            
            trs.tr.insertBefore(that.$lastSelect)
            if (trs.lockTr) trs.lockTr.insertBefore(that.$lockTbody.find('> tr:eq('+ that.$lastSelect.index() +')'))
        } else if (addLocation === 'next') {
            linenumber = that.tools.getNoChildDataIndex(that.$lastSelect.index()) + 1
            trs = addTr()
            
            if (that.$lastSelect.next().hasClass(that.classnames.tr_child)) {
                trs.tr.insertAfter(that.$lastSelect.next())
                if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq('+ that.$lastSelect.next().index() +')'))
            } else {
                trs.tr.insertAfter(that.$lastSelect)
                if (trs.lockTr) trs.lockTr.insertAfter(that.$lockTbody.find('> tr:eq('+ that.$lastSelect.index() +')'))
            }
        }
        
        addData = $.extend({}, that.emptyData, addData, {addFlag:true})
        
        if (!that.data) that.data = []
        if (!that.allData) that.allData = []
        if (that.allData.length) {
            that.allData.splice(linenumber + startNumber, 0, addData)
        } else {
            that.allData.push(addData)
        }
        
        if (that.data.length) {
            that.data.splice(linenumber, 0, addData)
        } else {
            that.data.push(addData)
        }
        
        that.tools.updateGridIndex()
        
        trs.tr.initui()
        trs.lockTr && trs.lockTr.initui()
        
        if (addData[options.keys.treePTr])
            trs.tr.data(options.keys.treePTr, addData[options.keys.treePTr])
        if (options.showNoDataTip)
            that.$tbody.find('> tr.datagrid-nodata').remove()
        if (options.height === 'auto')
            that.fixedHeight()
        
        setTimeout(function() {
            that.initEvents(trs.tr)
        }, 20)
        
        if (trs.lockTr) that.initLockEvents(trs.lockTr)
        that.edit(trs.tr)
        
        if (options.hasChild && options.childOptions)
            trs.tr.after('<tr class="'+ that.classnames.tr_child +'"></tr>')
        
        if (noEdit) {
            that.inlineEditComplete(trs.tr, addData)
            if (that.data.length == 1)
                that.needfixedWidth = true
            that.tools.afterSave(trs.tr, addData)
        } else {
            if (options.editMode != 'dialog') {
                that.doEditRow(trs.tr, 'inline', true)
            } else {
                that.dialogEdit(trs.tr, true)
            }
        }
    }
    
    // edit
    Datagrid.prototype.edit = function($trs) {
        var that = this, options = that.options, tools = that.tools, type = options.editMode, columnModel = that.columnModel, $editTd
        
        that.editInit = false
        
        if (!type) return
        if (typeof type === 'object') {
            var editOptions = {}, types = ['dialog', 'navtab', 'div'], editFlag = false
            
            that.editOptions = null
            
            for (var key in type) {
                if ($.inArray(key, types) != -1) {
                    editOptions.type    = key
                    editOptions.options = type[key]
                    
                    editFlag = true
                    
                    break
                }
            }
            
            if (!editFlag) {
                BJUI.debug('Dialog Plugn: The options \'editModel\' set error!')
                return
            }
            
            if (!$.isEmptyObject(editOptions))
                that.editOptions = editOptions
            
        } else {
            if (that.options.editUrl)
                that.options.editUrl = that.options.editUrl.replace(/{\/?[^}]*}/g, '')
            
            if (type != 'dialog')
                type = 'inline'
        }
        
        if (!$trs) $trs = that.$tbody.find('> tr')
        
        $editTd = $trs.find('> td.'+ that.classnames.s_edit)
        
        that.editInit = true
        
        /* events */
        $editTd.each(function() {
            var $td = $(this), $btns = $td.find('button.bjui-datagrid-btn'), $edit = $btns.first(), $update = $edit.next(), $save = $update.next(), $cancel = $save.next(), $delete = $cancel.next()
            
            $edit.on('click', function(e, data) {
                var $btn = $(this), $tr = $btn.closest('tr'), data_index = $tr.index(), isAdd = $td.data('isAdd')
                
                data_index = that.tools.getNoChildDataIndex(data_index)
                
                if (!data) {
                    if (that.isDom) data = $tr.data('initData') || tools.setDomData($tr)
                    else data = that.data[data_index]
                }
                if (!tools.beforeEdit($tr, data)) {
                    return false
                }
                if (that.editOptions) {
                    that.externalEdit($tr, null, data)
                    return false
                }
                if (type != 'dialog') {
                    that.inlineEdit($tr, isAdd)
                    
                    $btns.hide()
                    $update.show()
                    $cancel.show()
                    
                    if (isAdd) {
                        $update.hide()
                        $save.show()
                    }
                } else {
                    that.dialogEdit($tr, isAdd)
                }
                
                e.stopPropagation()
            })
            
            $save.on('click', function(e) {
                var $btn = $(this), $tr = $btn.closest('tr')
                
                that.updateEdit($tr, $btn)
                
                e.stopPropagation()
            }).on('bjui.datagrid.update.tr', function() {
                $btns.hide()
                $edit.show()
                $delete.show()
            })
            
            $update.on('click', function(e) {
                var $btn = $(this), $tr = $btn.closest('tr')
                
                that.updateEdit($tr, $btn)
                
                e.stopPropagation()
            }).on('bjui.datagrid.update.tr', function() {
                $btns.hide()
                $edit.show()
                $delete.show()
            })
            
            $cancel.on('click', function(e) {
                var $btn = $(this), $tr = $btn.closest('tr')
                
                that.cancelEdit($tr)
                
                $btns.hide()
                $edit.show()
                $delete.show()
                
                e.stopPropagation()
            })
            
            $delete.on('click', function(e) {
                var $btn = $(this), $tr = $btn.closest('tr')
                
                that.delRows($tr)
                
                e.stopPropagation()
            })
        })
    }
    
    Datagrid.prototype.externalEdit = function(row, editOpts, data) {
        var that = this, options = that.options, editOptions = {}, $tr, data_index, editUrl, type, types = ['dialog', 'navtab', 'div']
        
        if (editOpts && typeof editOpts === 'object') {
            for (var key in editOpts) {
                if ($.inArray(key, types) != -1) {
                    editOptions.type    = key
                    editOptions.options = editOpts[key]
                    
                    break
                }
            }
        } else
            editOptions = $.extend({}, that.editOptions) 
        
        if (row instanceof jQuery) {
            $tr = row
        } else if (!isNaN(row)) {
            $tr = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')').eq(parseInt(row, 10))
        } else {
            BJUI.debug('BJUI.Datagrid: Func \'externalEdit\', Parameter \'row\' is incorrect!')
            return
        }
        
        if (typeof editOptions === 'undefined') {
            BJUI.debug('BJUI.Datagrid: Func \'externalEdit\', Parameter \'editOptions\' is incorrect!')
            return
        }
        
        if (!$.isEmptyObject(editOptions)) {
            if (!data) {
                if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
                else {
                    data_index = that.tools.getNoChildDataIndex($tr.index())
                    data = that.data[data_index]
                }
            }
            if (!editOptions.options.url || editOptions.options['datagrid.nourl']) {
                editOptions.options['datagrid.nourl'] = true
                editOptions.options.url = options.editUrl
            }
            
            editUrl = editOptions.options.url
            
            if (editUrl && !editUrl.isFinishedTm()) {
                if (!data || data.addFlag)
                    editUrl = editUrl.replace(/{\/?[^}]*}/g, '')
                else
                    editUrl = that.tools.replacePlh(editUrl, data)
                
                if (!editUrl.isFinishedTm()) {
                    BJUI.debug('BJUI.Datagrid: Func \'externalEdit\', the property \'url\' of options \'editOptions\' is incorrect: '+ editUrl)
                } else {
                    editUrl = editUrl
                }
            }
            
            if (editUrl) {
                editOptions.options.url = editUrl
                type = editOptions.type
                
                if (typeof data === 'object' && data) {
                    if (editOptions.options.data)
                        $.extend(editOptions.options.data, data)
                    else
                        editOptions.options.data = $.extend({}, data)
                }
                
                for (var key in options.keys)
                    delete editOptions.options.data[key]
                
                if (!editOptions.options.id)
                    editOptions.options.id = 'datagrid-external-edit'+ (new Date().getTime())
                if (!editOptions.options.type)
                    editOptions.options.type = options.editType
                
                // onClose
                if (typeof options.extOnClose === 'undefined') {
                    options.extOnClose = (editOptions.options.onClose && editOptions.options.onClose.toFunc()) || false
                }
                // for dialog && navtab (if not save)
                editOptions.options.onClose = function() {
                    if ($tr.hasClass(that.classnames.tr_add))
                        that.cancelEdit($tr)
                    
                    if (typeof options.extOnClose !== 'undefined') {
                        if (options.extOnClose) {
                            options.extOnClose.apply(that)
                        }
                    }
                }
                
                var okCallback = function(json) {
                    var complete = false, returnData, fixedWidth = false
                    
                    if (editOptions.options.data['addFlag'] && that.data.length == 1)
                        that.needfixedWidth = true
                    if ($.type(json) === 'array') {
                        complete   = true
                        returnData = json[0]
                    } else if (typeof json === 'object') {
                        complete = true
                        returnData = json
                    }
                    
                    if (complete) {
                        $.extend(data, typeof returnData === 'object' && returnData)
                        
                        if (that.allData && that.allData[data.gridIndex]) {
                            $.extend(that.allData[data.gridIndex], data)
                        }
                        
                        // update allData for filter
                        if (that.oldAllData) that.oldAllData = that.allData.concat()
                        
                        if (data.addFlag) data.addFlag = false
                        
                        that.dialogEditComplete($tr, data)
                        
                        if (type === 'dialog') {
                            if (editOptions.options.message)
                                BJUI.alertmsg('ok', editOptions.options.message)
                            if (typeof editOptions.options.closeCurrent === 'undefined' || editOptions.options.closeCurrent)
                                BJUI.dialog('close', editOptions.options.id)
                        }
                        else if (type === 'navtab')
                            BJUI.navtab('closeTab', editOptions.options.id)
                            
                        that.tools.afterSave($tr, data)
                        
                        // refresh child
                        if (data['refresh.datagrid.child']) {
                            var $child = $tr.data('bjui.datagrid.child')
                            
                            if ($child && $child.length)
                                $child.datagrid('refresh')
                            else
                                that.showChild($tr)
                        }
                    }
                }
                
                editOptions.options.onLoad = function($box) {
                    var $form = $box.find('form.datagrid-edit-form')
                    
                    if ($form && $form.length) {
                        $form = $form.first()
                        
                        $form
                            .removeAttr('okCallback')
                            .data('okCallback', $.proxy(okCallback, that))
                    }
                }
                
                if (type === 'dialog')
                    BJUI.dialog(editOptions.options)
                else if (type === 'navtab')
                    BJUI.navtab(editOptions.options)
                else if (type === 'div')
                    BJUI.ajax('doload', editOptions.options)
                
            }
        }
    }
    
    // Api - inline edit tr
    Datagrid.prototype.doEditRow = function(rows, type, isAdd) {
        if (!this.editInit) return
        
        var that = this, $trs, $editBtn, datas = []
        
        type = type || that.options.editMode
        
        if (typeof rows === 'object') {
            $trs = rows
        } else if (isNaN(rows)) {
            var $myTrs = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')'), $editTrs, rows = rows.split(',')
            
            rows = rows.unique()
            rows.sort(function(a, b) {return a.trim() - b.trim()})
            
            $.each(rows, function(i, n) {
                var row = parseInt(n.trim(), 10), tr
                
                tr = $myTrs.eq(row)
                
                if (tr && tr.length) {
                    if (!$editTrs) $editTrs = tr
                    else $editTrs = $editTrs.add(tr)
                }
            })
            
            $trs = $editTrs
        } else if (!isNaN(rows)) {
            $trs = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')').eq(rows)
        }
        
        if (!$trs.length) return
        
        $trs.each(function() {
            var $tr = $(this), data_index = $tr.index(), data
            
            data_index = that.tools.getNoChildDataIndex(data_index)
            
            if (that.isDom) data = $tr.data('initData') || tools.setDomData($tr)
            else data = that.data[data_index]
            
            datas.push(data)
        })
        
        if (!that.tools.beforeEdit($trs, datas)) {
            return
        }
        
        if (!that.options.editUrl) {
            BJUI.debug('BJUI.Datagrid: Edit url is not set!')
            return
        }
        
        if (that.editOptions) {
            that.externalEdit($trs.last(), null, datas[$trs.length - 1])
        } else {
            $trs.each(function(i) {
                var $tr = $(this)
                
                $editBtn = $tr.find('> td.'+ that.classnames.s_edit +' button.bjui-datagrid-btn.edit')
                
                if (type != 'dialog') {
                    if ($editBtn.length) $editBtn.trigger('click', [datas[i]])
                    else that.inlineEdit($tr, isAdd, datas[i])
                } else {
                    that.dialogEdit($tr, isAdd, datas[i])
                    return false
                }
            })
        }
    }
    
    // Api - cancel edit
    Datagrid.prototype.doCancelEditRow = function(row) {
        var that = this, $trs
        
        if ($.type(row) === 'number') {
            $trs = this.$tbody.find('> tr').eq(row)
        } else {
            $trs = row
        }
        
        $trs.each(function() {
            var $tr = $(this), $cancelBtn = $tr.find('> td.'+ that.classnames.s_edit +' > button.bjui-datagrid-btn.cancel')
            
            if ($cancelBtn.length) {
                $cancelBtn.trigger('click')
            } else {
                that.cancelEdit($tr)
            }
        })
    }
    
    // Api - save edit tr
    Datagrid.prototype.doSaveEditRow = function(row) {
        var that = this, options = that.options, $tr
        
        if ($.type(row) === 'number') {
            $tr = this.$tbody.find('> tr').eq(row)
        } else if (row) {
            $tr = row
        } else {
            $tr = that.$tbody.find('> tr.'+ that.classnames.tr_edit)
        }
        
        if (!$tr.length) {
            that.$grid.alertmsg('info', BJUI.getRegional('datagrid.saveMsg'))
            return
        }
        if ($tr.length == 1) {
            if ($tr.hasClass(that.classnames.tr_edit))
                this.updateEdit($tr)
        } else {
            if (options.saveAll) {
                that.saveAll($tr)
            } else {
                $tr.each(function() {
                    that.updateEdit($(this))
                })
            }
        }
    }
    
    // Api - del tr
    Datagrid.prototype.delRows = function(rows) {
        var that  = this, options = that.options, keys = options.keys, beforeDelete = options.beforeDelete, confirmMsg = options.delConfirm, $trs
        
        if (beforeDelete) {
            if (typeof beforeDelete === 'string') beforeDelete = beforeDelete.toFunc()
            if (typeof beforeDelete === 'function') {
                if (!beforeDelete.call(this)) {
                    return
                }
            }
        }
        
        if (typeof rows === 'object') {
            $trs = rows
        } else if (isNaN(rows)) {
            var $myTrs = that.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')'), $delTrs, rows = rows.split(',')
            
            rows = rows.unique()
            rows.sort(function(a, b) {return a.trim() - b.trim()})
            
            $.each(rows, function(i, n) {
                var tr = $myTrs.eq(parseInt(n.trim(), 10))
                
                if (tr && tr.length) {
                    if (!$delTrs) $delTrs = tr
                    else $delTrs = $delTrs.add(tr)
                }
            })
            
            $trs = $delTrs
        } else if (!isNaN(rows)) {
            $trs = this.$tbody.find('> tr:not(.'+ that.classnames.tr_child +')').eq(rows)
        }
        
        if (!$trs || !$trs.length) return
        
        var delEnd = function() {
            $trs.each(function() {
                var $tr = $(this), childUpdate = that.options.childUpdate, $parent = that.$element.data('bjui.datagrid.parent'),
                    updateParent = function($parent) {
                        $parent.closest('table').datagrid('updateRow', $parent)
                    }
                // update child parent
                if ($parent && childUpdate) {
                    if (typeof childUpdate === 'string') {
                        if (childUpdate.indexOf('all') !== -1 || childUpdate.indexOf('del') !== -1)
                            updateParent($parent)
                    } else {
                        updateParent($parent)
                    }
                }
                
                $tr.trigger('delete.bjui.datagrid.tr')
            })
            
            that.tools.afterDelete()
            
            if (options.height === 'auto')
                that.fixedHeight()
        }
        
        var doDel = function() {
            var $addTrs = $trs.filter('.'+ that.classnames.tr_add)
            
            if ($addTrs.length) {
                that.cancelEdit($addTrs)
                
                if (!$trs.not($addTrs).length)
                    return
            }
            
            // remote delete
            if (options.delUrl) {
                var postData = [], callback = options.delCallback
                
                $trs.not($addTrs).each(function() {
                    var $tr = $(this), index = $tr.index(), data, delData
                    
                    if (that.isDom) data = $tr.data('initData') || that.tools.setDomData($tr)
                    else data = that.data[that.tools.getNoChildDataIndex(index)]
                    
                    if (options.delPK) {
                        postData.push(data[options.delPK])
                    } else {
                        if (options.jsonPrefix) {
                            delData = {}
                            
                            $.each(data, function(name, value) {
                                if (!that.tools.isGridData(name))
                                    delData[options.jsonPrefix +'.'+ name] = value
                            })
                        } else {
                            delData = $.extend({}, data)
                            for (var key in keys)
                                delete delData[keys[key]]
                        }
                        
                        postData.push(delData)
                    }
                })
                
                if (typeof callback === 'string') callback = callback.toFunc()
                
                var type = options.delType, opts = {url:options.delUrl, data:(options.delPK ? [{ name:options.delPK, value:postData.join(',') }] : JSON.stringify(postData)), type:'POST', okCallback:callback || function(json) { delEnd() }}
                
                if (type && type === 'raw' && !options.delPK) {
                    opts.contentType = 'application/json'
                } else {
                    !options.delPK && (opts.data = {json:opts.data})
                    type && type !== 'raw' && (opts.type = type)
                }
                
                BJUI.ajax('doajax', opts)
            } else { // local delete
                delEnd()
            }
        }
        
        if (confirmMsg) {
            if (typeof confirmMsg !== 'string') confirmMsg = $trs.length == 1 ? BJUI.getRegional('datagrid.delMsg') : BJUI.getRegional('datagrid.delMsgM')
            
            that.$grid.alertmsg('confirm', confirmMsg, {
                okCall:function() {
                    doDel()
                }
            })
        } else {
            doDel()
        }
    }
    
    // inline edit
    Datagrid.prototype.inlineEdit = function($tr, isAdd, data) {
        if (!this.tools.beforeEdit($tr, data)) {
            return false
        }
        
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, $tds = $tr.find('> td'), tds_length = $tds.length, tr_index = $tr.index(), data_index = tr_index
        
        data_index = tools.getNoChildDataIndex(data_index)
        
        if (!data) {
            if (that.isDom) data = $tr.data('initData') || tools.setDomData($tr)
            else data = that.data[data_index]
        }
        
        if ($tr.hasClass(that.classnames.tr_edit)) return false
        if (!that.inputs || !that.inputs.length) tools.initEditInputs()
        
        $tr.addClass(that.classnames.tr_edit).data(that.datanames.changeData, {})
        if ($tr.data('validator')) $tr.validator('destroy') //remove old validate
        
        if (!options.inlineEditMult) {
            that.doCancelEditRow($tr.siblings('.'+ that.classnames.tr_edit))
        }
        
        that.$lastEditTr = $tr
        
        $tds.each(function(i) {
            var $td = $(this), op = columnModel[i], val = op && op.name && data[op.name], html = $td.html(), input = that.inputs[i], $input
            var onChange = function($el, $td, val) {
                var changeData = $tr.data(that.datanames.changeData), jsontype = op.jsontype, defaultVal = (typeof op.defaultVal === 'undefined') ? null : op.defaultVal
                
                switch (op.type) {
                case 'date':
                    $el
                        .change(function() {
                            $td.addClass(that.classnames.td_changed)
                            if ($el.val() == val) $td.removeClass(that.classnames.td_changed)
                            changeData[op.name] = $el.val()
                        })
                    
                    break
                case 'select':
                    $el.change(function() {
                        var value = $(this).val()
                        
                        $td.addClass(that.classnames.td_changed)
                        
                        if (value == String(val)) $td.removeClass(that.classnames.td_changed)
                        
                        if ($el.prop('multiple')) {
                            $.isArray(value) && (value = value.join(','))
                        } else if (jsontype) {
                            if (jsontype === 'boolean') {
                                value = Boolean(value)
                            } else if (jsontype === 'number') {
                                if (value.length)
                                    !isNaN(Number(value)) && (value = Number(value))
                                else
                                    value = null
                            }
                        }
                        
                        changeData[op.name] = value
                    })
                    
                    break
                case 'boolean':
                    $el.on('ifChanged', function() {
                        $td.toggleClass(that.classnames.td_changed)
                        
                        var checked = $(this).is(':checked')
                        
                        if (checked == val)
                            $td.removeClass(that.classnames.td_changed)
                        
                        changeData[op.name] = checked
                    })
                    
                    break
                case 'findgrid':
                    changeData[op.name] = $el.val()
                    
                    $el.change(function() {
                        var value = $(this).val()
                        
                        $td.addClass(that.classnames.td_changed)
                        
                        if (value == val)
                            $td.removeClass(that.classnames.td_changed)
                        if (jsontype && jsontype === 'number') {
                            if (value.length)
                                !isNaN(Number(value)) && (value = Number(value))
                            else
                                value = null
                        }
                        
                        changeData[op.name] = value
                    })
                    
                    $td.off('afterchange.bjui.findgrid').on('afterchange.bjui.findgrid', '[data-toggle="findgrid"]', function(e, data) {
                        var include = op.attrs && op.attrs['data-options'] && op.attrs['data-options']['include']
                        
                        if (include) {
                            $.each(include.split(','), function(i, n) {
                                var obj = n.trim().split(':'), name = obj[0], key = obj.length > 1 ? obj[1] : obj[0]
                                
                                data['data'] && (changeData[name] = data['data'][key])
                            })
                        }
                    })
                    
                    break
                case 'spinner':
                    $el.change(function() {
                        $td.addClass(that.classnames.td_changed)
                        if ($el.val() == val) $td.removeClass(that.classnames.td_changed)
                        changeData[op.name] = Number($el.val())
                    })
                    
                    break
                default:
                    $el.change(function() {
                        var value = $(this).val()
                        
                        $td.addClass(that.classnames.td_changed)
                        
                        if (value == val)
                            $td.removeClass(that.classnames.td_changed)
                        if (jsontype && jsontype === 'number') {
                            if (value.length)
                                !isNaN(Number(value)) && (value = Number(value))
                            else
                                value = null
                        }
                        
                        changeData[op.name] = value
                    })
                    
                    break
                }
                
                if (isAdd) {
                    if (op.type === 'boolean') {
                        defaultVal = Boolean(defaultVal)
                        changeData[op.name] = defaultVal
                        $el.prop('checked', defaultVal)
                    } else {
                        if (defaultVal != null) {
                            if ($el.isTag('select') && $el.prop('multiple')) {
                                $el.val($.isArray(defaultVal) ? defaultVal : defaultVal.split(','))
                            } else {
                                $el.val(String(defaultVal))
                            }
                            if (jsontype) {
                                if (jsontype === 'number') {
                                    if (defaultVal.length)
                                        !isNaN(Number(defaultVal)) && (defaultVal = Number(defaultVal))
                                    else
                                        defaultVal = null
                                }
                                else if (jsontype === 'boolean')
                                    defaultVal = Boolean(defaultVal)
                            }
                            
                            changeData[op.name] = defaultVal
                        } else {
                            if (($el.isTag('select') && !$el.prop('multiple'))) {
                                var $clone = $el.clone().appendTo('body'), val = $clone.val()
                                
                                changeData[op.name] = val
                                $clone.remove()
                            }
                        }
                    }
                } else if (jsontype) {
                    $el.trigger('change')
                }
            }
            
            $td.data(that.datanames.td_html, html)
            
            if (isAdd) {
                if (!op.add) input = ''
            } else {
                if (data.addFlag) data.addFlag = false
                if (!op.edit) input = ''
            }
            
            if (input) {
                $input = $(input)
                
                if (typeof val === 'undefined' || val === 'null' || val === null)
                    val = ''
                
                if (op.type === 'boolean')
                    $input.prop('checked', val)
                else if (op.type === 'findgrid')
                    $input.data('context', $tr).val(String(val))
                else {
                    $input.val(String(val))
                    
                    if ($input.isTag('select') && $input.prop('multiple') && val && !$.isArray(val)) {
                        $input.val(val.split(','))
                    }
                }
                
                if (isAdd) {
                    if (op.addAttrs && typeof op.addAttrs === 'object') {
                        $.each(op.addAttrs, function(i, n) {
                            $input.attr(i, n)
                        })
                    }
                } else {
                    if (op.editAttrs && typeof op.editAttrs === 'object') {
                        $.each(op.editAttrs, function(i, n) {
                            $input.attr(i, n)
                        })
                    }
                }
                
                $td
                    .empty()
                    .append($input)
                    .addClass(that.classnames.td_edit)
                
                if (that.treeColumn && that.treeColumn === op) {
                    var treeInput = that.tools.createTreePlaceholder(data, '')
                    
                    $td.attr('align', 'left').html(treeInput)
                    
                    $input.appendTo($td.find('.datagrid-tree-title'))
                }
                
                onChange($input, $td, val)
                
                if (op.locked) {
                    var $lockTr = that.$lockTbody.find('tr:eq('+ tr_index +')')
                    var $lockTd = $lockTr.find('> td:eq('+ op.lockIndex +')')
                    
                    $td.clone().html(html).insertAfter($td)
                    $td.show().insertAfter($lockTd).initui()
                    $lockTd.remove()
                }
            }
            
            if (!--tds_length) {
                $tr
                    .initui()
                    .validator({
                        msgClass : 'n-bottom',
                        theme    : 'red_bottom_effect_grid'
                    })
            }
        })
    }
    
    Datagrid.prototype.saveAll = function($trs) {
        var that = this, options = that.options, keys = options.keys, callback = options.editCallback, $tr, data, data_index, datas = [], changeData, tempData, postData = [], returnData = [], len = $trs && $trs.length
        
        if (!$trs || $trs.length) {
            $trs = that.$tbody.find('> tr.'+ that.classnames.tr_edit)
            len  = $trs.length
        }
        
        if (!len) return
        
        $trs.each(function() {
            $tr = $(this)
            
            data_index = that.tools.getNoChildDataIndex($tr.index())
            data = that.isDom ? $tr.data('initData') : that.data[data_index]
            datas.push(data)
            
            $tr.isValid(function(v) {
                if (v) {
                    // Update data
                    changeData = $tr.data(that.datanames.changeData)
                    $.extend(data, changeData)
                    // Specification post data
                    if (options.jsonPrefix) {
                        tempData = {}
                        
                        $.each(data, function(name, value) {
                            if (!that.tools.isGridData(name))
                                tempData[options.jsonPrefix +'.'+ name] = value
                        })
                    } else {
                        tempData = $.extend({}, data)
                        
                        for (var key in keys)
                            delete tempData[keys[key]]
                    }
                    
                    len --
                    postData.push(tempData)
                } else {
                    postData = []
                    
                    return false
                }
            })
        })
        
        // do save
        if (!len) {
            // Callback
            if (callback) {
                callback = callback.toFunc()
            } else {
                callback = function(json) {
                    if ($.type(json) === 'array') {
                        returnData = json
                    } else if (typeof json === 'object') {
                        returnData.push(json)
                    }
                    
                    $trs.each(function(i) {
                        $tr = $(this)
                        data_index = $tr.index()
                        
                        if (that.columnModel[0] === that.childColumn)
                            data_index = data_index / 2
                        
                        data = that.isDom ? $tr.data('initData') : that.data[data_index]
                        
                        $.extend(data, typeof returnData[i] === 'object' && returnData[i])
                        
                        if (that.allData && that.allData[data.gridIndex]) {
                            $.extend(that.allData[data.gridIndex], data)
                        }
                        
                        // update allData for filter
                        if (that.oldAllData) that.oldAllData = that.allData.concat()
                        
                        that.inlineEditComplete($tr, data)
                    })
                    
                    that.tools.afterSave($trs, postData)
                }
            }
            if (that.options.saveLocal) {
                callback(postData)
            } else {
                // Do ajax
                if (that.tools.beforeSave($trs, datas)) {
                    var type = options.editType, opts = {url:options.editUrl, data:JSON.stringify(postData), type:'POST', okCallback:callback}
                    
                    if (type && type === 'raw') {
                        opts.contentType = 'application/json'
                    } else {
                        opts.data = {json:opts.data}
                        type && (opts.type = type)
                    }
                    
                    BJUI.ajax('doajax', opts)
                }
            }
        }
    }
    
    // update - inline edit
    Datagrid.prototype.updateEdit = function($tr, $btn) {
        var that = this, options = that.options, keys = options.keys, callback = options.editCallback, data, datas = [], changeData, tempData, postData = [], returnData, data_index = $tr.index()
        
        data_index = that.tools.getNoChildDataIndex(data_index)
        
        if (that.isDom) data = $tr.data('initData')
        else data = that.data[data_index]
        
        if ($tr.hasClass(that.classnames.tr_edit)) {
            // validate
            $tr.isValid(function(v) {
                if (v) {
                    // Update data
                    changeData = $tr.data(that.datanames.changeData)
                    $.extend(data, changeData)
                    // Specification post data
                    if (options.jsonPrefix) {
                        tempData = {}
                        $.each(data, function(name, value) {
                            if (!that.tools.isGridData(name))
                                tempData[options.jsonPrefix +'.'+ name] = value
                        })
                    } else {
                        tempData = $.extend({}, data)
                        
                        for (var key in keys) {
                            delete tempData[keys[key]]
                        }
                    }
                    // Callback
                    if (callback) {
                        callback = callback.toFunc()
                    } else {
                        callback = function(json) {
                            if ($.type(json) === 'array') {
                                returnData = json[0]
                            } else if (typeof json === 'object') {
                                returnData = json
                            }
                            
                            $.extend(data, typeof returnData === 'object' && returnData)
                            
                            if (that.allData && that.allData[data.gridIndex]) {
                                $.extend(that.allData[data.gridIndex], data)
                            }
                            
                            // update allData for filter
                            if (that.oldAllData) that.oldAllData = that.allData.concat()
                            
                            that.inlineEditComplete($tr, data, $btn)
                            that.tools.afterSave($tr, data)
                        }
                    }
                    // Do ajax
                    datas.push(data)
                    if (that.tools.beforeSave($tr, datas)) {
                        postData.push(tempData)
                        
                        var type = options.editType, opts = {url:options.editUrl, data:JSON.stringify(postData), type:'POST', okCallback:callback}
                        
                        if (type && type === 'raw') {
                            opts.contentType = 'application/json'
                        } else {
                            opts.data = {json:opts.data}
                            type && (opts.type = type)
                        }
                        
                        BJUI.ajax('doajax', opts)
                    }
                }
            })
        }
    }
    
    /* cancel - inline edit */
    Datagrid.prototype.cancelEdit = function($trs) {
        var that = this, columnModel = that.columnModel
        
        $trs.each(function() {
            var $tr = $(this), tr_index = $tr.index(), data_index = tr_index
            
            if ($tr.hasClass(that.classnames.tr_edit)) {
                $tr
                    .removeClass(that.classnames.tr_edit)
                    .find('> td.'+ that.classnames.td_edit).each(function() {
                        var $td = $(this), td_index = $td.index(), model = columnModel[td_index], html = $td.data(that.datanames.td_html)
                        
                        $td.removeClass(that.classnames.td_edit).removeClass(that.classnames.td_changed).html(html)
                        if (model.locked) {
                            var $lockTr = that.$lockTbody.find('tr:eq('+ tr_index +')')
                            var $lockTd = $lockTr.find('> td:eq('+ model.lockIndex +')')
                            
                            html = $lockTd.data(that.datanames.td_html)
                            
                            $lockTr.removeClass(that.classnames.tr_edit)
                            $lockTd.removeClass(that.classnames.td_edit).removeClass(that.classnames.td_changed).html(html)
                        }
                    })
            }
            
            if ($tr.hasClass(that.classnames.tr_add)) {
                data_index = that.tools.getNoChildDataIndex(data_index)
                
                if (!that.isDom) {
                    that.allData = that.allData.remove(that.data[data_index].gridIndex)   // remove data in allData
                    that.data    = that.data.remove(data_index)
                    
                    that.tools.updateGridIndex()
                    that.$element.data('allData', that.allData)
                }
                
                if ($tr.next().hasClass(that.classnames.tr_child)) {
                    $tr.next().remove()
                    that.$lockTbody && that.$lockTbody.find('> tr:eq('+ tr_index +')').next().remove()
                }
                
                that.$lockTbody && that.$lockTbody.find('> tr:eq('+ tr_index +')').remove()
                $tr.remove()
                
                that.tools.createNoDataTr()
                
                if (that.options.height === 'auto')
                    that.fixedHeight()
            }
        })
    }
    
    // inline editComplete
    Datagrid.prototype.inlineEditComplete = function($tr, trData, $btn) {
        var that = this, columnModel = that.columnModel, tr_index = $tr.index(), $tds = $tr.find('> td'), hasLinenumber = false, $lockTr = that.$lockTbody && that.$lockTbody.find('> tr:eq('+ tr_index +')')
        var tdTemplate = that.options.tdTemplate, tempData = $.extend({}, trData)
        
        $.each(columnModel, function(i, n) {
            if (n === that.linenumberColumn) hasLinenumber = true
            if (that.tools.isGridModel(n)) return true
            
            var label = n.name ? trData[n.name] : '', render_label, $td = $tds.eq(n.index)
            
            if (that.isTemplate)
                $td = null
            if (typeof label === 'undefined' || label === 'null' || label === null)
                label = ''
            
            $td && ($td.text(label).removeClass(that.classnames.td_edit).removeClass(that.classnames.td_changed))
            
            if (n.render && typeof n.render === 'function') {
                if (n.items) {
                    render_label = n.render.call(that, label, trData, n.items, n.itemattr)
                    tempData[n.name] = render_label
                    $td && $td.html(render_label)
                } else {
                    render_label = n.render.call(that, label, trData)
                    tempData[n.name] = render_label
                    $td && $td.html(render_label)
                }
            } else if (n.items) {
                render_label = Datagrid.renderItem.call(that, label, trData, n.items, n.itemattr)
                tempData[n.name] = render_label
                $td && $td.html(render_label)
            }
            
            if (that.options.isTree && n.hasTree) {
                $td && ($td.attr('align', 'left').addClass('datagrid-tree-td').html(that.tools.createTreePlaceholder(trData, (render_label || label))))
            }
            
            if (n.locked && $lockTr) {
                var $lockTd = $lockTr.find('> td:eq('+ n.lockIndex +')')
                
                $lockTd.removeClass(that.classnames.td_changed).html($td.html())
            }
        })
        
        if (that.isTemplate) {
            if (typeof tdTemplate === 'function')
                tdTemplate = tdTemplate.apply(that, [trData])
            
            tdTemplate = that.tools.replacePlh4Template(tdTemplate, tempData)
            
            $tr.find('> td.datagrid-template-td').html(tdTemplate)
        }
        
        $tr.removeClass(that.classnames.tr_edit).initui()
        if ($lockTr)
            $lockTr.removeClass(that.classnames.tr_edit).initui()
        
        if (!$btn) $btn = $tds.filter('.'+ that.classnames.s_edit).find('button.update')
        if ($btn && $btn.length)
            $btn.trigger('bjui.datagrid.update.tr')
        
        if ($tr.hasClass(that.classnames.tr_add)) {
            $tr.removeClass(that.classnames.tr_add)
            $tr.find('> td.'+ that.classnames.s_edit).removeData('isAdd')
            
            // update linenumber
            if (hasLinenumber) {
                that.tools.updateLinenumber()
            }
            
            // tree
            if (that.options.isTree) {
                var $parentTr = trData[that.options.keys.treePTr], parentData = trData[that.options.keys.treePData], keys = that.options.treeOptions.keys, $treePTd
                
                if ($parentTr && parentData) {
                    if (!parentData[keys.isParent]) {
                        parentData[keys.isParent] = true
                        $treePTd = $parentTr.find('> td.datagrid-tree-td')
                        $treePTd.html(that.tools.createTreePlaceholder(parentData, $treePTd.find('span.datagrid-tree-title').html()))
                    }
                    if (!parentData[keys.childLen]) {
                        parentData[keys.childLen] = 1
                    } else {
                        parentData[keys.childLen] ++
                    }
                    
                    $tr.data(that.options.keys.treePTr).data('child', parentData[keys.childLen]).attr('data-child', parentData[keys.childLen])
                }
                
                $tr.addClass('datagrid-tree-tr datagrid-tree-level-'+ trData[keys.level]).attr('data-child', 0).attr('data-level', trData[keys.level])
            }
            
            // child
            $tr.next('.'+ that.classnames.tr_child).remove()
            that.tools.createChildTr($tr, trData)
        }
    }
    
    // inline edit
    Datagrid.prototype.dialogEdit = function($tr, isAdd, data) {
        if (!this.tools.beforeEdit($tr, data)) {
            return false
        }
        
        var that = this, options = that.options, tools = that.tools, columnModel = that.columnModel, tr_index = $tr.index(), data_index = tr_index, $dialog, $form, html = '', title
        
        if (!data) {
            data_index = tools.getNoChildDataIndex(data_index)
            
            if (that.isDom) data = $tr.data('initData') || tools.setDomData($tr)
            else data = that.data[data_index]
        }
        
        if (!that.inputs || !that.inputs.length) tools.initEditInputs()
        
        title = options.gridTitle || 'datagrid'
        
        if (isAdd) {
            title += ' - '+ BJUI.getRegional('datagrid.add')
        } else {
            if (data.addFlag) data.addFlag = false
            title += ' - '+ BJUI.getRegional('datagrid.edit')
        }
        
        $dialog = $('<div><div class="bjui-pageContent"></div><div class="bjui-pageFooter"></div></div>')
        $form   = $('<form class="datagrid-dialog-edit-form"></form>')
        
        var onChange = function($tr, $form, $el, model) {
            var changeData = $tr.data(that.datanames.changeData), jsontype = model.jsontype, defaultVal = (typeof model.defaultVal === 'undefined') ? null : model.defaultVal
            
            switch (model.type) {
            case 'date':
                $el.change(function() {
                    changeData[model.name] = $(this).val()
                })
                
                break
            case 'select':
                $el.change(function() {
                    var $element = $(this), val = $element.val(), multiple = $element.prop('multiple')
                    
                    if (multiple && $.isArray(val)) {
                        val = val.join(',')
                    }
                    if (!multiple && jsontype) {
                        if (jsontype === 'boolean') {
                            val = Boolean(val)
                        } else if (jsontype === 'number') {
                            if (val.length)
                                !isNaN(Number(val)) && (val = Number(val))
                            else
                                val = null
                        }
                    }
                    
                    changeData[model.name] = val
                })
                
                break
            case 'boolean':
                $el.on('ifChanged', function() {
                    changeData[model.name] = $(this).is(':checked')
                })
                
                break
            case 'findgrid':
                $el.change(function() {
                    var val = data.value
                    
                    if (jsontype && jsontype === 'number') {
                        if (val.length)
                            !isNaN(Number(val)) && (val = Number(val))
                        else
                            val = null
                    }
                    
                    changeData[model.name] = $(this).val()
                })
                
                $form.off('afterchange.bjui.findgrid').on('afterchange.bjui.findgrid', '[data-toggle="findgrid"]', function(e, data) {
                    var include = model.attrs && model.attrs['data-options'] && model.attrs['data-options']['include']
                    
                    if (include) {
                        $.each(include.split(','), function(i, n) {
                            var obj = n.trim().split(':'), name = obj[0], key = obj.length > 1 ? obj[1] : obj[0]
                            
                            data['data'] && (changeData[name] = data['data'][key])
                        })
                    }
                })
                
                break
            case 'spinner':
                $el.change(function() {
                    changeData[model.name] = Number($(this).val())
                })
                
                break
            default:
                $el.change(function() {
                    var val = $(this).val()
                    
                    if (jsontype && jsontype === 'number') {
                        if (val.length)
                            !isNaN(Number(val)) && (val = Number(val))
                        else
                            val = null
                    }
                    
                    changeData[model.name] = val
                })
                
                break
            }
            
            if (isAdd) {
                if (model.type === 'boolean') {
                    defaultVal = Boolean(defaultVal)
                    changeData[model.name] = defaultVal
                    $el.prop('checked', defaultVal)
                } else {
                    if (defaultVal != null) {
                        if ($el.isTag('select') && $el.prop('multiple')) {
                            $el.val($.isArray(defaultVal) ? defaultVal : defaultVal.split(','))
                        } else {
                            $el.val(String(defaultVal))
                        }
                        if (jsontype) {
                            if (jsontype === 'number')
                                !isNaN(Number(defaultVal)) && (defaultVal = Number(defaultVal))
                            else if (jsontype === 'boolean')
                                defaultVal = Boolean(defaultVal)
                        }
                        
                        changeData[model.name] = defaultVal
                    } else {
                        //($el.isTag('select') && !$el.prop('multiple')) && (changeData[model.name] = $el.val())
                        if (($el.isTag('select') && !$el.prop('multiple'))) {
                            var $clone = $el.clone().appendTo('body'), val = $clone.val()
                            
                            changeData[model.name] = val
                            $clone.remove()
                        }
                    }
                }
            } else if (jsontype) {
                $el.trigger('change')
            }
        }
        
        var onLoad = function($dialog) {
            var $form   = $dialog.find('form.datagrid-dialog-edit-form'),
                $btns   = $dialog.find('div.bjui-pageFooter button'),
                $prev   = $btns.first(),
                $next   = $btns.eq(1),
                $cancel = $btns.eq(2),
                $save   = $btns.last(),
                trindex, dataindex
            
            var createForm = function(data, $form, $tr) {
                $form.empty()
                if (!$tr.data(that.datanames.changeData)) $tr.data(that.datanames.changeData, {})
                
                if ($form.data('validator')) $form.validator('destroy')
                
                $.each(columnModel, function(i, n) {
                    if (!n.name || that.tools.isGridModel(n)) return true
                    
                    var input = that.inputs[i], $input, $p = $('<p></p>'), id = 'datagrid-dialog-edit-column-'+ i, val = data[n.name]
                    
                    if (n.hide) $p.addClass('hide')
                    if (isAdd) {
                        if (!n.add) input = ''
                    } else {
                        if (!n.edit) input = ''
                    }
                    
                    if (typeof val === 'undefined' || val === 'null' || val === null)
                        val = ''
                    
                    if (input) {
                        $input = $(input).attr('id', id)
                        if ($input.isTag('select')) $input.attr('data-width', 'auto')
                        else if (!$input.isTag('checkbox')) $input.attr('size', 30)
                        
                        if (n.type === 'boolean') {
                            $input.prop('checked', val)
                        } else if (n.type === 'findgrid') {
                            $input.val(String(val)).data('context', $form)
                        } else {
                            $input.val(String(val))
                        }
                        
                        if ($input.isTag('select') && $input.prop('multiple') && val && !$.isArray(val)) {
                            $input.val(val.split(','))
                        }
                        
                        if (isAdd) {
                            if (n.addAttrs && typeof n.addAttrs === 'object') {
                                $.each(n.addAttrs, function(k, v) {
                                    $input.attr(k, v)
                                })
                            }
                        } else {
                            if (n.editAttrs && typeof n.editAttrs === 'object') {
                                $.each(n.editAttrs, function(k, v) {
                                    $input.attr(k, v)
                                })
                            }
                        }
                    } else if (!isAdd) {
                        if (!n.edit)
                            input = val
                    }
                    
                    $p
                        .append('<label class="control-label x120" for="'+ id +'">'+ n.label +'：</label>')
                        .append($('<span class="datagrid-dialog-column-span"></span>').append($input || input))
                        .appendTo($form)
                    
                    if ($input)
                        onChange($tr, $form, $input, n)
                })
                
                $form
                    .initui()
                    .validator({
                        msgClass : 'n-bottom',
                        theme    : 'red_bottom_effect_grid'
                    })
            }
            
            if ($form.is(':empty')) createForm(data, $form, $tr)
            
            trindex = $tr.index()
            
            if (that.columnModel[0] === that.childColumn)
                trindex = trindex * 2
            
            if (!trindex) $prev.addClass('disabled')
            if (trindex == that.data.length - 1) $next.addClass('disabled')
            
            $prev.click(function() {
                var $tr_prev = $tr.prev(), data
                
                if (that.options.hasChild && that.options.childOptions)
                    $tr_prev = $tr_prev.prev()
                if ($tr_prev.length) {
                    dataindex = that.tools.getNoChildDataIndex($tr_prev.index())
                        
                    if (that.isDom) {
                        data = $tr_prev.data('initData') || tools.setDomData($tr_prev)
                    } else {
                        data = that.data[dataindex]
                    }
                    
                    $tr = $tr_prev
                    createForm(data, $form, $tr)
                    $next.removeClass('disabled')
                    
                    if (!$tr_prev.prev().length) $prev.addClass('disabled')
                } else {
                    $prev.addClass('disabled')
                }
            })
            
            $next.click(function() {
                var $tr_next = $tr.next(), data, $next_next
                
                if (that.options.hasChild && that.options.childOptions)
                    $tr_next = $tr_next.next()
                if ($tr_next.length) {
                    $next_next = $tr_next.next()
                    dataindex  = that.tools.getNoChildDataIndex($tr_next.index())
                    
                    if (that.options.hasChild && that.options.childOptions)
                        $next_next = $next_next.length && $next_next.next()
                    if (that.isDom) {
                        data = $tr_next.data('initData') || tools.setDomData($tr_next)
                    } else {
                        data = that.data[dataindex]
                    }
                    
                    $tr = $tr_next
                    createForm(data, $form, $tr)
                    $form.initui()
                    $prev.removeClass('disabled')
                    
                    if (!$next_next || !$next_next.length) $next.addClass('disabled')
                } else {
                    $next.addClass('disabled')
                }
            })
            
            $save.click(function() {
                var changeData, data, datas = [], postData, returnData, callback = options.editCallback
                
                dataindex = that.tools.getNoChildDataIndex($tr.index())
                
                if (that.isDom) data = $tr.data('initData')
                else data = that.data[dataindex]
                
                $form.isValid(function(v) {
                    if (v) {
                        changeData = $tr.data(that.datanames.changeData)
                        $.extend(data, changeData)
                        
                        if (options.jsonPrefix) {
                            postData = {}
                            $.each(data, function(name, value) {
                                if (!that.tools.isGridData(name))
                                    postData[options.jsonPrefix +'.'+ name] = value
                            })
                        } else {
                            postData = $.extend({}, data)
                            
                            for (var key in that.options.keys)
                                delete postData[that.options.keys[key]]
                        }
                        
                        // Callback
                        if (callback) {
                            callback = callback.toFunc()
                        } else {
                            callback = function(json) {
                                if ($.type(json) === 'array') {
                                    returnData = json[0]
                                } else if (typeof json === 'object') {
                                    returnData = json
                                }
                                
                                $.extend(data, typeof returnData === 'object' && returnData)
                                
                                that.dialogEditComplete($tr, data)
                                that.$grid.dialog('close', 'datagrid-dialog-edit')
                                that.tools.afterSave($tr, data)
                            }
                        }
                        
                        // Do ajax
                        datas.push(data)
                        if (that.tools.beforeSave($tr, datas)) {
                            var type = options.editType, opts = {url:options.editUrl, data:JSON.stringify(postData), type:'POST', okCallback:callback}
                            
                            if (type && type === 'raw') {
                                opts.contentType = 'application/json'
                            } else {
                                opts.data = {json:opts.data}
                                type && (opts.type = type)
                            }
                            
                            BJUI.ajax('doajax', opts)
                        }
                    }
                })
            })
            
            $cancel.click(function() {
                that.$grid.dialog('close', 'datagrid-dialog-edit')
            })
        }
        
        var onClose = function() {
            var addRemove = false
            
            that.$tbody.find('> tr.'+ that.classnames.tr_add).each(function() {
                var $tr = $(this), trindex = $tr.index(), dataindex = trindex
                
                dataindex = that.tools.getNoChildDataIndex(dataindex)
                
                that.data = that.data.remove(dataindex)
                
                if ($tr.next().hasClass(that.classnames.tr_child)) {
                    $tr.next().remove()
                    that.$lockTbody && that.$lockTbody.find('> tr:eq('+ trindex +')').next().remove()
                }
                
                that.$lockTbody && that.$lockTbody.find('> tr:eq('+ trindex +')').remove()
                $tr.remove()
                
                addRemove = true
            })
            
            if (addRemove && that.options.height === 'auto')
                that.fixedHeight()
        }
        
        $dialog.find('> div:first')
            .append($form)
            .next().append(BJUI.doRegional(FRAG.gridDialogEditBtns, that.regional))
        
        var dialog_options = $.extend({}, {id:'datagrid-dialog-edit', fresh:true, target:$dialog[0], width:520, height:400, mask:true, title:title, onLoad:onLoad, onClose:onClose}, (typeof options.editDialogOp === 'object' && options.editDialogOp))
        
        that.$grid.dialog(dialog_options)
    }
    
    // dialog editComplete
    Datagrid.prototype.dialogEditComplete = function($tr, trData) {
        var that = this, columnModel = that.columnModel, tr_index = $tr.index(), $tds = $tr.find('> td'), hasLinenumber = false, $trs = that.$tbody.find('> tr'), $lockTr = that.$lockTbody && that.$lockTbody.find('> tr:eq('+ tr_index +')')
        var tdTemplate = that.options.tdTemplate, tempData = $.extend({}, trData), treeOptions = that.options.treeOptions, treePData, treePTr, treePTd
        
        $.each(columnModel, function(i, n) {
            if (n === that.linenumberColumn) hasLinenumber = true
            if (that.tools.isGridModel(n)) return true
            
            var label = n.name ? trData[n.name] : '', render_label, $td = $tds.eq(n.index)
            
            if (that.isTemplate)
                $td = null
            if (typeof label === 'undefined' || label === 'null' || label === null)
                label = ''
            
            $td && ($td.text(label).removeClass(that.classnames.td_edit).removeClass(that.classnames.td_changed))
            
            if (n.render && typeof n.render === 'function') {
                if (n.items) {
                    render_label = n.render.call(that, label, trData, n.items, n.itemattr)
                    tempData[n.name] = render_label
                    $td && $td.html(render_label)
                } else {
                    render_label = n.render.call(that, label, trData)
                    tempData[n.name] = render_label
                    $td && $td.html(render_label)
                }
            } else if (n.items) {
                render_label = Datagrid.renderItem.call(that, label, trData, n.items, n.itemattr)
                tempData[n.name] = render_label
                $td && $td.html(render_label)
            }
            
            if (that.options.isTree && n.hasTree) {
                $td && ($td.attr('align', 'left').addClass('datagrid-tree-td').html(that.tools.createTreePlaceholder(trData, (render_label || label))))
            }
            
            if (n.locked && $lockTr && $td) {
                var $lockTd = $lockTr.find('> td:eq('+ n.lockIndex +')')
                
                $lockTd.removeClass(that.classnames.td_edit).removeClass(that.classnames.td_changed).html($td.html())
            }
        })
        
        if (that.isTemplate) {
            if (typeof tdTemplate === 'function')
                tdTemplate = tdTemplate.apply(that, [trData])
            
            tdTemplate = that.tools.replacePlh4Template(tdTemplate, tempData)
            
            $tr.find('> td.datagrid-template-td').html(tdTemplate)
        }
        
        $tr.initui()
        if ($lockTr)
            $lockTr.initui()
        
        if ($tr.hasClass(that.classnames.tr_add)) {
            $tr.removeClass(that.classnames.tr_add)
            
            // update linenumber
            if (hasLinenumber) {
                that.tools.updateLinenumber()
            }
            
            // tree
            if (that.options.isTree) {
                var $parentTr = trData[that.options.keys.treePTr], parentData = trData[that.options.keys.treePData], keys = that.options.treeOptions.keys, $treePTd
                
                if ($parentTr && parentData) {
                    if (!parentData[keys.isParent]) {
                        parentData[keys.isParent] = true
                        $treePTd = $parentTr.find('> td.datagrid-tree-td')
                        $treePTd.html(that.tools.createTreePlaceholder(parentData, $treePTd.find('span.datagrid-tree-title').html()))
                    }
                    if (!parentData[keys.childLen]) {
                        parentData[keys.childLen] = 1
                    } else {
                        parentData[keys.childLen] ++
                    }
                    
                    $tr.data(that.options.keys.treePTr).data('child', parentData[keys.childLen]).attr('data-child', parentData[keys.childLen])
                }
                
                $tr.addClass('datagrid-tree-tr datagrid-tree-level-'+ trData[keys.level]).attr('data-child', 0).attr('data-level', trData[keys.level])
            }
            
            // child
            $tr.next('.'+ that.classnames.tr_child).remove()
            that.tools.createChildTr($tr, trData)
        }
    }
    
    /* resize */
    Datagrid.prototype.resizeGrid = function() {
        var that = this, $target = that.$grid.getPageTarget(), parentW, parentH
        var _resizeGrid = function() {
            var ww = that.$grid.width(), $headDiv = that.$tableH.next('.datagrid-thead-dialog-div'),
                newTemplate = ((that.options.tdTemplate && that.options.templateWidth) && that.options.templateWidth > ww) || that.options.templateWidth === 0
            
            // tdtemplate
            if (newTemplate !== that.isTemplate) {
                that.isTemplate = newTemplate
                that.tools.coverTemplate()
                return
            }
            if ((that.options.dialogFilterW && ww < that.options.dialogFilterW)
                || that.options.dialogFilterW === 0) {
                
                that.$tableH.hide()
                
                if (!$headDiv.length) {
                    that.$tableH.after('<div class="datagrid-thead-dialog-div" style="padding:5px;"><button type="button" class="btn btn-orange datagrid-thead-dialog-view">'+ BJUI.getRegional('datagrid.fAndS') +'</button><span class="datagrid-thead-dialog-filter-msg"><span class="msg-sort"></span><span class="msg-filter"></span></span></div>')
                } else {
                    $headDiv.show()
                }
                
                if (!that.$headFilterUl)
                    that.filterInThead(true)
                
                that.$grid.off('click.datagrid.thead.view').on('click.datagrid.thead.view', '.datagrid-thead-dialog-view', function(e) {
                    BJUI.dialog({id:'datagrid-thead-view', html:'<div class="bjui-pageContent"></div><div class="bjui-pageFooter"><ul><li><button type="button" class="btn btn-close">关闭</button></li></ul></div>', width:360, height:300, title:'datagrid - thead - columns', 
                        onLoad: function($dialog) {
                            that.$headFilterUl.show().appendTo($dialog.find('.bjui-pageContent'))
                        },
                        beforeClose:function($dialog) {
                            that.$headFilterUl.hide().appendTo(that.$grid)
                            return true
                        }
                    })
                })
            } else {
                that.$grid.removeClass('datagrid-flowlayout')
                that.$tableH.show()
                $headDiv.hide()
                
                if (that.$colgroupB.is(':hidden')) {
                    that.$colgroupB.show()
                }
            }
            
            if (that.initFixedW && String(that.options.width).endsWith('%')) {
                parentW = that.$grid.parent().width()
                that.fixedWidth()
                
                if (that.options.hasChild && that.options.childOptions) {
                    that.$tbody.find('> tr.'+ that.classnames.tr_child +':visible').each(function() {
                        var $child = $(this), $tr = $child.prev(), $table = $tr.data('bjui.datagrid.child')
                        
                        if ($table && $table.length) {
                            $table.datagrid('fixedWidth')
                        }
                    })
                }
            }
            
            if (String(that.options.height).endsWith('%')) {
                that.tools.setBoxbH()
            }
        }
        
        // for tab
        $('a[data-toggle="tab"]').on('shown.bs.tab', $.proxy(function(e) {
            if (!that.$element.data('bjui.datagrid.init.tab')) {
                var $target = $(e.target), $box = $target.data('target'), href = $target.attr('href')
                
                if (!$box)
                    $box = $(href)
                
                if ($box && $box.length) {
                    if ($box.find(that.$element).length) {
                        that.$element.data('bjui.datagrid.init.tab', true)
                        that.$element.datagrid('fixedHeight')
                    }
                }
            }
        }, that))
        
        $(window).on(BJUI.eventType.resizeGrid, $.proxy(_resizeGrid, that))
    }
    
    
    // DATAGRID PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments,
            property = option
        
        return this.each(function () {
            var $this   = $(this),
                options = $.extend(true, {}, Datagrid.DEFAULTS, typeof option === 'object' && option),
                data    = $this.data('bjui.datagrid')
            
            if (!data) $this.data('bjui.datagrid', (data = new Datagrid(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.datagrid

    $.fn.datagrid             = Plugin
    //$.fn.datagrid.Constructor = Datagrid
    $.datagrid                = Datagrid
    
    // DATAGRID NO CONFLICT
    // =================
    
    $.fn.datagrid.noConflict = function () {
        $.fn.datagrid = old
        return this
    }
    
    // DATAGRID DATA-API
    // ==============
    
    $(document).on(BJUI.eventType.initUI, function(e) {
        $(e.target).find('table[data-toggle="datagrid"]').each(function() {
            var $this = $(this), options = $this.data()
            
            if (!$this.length) return
            
            if (options.options && typeof options.options === 'string') options.options = options.options.toObj()
            $.extend(options, typeof options.options === 'object' && options.options)
            
            Plugin.call($this, options)
        })
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-spinner.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-spinner.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // SPINNER CLASS DEFINITION
    // ======================
    var Spinner = function(element, options) {
        this.$element = $(element)
        this.options  = options
        this.tools    = this.TOOLS()
        this.$spinner = null
        this.height   = this.$element.addClass('form-control').innerHeight()
        this.ivalue   = Number(this.$element.val()) || 0
    }
    
    Spinner.DEFAULTS = {
        min: 0,
        max: 100,
        step: 1,
        decimalPlace: 0
    }
    
    Spinner.EVENTS = {
        afterChange : 'afterchange.bjui.spinner'
    }
    
    Spinner.prototype.TOOLS = function() {
        var that  = this
        var tools = {
            changeVal: function($btn) {
                var $input = that.$element
                var ivalue = Number($input.val()) || Number(that.ivalue)
                var type   = $btn.data('add') || -1
                var istart = that.options.min, iend = that.options.max, istep = that.options.step
                
                if (type == 1) {
                    if (ivalue <= iend - istep)
                        ivalue = ivalue + istep
                } else if (type == -1) {
                    if (ivalue >= (istart + istep))
                        ivalue = ivalue - istep
                } else if (ivalue > iend) {
                    ivalue = iend
                } else if (ivalue < istart) {
                    ivalue = istart
                }
                if (that.options.decimalPlace)
                    ivalue = new String(ivalue.toFixed(that.options.decimalPlace))
                
                that.ivalue = ivalue
                
                $input
                    .val(ivalue)
                    .trigger(Spinner.EVENTS.afterChange, {value:ivalue})
                    .trigger('change')
            }
        }
        
        return tools
    }
    
    Spinner.prototype.init = function() {
        var that     = this
        var $element = this.$element
        var options  = this.options
        
        if (isNaN(this.options.min) || isNaN(this.options.max) || isNaN(this.options.step)) {
            BJUI.debug('BJUI.Spinner: Parameter is non-numeric type!')
            return
        }
        
        this.addBtn()
    }
    
    Spinner.prototype.addBtn = function() {
        var that = this, $element = that.$element, fluid = $element.attr('size') ? '' : ' fluid'
        
        if (!this.$lookBtn && !$element.parent().hasClass('wrap_bjui_btn_box')) {
            this.$spinner = $(FRAG.spinnerBtn)
            
            $element.css({'paddingRight':'13px'}).wrap('<span class="wrap_bjui_btn_box'+ fluid +'"></span>')
            
            var $box = $element.parent()
            
            $box.css('position', 'relative')
            this.$spinner.css({'height':this.height}).appendTo($box)
            this.$spinner.on('selectstart', function() { return false })
            
            var timer = null
            
            that.$spinner.find('li').on('click', function(e) {
                that.tools.changeVal($(this))
            }).on('mousedown', function() {
                var $btn = $(this)
                
                timer = setInterval(function() {
                    that.tools.changeVal($btn)
                }, 150)
            }).on('mouseup', function() { clearTimeout(timer) })
        }
    }
    
    Spinner.prototype.destroy = function() {
        if (this.$element.parent().hasClass('wrap_bjui_btn_box')) {
            this.$element.parent().find('.bjui-spinner').remove()
            this.$element.unwrap()
        }
    }
    
    // SPINNER PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments
        var property = option
        
        return this.each(function () {
            var $this   = $(this)
            var options = $.extend({}, Spinner.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data    = $this.data('bjui.spinner')
            
            if (!data) $this.data('bjui.spinner', (data = new Spinner(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }
    
    var old = $.fn.spinner
    
    $.fn.spinner             = Plugin
    $.fn.spinner.Constructor = Spinner
    
    // SPINNER NO CONFLICT
    // =================
    
    $.fn.spinner.noConflict = function () {
        $.fn.spinner = old
        return this
    }
    
    // SPINNER DATA-API
    // ==============

    $(document).on(BJUI.eventType.initUI, function(e) {
        var $this = $(e.target).find('input[data-toggle="spinner"]')
        
        if (!$this.length) return
        
        Plugin.call($this)
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-findgrid.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-findgrid.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
 // FINDGRID GLOBAL ELEMENTS
    // ======================
    
    var group, suffix, pk, include, exclude, append, oldData, beforeSelect, onSelect, afterSelect, $box, $currentFindGrid
    
    // FINDGRID CLASS DEFINITION
    // ======================
    
    var FindGrid = function(element, options) {
        this.$element = $(element)
        this.options  = options
        this.$findBtn = null
    }
    
    FindGrid.DEFAULTS = {
        pk           : null,
        oldData      : null,
        group        : null,
        suffix       : null,
        include      : null,
        exclude      : null,
        multiple     : false,
        append       : false,
        empty        : true,
        dialogOptions: {
            id        : null,
            mask      : true,
            width     : 600,
            height    : 400,
            title     : 'FindGrid',
            maxable   : true,
            resizable : true
        },
        gridOptions : {
            width      : '100%',
            height     : '100%',
            tableWidth : '100%',
            columnMenu : false
        },
        context     : null,
        beforeSelect: null,
        onSelect    : null,
        afterSelect : null
    }
    
    FindGrid.EVENTS = {
        afterChange : 'afterchange.bjui.findgrid'
    }
    
    FindGrid.getField = function(key) {
        return (group ? (group +'.') : '') + (key) + (suffix ? suffix : '')
    }
    
    FindGrid.empty = function() {
        if (!include) {
            BJUI.debug('BJUI.FindGrid: No set options \'include\' !')
            return
        }
        
        var that = this, includes = include.split(','), $inputs = $box.find(':text, :input:hidden, textarea, select'), includeKeys = {}
        
        for (var i = 0; i < includes.length; i++) {
            var arr = includes[i].split(':'), key, fieldKey
            
            if (arr.length == 2) {
                fieldKey = arr[0].trim()
                key      = arr[1].trim()
            } else {
                fieldKey = includes[i].trim()
                key      = fieldKey
            }
            
            includeKeys[that.getField(fieldKey)] = true
        }
        
        for (key in includeKeys) {
            $inputs.filter('[name="'+ key +'"]')
                .val('')
                .trigger(FindGrid.EVENTS.afterChange, {value:'', data:''})
                .trigger('focus')
                .trigger('change')
        }
    }
    
    FindGrid.setSingle = function(data) {
        if (typeof onSelect === 'function') {
            onSelect.call(this, data)
            return
        }
        
        var that   = this
        var setVal = function($input, fieldKey, value) {
            var name = that.getField(fieldKey)
            
            if (name == $input.attr('name')) {
                $input
                    .val(value)
                    .trigger(FindGrid.EVENTS.afterChange, {value:value, data:data})
                    .trigger('focus')
                    .trigger('change')
                
                if ($input.isTag('select') && $input.data('toggle') && $input.data('toggle') === 'selectpicker') {
                    $input.selectpicker('refresh')
                }
            }
        }
        
        $box.find('input:text, input:hidden, textarea, select').each(function() {
            var $input = $(this), fieldKey, excludeKeys = []
            
            if (include) {
                var includes = include.split(',')
                
                for (var i = 0; i < includes.length; i++) {
                    var arr = includes[i].split(':'), key
                    
                    if (arr.length == 2) {
                        fieldKey = arr[0].trim()
                        key      = arr[1].trim()
                    } else {
                        fieldKey = includes[i].trim()
                        key      = fieldKey
                    }
                    
                    if (data && typeof data[key] !== 'undefined')
                        setVal($input, fieldKey, data[key])
                }
            } else {
                for (var key in data) {
                    if (exclude) {
                        $.each(exclude.split(','), function(i, n) {
                            excludeKeys.push(n.trim())
                        })
                        
                        if ($.inArray(key, excludeKeys) != -1) {
                            continue
                        }
                    }
                    
                    setVal($input, key, data[key])
                }
            }
        })
        
        if (typeof afterSelect === 'function') {
            afterSelect.call(that, data)
            return
        }
    }
    
    FindGrid.setMult = function(gridId) {
        var datas = $('#'+ gridId).data('selectedDatas')
        
        if (typeof beforeSelect === 'function') {
            if (!beforeSelect.apply(that, [datas])) {
                return false
            }
        }
        if (typeof onSelect === 'function') {
            onSelect.call(that, datas)
            return
        }
        
        var that = this, data, inputLen = 0, newVal
        var refreshSelect = function($input) {
            if ($input.isTag('select') && $input.data('toggle') && $input.data('toggle') === 'selectpicker') {
                $input.selectpicker('refresh')
            }
        }
        
        if (datas && datas.length) {
            var $inputs = $box.find('input:text, input:hidden, textarea, select'), fieldKey, includeKeys = {}, okObj = {}, excludeKeys = [], v
            
            if (!append && oldData)
                oldData = []
            
            if (include) {
                var includes = include.split(',')
                
                for (var i = 0; i < includes.length; i++) {
                    var arr = includes[i].split(':'), key, obj
                    
                    if (arr.length == 2) {
                        fieldKey = arr[0].trim()
                        key      = arr[1].trim()
                    } else {
                        fieldKey = includes[i].trim()
                        key      = fieldKey
                    }
                    
                    includeKeys[that.getField(fieldKey)] = key
                }
            } else {
                for (var key in datas[0]) {
                    if (exclude) {
                        $.each(exclude.split(','), function(i, n) {
                            excludeKeys.push(n.trim())
                        })
                        if ($.inArray(key, excludeKeys) === -1) {
                            includeKeys[that.getField(key)] = key
                        }
                    } else {
                        includeKeys[that.getField(key)] = key
                    }
                }
            }
            
            $inputs = $($.map($inputs, function(n) {
                var $n = $(n), name = $n.attr('name')
                
                if (!name) return null
                if (!includeKeys[name]) return null
                
                okObj[name] = includeKeys[name]
                
                return n
            }))
            
            for (var j = 0; j < datas.length; j++) {
                data = datas[j]
                
                if (oldData && $.inArray(data[pk], oldData) != -1 && !append)
                    continue
                
                $inputs.each(function(k) {
                    var $input = $(this), name = $input.attr('name'), value = $input.val()
                    
                    if (!newVal) newVal = new Array()
                    if (!newVal[k]) newVal[k] = []
                    
                    if (!append) {
                        $input.val('')
                        refreshSelect($input)
                    } else {
                        if (value) {
                            newVal[k].push(value)
                            
                            if ($input.isTag('select') && $input.prop('multiple')) {
                                newVal[k] = $.type(value) === 'array' ? value : []
                            }
                        }
                    }
                    
                    newVal[k].push(data[okObj[name]])
                    
                    if (oldData && $.inArray(data[pk], oldData) === -1)
                        oldData.push(data[pk])
                })
            }
            
            if (newVal) {
                $inputs.each(function(k) {
                    var $input = $(this)
                    
                    v = newVal[k].join(',')
                    
                    if ($input.isTag('select') && $input.prop('multiple'))
                        v = newVal[k]
                    
                    $input
                        .val(v)
                        .trigger(FindGrid.EVENTS.afterChange, {value:v, data:datas})
                        .trigger('change')
                    
                    $currentFindGrid.data('oldData', oldData)
                    
                    refreshSelect($input)
                })
            }
            
            if (typeof afterSelect === 'function') {
                afterSelect.call(that, data.data)
                return
            }
            
            BJUI.dialog('closeCurrent')
        }
    }
    
    FindGrid.prototype.init = function() {
        var that = this, options = this.options, gridOptions = options.gridOptions, tools = this.tools
        
        group            = options.group  || null
        suffix           = options.suffix || null
        $currentFindGrid = that.$element
        oldData          = options.oldData
        pk               = options.pk || null
        include          = options.include || null
        exclude          = options.exclude || null
        $box             = null
        
        if (options.context) {
            if (options.context instanceof jQuery)
                $box = options.context
            else
                $box = $(options.context)
        }
        if (!$box || !$box.length) {
            if (that.$element[0] === $('body')[0]) {
                $box = $.CurrentDialog || $.CurrentNavtab
            } else {
                $box = that.$element.closest('.unitBox')
            }
        }
        if (suffix)
            suffix = suffix.trim()
        
        if (pk) {
            if ($currentFindGrid.data('oldData')) {
                oldData = $currentFindGrid.data('oldData')
            } else {
                if (typeof oldData !== 'undefined') {
                    if (typeof oldData === 'string')
                        oldData = oldData.split(',')
                    else if (!$.isArray(oldData))
                        oldData = []
                } else {
                    oldData = []
                }
            }
        } else {
            oldData = null
        }
        
        if (gridOptions.dataUrl) {
            gridOptions.dataUrl = decodeURI(gridOptions.dataUrl).replacePlh($box)
            
            if (!gridOptions.dataUrl.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('Findgrid Plugin: gridOptions -> dataUrl is incorrect: '+ gridOptions.dataUrl)
                return
            }
            
            gridOptions.dataUrl = encodeURI(gridOptions.dataUrl)
        }
        
        this.open(that.$element)
    }
    
    FindGrid.prototype.addBtn = function() {
        var that = this, $element = that.$element, options = $element.data('bjui.findgrid.options'), fluid = $element.attr('size') ? '' : ' fluid'
        
        if (!this.$findBtn && !$element.parent().hasClass('wrap_bjui_btn_box')) {
            this.$findBtn = $(FRAG.findgridBtn)
            this.$element.css({'paddingRight':'15px'}).wrap('<span class="wrap_bjui_btn_box'+ fluid +'"></span>')
            
            var $box   = this.$element.parent()
            var height = this.$element.addClass('form-control').innerHeight()
            
            $box.css({'position':'relative', 'display':'inline-block'})
            
            delete options.toggle
            
            that.$findBtn.data('bjui.findgrid.options', options)
            
            this.$findBtn.css({'height':height, 'lineHeight':height +'px'}).appendTo($box)
            this.$findBtn.on('selectstart', function() { return false })
        }
    }
    
    FindGrid.prototype.open = function($obj) {
        var that = this, options = this.options, dialogOptions = options.dialogOptions, gridOptions = options.gridOptions, timestamp = (new Date().getTime())
        
        if (!dialogOptions.id)
            dialogOptions.id = 'dialog_findgrid_'+ timestamp
        if (!options.gridId)
            options.gridId = 'datagrid_findgrid_'+ timestamp
        
        gridOptions.showToolbar = false
        
        if (options.empty) {
            if (!gridOptions.toolbarCustom) gridOptions.toolbarCustom = ''
            gridOptions.showToolbar = true
        }
        if (options.multiple) {
            gridOptions.showToolbar = true
            
            if (typeof gridOptions.showCheckboxcol === 'undefined') {
                gridOptions.showCheckboxcol = true
            }
            if (!gridOptions.toolbarCustom) gridOptions.toolbarCustom = ''
            
            gridOptions.selectMult  = true
            
            // set multiple
            gridOptions.toolbarCustom += '　<button type="button" class="btn-blue" onclick="$.fn.findgrid.Constructor.setMult(\''+ options.gridId +'\')">'+ (BJUI.getRegional('findgrid.choose')) +'</button>'
            gridOptions.toolbarCustom += '　<label class="ilabel"><input type="checkbox" data-toggle="icheck" id="checkbox_findgrid_'+ timestamp +'" '+ (options.append ? 'checked' : '') +'>&nbsp;'+ (BJUI.getRegional('findgrid.append')) +'</label>'
            
            append = options.append
        }
        
        if (options.empty)
            gridOptions.toolbarCustom += '　<button type="button" class="btn-orange" onclick="$.fn.findgrid.Constructor.empty(\''+ options.gridId +'\')">'+ (BJUI.getRegional('findgrid.empty')) +'</button>'
        
        delete dialogOptions.url
        delete dialogOptions.target
        
        if (options.onSelect) {
            onSelect = options.onSelect
            if (typeof onSelect === 'string') onSelect = onSelect.toFunc()
        } else {
            onSelect = null
        }
        if (options.beforeSelect) {
            beforeSelect = options.beforeSelect
            if (typeof beforeSelect === 'string') beforeSelect = beforeSelect.toFunc()
        } else {
            beforeSelect = null
        }
        if (options.afterSelect) {
            afterSelect = options.afterSelect
            if (typeof afterSelect === 'string') afterSelect = afterSelect.toFunc()
        } else {
            afterSelect = null
        }
        
        dialogOptions.html = '<div class="bjui-pageContent tableContent"><table id="'+ options.gridId +'"></table></div>'
        dialogOptions.onLoad = function($dialog) {
            var gridId = '#'+ options.gridId, $grid = $(gridId)
            
            $grid.datagrid(gridOptions)
            
            // after load - selected row by (oldData && pk)
            if ($.isArray(oldData) && oldData.length && pk) {
                $(document).on('afterLoad.bjui.datagrid', gridId, function(e, data) {
                    if (data.datas) {
                        $.each(data.datas, function(i, data) {
                            if (data[pk]) {
                                if ($.inArray(data[pk], oldData) != '-1') {
                                    $grid.datagrid('selectedRows', i, true)
                                }
                            }
                        })
                    }
                })
            }
            
            // set single
            if (!options.multiple) {
                $(document).on('clicked.bjui.datagrid.tr', gridId, function(e, data) {
                    if (typeof beforeSelect === 'function') {
                        if (!beforeSelect.apply(that, [data.data])) {
                            return false
                        }
                    }
                    if (typeof onSelect === 'function') {
                        onSelect.call(that, data.data)
                    } else {
                        FindGrid.setSingle(data.data)
                        BJUI.dialog('closeCurrent')
                    }
                })
            }
            
            // append checkbox
            $(document).on('ifChanged', '#checkbox_findgrid_'+ timestamp, function(e) {
                append = $(this).is(':checked')
            })
        }
        
        BJUI.dialog(dialogOptions)
    }
    
    // FINDGRID PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments
        var property = option
        
        return this.each(function () {
            var $this   = $(this),
                options = {},
                data
            
            $.extend(true, options, FindGrid.DEFAULTS, typeof option === 'object' && option)
            
            data = new FindGrid(this, options)
            
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }
    
    var old = $.fn.findgrid
    
    $.fn.findgrid             = Plugin
    $.fn.findgrid.Constructor = FindGrid
    
    // FINDGRID NO CONFLICT
    // =================
    
    $.fn.findgrid.noConflict = function () {
        $.fn.findgrid = old
        return this
    }
    
    // NOT SELECTOR
    // ==============
    
    BJUI.findgrid = function(option) {
        Plugin.apply($('body'), [option])
    }
    
    // FINDGRID DATA-API
    // ==============
    
    $(document).on(BJUI.eventType.initUI, function(e) {
        $(e.target).find('input[data-toggle="findgrid"]').each(function() {
            var $this = $(this), data = $this.data(), options = data.options
            
            if (options) {
                if (typeof options === 'string') {
                    if (options.trim().startsWith('{')) 
                        options = options.toObj()
                    else
                        options = options.toFunc()
                }
                if (typeof options === 'function') {
                    options = options.apply()
                }
                if (typeof options === 'object') {
                    delete data.options
                    
                    $.extend(data, options)
                }
            }
            
            $this.data('bjui.findgrid.options', data)
            Plugin.call($this, 'addBtn')
        })
    })
    
    $(document).on('click.bjui.findgrid.data-api', '[data-toggle="findgridbtn"]', function(e) {
        var $this = $(this), opts = $this.data('bjui.findgrid.options')
        
        if (!opts) {
            var data = $this.data(), options = data.options
            
            if (options) {
                if (typeof options === 'string') {
                    if (options.trim().startsWith('{')) 
                        options = options.toObj()
                    else
                        options = options.toFunc()
                }
                if (typeof options === 'function') {
                    options = options.apply()
                }
                if (typeof options === 'object') {
                    delete data.options
                    
                    $.extend(data, options)
                }
            }
            opts = data
        }
        
        delete opts['bjui.findgrid.options']
        
        Plugin.call($this, opts)
        
        e.preventDefault()
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-suggest.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-suggest.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
 // FINDGRID GLOBAL ELEMENTS
    // ======================
    
    var group, suffix, keys, include, autofill, onSelect, $box, $currentSuggest
    
    // FINDGRID CLASS DEFINITION
    // ======================
    
    var Suggest = function(element, options) {
        this.$element = $(element)
        this.options  = options
        this.$findBtn = null
    }
    
    Suggest.DEFAULTS = {
        keys      : null,
        group     : null,
        suffix    : null,
        include   : null,
        context   : null,
        eventType : 'change',
        autofill  : false
    }
    
    Suggest.EVENTS = {
        afterChange : 'afterchange.bjui.suggest'
    }
    
    Suggest.getField = function(key) {
        return (group ? (group +'.') : '') + (key) + (suffix ? suffix : '')
    }
    
    Suggest.setSingle = function(datas) {
        var that   = this, items, menus = [], $parents, inputs = []
        var replacePlh = function(val, data) {
            return val.replace(/#\/?[^#]*#/g, function($1) {
                var key = $1.replace(/[##]+/g, ''), val = data[key]
                
                if (typeof val === 'undefined')
                    return $1
                
                if (typeof val === 'undefined' || val === 'null' || val === null)
                    val = ''
                
                return val
            })
        }
        var createMenu = function($input, value, label) {
            items = []
            
            var $suggest = $input.data('suggest.menu'), val, lab
            
            if (!$suggest || !$suggest.length) {
                $suggest = $('<ul class="bjui-suggest-menu"></ul>')
            }
            
            $.each(datas, function(i, n) {
                val = value
                lab = label
                val = replacePlh(val, n)
                lab = replacePlh(lab, n)
                
                if (val || lab)
                    items.push('<li data-value="'+ val +'">'+ lab +'</li>')
            })
            
            if (items.length) {
                $suggest
                    .html(items.join(''))
                    .appendTo('body').css({position:'absolute', top:$input.offset().top + $input.outerHeight(), left:$input.offset().left})
                    .on('click.bjui.suggest', 'li', function() {
                        $input.val($(this).attr('data-value'))
                    })
                    .on('mouseover.bjui.suggest', 'li', function() {
                        $(this).addClass('menu-highlight')
                    })
                    .on('mouseout.bjui.suggest', 'li', function() {
                        $(this).removeClass('menu-highlight')
                    })
                
                $input.addClass('bjui-suggest-input').attr('autocomplete', false).data('suggest.menu', $suggest).off('focus.bjui.suggest').on('focus.bjui.suggest', function() {
                    $suggest.css({top:$input.offset().top + $input.outerHeight(), left:$input.offset().left}).show()
                }).off('destroy.bjui.suggest').on('destroy.bjui.suggest', function() {
                    $suggest.remove()
                })
                
                menus.push($suggest)
            }
        }
        
        $.each(keys, function(k, v) {
            var name = that.getField(k), $input = $box.find('input[name="'+ name +'"], select[name="'+ name +'"], textarea[name="'+ name +'"]'), arr = v ? v.split('/') : ['#'+ k +'#'], item, value, label
            
            if ($input && $input.length) {
                if (arr.length > 1) {
                    value = arr[0]
                    label = arr[1]
                } else {
                    value = label = arr[0]
                }
                
                if (($input.isTag('select') || $input.isTag('textarea')) && autofill) {
                    $input.val(datas[0] ? datas[0][k] : '')
                    if ($input.isTag('select')) {
                        $input.selectpicker('refresh')
                    }
                }
                else {
                    createMenu($input, value, label)
                    if (autofill)
                        $input.val(datas[0] ? datas[0][k] : '')
                }
                
                inputs.push($input)
                
                if (!$parents)
                    $parents = $input.parents()
            }
        })
        
        $(document).on('click.bjui.suggest', function(e) {
            var $suggest = $(e.target).data('suggest.menu')
            
            if (!$suggest || !$suggest.length)
                $('body').find('> .bjui-suggest-menu').hide()
            else
                $suggest.siblings('.bjui-suggest-menu').hide()
        })
        
        $parents.on('scroll.bjui.suggest', function() {
            $.each(inputs, function(i, n) {
                var $suggest = n.data('suggest.menu')
                
                if ($suggest && $suggest.is(':visible')) {
                    $suggest.css({top:n.offset().top + n.outerHeight(), left:n.offset().left})
                }
            })
        })
        
        $currentSuggest.data('menus', menus)
    }
    
    Suggest.prototype.init = function() {
        var that = this, options = this.options, tools = this.tools
        
        keys            = options.keys     || null
        group           = options.group    || null
        suffix          = options.suffix   || null
        autofill        = options.autofill || false
        $currentSuggest = that.$element
        $box            = null
        
        if (options.context) {
            if (options.context instanceof jQuery)
                $box = options.context
            else
                $box = $(options.context)
        }
        if (!$box || !$box.length)
            $box = that.$element.closest('.unitBox')
        if (suffix)
            suffix = suffix.trim()
        
        if (!options.keys) {
            BJUI.debug('BJUI.Suggest: options -> keys is not defined!')
            return
        }
        if (!options.url) {
            BJUI.debug('BJUI.Suggest: options -> url is not defined!')
            return
        } else {
            options.url = decodeURI(options.url).replacePlh($box)
            
            if (!options.url.isFinishedTm()) {
                BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                BJUI.debug('BJUI.Suggest: options -> url is incorrect: '+ options.url)
                return
            }
            
            options.url = encodeURI(options.url)
        }
        
        that.$element.on(options.eventType || 'change', function() {
            var val = $(this).val()
            
            if (val)
                that.load(val)
        })
    }
    
    Suggest.prototype.load = function(value) {
        var that = this, options = this.options, url = options.url
        
        if (url.indexOf('#val#') != -1) {
            url = url.replaceAll('#val#', value)
        }
        
        BJUI.ajax('doajax', {
            url   : url,
            data  : {value : value},
            type  : options.type,
            cache : options.cache,
            okCallback: function(json) {
                Suggest.setSingle(json)
            }
        })
    }
    
    Suggest.prototype.destroyMenu = function() {
        var menus = $currentSuggest.data('menus') || []
        
        if (menus.length) {
            $.each(menus, function(i, n) {
                n.remove()
            })
        }
    }
    
    // FINDGRID PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments
        var property = option
        
        return this.each(function () {
            var $this   = $(this),
                options = {},
                data
            
            $.extend(true, options, Suggest.DEFAULTS, typeof option === 'object' && option)
            
            data = new Suggest(this, options)
            
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }
    
    var old = $.fn.suggest
    
    $.fn.suggest             = Plugin
    $.fn.suggest.Constructor = Suggest
    
    // FINDGRID NO CONFLICT
    // =================
    
    $.fn.suggest.noConflict = function () {
        $.fn.suggest = old
        return this
    }
    
    // FINDGRID DATA-API
    // ==============
    
    $(document).on(BJUI.eventType.initUI, function(e) {
        $(e.target).find('input[data-toggle="suggest"]').each(function() {
            var $this = $(this), data = $this.data(), options = data.options
            
            if (options) {
                if (typeof options === 'string') {
                    if (options.trim().startsWith('{')) 
                        options = options.toObj()
                    else
                        options = options.toFunc()
                }
                if (typeof options === 'function') {
                    options = options.apply()
                }
                if (typeof options === 'object') {
                    delete data.options
                    
                    $.extend(data, options)
                }
            }
            
            Plugin.call($this, data)
        })
    })
    
    $(document).on('click.bjui.suggest.data-api', '[data-toggle="suggestbtn"]', function(e) {
        var $this = $(this), opts = $this.data('bjui.suggest.options')
        
        if (!opts) {
            var data = $this.data(), options = data.options
            
            if (options) {
                if (typeof options === 'string') {
                    if (options.trim().startsWith('{')) 
                        options = options.toObj()
                    else
                        options = options.toFunc()
                }
                if (typeof options === 'function') {
                    options = options.apply()
                }
                if (typeof options === 'object') {
                    delete data.options
                    
                    $.extend(data, options)
                }
            }
            opts = data
        }
        
        delete opts['bjui.suggest.options']
        
        Plugin.call($this, opts)
        
        e.preventDefault()
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-tags.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-tags.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // TAGS CLASS DEFINITION
    // ======================
    var Tags = function(element, options) {
        this.$element = $(element)
        this.options  = options
        this.tools    = this.TOOLS()
        this.$box     = $('<div></div>')
        this.timeout  = null
        this.$tagsArr = {}
        this.tags     = []
        
        if (options.istag) {
            this.$element.addClass('tag-input')
            this.$box.addClass('bjui-tags')
        } else {
            this.$box.addClass('bjui-autocomplete')
            if (!this.$element.attr('size'))
                this.$box.addClass('fluid')
        }
        
        this.$element.after(this.$box)
        this.$element.appendTo(this.$box)
    }
    
    Tags.DEFAULTS = {
        width     : 300,
        url       : '',
        global    : false,
        type      : 'GET',
        term      : 'term',
        tagname   : 'tag',     // Appended "<input type='hidden'>" name attribute
        max       : 0,         // The maximum allowable number of tags(0=unlimited)
        clear     : false,     // If not found, clear the input characters
        lightCls  : 'tags-highlight',
        istag     : true,      // If only need auto complete, set false
        data      : {},
        container : '',
        keys      : {
            value : 'value',
            label : 'label'
        }
    }
    
    Tags.EVENTS = {
        afterCreated : 'aftercreated.bjui.tags'
    }
    
    Tags.prototype.TOOLS = function() {
        var that  = this, options = this.options
        var tools = {
            keyDown: function(e) {
                if (e.which == 13) {
                    return false
                }
            },
            keyUp: function(e) {
                switch(e.which) {
                case BJUI.keyCode.BACKSPACE:
                    if (!$.trim(that.$element.val()).length) {
                        that.tools.removeMenu()
                        return false
                    }
                    break
                case BJUI.keyCode.ESC:
                    that.tools.removeMenu()
                    break
                case BJUI.keyCode.DOWN:
                    if (!that.$menu || !that.$menu.length) return
                    
                    var $highlight = that.$menu.find('> .'+ options.lightCls),
                        $first     = that.$menu.find('> li:first-child')
                    
                    if (!$highlight.length) {
                        $first.addClass(options.lightCls)
                    } else {
                        var $hight_next = $highlight.removeClass(options.lightCls).next()
                        
                        if ($hight_next.length) {
                            $hight_next.addClass(options.lightCls)
                        } else {
                            $first.addClass(options.lightCls)
                        }
                    }
                    return false
                    break
                case BJUI.keyCode.UP:
                    if (!that.$menu || !that.$menu.length) return
                    var $highlight = that.$menu.find('> .'+ options.lightCls),
                        $last      = that.$menu.find('> li:last-child')
                    
                    if (!$highlight.length) {
                        $last.addClass(options.lightCls)
                    } else {
                        var $hight_prev = $highlight.removeClass(options.lightCls).prev()
                        
                        if ($hight_prev.length) {
                            $hight_prev.addClass(options.lightCls)
                        } else {
                            $last.addClass(options.lightCls)
                        }
                    }
                    return false
                    break
                case BJUI.keyCode.ENTER:
                    if (options.max > 0 && that.$tagsArr.length >= options.max) return false
                    
                    var label = false, value = false, item = null
                    var $selectedItem = that.$menu && that.$menu.find('> .'+ options.lightCls)
                    
                    if ($selectedItem && $selectedItem.length) {
                        label = $selectedItem.text()
                        item  = $selectedItem.data('item')
                        value = item[options.keys.value]
                    } else {
                        label = $.trim(that.$element.val())
                        
                        if (!label.length) return false
                        if (options.clear) {
                            if ($.inArray(label, that.tags) == -1) {
                                options.istag && (that.$element.val(''))
                                return false
                            }
                        }
                        value = label
                    }
                    if (!label) return
                    
                    /* Check the repeatability */
                    var isRepeat = false
                    
                    that.$tagsArr.length && that.$tagsArr.each(function() {
                        if ($(this).val() == value) {
                            isRepeat = true
                            return
                        }
                    })
                    
                    if (isRepeat) {
                        options.istag && (that.$element.val(''))
                        return false
                    }
                    
                    that.tools.createTag(label, value)
                    that.tools.removeMenu()
                    options.istag && (that.$element.val(''))
                    
                    //events
                    $.proxy(that.tools.onAfterCreated(item, value), that)
                    
                    return false
                    break
                }
            },
            query: function() {
                if (that.timeout) clearTimeout(that.timeout)
                
                that.timeout = setTimeout(that.tools.doQuery, 300)
            },
            doQuery: function() {
                var options = that.options
                
                if (options.max > 0 && that.$tagsArr.length >= options.max) return
                
                var term = that.$element.val(), $menu = that.$menu, tags = [], $item = null
                var $parentBox = that.$element.closest('.navtab-panel').length ? $.CurrentNavtab : $.CurrentDialog
                var postData = {} 
                
                if (that.$element.closest('.bjui-layout').length) $parentBox = that.$element.closest('.bjui-layout')
                if (!term.length) return
                
                that.$element.one('ajaxStart', function() {
                    $parentBox.trigger('bjui.ajaxStart')
                }).one('ajaxStop', function() {
                    $parentBox.trigger('bjui.ajaxStop')
                })
                
                postData[options.term] = term
                $.extend(postData, typeof options.data === 'object' && options.data)
                
                options.url = decodeURI(options.url).replacePlh(that.$element.closest('.unitBox'))
                
                if (!options.url.isFinishedTm()) {
                    BJUI.alertmsg('error', (options.warn || BJUI.regional.plhmsg))
                    BJUI.debug('BJUI.Tags: The query tags url is incorrect, url: '+ options.url)
                    return
                }
                
                options.url = encodeURI(options.url)
                
                $.ajax({
                    url      : options.url,
                    global   : options.global,
                    type     : options.type,
                    data     : postData,
                    dataType : 'json',
                    success  : function(json) {
                        if (json.length != 0) {
                            if (!$menu || !$menu.length) $menu = $('<ul class="bjui-tags-menu"></ul>')
                            
                            $menu.empty().hide();
                            
                            if (options.container)
                                $menu.css('position', 'absolute').appendTo(options.container)
                            else
                                $menu.appendTo(that.$box)
                            
                            for (var i = 0; i < json.length; i++) {
                                var obj = json[i]
                                
                                if (typeof obj === 'string') {
                                    obj = {}
                                    obj[options.keys.label] = json[i]
                                    obj[options.keys.value] = json[i]
                                }
                                
                                $item = $('<li class="tags-item">'+ obj[options.keys.label] + '</li>').data('item', obj)
                                $item.appendTo($menu)
                                
                                tags.push(obj[options.keys.label])
                            }
                            
                            that.tags = tags
                            
                            var h = $(window).height() - that.$element.offset().top - 50, top = 0, left = 0
                            
                            if (options.container) {
                                top  = that.$element.offset().top + that.$element.outerHeight()
                                left = that.$element.offset().left
                            } else {
                                top  = that.$element.position().top + that.$element.outerHeight()
                                left = that.$element.position().left
                            }
                            
                            $menu
                                .css({'top':top, 'left':left})
                                .fadeIn()
                                .find('> li')
                                    .hover(function() {
                                        $(this).addClass(options.lightCls).siblings().removeClass(options.lightCls)
                                    }, function() {
                                        $(this).removeClass(options.lightCls)
                                    })
                                    .click(function() {
                                        var label    = $(this).text()
                                        var item     = $(this).data('item')
                                        var value    = item[options.keys.value]
                                        var isRepeat = false
                                        
                                        that.$box.find('input:hidden').each(function() {
                                            if ($(this).val() == value) {
                                                isRepeat = true
                                                return
                                            }
                                        })
                                        
                                        if (isRepeat) {
                                            options.istag && (that.$element.val(''))
                                            $menu.remove()
                                            return
                                        }
                                        
                                        $.proxy(that.tools.createTag(label, value), that)
                                        
                                        $menu.remove()
                                        options.istag && (that.$element.val(''))
                                        
                                        //events
                                        $.proxy(that.tools.onAfterCreated(item, value), that)
                                    })
                            
                            if ($menu.height() > h) {
                                $menu.height(h - 10).css('overflow-y', 'scroll')
                            }
                            
                            that.$menu = $menu
                        }
                    }
                })
            },
            createTag: function(label, value) {
                if (!that.options.istag) {
                    that.$element.val(label)
                    
                    return
                }
                var $btn = $('<span class="label label-tag" data-value="' + value +'" style="margin-left: 1px; margin-top: 1px;"><i class="fa fa-tag"></i> ' + label + '&nbsp;&nbsp;<a href="#" class="close">&times;</a></span>')
                
                $btn
                    .insertBefore(that.$element)
                    .find('a.close')
                    .click(function() {
                        var value = $btn.data('value')
                        
                        that.$box.find('input:hidden').each(function() {
                            if ($(this).val() == value) {
                                $(this).remove()
                            }
                        })
                        
                        $btn.remove()
                        that.$tagsArr = that.$box.find('input[name="'+ that.options.tagname +'"]')
                    })
                
                var $hidden = $('<input type="hidden" name="'+ that.options.tagname +'">').val(value)
                
                $hidden.appendTo(that.$box)
                that.$tagsArr = that.$box.find('input[name="'+ that.options.tagname +'"]')
            },
            removeMenu: function() {
                if (that.$menu) that.$menu.remove()
            },
            onAfterCreated: function(item, value) {
                var alltags = []
                
                that.$tagsArr.length && that.$tagsArr.each(function() {
                    alltags.push($(this).val())
                })
                
                that.$element.trigger(Tags.EVENTS.afterCreated, {item:item, value:value, tags:alltags.join(',')})
            }
        }
        
        return tools
    }
    
    Tags.prototype.init = function() {
        var that     = this
        var $element = this.$element
        var options  = this.options
        
        if (!(options.url)) {
            BJUI.debug('BJUI.Tags: Do query tags, url is undefined!')
            return
        }
        if (isNaN(this.options.max)) {
            BJUI.debug('BJUI.Tags: Parameter \'max\' is non-numeric type!')
            return
        }
        
        that.$box.on('click', function() {
            $element.focus()
        })
        
        if (options.istag)
            that.$box.css('width', options.width)
        
        $element
            //.on('blur', $.proxy(this.tools.removeMenu, this))
            .on('keydown', $.proxy(this.tools.keyDown, this))
            .on('keyup', $.proxy(this.tools.keyUp, this))
        
        if (!$.support.leadingWhitespace) { // for ie8
            $element.on('propertychange', $.proxy(this.tools.query, this))
        } else {
            $element.on('input', $.proxy(this.tools.query, this))
        }
        
        $(document).on('click.bjui.tags', $.proxy(function(e) {
            if (!$(e.target).closest(this.$box).length) this.tools.removeMenu()
        }, this))
    }
    
    Tags.prototype.destroy = function() {
        if (this.$tags) {
            this.$element.upwrap()
            $tags.remove()
        }
    }
    
    // TAGS PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments,
            property = option
        
        return this.each(function () {
            var $this   = $(this),
                options = {},
                data
            
            $.extend(true, options, Tags.DEFAULTS, typeof option === 'object' && option)
            
            data = new Tags(this, options)
            
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }
    
    var old = $.fn.tags
    
    $.fn.tags             = Plugin
    $.fn.tags.Constructor = Tags
    
    // TAGS NO CONFLICT
    // =================
    
    $.fn.tags.noConflict = function () {
        $.fn.tags = old
        return this
    }
    
    // TAGS DATA-API
    // ==============
    
    $(document).on(BJUI.eventType.initUI, function(e) {
        $(e.target).find('input[data-toggle="tags"]').each(function() {
            var $this = $(this), data = $this.data(), options = data.options
            
            if (options) {
                if (typeof options === 'string') {
                    if (options.trim().startsWith('{')) 
                        options = options.toObj()
                    else
                        options = options.toFunc()
                }
                if (typeof options === 'function') {
                    options = options.apply()
                }
                if (typeof options === 'object') {
                    delete data.options
                    
                    $.extend(data, options)
                }
            } else {
                if (data.keys)
                    data.keys = data.keys.toObj()
                if (data.data)
                    data.data = data.data.toObj()
            }
            
            Plugin.call($this, data)
        })
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-theme.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-theme.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // THEME GLOBAL ELEMENTS
    // ======================
    
    var $themeLink, $themeLis, $fontLis
    
    $(function() {
        var INIT_THEME = function() {
            $themeLink = $('#bjui-link-theme')
            $themeLis  = $('#bjui-themes')
            $fontLis   = $('#bjui-fonts')
            
            if ($.cookie) {
                var themeName = $.cookie('bjui_theme') || 'blue'
                var $li = $themeLis.find('a.theme_'+ themeName)
                
                $li.theme({})
                
                /* font */
                var fontSize = $.cookie('bjui_font') || 'bjui-font-c'
                var $fontLi  = $fontLis.find('a.'+ fontSize)
                
                $fontLi.theme('setFont', fontSize)
            }
        }
        
        INIT_THEME()
    })
    
    // THEME CLASS DEFINITION
    // ======================
    var Theme = function(element, options) {
        this.$element = $(element)
        this.options  = options
    }
    
    Theme.DEFAULTS = {
        theme: 'purple'
    }
    
    Theme.prototype.init = function() {
        if (!$themeLink.length) return
        var themeHref = $themeLink.attr('href')
        
        themeHref = themeHref.substring(0, themeHref.lastIndexOf('/'))
        themeHref = themeHref.substring(0, themeHref.lastIndexOf('/'))
        themeHref += '/'+ this.options.theme +'/core.css'
        $themeLink.attr('href', themeHref)
        
        var $themeA = this.$element.closest('li').filter('.active')
        var classA  = $themeA.data('theme') || 'default'
        
        classA      = classA.replace(/(theme[\s][a-z]*)/g, '')
        $themeA.removeClass().addClass(classA).addClass('theme').addClass(this.options.theme)
        $themeLis.find('li').removeClass('active')
        this.$element.parent().addClass('active')
        this.cookie()
    }
    
    Theme.prototype.setTheme = function(themeName) {
        $themeLis.find('a.theme_'+ themeName).trigger('click')
    }
    
    Theme.prototype.setFont = function(fontSize) {
        $('html').removeClass().addClass(fontSize)
        
        this.$element.closest('ul').prev().html(this.$element.html()).removeClass().addClass('dropdown-toggle bjui-fonts-tit '+ fontSize)
        
        $('body').find('table').each(function() {
            var $table = $(this), datagrid = $table.data('bjui.datagrid')
            
            if (datagrid) {
                datagrid.needfixedWidth = true
                $table.datagrid('fixedWidth')
                datagrid.needfixedWidth = false
            }
        })
        
        $(window).trigger(BJUI.eventType.resizeGrid)
        
        if ($.cookie) $.cookie('bjui_font', fontSize, { path: '/', expires: 30 });
    }
    
    Theme.prototype.cookie = function() {
        var theme = this.options.theme
        
        if ($.cookie) $.cookie('bjui_theme', theme, { path: '/', expires: 30 });
    }
    
    // THEME PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments
        var property = option
        
        return this.each(function () {
            var $this   = $(this)
            var options = $.extend({}, Theme.DEFAULTS, $this.data(), typeof option === 'object' && option)
            var data    = $this.data('bjui.theme')
            
            if (!data) $this.data('bjui.theme', (data = new Theme(this, options)))
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }
    
    var old = $.fn.theme
    
    $.fn.theme             = Plugin
    $.fn.theme.Constructor = Theme
    
    // THEME NO CONFLICT
    // =================
    
    $.fn.theme.noConflict = function () {
        $.fn.theme = old
        return this
    }
    
    // THEME DATA-API
    // ==============

    $(document).on('click.bjui.theme.data-api', '[data-toggle="theme"]', function(e) {
        Plugin.call($(this))
        
        e.preventDefault()
    })
    
    $(document).on('click.bjui.fonts.data-api', '[data-toggle="fonts"]', function(e) {
        Plugin.call($(this), 'setFont', $(this).attr('class'))
        
        e.preventDefault()
    })

}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-initui.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-initui.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    // INITUI CLASS DEFINITION
    // ======================
    var Initui = function(element, options) {
        var $this     = this
        this.$element = $(element)
        this.options  = options
    }
    
    Initui.DEFAULTS = {}
    
    Initui.prototype.init = function() {
        var that = this, $element = that.$element
        
        $.when(that.initUI()).done(function(){
            $element.trigger(BJUI.eventType.afterInitUI)
        })
    }
    
    Initui.prototype.initUI = function() {
        var $element = this.$element
        
        $.when($element.trigger(BJUI.eventType.beforeInitUI)).done(function(){
            $element.trigger(BJUI.eventType.initUI)
        })
    }
    
    // INITUI PLUGIN DEFINITION
    // =======================
    
    function Plugin(option) {
        var args     = arguments,
            property = option
        
        return this.each(function () {
            var $this   = $(this),
                options = $.extend({}, Initui.DEFAULTS, $this.data(), typeof option === 'object' && option),
                data    = $this.data('bjui.initui')
            
            if (!data) $this.data('bjui.initui', (data = new Initui(this, options)))
            
            if (typeof property === 'string' && $.isFunction(data[property])) {
                [].shift.apply(args)
                if (!args) data[property]()
                else data[property].apply(data, args)
            } else {
                data.init()
            }
        })
    }

    var old = $.fn.initui

    $.fn.initui             = Plugin
    $.fn.initui.Constructor = Initui
    
    // INITUI NO CONFLICT
    // =================
    
    $.fn.initui.noConflict = function () {
        $.fn.initui = old
        return this
    }
    
    // INITUI DATA-API
    // ==============

    $(document).on('click.bjui.initui.data-api', '[data-toggle="initui"]', function(e) {
        Plugin.call($this, $this.data())
        
        e.preventDefault()
    })
    
    /* beforeInitUI */
    $(document).on(BJUI.eventType.beforeInitUI, function(e) {
        var $box    = $(e.target),
            noinits = [],
            $noinit = $box.find('[data-noinit], pre')
        
        //progress
        $box.find('> .bjui-maskProgress').find('.progress').stop().animate({width:'85%'}, 'fast')
        
        // Hide not need to initialize the UI DOM
        $noinit.each(function(i) {
            var $this = $(this), pos = {}
                        
            pos.$target = $this
            pos.$next   = $this.next()
            pos.$prev   = $this.prev()
            pos.$parent = $this.parent()
            pos.visible = $this.is(':visible') ? true : false
            
            noinits.push(pos)
            $this.remove()
        })
        
        $box.data('bjui.noinit', noinits)
    })
    
    /* initUI */
    $(document).on(BJUI.eventType.initUI, function(e) {
        var $box    = $(e.target)
        
        //progress
        $box.find('> .bjui-maskProgress').find('.progress').stop().animate({width:'95%'}, 'fast')
    })
    
    /* afterInitUI */
    $(document).on(BJUI.eventType.afterInitUI, function(e) {
        var $box    = $(e.target),
            noinits = $box.data('bjui.noinit'),
            $form   = $box.find('> .bjui-pageContent').find('form')
        
        // Recovery not need to initialize the UI DOM
        if (noinits) {
            $.each(noinits, function(i, n) {
                if (n.$next.length) n.$next.before(n.$target)
                else if (n.$prev.length) n.$prev.after(n.$target)
                else if (n.$parent.length) n.$parent.append(n.$target)
                
                if (n.visible) n.$target.show()
                
                $box.removeData('bjui.noinit')
            })
        }
        
        /* resizePageH */
        $box.resizePageH()
        
        //submit
        if ($form.length) {
            $box.find('> .bjui-pageFooter').find(':submit').on('click.bjui.submit', function(e) {
                e.preventDefault()
                
                $form.submit()
            })
        }
        
        $box.find('> .bjui-pageFooter').find(':submit.btn-submit-header').on('click.bjui.submit', function(e) {
            e.preventDefault()
            
            var $form = $box.find('> .bjui-pageHeader').find('form')
            
            $form.length && $form.submit()
        })
        
        //progress
        $box.find('> .bjui-maskProgress').find('.progress').stop().animate({width:'100%'}, 'fast', function() {
            $box.find('> .bjui-ajax-mask').fadeOut('normal', function() { $(this).remove() })
        })
    })
    
    /* Lateral Navigation */
    $(document).one(BJUI.eventType.afterInitUI, function(e) {
        var $hnavbar = $('#bjui-hnav-navbar'), $active = $hnavbar.find('> li.active'), $a = $active.find('> a')
        
        if ($active.length) {
            if ($active.find('> .items').length) {
                $a.trigger('click')
            } else {
                var href = $a.attr('href')
                
                if (href && !(href.startsWith('#') || href.startsWith('javascript'))) {
                    $a.trigger('click')
                }
            }
        }
    })
    
    /* ajaxStatus */
    var bjui_ajaxStatus = function($target) {
        var $this    = $target,
            $offset  = $this,
            position = $this.css('position'),
            top      = $this.scrollTop() || 0,
            height   = ($this[0].clientHeight) / 2
        
        if (position == 'static') {
            $this.css('position', 'relative').data('bjui.ajax.static', true)
            $offset  = $this.offsetParent()
        }
        
        var zIndex   = parseInt($offset.css('zIndex')) || 0,
            $ajaxBackground = $this.find('> .bjui-maskBackground'),
            $ajaxProgress   = $this.find('> .bjui-maskProgress')
            
        if (!$ajaxBackground.length) {
            $ajaxBackground = $(FRAG.maskBackground)
            $ajaxProgress   = $(BJUI.doRegional(FRAG.maskProgress, BJUI.regional))
            $this.prepend($ajaxBackground).prepend($ajaxProgress)
        }
        
        var bgZindex = parseInt($ajaxBackground.css('zIndex')) || 0,
            prZindex = parseInt($ajaxProgress.css('zIndex')) || 0
        
        $ajaxBackground.css('zIndex', zIndex + 1).css('top', top)
        $ajaxProgress.css('zIndex', zIndex + 2)
        
        if (top)
            $ajaxProgress.css('top', top + height)
        
        if (height == 0) {
            setTimeout(function() {
                $ajaxProgress.css('top', $(this).scrollTop() + $this[0].clientHeight / 2)
            }, 50)
        }
        
        $this.off('scroll.ajaxmask').on('scroll.ajaxmask', function() {
            var top = $(this).scrollTop()
            
            $ajaxBackground.css('top', $this.scrollTop())
            $ajaxProgress.css('top', top + this.clientHeight / 2)
        })
        
        return {$bg:$ajaxBackground, $pr:$ajaxProgress}
    }
    
    $(document)
        .on('bjui.ajaxStart', function(e, timeout, callback) {
            var ajaxMask = bjui_ajaxStatus($(e.target))
            
            ajaxMask.$bg.fadeIn()
            ajaxMask.$pr.fadeIn()
            ajaxMask.$pr.find('.progress').animate({width:'80%'}, timeout || 500)
            
            if (callback) {
                setTimeout(function() {
                    callback.toFunc().call(this)
                }, 25)
            }
        })
        .on('bjui.ajaxStop', function(e) {
            var $target = $(e.target), ajaxMask = bjui_ajaxStatus($target)
            
            ajaxMask.$pr.find('.progress').animate({width:'100%'}, 'fast', function() {
                ajaxMask.$bg.remove()
                ajaxMask.$pr.remove()
                
                if ($target.data('bjui.ajax.static'))
                    $target.css('position', 'static')
            })
        })
        .on('bjui.ajaxError', function(e) {
            var $target = $(e.target), ajaxMask = bjui_ajaxStatus($target)
            
            ajaxMask.$bg.remove()
            ajaxMask.$pr.remove()
            
            if ($target.data('bjui.ajax.static'))
                $target.css('position', 'static')
        })
    
    $(document).on(BJUI.eventType.ajaxStatus, function(e) {
        var $target = $(e.target), ajaxMask = bjui_ajaxStatus($target)
        
        $target
            .one('ajaxStart', function() {
                ajaxMask.$bg.fadeIn()
                ajaxMask.$pr.fadeIn()
                
                ajaxMask.$pr.find('.progress').animate({width:'10%'}, 'fast')
            })
            .one('ajaxStop', function() {
                //ajaxMask.$bg.fadeOut()
                //ajaxMask.$pr.fadeOut()
                //ajaxMask.$pr.find('.progress').animate({width:'80%'}, 'fast')
            })
            .one('ajaxError', function() {
                ajaxMask.$bg.remove()
                ajaxMask.$pr.remove()
                
                if ($target.data('bjui.ajax.static'))
                    $target.css('position', 'static')
            })
    })
    
    /* Clean plugins generated 'Dom elements' in the body */
    var bodyClear = function($target) {
        $target.find('select[data-toggle="selectpicker"]').selectpicker('destroyMenu')
        $target.find('[data-toggle="selectztree"]').trigger('destroy.bjui.selectztree')
        $target.find('.bjui-suggest-input').trigger('destroy.bjui.suggest')
    }
    
    $(document).on(BJUI.eventType.beforeLoadDialog, function(e) {
        
    }).on(BJUI.eventType.beforeAjaxLoad, function(e) {
        bodyClear($(e.target))
    }).on(BJUI.eventType.beforeCloseNavtab, function(e) {
        bodyClear($(e.target))
    }).on(BJUI.eventType.beforeCloseDialog, function(e) {
        bodyClear($(e.target))
    })
    
    /* other */
    $(function() {
        $(document).on('keydown keyup', function(e) {
            if (e.which === BJUI.keyCode.CTRL) {
                BJUI.KeyPressed.ctrl = e.type == 'keydown' ? true : false
            }
            if (e.which === BJUI.keyCode.SHIFT) {
                BJUI.KeyPressed.shift = e.type == 'keydown' ? true : false
            }
        })
    })
    
}(jQuery);
/*!
 * B-JUI  V1.31 (http://b-jui.com)
 * Git@OSC (http://git.oschina.net/xknaan/B-JUI)
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 */

/* ========================================================================
 * B-JUI: bjui-plugins.js  V1.31
 * @author K'naan
 * http://git.oschina.net/xknaan/B-JUI/blob/master/BJUI/js/bjui-plugins.js
 * ========================================================================
 * Copyright 2014 K'naan.
 * Licensed under Apache (http://www.apache.org/licenses/LICENSE-2.0)
 * ======================================================================== */

+function ($) {
    'use strict';
    
    $(document).on(BJUI.eventType.initUI, function(e) {
        var $box    = $(e.target)
        
        // UI init begin...
        
        /* i-check */
        if ($.fn.iCheck) {
            var $icheck = $box.find('input[data-toggle="icheck"]')
            
            $icheck.each(function(i) {
                var $element = $(this),
                    id       = $element.attr('id'),
                    name     = $element.attr('name'),
                    label    = $element.data('label')
                
                if (label) $element.after('<label for="'+ id +'" class="ilabel">'+ label +'</label>')
                
                $element
                    .on('ifCreated', function(e) {
                        /* Fixed validate msgbox position */
                        var $parent = $(this).closest('div'),
                            $ilabel = $parent.next('[for="'+ id +'"]')
                        
                        $parent.attr('data-icheck', name)
                        $ilabel.attr('data-icheck', name)
                    })
                    .iCheck({
                        checkboxClass : 'icheckbox_minimal-purple',
                        radioClass    : 'iradio_minimal-purple',
                        increaseArea  : '20%' // optional
                    })
                    .on('ifChanged', function() {
                        /* Trigger validation */
                        $.fn.validator && $(this).trigger('validate')
                    })
                
                if ($element.prop('disabled')) $element.iCheck('disable')
            })
            /* i-check check all */
            $icheck.filter('.checkboxCtrl').on('ifChanged', function(e) {
                var checked = e.target.checked == true ? 'check' : 'uncheck'
                var group   = $(this).data('group')
                
                $box.find(':checkbox[name="'+ group +'"]').iCheck(checked)
            })
        }
        
        /* fixed ui style */
        $box.find('button').each(function() {
            var $element = $(this), icon = $element.data('icon')
            
            $element.addClass('btn')
            
            if (icon && !$element.find('> i').length) {
                icon = 'fa-'+ icon.replace('fa-', '')
                
                if (!$element.data('bjui.icon')) {
                    $element.html('<i class="fa '+ icon +'"></i> '+ $element.html()).data('bjui.icon', true)
                }
            }
        })
        
        $box.find('input:text, input:password').each(function() {
            var $element = $(this).addClass('form-control'), size = $element.attr('size') || 0, width = size * 10
                
            width && $element.css('width', width)
        })
        
        $box.find('textarea').each(function() {
            var $element = $(this).addClass('form-control'), cols = $element.attr('cols') || 0, width = cols * 10, toggle = $element.attr('data-toggle')
                
            width && $element.css('width', width)
            
            if (toggle && toggle == 'autoheight' && $.fn.autosize)
                $element.addClass('autosize').autosize()
        })
        
        $box.find('a.btn').each(function() {
            var $element = $(this), icon = $element.data('icon')
                
            if (icon && !$element.find('> i').length) {
                icon = 'fa-'+ icon.replace('fa-', '')
                
                if (!$element.data('bjui.icon')) {
                    $element.html('<i class="fa '+ icon +'"></i> '+ $element.html()).data('bjui.icon', true)
                }
            }
        })
        
        /* form validate */
        if ($.fn.validator) {
            $box.find('form[data-toggle="validate"]').each(function() {
                var $element = $(this)
                
                $(this)
                    .validator({
                        valid: function(form) {
                            $(form).bjuiajax('_ajaxform', $(form), $(form).data())
                        },
                        validClass : 'ok',
                        msgClass   : 'n-bottom',
                        theme      : 'red_bottom_effect_grid'
                    })
                    .on('invalid.form', function(e, form, errors) {
                        if ($(form).data('alertmsg')) $(form).alertmsg('error', BJUI.regional.validatemsg.replaceMsg(errors.length))
                    })
            })
        }
        
        /* moreSearch */
        $box.find('[data-toggle="moresearch"]').each(function() {
            var $element = $(this),
                $parent  = $element.closest('.bjui-pageHeader'),
                $more    = $parent && $parent.find('.bjui-moreSearch'),
                name     = $element.data('name')
            
            if (!$element.attr('title')) $element.attr('title', '更多查询条件')
            $element.click(function(e) {
                if (!$more.length) {
                    BJUI.debug('Not created \'moresearch\' box[class="bjui-moreSearch"]!')
                    return
                }
                $more.css('top', $parent.outerHeight() - 1)
                if ($more.is(':visible')) {
                    $element.html('<i class="fa fa-angle-double-down"></i>')
                    if (name) $('body').data('moresearch.'+ name, false)
                } else {
                    $element.html('<i class="fa fa-angle-double-up"></i>')
                    if (name) $('body').data('moresearch.'+ name, true)
                }
                $more.fadeToggle('slow', 'linear')
                
                e.preventDefault()
            })
            
            if (name && $('body').data('moresearch.'+ name)) {
                $more.css('top', $parent.outerHeight() - 1).fadeIn()
                $element.html('<i class="fa fa-angle-double-up"></i>')
            }
        })
        
        /* bootstrap - select */
        if ($.fn.selectpicker) {
            var $selectpicker       = $box.find('select[data-toggle="selectpicker"]')
            var bjui_select_linkage = function($obj, $next) {
                if (!$next || !$next.length) return
                
                var refurl    = $obj.data('refurl')
                var _setEmpty = function($select) {
                    var $_nextselect = $($select.data('nextselect'))
                    
                    if ($_nextselect && $_nextselect.length) {
                        var emptytxt = $_nextselect.data('emptytxt') || '&nbsp;'
                        
                        $_nextselect.html('<option>'+ emptytxt +'</option>').selectpicker('refresh')
                        _setEmpty($_nextselect)
                    }
                }
                
                if (($next && $next.length) && refurl) {
                    var val = $obj.data('val'), nextVal = $next.data('val'), keys = $obj.data('keys')
                    
                    if (keys && typeof keys === 'string')
                        keys = keys.toObj()
                    if (!keys)
                        keys = {}
                    
                    if (typeof val === 'undefined') val = $obj.val()
                    $.ajax({
                        type     : 'POST',
                        dataType : 'json', 
                        url      : refurl.replace('{value}', encodeURIComponent(val)), 
                        cache    : false,
                        data     : {},
                        success  : function(json) {
                            if (!json) return
                            
                            var html = '', selected = ''
                            
                            $.each(json, function(i) {
                                var value, label
                                
                                if (json[i] && json[i].length) {
                                    value = json[i][0]
                                    label = json[i][1]
                                } else {
                                    value = json[i][keys.value || 'value']
                                    label = json[i][keys.label || 'label']
                                }
                                if (typeof nextVal !== 'undefined') selected = value == nextVal ? ' selected' : ''
                                html += '<option value="'+ value +'"'+ selected +'>' + label + '</option>'
                            })
                            
                            $obj.removeAttr('data-val').removeData('val')
                            $next.removeAttr('data-val').removeData('val')
                            
                            if (!html) {
                                html = $next.data('emptytxt') || '&nbsp;'
                                html = '<option>'+ html +'</option>'
                            }
                            
                            $next.html(html).selectpicker('refresh')
                            _setEmpty($next)
                        },
                        error   : BJUI.ajaxError
                    })
                }
            }
            
            $selectpicker.each(function() {
                var $element  = $(this)
                var options   = $element.data()
                var $next     = $(options.nextselect)
                
                $element.addClass('show-tick')
                if (!options.style) $element.data('style', 'btn-default')
                if (!options.width) $element.data('width', 'auto')
                if (!options.container) $element.data('container', 'body')
                else if (options.container == true) $element.attr('data-container', 'false').data('container', false)
                
                $element.selectpicker()
                
                if ($next && $next.length && (typeof $next.data('val') != 'undefined'))
                    bjui_select_linkage($element, $next)
            })
            
            /* bootstrap - select - linkage && Trigger validation */
            $selectpicker.change(function() {
                var $element    = $(this)
                var $nextselect = $($element.data('nextselect'))
                
                bjui_select_linkage($element, $nextselect)
                
                /* Trigger validation */
                if ($element.attr('aria-required')) {
                    $.fn.validator && $element.trigger('validate')
                }
            })
        }
        
        if ($.fn.zTree) {
            /* zTree - plugin */
            $box.find('[data-toggle="ztree"]').each(function() {
                var $this = $(this), op = $this.data(), options = op.options, _setting
                
                if (options && typeof options === 'string') options = options.toObj()
                if (options) $.extend(op, typeof options === 'object' && options)
                
                _setting = op.setting
                
                if (!op.nodes) {
                    op.nodes = []
                    $this.find('> li').each(function() {
                        var $li   = $(this)
                        var node  = $li.data()
                        
                        if (node.pid) node.pId = node.pid
                        node.name = $li.html()
                        op.nodes.push(node)
                    })
                    $this.empty()
                } else {
                    if (typeof op.nodes === 'string') {
                        if (op.nodes.trim().startsWith('[') || op.nodes.trim().startsWith('{')) {
                            op.nodes = op.nodes.toObj()
                        } else {
                            op.nodes = op.nodes.toFunc()
                        }
                    }
                    if (typeof op.nodes === 'function') {
                        op.nodes = op.nodes.call(this)
                    }
                    
                    $this.removeAttr('data-nodes')
                }
                
                if (!op.showRemoveBtn) op.showRemoveBtn = false
                if (!op.showRenameBtn) op.showRenameBtn = false
                if (op.addHoverDom && typeof op.addHoverDom !== 'function')       op.addHoverDom    = (op.addHoverDom === 'edit')    ? _addHoverDom    : op.addHoverDom.toFunc()
                if (op.removeHoverDom && typeof op.removeHoverDom !== 'function') op.removeHoverDom = (op.removeHoverDom === 'edit') ? _removeHoverDom : op.removeHoverDom.toFunc()
                if (!op.maxAddLevel)   op.maxAddLevel   = 2
                
                var setting = {
                    view: {
                        addHoverDom    : op.addHoverDom || null,
                        removeHoverDom : op.removeHoverDom || null,
                        addDiyDom      : op.addDiyDom ? op.addDiyDom.toFunc() : null
                    },
                    edit: {
                        enable        : op.editEnable,
                        showRemoveBtn : op.showRemoveBtn,
                        showRenameBtn : op.showRenameBtn
                    },
                    check: {
                        enable    : op.checkEnable,
                        chkStyle  : op.chkStyle,
                        radioType : op.radioType
                    },
                    callback: {
                        onClick       : op.onClick      ? op.onClick.toFunc()      : null,
                        beforeDrag    : op.beforeDrag   ? op.beforeDrag.toFunc()   : _beforeDrag,
                        beforeDrop    : op.beforeDrop   ? op.beforeDrop.toFunc()   : _beforeDrop,
                        onDrop        : op.onDrop       ? op.onDrop.toFunc()       : null,
                        onCheck       : op.onCheck      ? op.onCheck.toFunc()      : null,
                        beforeRemove  : op.beforeRemove ? op.beforeRemove.toFunc() : null,
                        onRemove      : op.onRemove     ? op.onRemove.toFunc()     : null,
                        onNodeCreated : _onNodeCreated,
                        onCollapse    : _onCollapse,
                        onExpand      : _onExpand
                    },
                    data: {
                        simpleData: {
                            enable: op.simpleData || true
                        },
                        key: {
                            title: op.title || ''
                        }
                    }
                }
                
                if (_setting && typeof _setting === 'string') _setting = _setting.toObj()
                if (_setting) $.extend(true, setting, typeof _setting === 'object' && _setting)
                
                $.fn.zTree.init($this, setting, op.nodes)
                
                var IDMark_A = '_a'
                var zTree    = $.fn.zTree.getZTreeObj($this.attr('id'))
                
                if (op.expandAll) zTree.expandAll(true)
                
                // onCreated
                function _onNodeCreated(event, treeId, treeNode) {
                    if (treeNode.faicon) {
                        var $a    = $('#'+ treeNode.tId +'_a')
                        
                        if (!$a.data('faicon')) {
                            $a.data('faicon', true)
                              .addClass('faicon')
                              .find('> span.button').append('<i class="fa fa-'+ treeNode.faicon +'"></i>')
                        }
                    }
                    if (op.onNodeCreated) {
                        op.onNodeCreated.toFunc().call(this, event, treeId, treeNode)
                    }
                }
                // onCollapse
                function _onCollapse(event, treeId, treeNode) {
                    if (treeNode.faiconClose) {
                        $('#'+ treeNode.tId +'_ico').find('> i').attr('class', 'fa fa-'+ treeNode.faiconClose)
                    }
                    
                    if (op.onCollapse) {
                        op.onCollapse.toFunc().call(this, event, treeId, treeNode)
                    }
                }
                // onExpand
                function _onExpand(event, treeId, treeNode) {
                    if (treeNode.faicon && treeNode.faiconClose) {
                        $('#'+ treeNode.tId +'_ico').find('> i').attr('class', 'fa fa-'+ treeNode.faicon)
                    }
                    if (op.onExpand) {
                        op.onExpand.toFunc().call(this, event, treeId, treeNode)
                    }
                }
                // add button, del button
                function _addHoverDom(treeId, treeNode) {
                    var level = treeNode.level
                    var $obj  = $('#'+ treeNode.tId + IDMark_A)
                    var $add  = $('#diyBtn_add_'+ treeNode.id)
                    var $del  = $('#diyBtn_del_'+ treeNode.id)
                    
                    if (!$add.length) {
                        if (level < op.maxAddLevel) {
                            $add = $('<span class="tree_add" id="diyBtn_add_'+ treeNode.id +'" title="添加"></span>')
                            $add.appendTo($obj);
                            $add.on('click', function(){
                                zTree.addNodes(treeNode, {name:'新增Item'})
                            })
                        }
                    }
                    
                    if (!$del.length) {
                        var $del = $('<span class="tree_del" id="diyBtn_del_'+ treeNode.id +'" title="删除"></span>')
                        
                        $del
                            .appendTo($obj)
                            .on('click', function(event) {
                                var delFn = function() {
                                    $del.alertmsg('confirm', '确认要删除 '+ treeNode.name +' 吗？', {
                                        okCall: function() {
                                            zTree.removeNode(treeNode)
                                            if (op.onRemove) {
                                                var fn = op.onRemove.toFunc()
                                                
                                                if (fn) fn.call(this, event, treeId, treeNode)
                                            }
                                        },
                                        cancelCall: function () {
                                            return
                                        }
                                    })
                                }
                                
                                if (op.beforeRemove) {
                                    var fn = op.beforeRemove.toFunc()
                                    
                                    if (fn) {
                                        var isdel = fn.call(fn, treeId, treeNode)
                                        
                                        if (isdel && isdel == true) delFn()
                                    }
                                } else {
                                    delFn()
                                }
                            }
                        )
                    }
                }
                
                // remove add button && del button
                function _removeHoverDom(treeId, treeNode) {
                    var $add = $('#diyBtn_add_'+ treeNode.id)
                    var $del = $('#diyBtn_del_'+ treeNode.id)
                    
                    if ($add && $add.length) {
                        $add.off('click').remove()
                    }
                    
                    if ($del && $del.length) {
                        $del.off('click').remove()
                    }
                }
                
                // Drag
                function _beforeDrag(treeId, treeNodes) {
                    for (var i = 0; i < treeNodes.length; i++) {
                        if (treeNodes[i].drag === false) {
                            return false
                        }
                    }
                    return true
                }
                
                function _beforeDrop(treeId, treeNodes, targetNode, moveType) {
                    return targetNode ? targetNode.drop !== false : true
                }
            })
            
            /* zTree - drop-down selector */
            
            var $selectzTree = $box.find('[data-toggle="selectztree"]')
            
            $selectzTree.each(function() {
                var $this   = $(this)
                var options = $this.data(),
                    $tree   = $(options.tree),
                    w       = parseFloat($this.css('width')),
                    h       = $this.outerHeight()
                
                options.width   = options.width || $this.outerWidth()
                options.height  = options.height || 'auto'
                
                if (!$tree || !$tree.length) return
                
                var treeid = $tree.attr('id')
                var $box   = $('#'+ treeid +'_select_box')
                var setPosition = function($box) {
                    var top        = $this.offset().top,
                        left       = $this.offset().left,
                        $clone     = $tree.clone().appendTo($('body')),
                        treeHeight = $clone.outerHeight()
                    
                    $clone.remove()
                    
                    var offsetBot = $(window).height() - treeHeight - top - h,
                        maxHeight = $(window).height() - top - h
                    
                    if (options.height == 'auto' && offsetBot < 0) maxHeight = maxHeight + offsetBot
                    $box.css({top:(top + h), left:left, 'max-height':maxHeight})
                }
                
                $this.click(function() {
                    if ($box && $box.length) {
                        setPosition($box)
                        $box.show()
                        return
                    }
                    
                    var zindex = 2
                    var dialog = $.CurrentDialog
                    
                    if (dialog && dialog.length) {
                        zindex = dialog.css('zIndex') + 1
                    }
                    $box  = $('<div id="'+ treeid +'_select_box" class="tree-box"></div>')
                                .css({position:'absolute', 'zIndex':zindex, 'min-width':options.width, height:options.height, overflow:'auto', background:'#FAFAFA', border:'1px #EEE solid'})
                                .hide()
                                .appendTo($('body'))
                    
                    $tree.appendTo($box).css('width','100%').data('fromObj', $this).removeClass('hide').show()
                    setPosition($box)
                    $box.show()
                })
                
                $('body').on('mousedown', function(e) {
                    var $target = $(e.target)
                    
                    if (!($this[0] == e.target || ($box && $box.length > 0 && $target.closest('.tree-box').length > 0))) {
                        $box.hide()
                    }
                })
                
                var $scroll = $this.closest('.bjui-pageContent')
                
                if ($scroll && $scroll.length) {
                    $scroll.scroll(function() {
                        if ($box && $box.length) {
                            setPosition($box)
                        }
                    })
                }
                
                //destroy selectzTree
                $this.on('destroy.bjui.selectztree', function() {
                    $box.remove()
                })
            })
        }
        
        /* Kindeditor */
        if (KindEditor) {
            $box.find('textarea[data-toggle="kindeditor"]').each(function() {
                var $editor = $(this).removeClass('form-control'), options = $editor.data(), _op = options.options
                
                if (_op && typeof _op === 'string') _op = _op.toObj()
                if (_op && typeof _op === 'object') {
                    $.extend(options, _op)
                    delete options.options
                }
                
                if (options.items && typeof options.items === 'string')
                    options.items = options.items.replaceAll('\'', '').replaceAll(' ', '').split(',')
                if (options.afterUpload)         options.afterUpload = options.afterUpload.toFunc()
                if (options.afterSelectFile) options.afterSelectFile = options.afterSelectFile.toFunc()
                if (options.confirmSelect)     options.confirmSelect = options.confirmSelect.toFunc()
                
                var htmlTags = {
                    font : [/*'color', 'size', 'face', '.background-color'*/],
                    span : ['.color', '.background-color', '.font-size', '.font-family'
                            /*'.color', '.background-color', '.font-size', '.font-family', '.background',
                            '.font-weight', '.font-style', '.text-decoration', '.vertical-align', '.line-height'*/
                    ],
                    div : ['.margin', '.padding', '.text-align'
                            /*'align', '.border', '.margin', '.padding', '.text-align', '.color',
                            '.background-color', '.font-size', '.font-family', '.font-weight', '.background',
                            '.font-style', '.text-decoration', '.vertical-align', '.margin-left'*/
                    ],
                    table: ['align', 'width'
                            /*'border', 'cellspacing', 'cellpadding', 'width', 'height', 'align', 'bordercolor',
                            '.padding', '.margin', '.border', 'bgcolor', '.text-align', '.color', '.background-color',
                            '.font-size', '.font-family', '.font-weight', '.font-style', '.text-decoration', '.background',
                            '.width', '.height', '.border-collapse'*/
                    ],
                    'td,th': ['align', 'valign', 'width', 'height', 'colspan', 'rowspan'
                            /*'align', 'valign', 'width', 'height', 'colspan', 'rowspan', 'bgcolor',
                            '.text-align', '.color', '.background-color', '.font-size', '.font-family', '.font-weight',
                            '.font-style', '.text-decoration', '.vertical-align', '.background', '.border'*/
                    ],
                    a : ['href', 'target', 'name'],
                    embed : ['src', 'width', 'height', 'type', 'loop', 'autostart', 'quality', '.width', '.height', 'align', 'allowscriptaccess'],
                    img : ['src', 'width', 'height', 'border', 'alt', 'title', 'align', '.width', '.height', '.border'],
                    'p,ol,ul,li,blockquote,h1,h2,h3,h4,h5,h6' : [
                        'class', 'align', '.text-align', '.color', /*'.background-color', '.font-size', '.font-family', '.background',*/
                        '.font-weight', '.font-style', '.text-decoration', '.vertical-align', '.text-indent', '.margin-left'
                    ],
                    pre : ['class'],
                    hr : ['class', '.page-break-after'],
                    'br,tbody,tr,strong,b,sub,sup,em,i,u,strike,s,del' : []
                }
                
                KindEditor.create($editor, {
                    pasteType                : options.pasteType,
                    minHeight                : options.minHeight || 260,
                    autoHeightMode           : options.autoHeight || false,
                    items                    : options.items || KindEditor.options.items,
                    uploadJson               : options.uploadJson,
                    fileManagerJson          : options.fileManagerJson,
                    allowFileManager         : options.allowFileManager || true,
                    //fillDescAfterUploadImage : options.fillDescAfterUploadImage || true, //上传图片成功后转到属性页，为false则直接插入图片[设为true方便自定义函数(X_afterSelect)]
                    afterUpload              : options.afterUpload,
                    afterSelectFile          : options.afterSelectFile,
                    //X_afterSelect            : options.confirmSelect,
                    //htmlTags                 : htmlTags,
                    /*
                    cssPath                  : [
                                                    BJUI.PLUGINPATH + 'kindeditor_4.1.10/editor-content.css', 
                                                    BJUI.PLUGINPATH + 'kindeditor_4.1.10/plugins/code/prettify.css'
                                               ],
                    */
                    afterBlur                : function() { this.sync() }
                })
            })
        }
        
        if ($.fn.colorpicker) {
            /* colorpicker */
            $box.find('[data-toggle="colorpicker"]').each(function() {
                var $this     = $(this)
                var isbgcolor = $this.data('bgcolor')
                
                $this.colorpicker()
                if (isbgcolor) {
                    $this.on('changeColor', function(ev) {
                        $this.css('background-color', ev.color.toHex())
                    })
                }
            })
            
            $box.find('[data-toggle="clearcolor"]').each(function() {
                var $this   = $(this)
                var $target = $this.data('target') ? $($this.data('target')) : null
                
                if ($target && $target.length) {
                    $this.click(function() {
                        $target.val('')
                        if ($target.data('bgcolor')) $target.css('background-color', '')
                    })
                }
            })
        }
        
        /* tooltip */
        $box.find('[data-toggle="tooltip"]').each(function() {
            $(this).tooltip()
        })
        
        $box.find('[data-toggle="popover"]').each(function() {
            var $element = $(this), target = $element.data('target')
            
            if (target && $(target).length) {
                $element.attr('data-content', $(target).html())
            }
            
            $element.popover()
        })
        
        /* WebUploader */
        if (WebUploader) {
            var initWebUploader = function($element, index) {
                var old = $element.data('webuploader'), options = $element.data('options')
                
                if (old) {
                    old.destroy()
                    $element.data('webuploader.wrap').remove()
                }
                
                if (options) {
                    if (typeof options === 'string') {
                        options = options.trim().toObj()
                    }
                    
                    if (typeof options === 'object') {
                        $element.hide()
                        
                        var $wrap = $('<div id="uploader" class="wu-example"><div class="queueList"><div id="dndArea" class="placeholder"><div id="filePicker"></div><p>或将文件拖到这里</p></div></div><div class="statusBar" style="display:none;"><div class="progress"><span class="text">0%</span><span class="percentage"></span></div><div class="info"></div><div class="btns"><div id="filePicker2"></div><div class="uploadBtn">开始上传</div></div></div>'),
                        // 图片容器
                        $queue = $('<ul class="filelist"></ul>').appendTo($wrap.find('.queueList')),
                        // 状态栏，包括进度和控制按钮
                        $statusBar = $wrap.find('.statusBar'),
                        // 文件总体选择信息。
                        $info = $statusBar.find('.info'),
                        // 上传按钮
                        $upload = $wrap.find('.uploadBtn'),
                        // 没选择文件之前的内容。
                        $placeHolder = $wrap.find('.placeholder'),
                        // 总体进度条
                        $progress = $statusBar.find('.progress').hide(),
                        // 添加的文件数量
                        fileCount = 0,
                        // 添加的文件总大小
                        fileSize = 0,
                        // 优化retina, 在retina下这个值是2
                        ratio = window.devicePixelRatio || 1,
                        // 缩略图大小
                        thumbnailWidth = 110 * ratio,
                        thumbnailHeight = 110 * ratio,
                        // 可能有pedding, ready, uploading, confirm, done.
                        state = 'pedding',
                        // 所有文件的进度信息，key为file id
                        percentages = {},
                        supportTransition = (function() {
                            var s = document.createElement('p').style,
                                r = 'transition' in s ||
                                    'WebkitTransition' in s ||
                                    'MozTransition' in s ||
                                    'msTransition' in s ||
                                    'OTransition' in s
                            
                            s = null
                            
                            return r
                        })(),
                        // 图片访问基地址
                        basePath = options.basePath || '',
                        // WebUploader实例
                        uploader,
                        // 上传文件的单位(单位 + 类型)
                        upunit = options.upunit || '张图片'
                        
                        // 当有文件添加进来时执行，负责view的创建
                        var addFile = function(file, isuploaded, _index) {
                            if (!file && options.uploaded) {
                                $.each(options.uploaded.split(','), function(i, n) {
                                    var uploadedFile = {id:'WU_FILE_UP_'+ i, name:n.trim(), src:basePath + n.trim()}
                                    
                                    addFile(uploadedFile, true, i)
                                    
                                    fileCount++
                                })
                                
                                if (fileCount) {
                                    $placeHolder.addClass('element-invisible');
                                    $statusBar.show()
                                    setState('uploaded')
                                }
                                
                                return
                            }
                            
                            if (fileCount >= options.fileNumLimit) {
                                $statusBar.find('#filePicker2').hide()
                            }
                            
                            var uploadedAttr = isuploaded && upunit === '张图片' ? ' style="cursor:pointer;" data-toggle="dialog" data-options="{id:\'bjui-dialog-view-upload-image\', image:\''+ encodeURIComponent(file.src) +'\', width:800, height:500, mask:true, title:\'查看已上传图片\'}"' : '',
                                $li = $('<li class="'+ (isuploaded ? 'uploaded' : '') +'" id="'+ file.id +'_'+ index +'">' +
                                    '<p class="title">' + file.name + '</p>' +
                                    '<p class="imgWrap" '+ uploadedAttr +'></p>'+
                                    '<p class="progress"><span></span></p>' +
                                    '</li>'),
                                $btns = $('<div class="file-panel">' +
                                    '<span class="cancel">删除</span>' +
                                    '<span class="rotateRight">向右旋转</span>' +
                                    '<span class="rotateLeft">向左旋转</span></div>').appendTo($li),
                                $prgress = $li.find('p.progress span'),
                                $imgWrap = $li.find('p.imgWrap'),
                                $info = $('<p class="error"></p>'),
                                text = '',
                                showError = function(code) {
                                    switch(code) {
                                        case 'exceed_size':
                                            text = '文件大小超出'
                                            
                                            break
                                        case 'interrupt':
                                            text = '上传暂停'
                                            
                                            break
                                        default:
                                            text = '上传失败，请重试'
                                            
                                            break
                                    }
                                    
                                    $info.text(text).appendTo($li)
                                }
                            
                            if (!isuploaded) {
                                if (file.getStatus() === 'invalid') {
                                    showError(file.statusText)
                                } else {
                                    // @todo lazyload
                                    $imgWrap.text('预览中')
                                    uploader.makeThumb(file, function(error, src) {
                                        if (error) {
                                            $imgWrap.text('不能预览')
                                            return
                                        }
                                        
                                        var img = $('<img src="'+src+'">')
                                        
                                        $imgWrap.empty().append(img)
                                    }, thumbnailWidth, thumbnailHeight)
                                    
                                    percentages[file.id] = [file.size, 0]
                                    file.rotation = 0
                                }
                                
                                file.on('statuschange', function(cur, prev) {
                                    if (prev === 'progress') {
                                        $prgress.hide().width(0)
                                    } else if (prev === 'queued') {
                                        
                                    }
                                    
                                    // 成功
                                    if (cur === 'error' || cur === 'invalid') {
                                        showError(file.statusText)
                                        percentages[file.id][1] = 1
                                    } else if (cur === 'interrupt') {
                                        showError('interrupt')
                                    } else if (cur === 'queued') {
                                        percentages[file.id][1] = 0
                                    } else if (cur === 'progress') {
                                        $info.remove()
                                        $prgress.css('display', 'block')
                                    } else if (cur === 'complete') {
                                        $li.append('<span class="success"></span>')
                                    }
                                    
                                    $li.removeClass('state-' + prev).addClass('state-' + cur)
                                })
                            } else {
                                $imgWrap.empty().append('<img src="'+ file.src +'">')
                                if (options.initUploaded) {
                                    var arr = options.initUploaded.split(',')
                                    
                                    $li.append('<input type="hidden" class="upload" name="'+ (options.upname || $element.data('name')) +'" value="'+ arr[_index] +'">')
                                }
                            }
                            
                            $li.on('mouseenter', function() {
                                $btns.stop().animate({height: 30})
                            })
                            
                            $li.on('mouseleave', function() {
                                $btns.stop().animate({height: 0})
                            })
                            
                            $btns.on('click', 'span', function() {
                                var index = $(this).index(),
                                    deg
                                
                                switch (index) {
                                    case 0:
                                        if (isuploaded) {
                                            fileCount --
                                            removeFile(file)
                                            
                                            if (!fileCount) {
                                                setState('pedding');
                                            }
                                            uploader.refresh()
                                            updateTotalProgress()
                                        } else {
                                            uploader.removeFile(file)
                                        }
                                        
                                        return
                                    case 1:
                                        file.rotation += 90
                                        
                                        break
                                    case 2:
                                        file.rotation -= 90
                                        
                                        break
                                }
                                
                                if (supportTransition) {
                                    deg = 'rotate(' + file.rotation + 'deg)'
                                    $imgWrap.css({
                                        '-webkit-transform': deg,
                                        '-mos-transform': deg,
                                        '-o-transform': deg,
                                        'transform': deg
                                    })
                                } else {
                                    $imgWrap.css('filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')')
                                }
                            })
                            
                            $li.appendTo($queue)
                        }
                        
                        // 负责view的销毁
                        var removeFile = function(file) {
                            var $li = $wrap.find('#'+ file.id +'_'+ index)
                            
                            delete percentages[file.id]
                            updateTotalProgress()
                            
                            $li.off().find('.file-panel').off().end().remove()
                            
                            if (fileCount < options.fileNumLimit) {
                                $statusBar.find('#filePicker2').show()
                            }
                        }
                        
                        var updateTotalProgress = function() {
                            var loaded = 0,
                                total  = 0,
                                spans  = $progress.children(),
                                percent
                            
                            $.each(percentages, function(k, v) {
                                total  += v[0]
                                loaded += v[0] * v[1]
                            })
                            
                            percent = total ? loaded / total : 0
                            
                            spans.eq(0).text(Math.round(percent * 100) + '%')
                            spans.eq(1).css('width', Math.round(percent * 100) + '%')
                            updateStatus()
                        }
                        
                        var updateStatus = function() {
                            var text = '', stats;
                            
                            if (state === 'ready') {
                                text = '选中'+ fileCount + upunit + '，共'+ WebUploader.formatSize(fileSize) +'。'
                            } else if (state === 'confirm') {
                                stats = uploader.getStats()
                                if (stats.uploadFailNum) {
                                    text = '已成功上传'+ stats.successNum + upunit +'，'+
                                        stats.uploadFailNum + upunit +'上传失败，<a class="retry" href="#">重新上传</a> 或 <a class="ignore" href="#">忽略</a>'
                                }
                                
                            } else if (state === 'uploaded') {
                                text = '已上传'+ fileCount + upunit
                            } else {
                                stats = uploader.getStats()
                                text = '共'+ fileCount + upunit +'（' + WebUploader.formatSize(fileSize) +'），已上传' + stats.successNum
                                
                                if (stats.uploadFailNum) {
                                    text += '，失败' + stats.uploadFailNum
                                }
                            }
                            
                            $info.html(text)
                            $element.data('fileCount', fileCount)
                        }
                        
                        var setState = function(val) {
                            var file, stats
                            
                            if (val === state) {
                                return
                            }
                            
                            $upload.removeClass('state-' + state)
                            $upload.addClass('state-' + val)
                            state = val
                            
                            switch (state) {
                                case 'pedding':
                                    $placeHolder.removeClass('element-invisible')
                                    $queue.parent().removeClass('filled')
                                    $queue.hide();
                                    $statusBar.addClass('element-invisible')
                                    uploader.refresh()
                                    
                                    break
                                case 'ready':
                                    $placeHolder.addClass('element-invisible')
                                    $wrap.find('#filePicker2').removeClass('element-invisible')
                                    $queue.parent().addClass('filled')
                                    $queue.show()
                                    $statusBar.removeClass('element-invisible')
                                    uploader.refresh()
                                    $upload.removeClass('disabled')
                                    
                                    break
                                case 'uploading':
                                    $wrap.find('#filePicker2').addClass('element-invisible')
                                    $progress.show()
                                    $upload.text('暂停上传')
                                    
                                    break
                                case 'paused':
                                    $progress.show()
                                    $upload.text('继续上传')
                                    
                                    break
                                case 'confirm':
                                    $progress.hide()
                                    $upload.text('开始上传').addClass('disabled')
                                    
                                    stats = uploader.getStats()
                                    if (stats.successNum && !stats.uploadFailNum) {
                                        setState('finish')
                                        return
                                    }
                                    
                                    break
                                case 'finish':
                                    stats = uploader.getStats()
                                    if (stats.successNum) {
                                        
                                    } else {
                                        // 没有成功的图片，重设
                                        state = 'done'
                                        
                                        BJUI.alertmsg('info', '上传失败！')
                                    }
                                    
                                    break
                                case 'uploaded':
                                    $upload.text('开始上传').addClass('disabled')
                                    
                                    break
                            }
                            
                            if (state !== 'uploaded')
                                updateStatus()
                        }
                        
                        $wrap.insertAfter($element)
                        
                        if (!WebUploader.Uploader.support()) {
                            alert('Web Uploader 不支持您的浏览器！如果你使用的是IE浏览器，请尝试升级 flash 播放器');
                            throw new Error('WebUploader does not support the browser you are using.');
                        }
                        
                        // 是否允许重新上传
                        if (typeof options.reupload === 'undefined')
                            options.reupload = true
                        
                        options = $.extend(true, {}, {
                            pick: {
                                id: $wrap.find('#filePicker'),
                                label: '点击选择图片'
                            },
                            dnd: $wrap.find('.queueList'),
                            paste: false,
                            accept: {
                                title: 'Images',
                                extensions: 'gif,jpg,jpeg,bmp,png',
                                mimeTypes: 'image/*'
                            },
                            swf: BJUI.PLUGINPATH + 'webuploader/Uploader.swf', // swf文件路径
                            disableGlobalDnd: false,
                            chunked: false,
                            server: null,
                            fileNumLimit: 300,
                            fileSizeLimit: 200 * 1024 * 1024,        // 200 M
                            fileSingleSizeLimit: 50 * 1024 * 1024    // 50 M
                        }, options)
                        
                        // 实例化
                        uploader = WebUploader.create(options)
                        
                        // 如果有已上传的图片(编辑时)
                        if (typeof $element.data('uploaded') !== 'undefined')
                            options.uploaded = $element.data('uploaded')
                        if (options.uploaded) {
                            // 将已上传图片加到队列
                            addFile()
                            
                            $queue.parent().addClass('filled')
                        }
                        
                        // 上传成功
                        uploader.on('uploadSuccess', function(file, response) {
                            if (response[BJUI.keys.statusCode] != BJUI.statusCode.ok) {
                                BJUI.alertmsg('error', response.message)
                            } else {
                                var $li = $wrap.find('#'+ file.id +'_'+ index)
                                
                                $li.find('input.upload').remove().end()
                                    .append('<input type="hidden" class="upload" name="'+ (options.upname || $element.data('name')) +'" value="'+ response.filename +'">')
                            }
                        })
                        // 上传失败
                        uploader.on('uploadError', function(file, response) {
                            BJUI.alertmsg('error', response.message)
                        })
                        
                        // 添加“添加文件”的按钮，
                        if (options.fileNumLimit > 1) {
                            uploader.addButton({
                                id: $wrap.find('#filePicker2'),
                                label: '继续添加'
                            })
                            
                            if (fileCount >= options.fileNumLimit)
                                $statusBar.find('#filePicker2').hide()
                        }
                        
                        uploader.onUploadProgress = function(file, percentage) {
                            var $li = $wrap.find('#'+ file.id +'_'+ index),
                                $percent = $li.find('.progress span')
                            
                            $percent.css('width', percentage * 100 + '%')
                            percentages[file.id][1] = percentage
                            updateTotalProgress()
                        }
                        
                        uploader.onFileQueued = function(file) {
                            fileCount++
                            fileSize += file.size
                            
                            if (fileCount === 1) {
                                $placeHolder.addClass('element-invisible');
                                $statusBar.show()
                            }
                            
                            addFile(file)
                            setState('ready')
                            updateTotalProgress()
                        }
                        
                        uploader.onFileDequeued = function(file) {
                            fileCount--
                            fileSize -= file.size
                            
                            if (!fileCount) {
                                setState('pedding');
                            }
                            
                            removeFile(file)
                            updateTotalProgress()
                        }
                        
                        uploader.on('all', function(type) {
                            var stats
                            
                            switch(type) {
                                case 'uploadFinished':
                                    setState('confirm')
                                    
                                    break
                                case 'startUpload':
                                    setState('uploading')
                                    
                                    break
                                case 'stopUpload':
                                    setState('paused')
                                    
                                    break
                            }
                        })
                        
                        uploader.onError = function(code) {
                            if (code === 'Q_EXCEED_NUM_LIMIT') {
                                BJUI.alertmsg('info', '只允许上传'+ options.fileNumLimit + upunit +'！')
                            } else if (code === 'Q_TYPE_DENIED') {
                                BJUI.alertmsg('info', '不支持的文件类型！')
                            } else if (code === 'F_EXCEED_SIZE') {
                                BJUI.alertmsg('info', '文件太大了！')
                            } else if (code === 'F_DUPLICATE') {
                                BJUI.alertmsg('info', '已添加过该文件！')
                            } else {
                                BJUI.alertmsg('info', code)
                            }
                        }
                        
                        $upload.on('click', function() {
                            if ($(this).hasClass('disabled')) {
                                return false
                            }
                            
                            if (state === 'ready') {
                                uploader.upload()
                            } else if (state === 'paused') {
                                uploader.upload()
                            } else if (state === 'uploading') {
                                uploader.stop()
                            }
                        })
                        
                        $info.on('click', '.retry', function() {
                            uploader.retry()
                        })
                        
                        $info.on('click', '.ignore', function() {
                            alert('todo')
                        })
                        
                        $upload.addClass('state-' + state)
                        updateTotalProgress()
                        
                        $element.data('webuploader', uploader).data('webuploader.wrap', $wrap)
                    }
                }
            }
            
            $box.find('input[data-toggle="webuploader"]').each(function(i) {
                initWebUploader($(this), i)
                
                $(this).on('reload.webuploader', function() {
                    initWebUploader($(this), i)
                })
            })
        }
        
        /* fixed dropdown-menu width */
        $box.find('[data-toggle="dropdown"]').parent().on('show.bs.dropdown', function(e) {
            var $this = $(this), width = $this.outerWidth(), $menu = $this.find('> .dropdown-menu'), menuWidth = $menu.outerWidth()
            
            if (width > menuWidth) {
                $menu.css('min-width', width)
            }
        })
        
        /* ========================================================================
         * @description highCharts
         * @author 小策一喋 <xvpindex@qq.com>
         * @Blog http://www.topjui.com
         * ======================================================================== */
        var $highcharts = $box.find('[data-toggle="highcharts"]')
        
        $highcharts.each(function(){
            var $element = $(this)
            var options  = $element.data()
            
            $.get(options.url, function(chartData){
                $element.highcharts(chartData)
            }, 'json')
        })
        
        /* ========================================================================
         * @description ECharts
         * @author 小策一喋 <xvpindex@qq.com>
         * @Blog http://www.topjui.com
         * ======================================================================== */
        var $echarts = $box.find('[data-toggle="echarts"]')
        
        $echarts.each(function(){
            var $element = $(this)
            var options  = $element.data()
            var theme    = options.theme ? options.theme : 'default'
            var typeArr  = options.type.split(',')
            
            require.config({
                paths: {
                    echarts: BJUI.PLUGINPATH + 'echarts'
                }
            })
            
            require(
                [
                    'echarts',
                    'echarts/theme/' + theme,
                    'echarts/chart/' + typeArr[0],
                    typeArr[1] ? 'echarts/chart/' + typeArr[1] : 'echarts'
                ],
                function (ec,theme) {
                    var myChart = ec.init($element[0],theme)
                    
                    $.get(options.url, function(chartData){
                        myChart.setOption(chartData)
                    }, 'json')
                }
            )
        })
        
    })
    
}(jQuery);