日期选择器-----tablesorter

HTML:

<li>
  <label>检查时间</label>
  <ul class="inspect_time">
    <li class="retrieval_color">今天</li>
    <li>昨天</li>
    <li>最近3天</li>
    <li>最近7天</li>
    <li style="width:200px"><span class="date_title" id="date1" style="display:block;border-radius: 3px;"></span></li>//日期选择插件
  </ul>
</li>

<script>

    //date

            var dateRange1 = new pickerDateRange('date1', {

                stopToday : false,

                isTodayValid : true,

                startDate: Today,

                endDate: Today,

                needCompare : false,

                // defaultText : ' 离开 ',

                autoSubmit : false,

                inputTrigger : 'input_trigger1',

                theme : 'ta'

            });

</script>

CSS:  

/*========== reset ==========*/
html, body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, textarea, p, blockquote, th, td, iframe,hr{margin:0;padding:0;}
body{font:12px/1.6 Tahoma,microsoft yahei,"微软雅黑","宋体";*font-family:"微软雅黑","宋体";}
fieldset, img { border:0; }
address, caption, cite, dfn, em, th, var{font-style:normal;font-weight:normal;}
ol, ul { list-style:none; }
caption, th { text-align:left; }
h1, h2, h3, h4, h5, h6 { font-size:100%; }
table {border-collapse:collapse;border-spacing:0;}
select,input,label,button,textarea{margin:0;padding:0;font:normal normal normal "微软雅黑",arial,Simsun,Arial Unicode MS,Mingliu,Arial;overflow:visible;}
input{padding:2px 0 1px;*padding:4px 0 0;_padding:4px 0 0;_height:21px;}

/**
* GRI主题
*/
.gri_contrast {
float: left;
margin: 4px 8px 0 8px;
line-height: 20px;
color: #666;
cursor: pointer;
font: 12px/1.5 Tahoma, Helvetica, 'SimSun', sans-serif;
}


.gri_date {
/* margin: 4px 4px;*/
padding: 0 26px 0 6px;
width: 195px;
height: 20px;
line-height: 20px;
border: 1px solid #D6D6D6;
background: #FFF url('./images/icon_date.png') no-repeat 100% 50%;
cursor: pointer;
color: #666;
}

.gri_date_month {
width: 180px
}

.gri_dateRangeCalendar {
position: absolute;
display: none;
background: #FFF;
border: 1px solid #6FB1DF;
padding: 10px;
-moz-box-shadow: 0px 1px 3px #6FB1DF;
filter: progid:DXImageTransform.Microsoft.Shadow(Strength = 5, Direction = 135, Color = "#CCCCCC");
font: 12px/1.5 Tahoma, Helvetica, 'SimSun', sans-serif;
}

.gri_dateRangeCalendar a {
color: #369;
}

.gri_dateRangePicker {
float: left;
border: 0;
margin: 0;
padding: 0;
}

.gri_dateRangeOptions {
float: left;
}

.gri_dateRangeOptions input.gri_dateRangeInput {
width: 80px;
text-align: center;
border: 1px solid #DDD;
}

.gri_dateRangeOptions div.gri_dateRangeInput {
margin-bottom: 5px;
}

.gri_dateRangePreMonth {
float: left;
width: 15px;
height: 17px;
background: url('./images/page.png') no-repeat 0 0;
overflow: hidden;
}

.gri_dateRangeNextMonth {
float: right;
width: 15px;
height: 17px;
background: url('./images/page.png') no-repeat -15px 0;
overflow: hidden;
}

.gri_dateRangePreMonth span, .gri_dateRangeNextMonth span {
display: none;
}

.gri_dateRangeDateTable {
margin: 0 10px 0 0px;
padding: 0px;
float: left;
empty-cells: show;
border-collapse: collapse;
display: inline;
font-size: 12px;
}

.gri_dateRangeDateTable td {
border: 1px solid #EEE;
text-align: right;
cursor: pointer;
padding: 1px 2px;
}

.gri_dateRangeDateTable th {
border-top: 1px solid #DEE6F6;
border-left: 1px solid #DEE6F6;
background: #E0E8F7;
font-weight: 400;
border-left: 1px solid #DDD;
}

.gri_dateRangeDateTable td.gri_dateRangeGray {
color: #BBB;
cursor: default;
}

.gri_dateRangeDateTable td.gri_dateRangeToday {
color: #F90;
font-weight: bold;
}

.gri_dateRangeSelected {
background-color: #007CD9;
color: #FFF;
}

.gri_dateRangeCompare {
background-color: #B9E078;
color: #FFF;
}

.gri_dateRangeCoincide {
background-color: #FFFFC4;
}

.gri_pn {
background: url("../img/pn.png") repeat-x scroll 0 -48px #E5E5E5;
color: #fff;
}

.gri_pnc {
background: url("../img/pn.png") repeat-x scroll 0 0 #E5E5E5;
}

.gri_co {
border: 1px solid #999999;
box-shadow: 0 1px 0 #E5E5E5;
cursor: pointer;
font-family: Tahoma, 'Microsoft Yahei', 'Simsun';
font-size: 12px;
height: 21px;
overflow: hidden;
vertical-align: middle
}

/**
* =================================================
* TA主题
* =================================================
*/
.ta_date{
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background-color: #fefefe;
background-image: -ms-linear-gradient(top, #fafafa, #f5f5f5);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fafafa), to(#f5f5f5));
background-image: -moz-linear-gradient(top, #fafafa, #f5f5f5);
background-image: -o-linear-gradient(top, #fafafa, #f5f5f5);
background-image: linear-gradient(top, #fafafa, #f5f5f5);
background-repeat: repeat-x;
float:left;
}

.ta_date .date_title {
font-family:Arial;
font-size:14px;
color:#666666;
padding:6px 10px;
*padding:0px 10px;
border-right:1px solid #d8d8d8;
vertical-align:middle;
cursor:pointer;
*zoom:1;
}
.ta_date .date_title:before{content: " "}
.ta_date:hover {
/* background-image:none;
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
-moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);*/
background: #9DC970;

}
.ta_date:hover .date_title{
color: #fff;
}
.ta_date .to{ padding:0 5px;}
.ta_date .opt_sel{/*====*/
width:30px;
height:28px;
line-height:28px;
display:inline-block;
text-align:center;
vertical-align:middle;
margin-left:-4px;
}
.ta_date a.opt_sel:link, .ta_date a.opt_sel:visited {

}
.ta_date a.opt_sel:active, .ta_date a.opt_sel:hover {

}

.ta_date .i_orderd{
display: inline-block;
width: 0;
height: 0;
vertical-align:middle;
border-top: 5px solid #727272;
border-right: 5px dashed transparent;
border-left: 5px dashed transparent;
font-size:0;
content: "";
overflow:hidden;
*margin-top:10px;
}


.ta_calendar2{*width:536px;}
.ta_calendar1{*width:268px;}
.ta_calendar{background-color: #ffffff;
font-size:12px;
text-align:left;
z-index:100;
position: absolute;
right: 0;
}
.i_pre,.i_next,.ta_calendar td.ta_dateRangeSelected,.ta_calendar td.first,.ta_calendar td.last,.ta_calendar td.today{
/*background:url(http://imgcache.qq.com/bossweb/mta/images/calendar_all.png) no-repeat;*/
background:url(../img/calendar_all.png) no-repeat;
cursor:pointer;
}
.i_pre,.i_next{ width:23px; height:23px;display:inline-block; }
.i_pre{ background-position:0 0;}
.i_pre:hover{ background-position:-46px 0px;}
.i_next{ background-position:-23px 0;}
.i_next:hover{ background-position:-69px 0px;}

.ta_calendar td.ta_dateRangeSelected{
background:#cbe6f5;
}
.ta_calendar td.ta_dateRangeGray{
color: #BBB;
cursor: default;
}

.ta_calendar td.first,.ta_calendar td.today{
background:#4eb5f7;
}
.ta_calendar td.first:after,.ta_calendar td.today:after{content: "";display: block; font-size: 10px;color:#fff;}

.ta_calendar td.last{
background:#4eb5f7;
}
.ta_calendar td.last:after{content: "";display: block; font-size: 10px;color:#fff;}

.ta_calendar .dis{
color:#9e9e9e;
}
.ta_calendar table {
font-size: 12px;
_display:inline;
border-spacing:0 7px;
border-collapse:collapse;
width: 100%;
}
.ta_calendar table caption{ text-align:center; height:40px; line-height:40px; font-size:14px; box-shadow:0px 1px 1px rgba(0,0,0,0.1);}
.ta_calendar table thead tr {
background:#fff;

}
.ta_calendar table thead th {
cursor: pointer;
text-align:center;
height: 40px;

}

.ta_calendar table.calendar-month {
font-size: 12px;
float:left;
margin:0 8px;
_display:inline;
border-spacing:7px;
border-collapse:separate;
margin-bottom:10px;
}
.calendar-month caption{
border-bottom:1px solid #E1E1E1;
*padding-bottom:0px;
}

.calendar-month tbody td {
line-height: 30px;
padding: 4px 11px;
text-align:center;
white-space:nowrap;
font-family:"΢���ź�";
cursor:pointer;
}
.calendar-month td.hover,.calendar-month td:hover,.calendar-month caption span:hover{
background:#;
color:#6590c1;
border:1px solid #6590c1;
padding: 3px 10px;
border-radius:2px;
cursor:pointer;
}
.calendar .dis:hover{
color:#9e9e9e;
border:1px solid #d3d5d6;
padding: 3px 10px;
}
.calendar-month td.current{
background:#6590c1;
color:#fff;
border-radius:2px;
}

.ta_calendar table thead th.sun{color: #999;}
.ta_calendar table thead th.sat{color: #999;}
.ta_calendar table td:first-child{height: 0px;}
.ta_calendar table tbody td {
text-align:center;
white-space:nowrap;
font-family:"Tahoma";
background: #edf8fe;
height: 40px;
width: 14%;
border: 1px solid #fff;
}

.ta_calendar_cont{position:relative;}
.ta_calendar_cont .i_pre,.ta_calendar_cont .i_next{position:absolute; top:7px;}
.ta_calendar_cont .i_pre{left:10px;}
.ta_calendar_cont .i_next{right:10px;}
.ta_calendar_footer{
border-top:1px solid #e5e5e5;
background:#fafafa;
padding-top:6px;
height:34px;
}
.ta_calendar_footer .frm_btn{
float:right;
}

.ta_calendar_footer .frm_msg{
float:left;
vertical-align:middle;
}
.ta_calendar_footer .ipt_text_s{
padding:4px 4px;
}

.ta_ipt_text, .ta_ipt_textarea, .ta_ipt_text_s {
border: 1px solid #CCCCCC;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset;
color: #555555;
font-size: 12px;
height: 16px;
line-height: 16px;
padding: 6px 4px;
position: relative;
transition: border 0.2s linear 0s, box-shadow 0.2s linear 0s;
vertical-align: middle;
width: 180px;
z-index: 2;
}

.ta_ipt_text_s {
width: 80px;
}

.ta_btn {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #F5F5F5;
background-image: -moz-linear-gradient(center top , #FEFEFE, #F5F5F5);
background-repeat: repeat-x;
border-color: #CACACA #CACACA #B3B3B3;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.05);
color: #333333;
cursor: pointer;
display: inline-block;
font-family: "΢���ź�","����";
font-size: 12px;
line-height: 20px;
margin-bottom: 0;
outline: 0 none;
padding: 3px 12px;
text-align: center;
}
.ta_btn:hover, .ta_btn:active, .ta_btn.active, .ta_btn.disabled, .ta_btn[disabled] {
color: #333333;
}
.ta_btn:hover {
background-color: #FEFEFE;
background-image: none;
color: #333333;
text-decoration: none;
transition: background-position 0.1s linear 0s;
}
.ta_btn:focus {
outline: thin dotted #333333;
outline-offset: -2px;
}
.ta_btn.active, .ta_btn:active {
background-color: #E6E6E6;
background-image: none;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15) inset, 0 1px 2px rgba(0, 0, 0, 0.05);
outline: 0 none;
}
.ta_btn.disabled, .ta_btn[disabled] {
background-color: #E6E6E6;
background-image: none;
box-shadow: none;
cursor: default;
opacity: 0.65;
}
.ta_btn {
margin: 0px 5px 0 0;
vertical-align: top;
}
.ta_btn:hover{
background-position: 0 -16px;
}
.ta_btn_primary {
background-color: #B4D66F;
background-image: -moz-linear-gradient(center top , #C7E184, #A2CC59);
border: 1px solid #88AB4A;
color: #56740F;
}
.ta_btn_primary:hover{
background-color: #5C96DB;
background-image: -moz-linear-gradient(center top , #74A5ED, #4789CD);
border: 1px solid #286AB1;
color: #FFFFFF;
}

.cf:after {
clear: both;
}

.cf:before, .cf:after {
content: "";
display: table;
}

.cf:before, .cf:after {
content: "";
display: table;
}

JS:

/**

 *=======================================================================

 *日期选择器js组件。

 *@author :johnnyzheng(johnnyzheng@tencent.com) 郑灿双

 *@version : 2012-07-11

 *@modification list:2012-08-16  规范样式名称

 *                    2013-01-04  增加主题设置接口

 *                    2013-01-31  增加自定义灰掉周末 周几的选项,增加自动初始化自动提交的功能

 *                    2013-03-15  支持一个页面多个日期选择器,快捷日期选择

 *                    2013-03-26  增加确认、取消按钮的隐藏,而直接自动提交

 *  2013-08-01  扩展接口,增加最近90天,增加自定义可选时间

 *  2013-08-12  日期选择器框体宽度超出视窗大小的时候制动鼓靠右对齐

 *  2014-02-25  增加业务接口:获取当前日期对象的的选中日期

 *  2014-10-13  扩展参数,支持日期下拉选择自定义年和月份,配合theme:ta来使用。

 *=======================================================================

*/

/**

 * @description 整个日期选择器对象的构造函数入口,支持丰富的接口参数传递,大多数提供默认配置,可传入覆盖

 * @param {String} inputId 日期选择器ID

 * @param {object} options 配置数组

 */

function pickerDateRange(inputId, options) {

    /**

     * 默认配置参数数据,每个参数涵义在后解释

     */

    var defaults = {

aToday : 'aToday', //今天

aYesterday : 'aYesterday', //昨天

aRecent7Days : 'aRecent7Days', //最近7天

aRecent14Days : 'aRecent14Days',//最近14天

aRecent30Days : 'aRecent30Days', //最近30天

aRecent90Days : 'aRecent90Days', //最近90天

        startDate : '', // 开始日期

        endDate : '', // 结束日期

        startCompareDate : '', // 对比开始日期

        endCompareDate : '', // 对比结束日期

   minValidDate : '315507600', //最小可用时间,控制日期选择器的可选力度

        maxValidDate : '', // 最大可用时间,与stopToday 配置互斥

        success : function(obj) {return true;}, //回调函数,选择日期之后执行何种操作

        startDateId : 'startDate', // 开始日期输入框ID

        startCompareDateId : 'startCompareDate', // 对比开始日期输入框ID

        endDateId : 'endDate', // 结束日期输入框ID

        endCompareDateId : 'endCompareDate', // 对比结束日期输入框ID

        target : '', // 日期选择框的目标,一般为 <form> 的ID值

        needCompare : false, // 是否需要进行日期对比

suffix : '', //相应控件的后缀

inputTrigger : 'input_trigger',

compareTrigger : 'compare_trigger',

        compareCheckboxId : 'needCompare', // 比较选择框

        calendars : 1, // 展示的月份数,最大是2

        dayRangeMax : 0, // 日期最大范围(以天计算)

        monthRangeMax : 12, // 日期最大范围(以月计算)

        dateTable : 'dateRangeDateTable', // 日期表格的CSS类

        selectCss : 'dateRangeSelected', // 时间选择的样式

        compareCss : 'dateRangeCompare', // 比较时间选择的样式

        coincideCss : 'dateRangeCoincide', // 重合部分的样式

firstCss : 'first', //起始样式

lastCss : 'last', //结束样式

clickCss : 'today', //点击样式

        disableGray : 'dateRangeGray', // 非当前月的日期样式

        isToday : 'dateRangeToday', // 今天日期的样式

        joinLineId : 'joinLine',

        isSingleDay : false,

        defaultText : '至',

        singleCompare : false,

        stopToday : true,

        isTodayValid : false,

weekendDis : false, //灰掉周末不可选。

disCertainDay : [], //不可用的周日期设置数组,如:[1,3]是要周一, 周三 两天不可选,每个周的周一,周三都不可选择。

        disCertainDate : [],//不可用的日期设置数组,如:[1,3]是要1号,3号 两天不可选,特别的,[true,1,3]则反之,只有1,3可选,其余不可选。

shortOpr : false, //结合单天日期选择的短操作,不需要确定和取消的操作按钮。

noCalendar : false, //日期输入框是否展示

theme : 'gri', //日期选择器的主题,目前支持 'gri' / 'ta'

magicSelect : false, //用户自定义选择年、月,与{theme:ta}配合使用。

autoCommit : false, //加载后立马自动提交

autoSubmit : false, //没有确定,取消按钮,直接提交 

replaceBtn : 'btn_compare'

    };

    //将对象赋给__method变量

    var __method = this;

 

    this.inputId = inputId;

this.inputCompareId = inputId + 'Compare';

this.compareInputDiv = 'div_compare_'+inputId;

    // 配置参数

    this.mOpts = $.extend({}, defaults, options);

//默认日历参数最大是3

this.mOpts.calendars = Math.min(this.mOpts.calendars, 3);

//根据不同主题需要初始化的变量

this.mOpts.compareCss = this.mOpts.theme == 'ta' ? this.mOpts.selectCss :this.mOpts.compareCss

    //昨天,今天,最近7天,最近14天,最近30天

this.periodObj = {};

this.periodObj[__method.mOpts.aToday] = 0;

this.periodObj[__method.mOpts.aYesterday] = 1;

this.periodObj[__method.mOpts.aRecent7Days] = 6;

this.periodObj[__method.mOpts.aRecent14Days] = 13;

this.periodObj[__method.mOpts.aRecent30Days] = 29;

this.periodObj[__method.mOpts.aRecent90Days] = 89;

    // 记录初始默认时间

    this.startDefDate = '';

    // 随机ID后缀

    var suffix = '' == this.mOpts.suffix ? (new Date()).getTime() : this.mOpts.suffix;

    // 日期选择框DIV的ID

    this.calendarId = 'calendar_' + suffix;

    // 日期列表DIV的ID

    this.dateListId = 'dateRangePicker_' + suffix;

    // 日期比较层

    this.dateRangeCompareDiv = 'dateRangeCompareDiv_' + suffix;

//日期选择层

this.dateRangeDiv = 'dateRangeDiv_' + suffix;

    // 日期对比选择控制的checkbox

    this.compareCheckBoxDiv = 'dateRangeCompareCheckBoxDiv_' + suffix;

    // 时间选择的确认按钮

    this.submitBtn = 'submit_' + suffix;

    // 日期选择框关闭按钮

    this.closeBtn = 'closeBtn_' + suffix;

    // 上一个月的按钮

    this.preMonth = 'dateRangePreMonth_' + suffix;

    // 下一个月的按钮

    this.nextMonth = 'dateRangeNextMonth_' + suffix;

    // 表单中开始、结束、开始对比、结束对比时间

    this.startDateId = this.mOpts.startDateId + '_' + suffix;

    this.endDateId = this.mOpts.endDateId + '_' + suffix;

    this.compareCheckboxId = this.mOpts.compareCheckboxId + '_' + suffix;

    this.startCompareDateId = this.mOpts.startCompareDateId + '_' + suffix;

    this.endCompareDateId = this.mOpts.endCompareDateId + '_' + suffix;

    // 初始化日期选择器面板的HTML代码串

    var wrapper = {

gri :[

'<div id="' + this.calendarId + '" class="gri_dateRangeCalendar">',

'<table class="gri_dateRangePicker"><tr id="' + this.dateListId + '"></tr></table>',

'<div class="gri_dateRangeOptions" '+ (this.mOpts.autoSubmit ? ' style="display:none" ' : '') +'>',

'<div class="gri_dateRangeInput" id="' + this.dateRangeDiv + '" >',

'<input type="text" class="gri_dateRangeInput" name="' + this.startDateId + '" id="' + this.startDateId + '" value="' + this.mOpts.startDate + '" readonly />',

'<span id="' + this.mOpts.joinLineId + '"> - </span>',

'<input type="text" class="gri_dateRangeInput" name="' + this.endDateId + '" id="' + this.endDateId + '" value="' + this.mOpts.endDate + '" readonly /><br />',

'</div>',

'<div class="gri_dateRangeInput" id="' + this.dateRangeCompareDiv + '">',

'<input type="text" class="gri_dateRangeInput" name="' + this.startCompareDateId + '" id="' + this.startCompareDateId + '" value="' + this.mOpts.startCompareDate + '" readonly />',

'<span class="' + this.mOpts.joinLineId + '"> - </span>',

'<input type="text" class="gri_dateRangeInput" name="' + this.endCompareDateId + '" id="' + this.endCompareDateId + '" value="' + this.mOpts.endCompareDate + '" readonly />',

'</div>',

'<div>',

'<input type="button" name="' + this.submitBtn + '" id="' + this.submitBtn + '" value="确定" />',

'&nbsp;<a id="' + this.closeBtn + '" href="javascript:;">关闭</a>',

'</div>',

'</div>',

'</div>'

],

ta:[

'<div id="' + this.calendarId + '" class="ta_calendar ta_calendar2 cf">',

'<div class="ta_calendar_cont cf" id="'+ this.dateListId +'">',

//'<table class="dateRangePicker"><tr id="' + this.dateListId + '"></tr></table>',

'</div>',

'<div class="ta_calendar_footer cf" '+ (this.mOpts.autoSubmit ? ' style="display:none" ' : '') +'>',

'<div class="frm_msg">',

'<div id="' + this.dateRangeDiv + '">',

'<input type="text" class="ta_ipt_text_s" name="' + this.startDateId + '" id="' + this.startDateId + '" value="' + this.mOpts.startDate + '" readonly />',

'<span class="' + this.mOpts.joinLineId + '"> - </span>',

'<input type="text" class="ta_ipt_text_s" name="' + this.endDateId + '" id="' + this.endDateId + '" value="' + this.mOpts.endDate + '" readonly /><br />',

'</div>',

'<div id="' + this.dateRangeCompareDiv + '">',

'<input type="text" class="ta_ipt_text_s" name="' + this.startCompareDateId + '" id="' + this.startCompareDateId + '" value="' + this.mOpts.startCompareDate + '" readonly />',

'<span class="' + this.mOpts.joinLineId + '"> - </span>',

'<input type="text" class="ta_ipt_text_s" name="' + this.endCompareDateId + '" id="' + this.endCompareDateId + '" value="' + this.mOpts.endCompareDate + '" readonly />',

'</div>',

'</div>',

'<div class="frm_btn">',

'<input class="ta_btn ta_btn_primary" type="button" name="' + this.submitBtn + '" id="' + this.submitBtn + '" value="确定" />',

'<input class="ta_btn" type="button" id="' + this.closeBtn + '" value="取消"/>',

'</div>',

'</div>',

'</div>'

]

};

 

 

//对比日期框体的html串

var checkBoxWrapper = {

gri:[

'<label class="gri_contrast" for ="' + this.compareCheckboxId + '">',

            '<input type="checkbox" class="gri_pc" name="' + this.compareCheckboxId + '" id="' + this.compareCheckboxId + '" value="1"/>对比',

        '</label>',

'<input type="text" name="'+this.inputCompareId+'" id="'+this.inputCompareId+'" value="" class="gri_date"/>'

],

ta:[

'<label class="contrast" for ="' + this.compareCheckboxId + '">',

            '<input type="checkbox" class="pc" name="' + this.compareCheckboxId + '" id="' + this.compareCheckboxId + '" value="1"/>对比',

        '</label>',

'<div class="ta_date" id="'+this.compareInputDiv+'">',

'<span name="dateCompare" id="'+this.inputCompareId+'" class="date_title"></span>',

'<a class="opt_sel" id="'+ this.mOpts.compareTrigger +'" href="#">',

        '<i class="i_orderd"></i>',

        '</a>',

'</div>'

]

};

//把checkbox放到页面的相应位置,放置到inputid后面 added by johnnyzheng

 

if(this.mOpts.theme == 'ta'){

$(checkBoxWrapper[this.mOpts.theme].join('')).insertAfter($('#div_' + this.inputId));

}else{

$(checkBoxWrapper[this.mOpts.theme].join('')).insertAfter($('#' + this.inputId));

}

//根据传入参数决定是否展示日期输入框

if(this.mOpts.noCalendar){

$('#' + this.inputId).css('display', 'none');

$('#' + this.compareCheckboxId).parent().css('display','none');

}

    // 把时间选择框放到页面中

    $(0 < $('#appendParent').length ? '#appendParent' : document.body).append(wrapper[this.mOpts.theme].join(''));

    $('#' + this.calendarId).css('z-index', 9999);

    // 初始化目标地址的元素

    if(1 > $('#' + this.mOpts.startDateId).length) {

        $(''!=this.mOpts.target?'#'+this.mOpts.target:'body').append('<input type="hidden" id="' + this.mOpts.startDateId + '" name="' + this.mOpts.startDateId + '" value="' + this.mOpts.startDate + '" />');

    } else {

        $('#' + this.mOpts.startDateId).val(this.mOpts.startDate);

    }

    if(1 > $('#' + this.mOpts.endDateId).length) {

        $(''!=this.mOpts.target?'#'+this.mOpts.target:'body').append('<input type="hidden" id="' + this.mOpts.endDateId + '" name="' + this.mOpts.endDateId + '" value="' + this.mOpts.endDate + '" />');

    } else {

        $('#' + this.mOpts.endDateId).val(this.mOpts.endDate);

    }

    if(1 > $('#' + this.mOpts.compareCheckboxId).length) {

        $(''!=this.mOpts.target?'#'+this.mOpts.target:'body').append('<input type="checkbox" id="' + this.mOpts.compareCheckboxId + '" name="' + this.mOpts.compareCheckboxId + '" value="0" style="display:none;" />');

    }

    // 如果不需要比较日期,则需要隐藏比较部分的内容

    if(false == this.mOpts.needCompare) {

$('#' + this.compareInputDiv).css('display', 'none');

        $('#' + this.compareCheckBoxDiv).css('display', 'none');

        $('#' + this.dateRangeCompareDiv).css('display', 'none');

        $('#' + this.compareCheckboxId).attr('disabled', true);

        $('#' + this.startCompareDateId).attr('disabled', true);

        $('#' + this.endCompareDateId).attr('disabled', true);

//隐藏对比的checkbox

$('#' + this.compareCheckboxId).parent().css('display','none');

$('#'+ this.mOpts.replaceBtn).length > 0 && $('#'+ this.mOpts.replaceBtn).hide();

    } else {

        if(1 > $('#' + this.mOpts.startCompareDateId).length) {

            $(''!=this.mOpts.target?'#'+this.mOpts.target:'body').append('<input type="hidden" id="' + this.mOpts.startCompareDateId + '" name="' + this.mOpts.startCompareDateId + '" value="' + this.mOpts.startCompareDate + '" />');

        } else {

            $('#' + this.mOpts.startCompareDateId).val(this.mOpts.startCompareDate);

        }

        if(1 > $('#' + this.mOpts.endCompareDateId).length) {

            $(''!=this.mOpts.target?'#'+this.mOpts.target:'body').append('<input type="hidden" id="' + this.mOpts.endCompareDateId + '" name="' + this.mOpts.endCompareDateId + '" value="' + this.mOpts.endCompareDate + '" />');

        } else {

            $('#' + this.mOpts.endCompareDateId).val(this.mOpts.endCompareDate);

        }

        if('' == this.mOpts.startCompareDate || '' == this.mOpts.endCompareDate) {

            $('#' + this.compareCheckboxId).attr('checked', false);

            $('#' + this.mOpts.compareCheckboxId).attr('checked', false);

        } else {

            $('#' + this.compareCheckboxId).attr('checked', true);

            $('#' + this.mOpts.compareCheckboxId).attr('checked', true);

        }

 

    }

    // 输入框焦点定在第一个输入框

    this.dateInput = this.startDateId;

    // 为新的输入框加背景色

    this.changeInput(this.dateInput);

 

    // 开始时间 input 的 click 事件

    $('#' + this.startDateId).bind('click', function() {

        // 如果用户在选择基准结束时间时,换到对比时间了,则

        if(__method.endCompareDateId == __method.dateInput) {

            $('#' + __method.startCompareDateId).val(__method.startDefDate);

        }

        __method.startDefDate = '';

        __method.removeCSS(1);

        //__method.addCSS(1);

        __method.changeInput(__method.startDateId);

        return false;

    });

    $('#' + this.calendarId).bind('click', function(event) {

        //event.preventDefault();

        // 防止冒泡

        event.stopPropagation();

    });

    // 开始比较时间 input 的 click 事件

    $('#' + this.startCompareDateId).bind('click', function() {

        // 如果用户在选择基准结束时间时,换到对比时间了,则

        if(__method.endDateId == __method.dateInput) {

            $('#' + __method.startDateId).val(__method.startDefDate);

        }

        __method.startDefDate = '';

        __method.removeCSS(0);

        //__method.addCSS(0);

        __method.changeInput(__method.startCompareDateId);

        return false;

    });

    /**

     * 设置回调句柄,点击成功后,返回一个时间对象,包含开始结束时间

     * 和对比开始结束时间

     */

    var dateall = [];

        $('#' + this.submitBtn).bind('click', function() {

            __method.close(1);

            __method.mOpts.success({'startDate': $('#' + __method.mOpts.startDateId).val(), 

                                    'endDate': $('#' + __method.mOpts.endDateId).val(), 

                                    'needCompare' : $('#' + __method.mOpts.compareCheckboxId).val(),

                                    'startCompareDate':$('#' + __method.mOpts.startCompareDateId).val(), 

                                    'endCompareDate':$('#' + __method.mOpts.endCompareDateId).val()

                                    });

             // __method.close();

             $('.date_title').css({

                'background':'#9DC970',

                'color':'#fff'

             })

             $('.inspect_time li').removeClass('retrieval_color');

             var selectdate = $('.date_title').text();//所选日期 

             var selectdate1 = selectdate.replace('至',',');

             timer1 = selectdate1.substr(0, 10);//重新赋值

             timer2 = selectdate1.substr(11);//重新赋值 

             $.ajax({

                 type:"POST",

                 url:_ajaxUrl,

                 datatype:"json",

             data:{

               name: $("input[name='name']").val(),

               IMGCARD: $("input[name='imgcard']").val(),

               CHECKCARD: $("input[name='CHECKCARD']").val(),

               PHECARD: $("input[name='PHECARD']").val(),

               RADIOLOGDIAGNOSIS: $("input[name='RADIOLOGDIAGNOSIS']").val(),

               AUDITDOC: $("input[name='AUDITDOC']").val(),

               checkitem: $("input[name='checkitem']").val(),

strDate:timer1,

endDate:timer2,

stutype:type1,

DISYY:type2,

DIASTA:state

             },

                 success:function(data){

                 data = JSON.parse(data);

                 totalPage = Math.ceil(data.rows/5)//总页数

                 initPageLinks();

                 var sHtml = '';

                    if(data.code == 0){

                        arry = data.person;

                        for(var i = 0;i<arry.length;i++){    

                            //隔行变色

                            var trColor;

                            if (i % 2 == 0) {trColor = "even";}else {trColor = "odd";} 

                            //性别

                            var msex = '';

                            if (arry[i].sex == 2) { msex = '女';}else{msex = '男';}

                            //医疗状态

                            var mdiasta = '';if (arry[i].diasta == 1) {mdiasta = '待诊断';}else if(arry[i].diasta == 2){mdiasta = '诊断中';}else{mdiasta = '已诊断';}

                            //序号

                            var xuhao = pageNum*(currentPage-1)+i+1;

                            sHtml += "<tr class='" + trColor + "'>";

                            sHtml += "<td>"+xuhao+"</td>";

                            sHtml += "<td class=\"td_juid\" style=\"display:none\">"+arry[i].guid+"</td>";

                            sHtml += "<td>"+arry[i].filmdate+"</td>"

                            sHtml += "<td>"+arry[i].name+"</td>"

                            sHtml += "<td class=\"sextype\">"+msex+"</td>"

                            sHtml += "<td>"+arry[i].age+"</td>"

                            sHtml += "<td>"+mdiasta+"</td>"

                            sHtml += "<td>"+arry[i].phecard+"</td>"

                            sHtml += "<td>"+arry[i].imgcard+"</td>"   

                            sHtml += "<td>"+arry[i].checkcard+"</td>"

                            sHtml += "<td>"+arry[i].stutype+"</td>"

                            sHtml += "<td>"+arry[i].ckparts+"</td>"

                            sHtml += "<td><i>"+arry[i].checkitem+"</i></td>"

                            sHtml += "<td>"+arry[i].reportdate+"</td>"

                            sHtml += "<td>"+arry[i].auditdoc+"</td>"

                            sHtml += "<td>"+arry[i].ckdate+"</td>"

                            sHtml += "<td>"+arry[i].disyy+"</td>"

                            sHtml += "<td class=\"_operation\"><b title=\"影像报告\" class=\"img_presentation\"><img src=\"img/img.png\"></b><b></b><b></b><b></b></td>"   

                            sHtml += "</tr>";

                            $("#bbsTab").html(sHtml);                  

                        }

 

                    }else if(data.code == 1){

                        sHtml += "<tr>"

                        sHtml += "<td colspan=\"17\">没有可显示的数据!</td>"

                        sHtml += "</tr>"

                        $("#bbsTab").html(sHtml);

                    }else{

alert('查询出错!');

                    }

                 },

                 error:function(jqXHR){

                     alert('发生错误:'+jqXHR.status)

                 }

             })

            return false;

        });

 

    // 日期选择关闭按钮的 click 事件

    $('#' + this.closeBtn).bind('click', function() {

        __method.close();

        return false;

    });

    // 为输入框添加click事件

    $('#' + this.inputId).bind('click', function() {

        __method.init();

        __method.show(false, __method);

        return false;

    });

$('#' + this.mOpts.inputTrigger).bind('click', function() {

        __method.init();

        __method.show(false, __method);

        return false;

    });

$('#' + this.mOpts.compareTrigger).bind('click', function() {

        __method.init(true);

        __method.show(true, __method);

        return false;

    });

  // 为输入框添加click事件

    $('#' + this.inputCompareId).bind('click', function() {

        __method.init(true);

        __method.show(true, __method);

        return false;

    });

 

//判断是否是实时数据,如果是将时间默认填充进去 added by johnnyzheng 12-06

if(this.mOpts.singleCompare){

if(this.mOpts.theme === 'ta'){

$('#' + __method.startDateId).val(__method.mOpts.startDate);

$('#' + __method.endDateId).val(__method.mOpts.startDate);

$('#' + __method.startCompareDateId).val(__method.mOpts.startCompareDate);

$('#' + __method.endCompareDateId).val(__method.mOpts.startCompareDate);

}

else{

$('#' + __method.startDateId).val(__method.mOpts.startDate);

$('#' + __method.endDateId).val(__method.mOpts.startDate);

$('#' + __method.startCompareDateId).val(__method.mOpts.startCompareDate);

$('#' + __method.endCompareDateId).val(__method.mOpts.startCompareDate);

$('#' + this.compareCheckboxId).attr('checked',true);

$('#' + this.mOpts.compareCheckboxId).attr('checked',true);

}

 

 

}

    // 时间对比

    $('#' + this.dateRangeCompareDiv).css('display', $('#' + this.compareCheckboxId).attr('checked') ? '' : 'none');

$('#' + this.compareInputDiv).css('display', $('#' + this.compareCheckboxId).attr('checked') ? '' : 'none');

    $('#' + this.compareCheckboxId).bind('click', function() {

$('#' + __method.inputCompareId).css('display', this.checked ? '' : 'none');

        // 隐藏对比时间选择

        $('#' + __method.dateRangeCompareDiv).css('display', this.checked ? '' : 'none');

$('#' + __method.compareInputDiv).css('display', this.checked ? '' : 'none');

        // 把两个对比时间框置为不可用

        $('#' + __method.startCompareDateId).css('disabled', this.checked ? false : true);

        $('#' + __method.endCompareDateId).css('disabled', this.checked ? false : true);

        // 修改表单的 checkbox 状态

        $('#' + __method.mOpts.compareCheckboxId).attr('checked', $('#' + __method.compareCheckboxId).attr('checked'));

        // 修改表单的值

        $('#' + __method.mOpts.compareCheckboxId).val($('#' + __method.compareCheckboxId).attr('checked')?1:0);

        // 初始化选框背景

        if($('#' + __method.compareCheckboxId).attr('checked')) {

            sDate = __method.str2date($('#' + __method.startDateId).val());

            sTime = sDate.getTime();

            eDate = __method.str2date($('#' + __method.endDateId).val());

eTime = eDate.getTime();

            scDate = $('#' + __method.startCompareDateId).val();

            ecDate = $('#' + __method.endCompareDateId).val();

            if('' == scDate || '' == ecDate) {

                ecDate = __method.str2date(__method.date2ymd(sDate).join('-'));

                ecDate.setDate(ecDate.getDate() - 1);

scDate = __method.str2date(__method.date2ymd(sDate).join('-'));

                scDate.setDate(scDate.getDate() - ((eTime - sTime) / 86400000) - 1);

//这里要和STATS_START_TIME的时间进行对比,如果默认填充的对比时间在这个时间之前 added by johnnyzheng

if(ecDate.getTime() < __method.mOpts.minValidDate * 1000){

scDate = sDate;

ecDate = eDate;

}

if(ecDate.getTime() >= __method.mOpts.minValidDate * 1000 && scDate.getTime() < __method.mOpts.minValidDate * 1000){

scDate.setTime(__method.mOpts.minValidDate * 1000)

scDate = __method.str2date(__method.date2ymd(scDate).join('-'));

ecDate.setDate(scDate.getDate() + ((eTime - sTime) / 86400000) - 1);

}

                $('#' + __method.startCompareDateId).val(__method.formatDate(__method.date2ymd(scDate).join('-')));

                $('#' + __method.endCompareDateId).val(__method.formatDate(__method.date2ymd(ecDate).join('-')));

            }

            __method.addCSS(1);

            // 输入框焦点切换到比较开始时间

            __method.changeInput(__method.startCompareDateId);

 

        } else {

            __method.removeCSS(1);

            // 输入框焦点切换到开始时间

            __method.changeInput(__method.startDateId);

        }

//用户点击默认自动提交 added by johnnyzheng 12-08

__method.close(1);

__method.mOpts.success({'startDate': $('#' + __method.mOpts.startDateId).val(), 

'endDate': $('#' + __method.mOpts.endDateId).val(), 

'needCompare' : $('#' + __method.mOpts.compareCheckboxId).val(),

'startCompareDate':$('#' + __method.mOpts.startCompareDateId).val(), 

'endCompareDate':$('#' + __method.mOpts.endCompareDateId).val()

});

    });

 

    // 初始化开始

    this.init();

    // 关闭日期选择框,并把结果反显到输入框

    this.close(1);

if(this.mOpts.replaceBtn && $('#'+this.mOpts.replaceBtn).length > 0){

$('#'+ __method.compareCheckboxId).hide();

$('.contrast').hide();

$('#'+this.mOpts.replaceBtn).bind('click', function(){

var self = this;

$('#'+ __method.compareCheckboxId).attr('checked')

? $('#'+ __method.compareCheckboxId).removeAttr('checked')

: $('#'+ __method.compareCheckboxId).attr('checked', 'checked');

$('#'+ __method.compareCheckboxId).click();

$('#'+ __method.compareCheckboxId).attr('checked')

? function(){

$('#'+ __method.compareCheckboxId).removeAttr('checked');

$('.contrast').hide();

$(self).text('按时间对比');

}()

: function(){

$('#'+ __method.compareCheckboxId).attr('checked', 'checked');

$('.contrast').show();

$(self).text('取消对比');

}();

});

}

 

if(this.mOpts.autoCommit){

this.mOpts.success({'startDate': $('#' + __method.mOpts.startDateId).val(), 

'endDate': $('#' + __method.mOpts.endDateId).val(),

'needCompare' : $('#' + __method.mOpts.compareCheckboxId).val(), 

'startCompareDate':$('#' + __method.mOpts.startCompareDateId).val(), 

'endCompareDate':$('#' + __method.mOpts.endCompareDateId).val()

});

}

    //让用户点击页面即可关闭弹窗

    $(document).bind('click', function () {

       __method.close();

    });

};

 

/**

 * @description 日期选择器的初始化方法,对象原型扩展

 * @param {Boolean} isCompare 标识当前初始化选择面板是否是对比日期

 */

pickerDateRange.prototype.init = function(isCompare) {

    var __method = this;

    var minDate, maxDate;

var isNeedCompare = typeof(isCompare) != 'undefined'? isCompare && $("#" + __method.compareCheckboxId).attr('checked') : $("#" + __method.compareCheckboxId).attr('checked');

    // 清空日期列表的内容

    $("#" + this.dateListId).empty();

 

    // 如果开始日期为空,则取当天的日期为开始日期

    var endDate = '' == this.mOpts.endDate ? (new Date()) : this.str2date(this.mOpts.endDate);

    // 日历结束时间

    this.calendar_endDate = new Date(endDate.getFullYear(), endDate.getMonth() + 1, 0);

 

//如果是magicSelect 自定义年和月份,则自定义填充日期

if(this.mOpts.magicSelect && this.mOpts.theme == 'ta'){

var i = 0;

do{

var td = null;

if(i==0){

td = this.fillDate(this.str2date($('#'+this.endDateId).val()).getFullYear(), this.str2date($('#'+this.endDateId).val()).getMonth(), i);

$("#" + this.dateListId).append(td);

}

else{

td = this.fillDate(this.str2date($('#'+this.startDateId).val()).getFullYear(), this.str2date($('#'+this.startDateId).val()).getMonth(), i);

var firstTd = (this.mOpts.theme == 'ta' ? $("#" + this.dateListId).find('table').get(0) : $("#" + this.dateListId).find('td').get(0));

$(firstTd).before(td);

}

i++;

}while(i<2);

// 日历开始时间

this.calendar_startDate = new Date(this.str2date($('#'+this.startDateId).val()).getFullYear(), this.str2date($('#'+this.startDateId).val()).getMonth(), 1);

 

}else{

// 计算并显示以 endDate 为结尾的最近几个月的日期列表

for(var i = 0; i < this.mOpts.calendars; i ++) {

var td = null;

if(this.mOpts.theme == 'ta'){

td = this.fillDate(endDate.getFullYear(), endDate.getMonth(), i);

}

else{

td = document.createElement('td');

$(td).append(this.fillDate(endDate.getFullYear(), endDate.getMonth(), i));

$(td).css('vertical-align', 'top');

}

if(0 == i) {

$("#" + this.dateListId).append(td);

} else {

var firstTd = (this.mOpts.theme == 'ta' ? $("#" + this.dateListId).find('table').get(0) : $("#" + this.dateListId).find('td').get(0));

$(firstTd).before(td);

}

endDate.setMonth(endDate.getMonth() - 1, 1);

}

// 日历开始时间

this.calendar_startDate = new Date(endDate.getFullYear(), endDate.getMonth() + 1, 1);

}

 

    // 上一个月

    $('#' + this.preMonth).bind('click', function() {

        __method.calendar_endDate.setMonth(__method.calendar_endDate.getMonth() - 1, 1);

        __method.mOpts.endDate = __method.date2ymd(__method.calendar_endDate).join('-');

        __method.init(isCompare);

//如果是单月选择的时候,要控制input输入框 added by johnnyzheng 2011-12-19

if(1 == __method.mOpts.calendars){

if('' == $('#' + __method.startDateId).val()){

__method.changeInput(__method.startDateId);

}

else{

__method.changeInput(__method.endDateId);

}

}

        return false;

    });

    // 下一个月

    $('#' + this.nextMonth).bind('click', function() {

        __method.calendar_endDate.setMonth(__method.calendar_endDate.getMonth() + 1, 1);

        __method.mOpts.endDate = __method.date2ymd(__method.calendar_endDate).join('-');

__method.init(isCompare);

//如果是单月选择的时候,要控制input输入框 added by johnnyzheng 2011-12-19

if(1 == __method.mOpts.calendars){

if('' == $('#' + __method.startDateId).val()){

__method.changeInput(__method.startDateId);

}

else{

__method.changeInput(__method.endDateId);

}

}

        return false;

    });

 

//如果有用户自定义选择月份,则为其绑定事件

if(this.mOpts.magicSelect) this.bindChangeForSelect();

 

 

    // 初始化时间选区背景

    if(this.endDateId != this.dateInput && this.endCompareDateId != this.dateInput) {

         (isNeedCompare && typeof(isCompare) !='undefined') ? this.addCSS(1) : this.addCSS(0);

    }

 

if(isNeedCompare && typeof(isCompare) !='undefined'){

__method.addCSS(1);

}

else{

__method.addCSS(0);

 

}

 

// 隐藏对比日期框

$('#' + __method.inputCompareId).css('display', isNeedCompare ? '' : 'none');

$('#' + this.compareInputDiv).css('display', $('#' + this.compareCheckboxId).attr('checked') ? '' : 'none');

//昨天,今天,最近7天,最近30天快捷的点击,样式要自己定义,id可以传递默认,也可覆盖

for(var property in __method.periodObj){

if($('#'+ property).length > 0){

$('#' +  property).unbind('click');

$('#' +  property).bind('click' , function(){

//处理点击样式

var cla = __method.mOpts.theme == 'ta' ? 'active' : 'a';

$(this).parent().nextAll().removeClass(cla);

$(this).parent().prevAll().removeClass(cla);

$(this).parent().addClass(cla);

//拼接提交时间串

var timeObj = __method.getSpecialPeriod(__method.periodObj[$(this).attr('id')]);

$('#' + __method.startDateId).val(__method.formatDate(timeObj.otherday));

$('#' + __method.endDateId).val(__method.formatDate(timeObj.today));

$('#' + __method.mOpts.startDateId).val($('#' + __method.startDateId).val());

$('#' + __method.mOpts.endDateId).val($('#' + __method.endDateId).val());

__method.mOpts.theme == 'ta' ? $('#'+__method.compareInputDiv).hide() : $('#' + __method.inputCompareId).css('display','none');

$('#' + __method.compareCheckboxId).attr('checked', false);

$('#' + __method.mOpts.compareCheckboxId).attr('checked', false);

$('#' + this.compareInputDiv).css('display', $('#' + this.compareCheckboxId).attr('checked') ? '' : 'none');

                __method.close(1);

//于此同时清空对比时间框的时间

$('#' + __method.startCompareDateId).val('');

$('#' + __method.endCompareDateId).val('');

$('#' + __method.mOpts.startCompareDateId).val('');

$('#' + __method.mOpts.endCompareDateId).val('');

$('#' + __method.mOpts.compareCheckboxId).val(0);

 

if($('#'+ __method.mOpts.replaceBtn).length > 0){

$('.contrast').hide();

$('#'+ __method.mOpts.replaceBtn).text('按时间对比');

}

//点击提交

__method.mOpts.success({'startDate': $('#' + __method.mOpts.startDateId).val(), 

'endDate': $('#' + __method.mOpts.endDateId).val(), 

'needCompare' : $('#' + __method.mOpts.compareCheckboxId).val(),

'startCompareDate':$('#' + __method.mOpts.startCompareDateId).val(), 

'endCompareDate':$('#' + __method.mOpts.endCompareDateId).val()

});

}); 

}

}

 

    // 让用户手动关闭或提交日历,每次初始化的时候绑定,关闭的时候解绑 by zacharycai

    $(document).bind('click', function () {

        __method.close();

    });

 

    //完全清空日期控件的值 by zacharycai

    $('#' + this.inputId).bind('change', function(){

        if ($(this).val() === ''){

            $('#' + __method.startDateId).val('');

            $('#' + __method.endDateId).val('');

            $('#' + __method.startCompareDateId).val('');

            $('#' + __method.endCompareDateId).val('');

        }

    })

};

 

pickerDateRange.prototype.bindChangeForSelect = function(){

var __method = this;

//气泡弹窗

var _popup = function(btn, ctn, wrap, css) {

css = css || 'open';

var ITEMS_TIMEOUT = null, time_out = 500;

 

function hidePop() {

$('#' + ctn).removeClass(css);

}

 

function showPop() {

$('#' + ctn).addClass(css);

}

 

function isPopShow() {

return $('#' + ctn).attr('class') == css;

}

 

 

$("#" + btn).click(function() {

isPopShow() ? hidePop() : showPop();

}).mouseover(function() {

clearTimeout(ITEMS_TIMEOUT);

}).mouseout(function() {

ITEMS_TIMEOUT = setTimeout(hidePop, time_out);

});

 

$('#' + wrap).mouseover(function() {

clearTimeout(ITEMS_TIMEOUT);

}).mouseout(function() {

ITEMS_TIMEOUT = setTimeout(hidePop, time_out);

});

};

 

//自定义选择的触发动作

try{

$("#" + this.dateListId).find('div[id*="selected"]').each(function(){

//绑定pop

var _match = $(this).attr('id').match(/(\w+)_(\d)/i);

if(_match){

var _name = _match[1];//名称

var _idx = _match[2];//下标

 

if(_name=='yselected'){

_popup('_ybtn_'+_idx, $(this).attr('id'), '_yctn_'+_idx);

}

else if(_name=='mselected'){

_popup('_mbtn_'+_idx, $(this).attr('id'), '_mctn_'+_idx);

}

 

$(this).find('li a').each(function(){

$(this).click(function() {

var match = $(this).parents('.select_wrap').attr('id').match(/(\w+)_(\d)/i);

//if(match){

var name = match[1];//名称

var idx = match[2];//下标

var nt = null;

if(idx^1 == 0){

//开始

if(name == 'yselected'){

__method.calendar_startDate.setYear($(this).text()*1 , 1);

//__method.calendar_startDate.setMonth(__method.str2date($('#'+__method.startDateId).val()).getMonth(), 1);

}

else if(name='mselected'){

//__method.calendar_startDate.setYear(__method.str2date($('#'+__method.startDateId).val()).getFullYear(), 1);

__method.calendar_startDate.setMonth($(this).text()*1-1, 1);

}

__method.mOpts.startDate = __method.date2ymd(__method.calendar_startDate).join('-');

nt = __method.fillDate(__method.calendar_startDate.getFullYear(), __method.calendar_startDate.getMonth(), idx);

}

else{

//结束

if(name == 'yselected'){

__method.calendar_endDate.setYear($(this).text()*1 , 1);

//__method.calendar_endDate.setMonth(__method.str2date($('#'+__method.endDateId).val()).getMonth(), 1);

}

else if(name='mselected'){

//__method.calendar_endDate.setYear(__method.str2date($('#'+__method.endDateId).val()).getFullYear(), 1);

__method.calendar_endDate.setMonth($(this).text()*1-1, 1);

}

__method.mOpts.endDate = __method.date2ymd(__method.calendar_endDate).join('-');

nt = __method.fillDate(__method.calendar_endDate.getFullYear(), __method.calendar_endDate.getMonth(), idx);

}

var tb = $("#" + __method.dateListId).find('table').get(idx^1);

$(tb).replaceWith(nt);

//}

__method.removeCSS(0);

__method.bindChangeForSelect();

});

});

}

});

}catch(e){

window.console && console.log(e);

}

}

/**

 * @description 计算今天,昨天,最近7天,最近30天返回的时间范围

 * @param {Num} period 快捷选择的时间段,今天、昨天、最近7天、最近30天

 */

pickerDateRange.prototype.getSpecialPeriod = function(period){

var __method = this;

var date = new Date();

//如果今天不可用,则从昨天向前推 added by johnnyzheng 12-07

(true == __method.mOpts.isTodayValid && ('' != __method.mOpts.isTodayValid) || 2 > period)? '' : date.setTime(date.getTime() - ( 1 * 24 * 60 * 60 * 1000));

var timeStamp = ((date.getTime()- ( period * 24 * 60 * 60 * 1000)) < (__method.mOpts.minValidDate * 1000)) ? (__method.mOpts.minValidDate * 1000) : (date.getTime()- ( period * 24 * 60 * 60 * 1000)) ;

var todayStr = date.getFullYear() + '-' + (date.getMonth()+ 1 ) + '-' + date.getDate();

date.setTime(timeStamp);

var otherdayStr = date.getFullYear() + '-' + (date.getMonth()+ 1 ) + '-' + date.getDate();

if(period == __method.periodObj.aYesterday){

todayStr = otherdayStr;

}

return {today: todayStr , otherday : otherdayStr};

}

 

pickerDateRange.prototype.getCurrentDate = function(){

    return {

            'startDate': $('#' + this.mOpts.startDateId).val(), 

            'endDate': $('#' + this.mOpts.endDateId).val(), 

            'needCompare' : $('#' + this.mOpts.compareCheckboxId).val(),

            'startCompareDate':$('#' + this.mOpts.startCompareDateId).val(), 

            'endCompareDate':$('#' + this.mOpts.endCompareDateId).val()

            };

};

 

/**

 * @description 移除选择日期面板的样式

 * @param {Boolean} isCompare 是否是对比日期面板

 * @param {String} specialClass 特殊的样式,这里默认是常规和对比日期两种样式的重合样式

 */

pickerDateRange.prototype.removeCSS = function(isCompare, specialClass) {

    // 初始化对比时间重合部分的样式类

    if('undefined' == typeof(specialClass)) {

        specialClass = this.mOpts.theme + '_' + this.mOpts.coincideCss;

    }

    // 是否移除对比部分的样式:0 日期选择;1 对比日期选择

    if('undefined' == typeof(isCompare)) {

        isCompare = 0;

    }

 

    // 整个日期列表的开始日期

var s_date = this.calendar_startDate;

var e_date = this.calendar_endDate;

//如果是用户自定义选择的话,需要充值样式边界日期

if(this.mOpts.magicSelect){

s_date = this.str2date($('#'+this.startDateId).val());

e_date = this.str2date($('#'+this.endDateId).val());

}

    var bDate = new Date(s_date.getFullYear(), s_date.getMonth(), s_date.getDate());

    var cla = '';

    // 从开始日期循环到结束日期

    for(var d = new Date(bDate); d.getTime() <= e_date.getTime(); d.setDate(d.getDate() + 1)) {

            if(0 == isCompare) {

                // 移除日期样式

                cla = this.mOpts.theme + '_' + this.mOpts.selectCss;

            } else {

                // 移除对比日期样式

                cla = this.mOpts.theme + '_' + this.mOpts.compareCss;

            }

        // 移除指定样式

        $('#'+ this.calendarId + '_'  + this.date2ymd(d).join('-')).removeClass(cla);

$('#'+ this.calendarId + '_'  + this.date2ymd(d).join('-')).removeClass(this.mOpts.firstCss).removeClass(this.mOpts.lastCss).removeClass(this.mOpts.clickCss);

    }

};

 

/**

 * @description 为选中的日期加上样式:1=比较时间;0=时间范围

 * @param {Boolean} isCompare 是否是对比日期面板

 * @param {String} specialClass 特殊的样式,这里默认是常规和对比日期两种样式的重合样式

 */

pickerDateRange.prototype.addCSS = function(isCompare, specialClass) {

 

    // 初始化对比时间重合部分的样式类

    if('undefined' == typeof(specialClass)) {

        specialClass = this.mOpts.theme + '_' + this.mOpts.coincideCss;

    }

    // 是否移除对比部分的样式:0 日期选择;1 对比日期选择

    if('undefined' == typeof(isCompare)) {

        isCompare = 0;

    }

    // 获取4个日期

    var startDate = this.str2date($('#' + this.startDateId).val());

    var endDate = this.str2date($('#' + this.endDateId).val());

    var startCompareDate = this.str2date($('#' + this.startCompareDateId).val());

    var endCompareDate = this.str2date($('#' + this.endCompareDateId).val());

 

    // 循环开始日期

    var sDate = 0 == isCompare ? startDate : startCompareDate;

    // 循环结束日期

    var eDate = 0 == isCompare ? endDate : endCompareDate;

    var cla = '';

    for(var d = new Date(sDate); d.getTime() <= eDate.getTime(); d.setDate(d.getDate() + 1)) {

            if(0 == isCompare) {

                // 添加日期样式

                cla = this.mOpts.theme + '_' + this.mOpts.selectCss;

$('#' + this.calendarId + '_' + this.date2ymd(d).join('-')).removeClass(this.mOpts.firstCss).removeClass(this.mOpts.lastCss).removeClass(this.mOpts.clickCss);

$('#' + this.calendarId + '_' + this.date2ymd(d).join('-')).removeClass(cla);

            } else {

                // 添加对比日期样式

                cla = this.mOpts.theme + '_' + this.mOpts.compareCss;

            }

 

        $('#' + this.calendarId + '_' + this.date2ymd(d).join('-')).attr('class', cla);

    }

if(this.mOpts.theme == 'ta'){

//为开始结束添加特殊样式

$('#' + this.calendarId + '_' + this.date2ymd(new Date(sDate)).join('-')).removeClass().addClass(this.mOpts.firstCss);

$('#' + this.calendarId + '_' + this.date2ymd(new Date(eDate)).join('-')).removeClass().addClass(this.mOpts.lastCss);

//如果开始结束时间相同

sDate.getTime() == eDate.getTime() && $('#'+ this.calendarId + '_' + this.date2ymd(new Date(eDate)).join('-')).removeClass().addClass(this.mOpts.clickCss);

}

};

 

/**

 * @description 判断开始、结束日期是否处在允许的范围内

 * @param {String} startYmd 开始时间字符串

 * @param {String} endYmd 结束时间字符串

 */

pickerDateRange.prototype.checkDateRange = function(startYmd, endYmd) {

    var sDate = this.str2date(startYmd);

    var eDate = this.str2date(endYmd);

    var sTime = sDate.getTime();

    var eTime = eDate.getTime();

    var minEDate, maxEDate;

 

    if(eTime >= sTime) {

        // 判断是否超过最大日期外

        maxEDate = this.str2date(startYmd);

        maxEDate.setMonth(maxEDate.getMonth() + this.mOpts.monthRangeMax);

        maxEDate.setDate(maxEDate.getDate() + this.mOpts.dayRangeMax - 1);

        if(maxEDate.getTime() < eTime) {

            alert('结束日期不能大于:' + this.date2ymd(maxEDate).join('-'));

            return false;

        }

    } else {

        // 判断是否超过最大日期外

        //maxEDate = this.str2date(stPartYmd);

maxEDate = this.str2date(endYmd);

        maxEDate.setMonth(maxEDate.getMonth() - this.mOpts.monthRangeMax);

        maxEDate.setDate(maxEDate.getDate() - this.mOpts.dayRangeMax + 1);

        if(maxEDate.getTime() > eTime) {

            alert('开始日期不能小于:' + this.date2ymd(maxEDate).join('-'));

            return false;

        }

    }

    return true;

}

 

/**

 *  @description 选择日期

 *  @param {String} ymd 时间字符串

 */

pickerDateRange.prototype.selectDate = function(ymd) {

    //点击日期点的时候添加对应输入框的样式,而不是之前的 聚焦到输入框时显示样式 by zacharycai

    this.changeInput(this.dateInput);

    // 格式化日期

    var ymdFormat = this.formatDate(ymd);

 

    // start <-> end 切换

    if(this.startDateId == this.dateInput) {

        // 移除样式

        this.removeCSS(0);

this.removeCSS(1);

        // 为当前点加样式

        $('#'+ this.calendarId + '_'  + ymd).attr('class', (this.mOpts.theme == 'ta' ? this.mOpts.clickCss  : this.mOpts.theme + '_' + this.mOpts.selectCss));

// 获取开始时间的初始值

this.startDefDate = $('#' + this.dateInput).val();

// 更改对应输入框的值

$('#' + this.dateInput).val(ymdFormat);

        // 切换输入框焦点,如果是实时数据那么选择一天的数据

        if (true == this.mOpts.singleCompare || true == this.mOpts.isSingleDay) {

            this.dateInput = this.startDateId;

$('#' + this.endDateId).val(ymdFormat);

(this.mOpts.shortOpr || this.mOpts.autoSubmit) && this.close(1);

            this.mOpts.success({'startDate': $('#' + this.mOpts.startDateId).val(),

                'endDate': $('#' + this.mOpts.endDateId).val(),

                'needCompare' : $('#' + this.mOpts.compareCheckboxId).val(),

                'startCompareDate':$('#' + this.mOpts.startCompareDateId).val(),

                'endCompareDate':$('#' + this.mOpts.endCompareDateId).val()

            });

 

        } else {

            this.dateInput = this.endDateId;

        }

 

    } else if(this.endDateId == this.dateInput) {

        // 如果开始时间未选

        if('' == $('#' + this.startDateId).val()) {

            this.dateInput = this.startDateId;

            this.selectDate(ymd);

            return false;

        }

        // 判断用户选择的时间范围

        if(false == this.checkDateRange($('#' + this.startDateId).val(), ymd)) {

            return false;

        }

        // 如果结束时间小于开始时间

        if(-1 == this.compareStrDate(ymd, $('#' + this.startDateId).val())) {

            // 更改对应输入框的值(结束时间)

            $('#' + this.dateInput).val($('#' + this.startDateId).val());

            // 更改对应输入框的值(开始时间)

            $('#' + this.startDateId).val(ymdFormat);

            ymdFormat = $('#' + this.dateInput).val();

        }

        // 更改对应输入框的值

        $('#' + this.dateInput).val(ymdFormat);

        // 切换输入框焦点

        this.dateInput = this.startDateId;

this.removeCSS(0);

        this.addCSS(0);

//this.addCSS(0, this.mOpts.coincideCss);

        this.startDefDate = '';

if(this.mOpts.autoSubmit){

this.close(1);

            this.mOpts.success({'startDate': $('#' + this.mOpts.startDateId).val(),

                'endDate': $('#' + this.mOpts.endDateId).val(),

                'needCompare' : $('#' + this.mOpts.compareCheckboxId).val(),

                'startCompareDate':$('#' + this.mOpts.startCompareDateId).val(),

                'endCompareDate':$('#' + this.mOpts.endCompareDateId).val()

            });

}

    } else if(this.startCompareDateId == this.dateInput) {

        // 移除样式

        this.removeCSS(1);

this.removeCSS(0);

        // 为当前点加样式

$('#'+ this.calendarId + '_'  + ymd).attr('class', (this.mOpts.theme == 'ta' ? this.mOpts.clickCss  : this.mOpts.theme + '_' + this.mOpts.compareCss));

        // 获取开始时间的初始值

        this.startDefDate = $('#' + this.dateInput).val();

        // 更改对应输入框的值

        $('#' + this.dateInput).val(ymdFormat);

        // 切换输入框焦点

if (true == this.mOpts.singleCompare || true == this.mOpts.isSingleDay) {

            this.dateInput = this.startCompareDateId;

$('#' + this.endCompareDateId).val(ymdFormat);

(this.mOpts.shortOpr || this.mOpts.autoSubmit) && this.close(1);

            this.mOpts.success({'startDate': $('#' + this.mOpts.startDateId).val(),

                'endDate': $('#' + this.mOpts.endDateId).val(),

                'needCompare' : $('#' + this.mOpts.compareCheckboxId).val(),

                'startCompareDate':$('#' + this.mOpts.startCompareDateId).val(),

                'endCompareDate':$('#' + this.mOpts.endCompareDateId).val()

            });

        }

else{

    this.dateInput = this.endCompareDateId;

}

 

    } else if(this.endCompareDateId == this.dateInput) {

        // 如果开始时间未选

        if('' == $('#' + this.startCompareDateId).val()) {

            this.dateInput = this.startCompareDateId;

            this.selectDate(ymd);

            return false;

        }

        // 判断用户选择的时间范围

        if(false == this.checkDateRange($('#' + this.startCompareDateId).val(), ymd)) {

            return false;

        }

        // 如果结束时间小于开始时间

        if(-1 == this.compareStrDate(ymd, $('#' + this.startCompareDateId).val())) {

            // 更改对应输入框的值(结束时间)

            $('#' + this.dateInput).val($('#' + this.startCompareDateId).val());

            // 更改对应输入框的值(开始时间)

            $('#' + this.startCompareDateId).val(ymdFormat);

            ymdFormat = $('#' + this.dateInput).val();

        }

        // 更改对应输入框的值

        $('#' + this.dateInput).val(ymdFormat);

        // 切换输入框焦点

        this.dateInput = this.startCompareDateId;

        //this.addCSS(1, this.mOpts.coincideCss);

this.removeCSS(1);

this.addCSS(1);

        this.startDefDate = '';

if(this.mOpts.autoSubmit){

this.close(1);

            this.mOpts.success({'startDate': $('#' + this.mOpts.startDateId).val(),

                'endDate': $('#' + this.mOpts.endDateId).val(),

                'needCompare' : $('#' + this.mOpts.compareCheckboxId).val(),

                'startCompareDate':$('#' + this.mOpts.startCompareDateId).val(),

                'endCompareDate':$('#' + this.mOpts.endCompareDateId).val()

            });

}

    }

    // 切换到下一个输入框

//    this.changeInput(this.dateInput);

};

 

/**

 * @description显示日期选择框

 * @param {Boolean} isCompare 是否是对比日期选择框

 * @param {Object} __method 时期选择器超级对象

 */ 

pickerDateRange.prototype.show = function(isCompare, __method) {

$('#' + __method.dateRangeDiv).css('display', isCompare ? 'none' : '');

$('#' + __method.dateRangeCompareDiv).css('display', isCompare ? '' : 'none');

    var pos = isCompare ?  $('#' + this.inputCompareId).offset() : $('#' + this.inputId).offset();

var offsetHeight = isCompare ? $('#' + this.inputCompareId).height() : $('#' + this.inputId).height();

    var clientWidth = parseInt($(document.body)[0].clientWidth);

    var left = pos.left;

    $("#" + this.calendarId).css('display', 'block');

    if (true == this.mOpts.singleCompare || true == this.mOpts.isSingleDay) {

        $('#' + this.endDateId).css('display', 'none');

$('#' + this.endCompareDateId).css('display','none');

        $('#' + this.mOpts.joinLineId).css('display', 'none');

$('.' + this.mOpts.joinLineId).css('display', 'none');

    }

    // 如果和输入框左对齐时超出了宽度范围,则右对齐

    if(0 < clientWidth && $("#" + this.calendarId).width() + pos.left > clientWidth) {

        left = pos.left + $('#' + this.inputId).width() - $("#" + this.calendarId).width() + ((/msie/i.test(navigator.userAgent) && !(/opera/i.test(navigator.userAgent)))? 5 : 0) ;

__method.mOpts.theme=='ta' && (left += 50);

}

    $("#" + this.calendarId).css('left', left  + 'px');

    //$("#" + this.calendarId).css('top', pos.top + (offsetHeight ? offsetHeight- 1 : (__method.mOpts.theme=='ta'?35:22)) + 'px');

$("#" + this.calendarId).css('top', pos.top + (__method.mOpts.theme=='ta'?35:22) + 'px');

//第一次显示的时候,一定要初始化输入框

isCompare ? this.changeInput(this.startCompareDateId) : this.changeInput(this.startDateId);

    return false;

};

 

/**

 * @description 关闭日期选择框

 * @param {Boolean} btnSubmit 是否是点击确定按钮关闭的 

 */

pickerDateRange.prototype.close = function(btnSubmit) {

var __method = this;

    //by zacharycai 关闭后就解绑了

    //$(document).unbind('click');

 

    // 把开始、结束时间显示到输入框 (PS:如果选择的今日,昨日,则只填入一个日期)

    // 如果开始和结束同个时间也照样分段by zacharycai

    //$('#' + this.inputId).val($('#' + this.startDateId).val() + ($('#' + this.startDateId).val() == $('#' + this.endDateId).val() ? '' : this.mOpts.defaultText + $('#' + this.endDateId).val()));

if(btnSubmit){

//如果是单日快捷选择

if (this.mOpts.shortOpr === true){

$('#' + this.inputId).val($('#' + this.startDateId).val());

$('#' + this.inputCompareId).val($('#' + this.startCompareDateId).val());

}else{

$('#' + this.inputId).val($('#' + this.startDateId).val() + ('' == $('#' + this.endDateId).val() ? '' : this.mOpts.defaultText + $('#' + this.endDateId).val()));

}

//判断当前天是否可选,来决定从后往前推修改日期是从哪一点开始

var nDateTime = ((true == this.mOpts.isTodayValid && '' != this.mOpts.isTodayValid)) ? new Date().getTime() : new Date().getTime() - (1 * 24 * 60 * 60 * 1000);

var bDateTime = this.str2date($('#' + this.startDateId).val()).getTime();

var eDateTime = this.str2date($('#' + this.endDateId).val()).getTime();

//如果endDateTime小于bDateTime 相互交换

if(eDateTime < bDateTime){

var tmp = $('#' + this.startDateId).val();

$('#' + this.startDateId).val($('#' + this.endDateId).val());

$('#' + this.endDateId).val(tmp);

}

var _val = this.mOpts.shortOpr == true ? $('#' + this.startDateId).val() : ($('#' + this.startDateId).val() + ('' == $('#' + this.endDateId).val() ? '' : this.mOpts.defaultText + $('#' + this.endDateId).val()));

// 把开始、结束时间显示到输入框 (PS:如果选择的今日,昨日,则只填入一个日期)

var input = document.getElementById(this.inputId);

if(input && input.tagName == 'INPUT'){

$('#' + this.inputId).val(_val);

$('#'+this.inputCompareId).is(':visible') && $('#'+this.inputCompareId).val(_compareVal);

}else{

$('#' + this.inputId).html(_val);

$('#'+this.inputCompareId).is(':visible') && $('#'+this.inputCompareId).html(_compareVal);

}

////在js侧就做好日期校准,以前面的日期选择的跨度为准,如果后面的跨度超过了当前可用时间,则以当前可用时间向前推 added by johnnyzheng 11-29

if(this.mOpts.theme != 'ta'){

if('' !=  $('#' + this.startCompareDateId).val() && '' != $('#' + this.endCompareDateId).val()){

var bcDateTime = this.str2date($('#' + this.startCompareDateId).val()).getTime();

var ecDateTime = this.str2date($('#' + this.endCompareDateId).val()).getTime();

var _ecDateTime = bcDateTime + eDateTime - bDateTime;

if(_ecDateTime > nDateTime){

//如果计算得到的时间超过了当前可用时间,那么就和服务器端保持一致,将当前可用的天数向前推日期选择器的跨度 added by johnnyzheng 11-29

_ecDateTime = nDateTime;

$('#' + this.startCompareDateId).val(this.formatDate(this.date2ymd(new Date(_ecDateTime + bDateTime - eDateTime)).join('-')));

}

$('#' + this.endCompareDateId).val(this.formatDate(this.date2ymd(new Date(_ecDateTime)).join('-')));

 

//把开始结束对比时间大小重新矫正一下

var bcDateTime = this.str2date($('#' + this.startCompareDateId).val()).getTime();

var ecDateTime = this.str2date($('#' + this.endCompareDateId).val()).getTime();

if(ecDateTime < bcDateTime){

var tmp = $('#' + this.startCompareDateId).val();

$('#' + this.startCompareDateId).val($('#' + this.endCompareDateId).val());

$('#' + this.endCompareDateId).val(tmp);

}

}

}

//把对比时间填入输入框 (PS:如果选择今日,昨日,则只填入一个日期)

//$('#' + this.inputCompareId).val($('#' + this.startCompareDateId).val() + this.mOpts.defaultText + $('#' + this.endCompareDateId).val());

var _compareVal = this.mOpts.shortOpr == true ? $('#' + this.startCompareDateId).val() : ($('#' + this.startCompareDateId).val() + ('' == $('#' + this.endCompareDateId).val() ? '' : this.mOpts.defaultText + $('#' + this.endCompareDateId).val()));

if(input && input.tagName == 'INPUT'){

$('#' + this.inputCompareId).val(_compareVal);

}else{

$('#' + this.inputCompareId).html(_compareVal);

}

// 计算相隔天数

var step = (bDateTime - eDateTime) / 86400000;

 

// 更改目标元素值

$('#' + this.mOpts.startDateId).val($('#' + this.startDateId).val());

$('#' + this.mOpts.endDateId).val($('#' + this.endDateId).val());

$('#' + this.mOpts.startCompareDateId).val($('#' + this.startCompareDateId).val());

$('#' + this.mOpts.endCompareDateId).val($('#' + this.endCompareDateId).val());

//点击确定按钮进行查询后将取消所有的今天 昨天 最近7天的快捷链接 added by johnnyzheng 11-29

for(var property in this.periodObj){

if($('#' + this.mOpts[property])){

$('#' + this.mOpts[property]).parent().removeClass('a');

}

}

}

// 隐藏日期选择框 延迟200ms 关闭日期选择框

$("#" + __method.calendarId).css('display', 'none');

    return false;

};

 

/**

 * @description 日期填充函数

 * @param {Num} year 年

 * @param {Num} month 月

 */ 

pickerDateRange.prototype.fillDate = function(year, month, index) {

    var __method = this;

var isTaTheme = this.mOpts.theme == 'ta';

    // 当月第一天

    var firstDayOfMonth = new Date(year, month, 1);

    var dateBegin = new Date(year, month, 1);

    var w = dateBegin.getDay();

    // 计算应该开始的日期

    dateBegin.setDate(1 - w);

 

    // 当月最后一天

    var lastDayOfMonth = new Date(year, month + 1, 0);

    var dateEnd = new Date(year, month + 1, 0);

    w = dateEnd.getDay();

    // 计算应该结束的日期

    dateEnd.setDate(dateEnd.getDate() + 6 - w);

 

    var today = new Date();

    var dToday = today.getDate();

    var mToday = today.getMonth();

    var yToday = today.getFullYear();

 

var table = document.createElement('table');

if(isTaTheme){

table.className = this.mOpts.dateTable;

 

cap = document.createElement('caption');

 

//如果是magicSelect,用户自定义的选择年和月份

if(this.mOpts.magicSelect){

var yh = ['<div class="select_wrap" id="yselected_'+index+'"><div class="select" id="_ybtn_'+index+'">'+year+'</div><div class="dropdown" id="_yctn_'+index+'"><ul class="list_menu">']

var mh = ['<div class="select_wrap" id="mselected_'+index+'"><div class="select" id="_mbtn_'+index+'">'+(month+1)+'</div><div class="dropdown" id="_mctn_'+index+'"><ul class="list_menu">']

 

//var yh = ['<select name="yselected_'+index+'" class="xxxs">'];

//var mh = ['<select name="mselected_'+index+'" class="xxxs">'];

i=1;

yt = yToday;

do{

//yh.push('<option value="'+yt+'" '+(yt == year? 'selected' : '')+'>'+(yt--)+'</option>');

//mh.push('<option value="'+i+'" '+(i == (month+1)? 'selected' : '')+'>'+(i++)+'</option>');

yh.push('<li><a href="javascript:;">'+(yt--)+'</a></li>');

mh.push('<li><a href="javascript:;">'+(i++)+'</a></li>');

}while(i <= 12);

//yh.push('</select>');

//mh.push('</select>');

yh.push('</ul></div></div>');

mh.push('</ul></div></div>');

$(cap).append(yh.join('') +'<span class="joinLine"> 年 </span>'+mh.join('')+'<span class="joinLine"> 月 </span>');

 

}

else{

$(cap).append(year + '年' + (month + 1) + '月');

}

 

$(table).append(cap);

thead = document.createElement('thead');

tr = document.createElement('tr');

var days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];

for(var i = 0; i < 7; i ++) {

th = document.createElement('th');

$(th).append(days[i]);

$(tr).append(th);

}

$(thead).append(tr);

$(table).append(thead);

 

tr = document.createElement('tr');

td = document.createElement('td');

// 如果是最后一个月的日期,则加上下一个月的链接

if(!this.mOpts.magicSelect){

if(0 == index) {

$(td).append('<a href="javascript:void(0);" id="' + this.nextMonth + '"><i class="i_next"></i></a>');

}

// 如果是第一个月的日期,则加上上一个月的链接

if(index + 1 == this.mOpts.calendars) {

$(td).append('<a href="javascript:void(0);" id="' + this.preMonth + '"><i class="i_pre"></i></a>');

}

}

 

//    $(td).append('<span style="font-size:16px">' + year + '年' + (month + 1) + '月' + '</span>');

$(td).attr('colSpan', 7);

$(td).css('text-align', 'center');

$(tr).append(td);

$(table).append(tr);

}

else{

table.className = this.mOpts.theme + '_' + this.mOpts.dateTable;

 

tr = document.createElement('tr');

td = document.createElement('td');

// 如果是最后一个月的日期,则加上下一个月的链接

if(0 == index) {

$(td).append('<a href="javascript:void(0);" id="' + this.nextMonth + '" class="gri_dateRangeNextMonth"><span>next</span></a>');

}

// 如果是第一个月的日期,则加上上一个月的链接

if(index + 1 == this.mOpts.calendars) {

$(td).append('<a href="javascript:void(0);" id="' + this.preMonth + '" class="gri_dateRangePreMonth"><span>pre</span></a>');

}

$(td).append(year + '年' + (month + 1) + '月');

$(td).attr('colSpan', 7);

$(td).css('text-align', 'center');

$(td).css('background-color', '#F9F9F9');

$(tr).append(td);

$(table).append(tr);

 

var days = ['日', '一', '二', '三', '四', '五', '六'];

tr = document.createElement('tr');

for(var i = 0; i < 7; i ++) {

td = document.createElement('td');

$(td).append(days[i]);

$(tr).append(td);

}

$(table).append(tr);

}

    // 当前月的所有日期(包括空白位置填充的日期)

    var tdClass = '', deviation = 0, ymd = '';

    for(var d = dateBegin; d.getTime() <= dateEnd.getTime(); d.setDate(d.getDate() + 1)) {

        if(d.getTime() < firstDayOfMonth.getTime()) { // 当前月之前的日期

            tdClass = this.mOpts.theme + '_' + this.mOpts.disableGray;

            deviation = '-1';

        } else if(d.getTime() > lastDayOfMonth.getTime()) { // 当前月之后的日期

            tdClass = this.mOpts.theme + '_' + this.mOpts.disableGray;

            deviation = '1';

        } else if((this.mOpts.stopToday == true && d.getTime() < today.getTime()) || d.getTime() < __method.mOpts.minValidDate * 1000 || ('' !== __method.mOpts.maxValidDate && d.getTime() > __method.mOpts.maxValidDate * 1000)) { // 当前时间之后的日期,或者开启统计之前的日期

            tdClass = this.mOpts.theme + '_' + this.mOpts.disableGray;

            deviation = '2';

        } else { // 当前月日期

            deviation = '0';

            if(d.getDate() == dToday && d.getMonth() == mToday && d.getFullYear() == yToday) {

                if (true == this.mOpts.isTodayValid) {

                    tdClass = this.mOpts.theme + '_' + this.mOpts.isToday;

                } else {

                    tdClass = this.mOpts.theme + '_' + this.mOpts.disableGray;

                    deviation = '2';

                }

            }

else {

                tdClass = '';

            }

//让周末不可选不可选

if(this.mOpts.weekendDis && (d.getDay()==6 || d.getDay()==0)){

tdClass = this.mOpts.theme + '_' + this.mOpts.disableGray;

deviation = '3';

}

//让周几不可选

if(this.mOpts.disCertainDay && this.mOpts.disCertainDay.length > 0 ){

for(var p in this.mOpts.disCertainDay){

if(!isNaN(this.mOpts.disCertainDay[p]) && d.getDay() === this.mOpts.disCertainDay[p]){

tdClass = this.mOpts.theme + '_' + this.mOpts.disableGray;

deviation = '4';

}

}

}

            //让几号不可选

            if(this.mOpts.disCertainDate && this.mOpts.disCertainDate.length > 0 ){

                var isDisabled = false;

 

                for(var p in this.mOpts.disCertainDate){

                    if(!isNaN(this.mOpts.disCertainDate[p]) || isNaN(parseInt(this.mOpts.disCertainDate[p]))){

                        if ( this.mOpts.disCertainDate[0] === true ){

                            isDisabled = !!(d.getDate() !== this.mOpts.disCertainDate[p]);

                            if ( !isDisabled ){

                                break;

                            }

                        }else {

                            isDisabled = !!(d.getDate() === this.mOpts.disCertainDate[p]);

                            if ( isDisabled ){

                                break;

                            }

                        }

 

                    }

                }

 

                if ( isDisabled ){

                    tdClass = this.mOpts.theme + '_' + this.mOpts.disableGray;

                    deviation = '4';

                }

 

            }

        }

 

        // 如果是周日

        if(0 == d.getDay()) {

            tr = document.createElement('tr');

        }

 

        td = document.createElement('td');

        td.innerHTML = d.getDate();

        if('' != tdClass) {

            $(td).attr('class', tdClass);

        }

 

        // 只有当前月可以点击

        if(0 == deviation) {

            ymd = d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate();

            $(td).attr('id', __method.calendarId + '_' + ymd);

$(td).css('cursor','pointer');

            (function(ymd) {

                $(td).bind("click", ymd, function() {

                    __method.selectDate(ymd);

                    return false;

                });

            })(ymd);

        }

 

        $(tr).append(td);

 

        // 如果是周六

        if(6 == d.getDay()) {

            $(table).append(tr);

        }

    }

 

    return table;

};

 

/**

 * @description 把时间字串转成时间格式

 * @param {String} str 时间字符串

 */ 

pickerDateRange.prototype.str2date = function(str) {

    var ar = str.split('-');

    // 返回日期格式

    return new Date(ar[0], ar[1] - 1, ar[2]);

};

 

/**

 * @description 比较两个时间字串的大小:1 大于; 0 等于; -1 小于

 * @param {String} b 待比较时间串1

 * @param {String} e 待比较时间串2

 */

pickerDateRange.prototype.compareStrDate = function(b, e) {

    var bDate = this.str2date(b);

    var eDate = this.str2date(e);

 

    // 1 大于; 0 等于; -1 小于

    if(bDate.getTime() > eDate.getTime()) {

        return 1;

    } else if(bDate.getTime() == eDate.getTime()) {

        return 0;

    } else {

        return -1;

    }

};

 

/**

 * @description 把时间格式转成对象

 * @param {Date} d 时间

 */ 

pickerDateRange.prototype.date2ymd = function(d) {

    return [d.getFullYear(), (d.getMonth() + 1), d.getDate()];

};

 

/**

 * @description 切换焦点到当前输入框

 * @param {String} 日期框体ID

 */

pickerDateRange.prototype.changeInput = function(ipt) {

    // 强制修改为开始输入框

    if (true == this.mOpts.isSingleDay) {

        ipt = this.startDateId;

    }

    // 所有4个输入框

    var allInputs = [this.startDateId, this.startCompareDateId, this.endDateId, this.endCompareDateId];

 

    // 如果 ipt 是日期输入框,则为日期样式,否则为对比日期样式

    var cla = '';

    if(ipt == this.startDateId || ipt == this.endDateId) {

        cla = this.mOpts.theme + '_' + this.mOpts.selectCss;

    } else {

        cla = this.mOpts.theme + '_' + this.mOpts.compareCss;

    }

    if(ipt == this.endDateId && this.mOpts.singleCompare) {

        cla = this.mOpts.theme + '_' + this.mOpts.compareCss;

    }

 

    // 移除所有输入框的附加样式

    for(var i in allInputs) {

        $('#' + allInputs[i]).removeClass(this.mOpts.theme + '_' + this.mOpts.selectCss);

        $('#' + allInputs[i]).removeClass(this.mOpts.theme + '_' + this.mOpts.compareCss);

    }

 

    // 为指定输入框添加样式

    $('#' + ipt).addClass(cla);

//背景图repeat

$('#' + ipt).css('background-repeat', 'repeat');

    // 把输入焦点移到指定输入框

    this.dateInput = ipt;

};

 

/**

 * @description 日期格式化,加前导零

 */ 

pickerDateRange.prototype.formatDate = function(ymd) {

    return ymd.replace(/(\d{4})\-(\d{1,2})\-(\d{1,2})/g, function(ymdFormatDate, y, m, d){

        if(m < 10){

            m = '0' + m;

        }

        if(d < 10){

            d = '0' + d;

        }

        return y + '-' + m + '-' + d;

    });

};

/*! TableSorter (FORK) v2.28.13 *//** Client-side table sorting with ease!* @requires jQuery v1.2.6+** Copyright (c) 2007 Christian Bach* fork maintained by Rob Garrison** Examples and original docs at: http://tablesorter.com* Dual licensed under the MIT and GPL licenses:* http://www.opensource.org/licenses/mit-license.php* http://www.gnu.org/licenses/gpl.html** @type jQuery* @name tablesorter (FORK)* @cat Plugins/Tablesorter* @author Christian Bach - christian.bach@polyester.se* @contributor Rob Garrison - https://github.com/Mottie/tablesorter* @docs (fork) - https://mottie.github.io/tablesorter/docs/*//*jshint browser:true, jquery:true, unused:false, expr: true */;( function( $ ) {'use strict';var ts = $.tablesorter = {
version : '2.28.13',
parsers : [],widgets : [],defaults : {
// *** appearancetheme            : 'default',  // adds tablesorter-{theme} to the table for stylingwidthFixed       : false,      // adds colgroup to fix widths of columnsshowProcessing   : false,      // show an indeterminate timer icon in the header when the table is sorted or filtered.
headerTemplate   : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = <i/> // class from cssIcononRenderTemplate : null,       // function( index, template ){ return template; }, // template is a stringonRenderHeader   : null,       // function( index ){}, // nothing to return
// *** functionalitycancelSelection  : true,       // prevent text selection in the headertabIndex         : true,       // add tabindex to header for keyboard accessibilitydateFormat       : 'mmddyyyy', // other options: 'ddmmyyy' or 'yyyymmdd'sortMultiSortKey : 'shiftKey', // key used to select additional columnssortResetKey     : 'ctrlKey',  // key used to remove sorting on a columnusNumberFormat   : true,       // false for German '1.234.567,89' or French '1 234 567,89'delayInit        : false,      // if false, the parsed table contents will not update until the first sortserverSideSorting: false,      // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used.resort           : true,       // default setting to trigger a resort after an 'update', 'addRows', 'updateCell', etc has completed
// *** sort optionsheaders          : {},         // set sorter, string, empty, locked order, sortInitialOrder, filter, etc.ignoreCase       : true,       // ignore case while sortingsortForce        : null,       // column(s) first sorted; always appliedsortList         : [],         // Initial sort order; applied initially; updated when manually sortedsortAppend       : null,       // column(s) sorted last; always appliedsortStable       : false,      // when sorting two rows with exactly the same content, the original sort order is maintained
sortInitialOrder : 'asc',      // sort direction on first clicksortLocaleCompare: false,      // replace equivalent character (accented characters)sortReset        : false,      // third click on the header will reset column to default - unsortedsortRestart      : false,      // restart sort to 'sortInitialOrder' when clicking on previously unsorted columns
emptyTo          : 'bottom',   // sort empty cell to bottom, top, none, zero, emptyMax, emptyMinstringTo         : 'max',      // sort strings in numerical column as max, min, top, bottom, zeroduplicateSpan    : true,       // colspan cells in the tbody will have duplicated content in the cache for each spanned columntextExtraction   : 'basic',    // text extraction method/function - function( node, table, cellIndex ){}textAttribute    : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function)textSorter       : null,       // choose overall or specific column sorter function( a, b, direction, table, columnIndex ) [alt: ts.sortText]numberSorter     : null,       // choose overall numeric sorter function( a, b, direction, maxColumnValue )
// *** widget optionsinitWidgets      : true,       // apply widgets on tablesorter initializationwidgetClass      : 'widget-{name}', // table class name template to match to include a widgetwidgets          : [],         // method to add widgets, e.g. widgets: ['zebra']widgetOptions    : {zebra : [ 'even', 'odd' ]  // zebra widget alternating row class names},
// *** callbacksinitialized      : null,       // function( table ){},
// *** extra css class namestableClass       : '',cssAsc           : '',cssDesc          : '',cssNone          : '',cssHeader        : '',cssHeaderRow     : '',cssProcessing    : '', // processing icon applied to header during sort/filter
cssChildRow      : 'tablesorter-childRow', // class name indiciating that a row is to be attached to its parentcssInfoBlock     : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!)cssNoSort        : 'tablesorter-noSort',   // class name added to element inside header; clicking on it won't cause a sortcssIgnoreRow     : 'tablesorter-ignoreRow',// header row to ignore; cells within this row will not be added to c.$headers
cssIcon          : 'tablesorter-icon', // if this class does not exist, the {icon} will not be added from the headerTemplatecssIconNone      : '', // class name added to the icon when there is no column sortcssIconAsc       : '', // class name added to the icon when the column has an ascending sortcssIconDesc      : '', // class name added to the icon when the column has a descending sortcssIconDisabled  : '', // class name added to the icon when the column has a disabled sort
// *** eventspointerClick     : 'click',pointerDown      : 'mousedown',pointerUp        : 'mouseup',
// *** selectorsselectorHeaders  : '> thead th, > thead td',selectorSort     : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sortselectorRemove   : '.remove-me',
// *** advanceddebug            : false,
// *** Internal variablesheaderList: [],empties: {},strings: {},parsers: [],
// *** parser options for validator; values must be falsy!globalize: 0,imgAttr: 0
// removed: widgetZebra: { css: ['even', 'odd'] }
},
// internal css classes - these will ALWAYS be added to// the table and MUST only contain one class name - fixes #381css : {table      : 'tablesorter',cssHasChild: 'tablesorter-hasChildRow',childRow   : 'tablesorter-childRow',colgroup   : 'tablesorter-colgroup',header     : 'tablesorter-header',headerRow  : 'tablesorter-headerRow',headerIn   : 'tablesorter-header-inner',icon       : 'tablesorter-icon',processing : 'tablesorter-processing',sortAsc    : 'tablesorter-headerAsc',sortDesc   : 'tablesorter-headerDesc',sortNone   : 'tablesorter-headerUnSorted'},
// labels applied to sortable headers for accessibility (aria) supportlanguage : {sortAsc      : 'Ascending sort applied, ',sortDesc     : 'Descending sort applied, ',sortNone     : 'No sort applied, ',sortDisabled : 'sorting is disabled',nextAsc      : 'activate to apply an ascending sort',nextDesc     : 'activate to apply a descending sort',nextNone     : 'activate to remove the sort'},
regex : {templateContent : /\{content\}/g,templateIcon    : /\{icon\}/g,templateName    : /\{name\}/i,spaces          : /\s+/g,nonWord         : /\W/g,formElements    : /(input|select|button|textarea)/i,
// *** sort functions ***// regex used in natural sort// chunk/tokenize numbers & letterschunk  : /(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi,// replace chunks @ endschunks : /(^\\0|\\0$)/,hex    : /^0x[0-9a-f]+$/i,
// *** formatFloat ***comma                : /,/g,digitNonUS           : /[\s|\.]/g,digitNegativeTest    : /^\s*\([.\d]+\)/,digitNegativeReplace : /^\s*\(([.\d]+)\)/,
// *** isDigit ***digitTest    : /^[\-+(]?\d+[)]?$/,digitReplace : /[,.'"\s]/g
},
// digit sort, text locationstring : {max      : 1,min      : -1,emptymin : 1,emptymax : -1,zero     : 0,none     : 0,'null'   : 0,top      : true,bottom   : false},
keyCodes : {enter : 13},
// placeholder date parser data (globalize)dates : {},
// These methods can be applied on table.config instanceinstanceMethods : {},
/*▄█████ ██████ ██████ ██  ██ █████▄▀█▄    ██▄▄     ██   ██  ██ ██▄▄██   ▀█▄ ██▀▀     ██   ██  ██ ██▀▀▀█████▀ ██████   ██   ▀████▀ ██*/
setup : function( table, c ) {// if no thead or tbody, or tablesorter is already present, quitif ( !table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true ) {if ( c.debug ) {if ( table.hasInitialized ) {console.warn( 'Stopping initialization. Tablesorter has already been initialized' );} else {console.error( 'Stopping initialization! No table, thead or tbody', table );}}return;}
var tmp = '',$table = $( table ),meta = $.metadata;// initialization flagtable.hasInitialized = false;// table is being processed flagtable.isProcessing = true;// make sure to store the config objecttable.config = c;// save the settings where they read$.data( table, 'tablesorter', c );if ( c.debug ) {console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter v' + ts.version );$.data( table, 'startoveralltimer', new Date() );}
// removing this in version 3 (only supports jQuery 1.7+)c.supportsDataObject = ( function( version ) {version[ 0 ] = parseInt( version[ 0 ], 10 );return ( version[ 0 ] > 1 ) || ( version[ 0 ] === 1 && parseInt( version[ 1 ], 10 ) >= 4 );})( $.fn.jquery.split( '.' ) );// ensure case insensitivityc.emptyTo = c.emptyTo.toLowerCase();c.stringTo = c.stringTo.toLowerCase();c.last = { sortList : [], clickedIndex : -1 };// add table theme class only if there isn't already one thereif ( !/tablesorter\-/.test( $table.attr( 'class' ) ) ) {tmp = ( c.theme !== '' ? ' tablesorter-' + c.theme : '' );}
// give the table a unique id, which will be used in namespace bindingif ( !c.namespace ) {c.namespace = '.tablesorter' + Math.random().toString( 16 ).slice( 2 );} else {// make sure namespace starts with a period & doesn't have weird charactersc.namespace = '.' + c.namespace.replace( ts.regex.nonWord, '' );}
c.table = table;c.$table = $table// add namespace to table to allow bindings on extra elements to target// the parent table (e.g. parser-input-select).addClass( ts.css.table + ' ' + c.tableClass + tmp + ' ' + c.namespace.slice(1) ).attr( 'role', 'grid' );c.$headers = $table.find( c.selectorHeaders );
c.$table.children().children( 'tr' ).attr( 'role', 'row' );c.$tbodies = $table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ).attr({'aria-live' : 'polite','aria-relevant' : 'all'});if ( c.$table.children( 'caption' ).length ) {tmp = c.$table.children( 'caption' )[ 0 ];if ( !tmp.id ) { tmp.id = c.namespace.slice( 1 ) + 'caption'; }c.$table.attr( 'aria-labelledby', tmp.id );}c.widgetInit = {}; // keep a list of initialized widgets// change textExtraction via data-attributec.textExtraction = c.$table.attr( 'data-text-extraction' ) || c.textExtraction || 'basic';// build headersts.buildHeaders( c );// fixate columns if the users supplies the fixedWidth option// do this after theme has been appliedts.fixColumnWidth( table );// add widgets from class namets.addWidgetFromClass( table );// add widget options before parsing (e.g. grouping widget has parser settings)ts.applyWidgetOptions( table );// try to auto detect column type, and store in tables configts.setupParsers( c );// start total row count at zeroc.totalRows = 0;ts.validateOptions( c );// build the cache for the tbody cells// delayInit will delay building the cache until the user starts a sortif ( !c.delayInit ) { ts.buildCache( c ); }// bind all header events and methodsts.bindEvents( table, c.$headers, true );ts.bindMethods( c );// get sort list from jQuery data or metadata// in jQuery < 1.4, an error occurs when calling $table.data()if ( c.supportsDataObject && typeof $table.data().sortlist !== 'undefined' ) {c.sortList = $table.data().sortlist;} else if ( meta && ( $table.metadata() && $table.metadata().sortlist ) ) {c.sortList = $table.metadata().sortlist;}// apply widget init codets.applyWidget( table, true );// if user has supplied a sort list to constructorif ( c.sortList.length > 0 ) {ts.sortOn( c, c.sortList, {}, !c.initWidgets );} else {ts.setHeadersCss( c );if ( c.initWidgets ) {// apply widget formatts.applyWidget( table, false );}}
// show processesing iconif ( c.showProcessing ) {$table.unbind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace ).bind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace, function( e ) {clearTimeout( c.timerProcessing );ts.isProcessing( table );if ( e.type === 'sortBegin' ) {c.timerProcessing = setTimeout( function() {ts.isProcessing( table, true );}, 500 );}});}
// initializedtable.hasInitialized = true;table.isProcessing = false;if ( c.debug ) {console.log( 'Overall initialization time:' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) );if ( c.debug && console.groupEnd ) { console.groupEnd(); }}$table.triggerHandler( 'tablesorter-initialized', table );if ( typeof c.initialized === 'function' ) {c.initialized( table );}},
bindMethods : function( c ) {var $table = c.$table,namespace = c.namespace,events = ( 'sortReset update updateRows updateAll updateHeaders addRows updateCell updateComplete ' +'sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup ' +'mouseleave ' ).split( ' ' ).join( namespace + ' ' );// apply easy methods that trigger bound events$table.unbind( events.replace( ts.regex.spaces, ' ' ) ).bind( 'sortReset' + namespace, function( e, callback ) {e.stopPropagation();// using this.config to ensure functions are getting a non-cached version of the configts.sortReset( this.config, function( table ) {if (table.isApplyingWidgets) {// multiple triggers in a row... filterReset, then sortReset - see #1361// wait to update widgetssetTimeout( function() {ts.applyWidget( table, '', callback );}, 100 );} else {ts.applyWidget( table, '', callback );}});}).bind( 'updateAll' + namespace, function( e, resort, callback ) {e.stopPropagation();ts.updateAll( this.config, resort, callback );}).bind( 'update' + namespace + ' updateRows' + namespace, function( e, resort, callback ) {e.stopPropagation();ts.update( this.config, resort, callback );}).bind( 'updateHeaders' + namespace, function( e, callback ) {e.stopPropagation();ts.updateHeaders( this.config, callback );}).bind( 'updateCell' + namespace, function( e, cell, resort, callback ) {e.stopPropagation();ts.updateCell( this.config, cell, resort, callback );}).bind( 'addRows' + namespace, function( e, $row, resort, callback ) {e.stopPropagation();ts.addRows( this.config, $row, resort, callback );}).bind( 'updateComplete' + namespace, function() {this.isUpdating = false;}).bind( 'sorton' + namespace, function( e, list, callback, init ) {e.stopPropagation();ts.sortOn( this.config, list, callback, init );}).bind( 'appendCache' + namespace, function( e, callback, init ) {e.stopPropagation();ts.appendCache( this.config, init );if ( $.isFunction( callback ) ) {callback( this );}})// $tbodies variable is used by the tbody sorting widget.bind( 'updateCache' + namespace, function( e, callback, $tbodies ) {e.stopPropagation();ts.updateCache( this.config, callback, $tbodies );}).bind( 'applyWidgetId' + namespace, function( e, id ) {e.stopPropagation();ts.applyWidgetId( this, id );}).bind( 'applyWidgets' + namespace, function( e, init ) {e.stopPropagation();// apply widgetsts.applyWidget( this, init );}).bind( 'refreshWidgets' + namespace, function( e, all, dontapply ) {e.stopPropagation();ts.refreshWidgets( this, all, dontapply );}).bind( 'removeWidget' + namespace, function( e, name, refreshing ) {e.stopPropagation();ts.removeWidget( this, name, refreshing );}).bind( 'destroy' + namespace, function( e, removeClasses, callback ) {e.stopPropagation();ts.destroy( this, removeClasses, callback );}).bind( 'resetToLoadState' + namespace, function( e ) {e.stopPropagation();// remove all widgetsts.removeWidget( this, true, false );var tmp = $.extend( true, {}, c.originalSettings );// restore original settings; this clears out current settings, but does not clear// values saved to storage.c = $.extend( true, {}, ts.defaults, tmp );c.originalSettings = tmp;this.hasInitialized = false;// setup the entire table againts.setup( this, c );});},
bindEvents : function( table, $headers, core ) {table = $( table )[ 0 ];var tmp,c = table.config,namespace = c.namespace,downTarget = null;if ( core !== true ) {$headers.addClass( namespace.slice( 1 ) + '_extra_headers' );tmp = $.fn.closest ? $headers.closest( 'table' )[ 0 ] : $headers.parents( 'table' )[ 0 ];if ( tmp && tmp.nodeName === 'TABLE' && tmp !== table ) {$( tmp ).addClass( namespace.slice( 1 ) + '_extra_table' );}}tmp = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ).replace( ts.regex.spaces, ' ' ).split( ' ' ).join( namespace + ' ' );// apply event handling to headers and/or additional headers (stickyheaders, scroller, etc)$headers// http://stackoverflow.com/questions/5312849/jquery-find-self;.find( c.selectorSort ).add( $headers.filter( c.selectorSort ) ).unbind( tmp ).bind( tmp, function( e, external ) {var $cell, cell, temp,$target = $( e.target ),// wrap event type in spaces, so the match doesn't trigger on inner wordstype = ' ' + e.type + ' ';// only recognize left clicksif ( ( ( e.which || e.button ) !== 1 && !type.match( ' ' + c.pointerClick + ' | sort | keyup ' ) ) ||// allow pressing enter( type === ' keyup ' && e.which !== ts.keyCodes.enter ) ||// allow triggering a click event (e.which is undefined) & ignore physical clicks( type.match( ' ' + c.pointerClick + ' ' ) && typeof e.which !== 'undefined' ) ) {return;}// ignore mouseup if mousedown wasn't on the same targetif ( type.match( ' ' + c.pointerUp + ' ' ) && downTarget !== e.target && external !== true ) {return;}// set target on mousedownif ( type.match( ' ' + c.pointerDown + ' ' ) ) {downTarget = e.target;// preventDefault needed or jQuery v1.3.2 and older throws an// "Uncaught TypeError: handler.apply is not a function" errortemp = $target.jquery.split( '.' );if ( temp[ 0 ] === '1' && temp[ 1 ] < 4 ) { e.preventDefault(); }return;}downTarget = null;// prevent sort being triggered on form elementsif ( ts.regex.formElements.test( e.target.nodeName ) ||// nosort class name, or elements within a nosort container$target.hasClass( c.cssNoSort ) || $target.parents( '.' + c.cssNoSort ).length > 0 ||// elements within a button$target.parents( 'button' ).length > 0 ) {return !c.cancelSelection;}if ( c.delayInit && ts.isEmptyObject( c.cache ) ) {ts.buildCache( c );}// jQuery v1.2.6 doesn't have closest()$cell = $.fn.closest ? $( this ).closest( 'th, td' ) :/TH|TD/.test( this.nodeName ) ? $( this ) : $( this ).parents( 'th, td' );// reference original table headers and find the same cell// don't use $headers or IE8 throws an error - see #987temp = $headers.index( $cell );c.last.clickedIndex = ( temp < 0 ) ? $cell.attr( 'data-column' ) : temp;// use column index if $headers is undefinedcell = c.$headers[ c.last.clickedIndex ];if ( cell && !cell.sortDisabled ) {ts.initSort( c, cell, e );}});if ( c.cancelSelection ) {// cancel selection$headers.attr( 'unselectable', 'on' ).bind( 'selectstart', false ).css({'user-select' : 'none','MozUserSelect' : 'none' // not needed for jQuery 1.8+});}},
buildHeaders : function( c ) {var $temp, icon, timer, indx;c.headerList = [];c.headerContent = [];c.sortVars = [];if ( c.debug ) {timer = new Date();}// children tr in tfoot - see issue #196 & #547// don't pass table.config to computeColumnIndex here - widgets (math) pass it to "quickly" index tbody cellsc.columns = ts.computeColumnIndex( c.$table.children( 'thead, tfoot' ).children( 'tr' ) );// add icon if cssIcon option existsicon = c.cssIcon ?'<i class="' + ( c.cssIcon === ts.css.icon ? ts.css.icon : c.cssIcon + ' ' + ts.css.icon ) + '"></i>' :'';// redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683c.$headers = $( $.map( c.$table.find( c.selectorHeaders ), function( elem, index ) {var configHeaders, header, column, template, tmp,$elem = $( elem );// ignore cell (don't add it to c.$headers) if row has ignoreRow classif ( $elem.parent().hasClass( c.cssIgnoreRow ) ) { return; }// make sure to get header cell & not column indexed cellconfigHeaders = ts.getColumnData( c.table, c.headers, index, true );// save original header contentc.headerContent[ index ] = $elem.html();// if headerTemplate is empty, don't reformat the header cellif ( c.headerTemplate !== '' && !$elem.find( '.' + ts.css.headerIn ).length ) {// set up header templatetemplate = c.headerTemplate.replace( ts.regex.templateContent, $elem.html() ).replace( ts.regex.templateIcon, $elem.find( '.' + ts.css.icon ).length ? '' : icon );if ( c.onRenderTemplate ) {header = c.onRenderTemplate.apply( $elem, [ index, template ] );// only change t if something is returnedif ( header && typeof header === 'string' ) {template = header;}}$elem.html( '<div class="' + ts.css.headerIn + '">' + template + '</div>' ); // faster than wrapInner}if ( c.onRenderHeader ) {c.onRenderHeader.apply( $elem, [ index, c, c.$table ] );}column = parseInt( $elem.attr( 'data-column' ), 10 );elem.column = column;tmp = ts.getOrder( ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder );// this may get updated numerous times if there are multiple rowsc.sortVars[ column ] = {count : -1, // set to -1 because clicking on the header automatically adds oneorder:  tmp ?( c.sortReset ? [ 1, 0, 2 ] : [ 1, 0 ] ) : // desc, asc, unsorted( c.sortReset ? [ 0, 1, 2 ] : [ 0, 1 ] ),  // asc, desc, unsortedlockedOrder : false};tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false;if ( typeof tmp !== 'undefined' && tmp !== false ) {c.sortVars[ column ].lockedOrder = true;c.sortVars[ column ].order = ts.getOrder( tmp ) ? [ 1, 1 ] : [ 0, 0 ];}// add cell to headerListc.headerList[ index ] = elem;// add to parent in case there are multiple rows$elem.addClass( ts.css.header + ' ' + c.cssHeader ).parent().addClass( ts.css.headerRow + ' ' + c.cssHeaderRow ).attr( 'role', 'row' );// allow keyboard cursor to focus on elementif ( c.tabIndex ) {$elem.attr( 'tabindex', 0 );}return elem;}) );// cache headers per columnc.$headerIndexed = [];for ( indx = 0; indx < c.columns; indx++ ) {// colspan in header making a column undefinedif ( ts.isEmptyObject( c.sortVars[ indx ] ) ) {c.sortVars[ indx ] = {};}$temp = c.$headers.filter( '[data-column="' + indx + '"]' );// target sortable column cells, unless there are none, then use non-sortable cells// .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6c.$headerIndexed[ indx ] = $temp.length ?$temp.not( '.sorter-false' ).length ?$temp.not( '.sorter-false' ).filter( ':last' ) :$temp.filter( ':last' ) :$();}c.$table.find( c.selectorHeaders ).attr({scope: 'col',role : 'columnheader'});// enable/disable sortingts.updateHeader( c );if ( c.debug ) {console.log( 'Built headers:' + ts.benchmark( timer ) );console.log( c.$headers );}},
// Use it to add a set of methods to table.config which will be available for all tables.// This should be done before table initializationaddInstanceMethods : function( methods ) {$.extend( ts.instanceMethods, methods );},
/*█████▄ ▄████▄ █████▄ ▄█████ ██████ █████▄ ▄███████▄▄██ ██▄▄██ ██▄▄██ ▀█▄    ██▄▄   ██▄▄██ ▀█▄██▀▀▀  ██▀▀██ ██▀██     ▀█▄ ██▀▀   ██▀██     ▀█▄██     ██  ██ ██  ██ █████▀ ██████ ██  ██ █████▀*/setupParsers : function( c, $tbodies ) {var rows, list, span, max, colIndex, indx, header, configHeaders,noParser, parser, extractor, time, tbody, len,table = c.table,tbodyIndex = 0,debug = {};// update table bodies in case we start with an empty tablec.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' );tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies;len = tbody.length;if ( len === 0 ) {return c.debug ? console.warn( 'Warning: *Empty table!* Not building a parser cache' ) : '';} else if ( c.debug ) {time = new Date();console[ console.group ? 'group' : 'log' ]( 'Detecting parsers for each column' );}list = {extractors: [],parsers: []};while ( tbodyIndex < len ) {rows = tbody[ tbodyIndex ].rows;if ( rows.length ) {colIndex = 0;max = c.columns;for ( indx = 0; indx < max; indx++ ) {header = c.$headerIndexed[ colIndex ];if ( header && header.length ) {// get column indexed table cell; adding true parameter fixes #1362 but// it would break backwards compatibility...configHeaders = ts.getColumnData( table, c.headers, colIndex ); // , true );// get column parser/extractorextractor = ts.getParserById( ts.getData( header, configHeaders, 'extractor' ) );parser = ts.getParserById( ts.getData( header, configHeaders, 'sorter' ) );noParser = ts.getData( header, configHeaders, 'parser' ) === 'false';// empty cells behaviour - keeping emptyToBottom for backwards compatibilityc.empties[colIndex] = (ts.getData( header, configHeaders, 'empty' ) ||c.emptyTo || ( c.emptyToBottom ? 'bottom' : 'top' ) ).toLowerCase();// text strings behaviour in numerical sortsc.strings[colIndex] = (ts.getData( header, configHeaders, 'string' ) ||c.stringTo ||'max' ).toLowerCase();if ( noParser ) {parser = ts.getParserById( 'no-parser' );}if ( !extractor ) {// For now, maybe detect somedayextractor = false;}if ( !parser ) {parser = ts.detectParserForColumn( c, rows, -1, colIndex );}if ( c.debug ) {debug[ '(' + colIndex + ') ' + header.text() ] = {parser : parser.id,extractor : extractor ? extractor.id : 'none',string : c.strings[ colIndex ],empty  : c.empties[ colIndex ]};}list.parsers[ colIndex ] = parser;list.extractors[ colIndex ] = extractor;span = header[ 0 ].colSpan - 1;if ( span > 0 ) {colIndex += span;max += span;while ( span + 1 > 0 ) {// set colspan columns to use the same parsers & extractorslist.parsers[ colIndex - span ] = parser;list.extractors[ colIndex - span ] = extractor;span--;}}}colIndex++;}}tbodyIndex += ( list.parsers.length ) ? len : 1;}if ( c.debug ) {if ( !ts.isEmptyObject( debug ) ) {console[ console.table ? 'table' : 'log' ]( debug );} else {console.warn( '  No parsers detected!' );}console.log( 'Completed detecting parsers' + ts.benchmark( time ) );if ( console.groupEnd ) { console.groupEnd(); }}c.parsers = list.parsers;c.extractors = list.extractors;},
addParser : function( parser ) {var indx,len = ts.parsers.length,add = true;for ( indx = 0; indx < len; indx++ ) {if ( ts.parsers[ indx ].id.toLowerCase() === parser.id.toLowerCase() ) {add = false;}}if ( add ) {ts.parsers[ ts.parsers.length ] = parser;}},
getParserById : function( name ) {/*jshint eqeqeq:false */if ( name == 'false' ) { return false; }var indx,len = ts.parsers.length;for ( indx = 0; indx < len; indx++ ) {if ( ts.parsers[ indx ].id.toLowerCase() === ( name.toString() ).toLowerCase() ) {return ts.parsers[ indx ];}}return false;},
detectParserForColumn : function( c, rows, rowIndex, cellIndex ) {var cur, $node, row,indx = ts.parsers.length,node = false,nodeValue = '',keepLooking = true;while ( nodeValue === '' && keepLooking ) {rowIndex++;row = rows[ rowIndex ];// stop looking after 50 empty rowsif ( row && rowIndex < 50 ) {if ( row.className.indexOf( ts.cssIgnoreRow ) < 0 ) {node = rows[ rowIndex ].cells[ cellIndex ];nodeValue = ts.getElementText( c, node, cellIndex );$node = $( node );if ( c.debug ) {console.log( 'Checking if value was empty on row ' + rowIndex + ', column: ' +cellIndex + ': "' + nodeValue + '"' );}}} else {keepLooking = false;}}while ( --indx >= 0 ) {cur = ts.parsers[ indx ];// ignore the default text parser because it will always be trueif ( cur && cur.id !== 'text' && cur.is && cur.is( nodeValue, c.table, node, $node ) ) {return cur;}}// nothing found, return the generic parser (text)return ts.getParserById( 'text' );},
getElementText : function( c, node, cellIndex ) {if ( !node ) { return ''; }var tmp,extract = c.textExtraction || '',// node could be a jquery object// http://jsperf.com/jquery-vs-instanceof-jquery/2$node = node.jquery ? node : $( node );if ( typeof extract === 'string' ) {// check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow!// http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/if ( extract === 'basic' && typeof ( tmp = $node.attr( c.textAttribute ) ) !== 'undefined' ) {return $.trim( tmp );}return $.trim( node.textContent || $node.text() );} else {if ( typeof extract === 'function' ) {return $.trim( extract( $node[ 0 ], c.table, cellIndex ) );} else if ( typeof ( tmp = ts.getColumnData( c.table, extract, cellIndex ) ) === 'function' ) {return $.trim( tmp( $node[ 0 ], c.table, cellIndex ) );}}// fallbackreturn $.trim( $node[ 0 ].textContent || $node.text() );},
// centralized function to extract/parse cell contentsgetParsedText : function( c, cell, colIndex, txt ) {if ( typeof txt === 'undefined' ) {txt = ts.getElementText( c, cell, colIndex );}// if no parser, make sure to return the txtvar val = '' + txt,parser = c.parsers[ colIndex ],extractor = c.extractors[ colIndex ];if ( parser ) {// do extract before parsing, if there is oneif ( extractor && typeof extractor.format === 'function' ) {txt = extractor.format( txt, c.table, cell, colIndex );}// allow parsing if the string is empty, previously parsing would change it to zero,// in case the parser needs to extract data from the table cell attributesval = parser.id === 'no-parser' ? '' :// make sure txt is a string (extractor may have converted it)parser.format( '' + txt, c.table, cell, colIndex );if ( c.ignoreCase && typeof val === 'string' ) {val = val.toLowerCase();}}return val;},
/*▄████▄ ▄████▄ ▄████▄ ██  ██ ████████  ▀▀ ██▄▄██ ██  ▀▀ ██▄▄██ ██▄▄██  ▄▄ ██▀▀██ ██  ▄▄ ██▀▀██ ██▀▀▀████▀ ██  ██ ▀████▀ ██  ██ ██████*/buildCache : function( c, callback, $tbodies ) {var cache, val, txt, rowIndex, colIndex, tbodyIndex, $tbody, $row,cols, $cells, cell, cacheTime, totalRows, rowData, prevRowData,colMax, span, cacheIndex, hasParser, max, len, index,table = c.table,parsers = c.parsers;// update tbody variablec.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' );$tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies,c.cache = {};c.totalRows = 0;// if no parsers found, return - it's an empty table.if ( !parsers ) {return c.debug ? console.warn( 'Warning: *Empty table!* Not building a cache' ) : '';}if ( c.debug ) {cacheTime = new Date();}// processing iconif ( c.showProcessing ) {ts.isProcessing( table, true );}for ( tbodyIndex = 0; tbodyIndex < $tbody.length; tbodyIndex++ ) {colMax = []; // column max value per tbodycache = c.cache[ tbodyIndex ] = {normalized: [] // array of normalized row data; last entry contains 'rowData' above// colMax: #   // added at the end};
totalRows = ( $tbody[ tbodyIndex ] && $tbody[ tbodyIndex ].rows.length ) || 0;for ( rowIndex = 0; rowIndex < totalRows; ++rowIndex ) {rowData = {// order: original row order #// $row : jQuery Object[]child: [], // child row text (filter widget)raw: []    // original row text};/** Add the table data to main data array */$row = $( $tbody[ tbodyIndex ].rows[ rowIndex ] );cols = [];// ignore "remove-me" rowsif ( $row.hasClass( c.selectorRemove.slice(1) ) ) {continue;}// if this is a child row, add it to the last row's children and continue to the next row// ignore child row class, if it is the first rowif ( $row.hasClass( c.cssChildRow ) && rowIndex !== 0 ) {len = cache.normalized.length - 1;prevRowData = cache.normalized[ len ][ c.columns ];prevRowData.$row = prevRowData.$row.add( $row );// add 'hasChild' class name to parent rowif ( !$row.prev().hasClass( c.cssChildRow ) ) {$row.prev().addClass( ts.css.cssHasChild );}// save child row content (un-parsed!)$cells = $row.children( 'th, td' );len = prevRowData.child.length;prevRowData.child[ len ] = [];// child row content does not account for colspans/rowspans; so indexing may be offcacheIndex = 0;max = c.columns;for ( colIndex = 0; colIndex < max; colIndex++ ) {cell = $cells[ colIndex ];if ( cell ) {prevRowData.child[ len ][ colIndex ] = ts.getParsedText( c, cell, colIndex );span = $cells[ colIndex ].colSpan - 1;if ( span > 0 ) {cacheIndex += span;max += span;}}cacheIndex++;}// go to the next for loopcontinue;}rowData.$row = $row;rowData.order = rowIndex; // add original row position to rowCachecacheIndex = 0;max = c.columns;for ( colIndex = 0; colIndex < max; ++colIndex ) {cell = $row[ 0 ].cells[ colIndex ];if ( cell && cacheIndex < c.columns ) {hasParser = typeof parsers[ cacheIndex ] !== 'undefined';if ( !hasParser && c.debug ) {console.warn( 'No parser found for row: ' + rowIndex + ', column: ' + colIndex +'; cell containing: "' + $(cell).text() + '"; does it have a header?' );}val = ts.getElementText( c, cell, cacheIndex );rowData.raw[ cacheIndex ] = val; // save original row text// save raw column text even if there is no parser settxt = ts.getParsedText( c, cell, cacheIndex, val );cols[ cacheIndex ] = txt;if ( hasParser && ( parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) {// determine column max value (ignore sign)colMax[ cacheIndex ] = Math.max( Math.abs( txt ) || 0, colMax[ cacheIndex ] || 0 );}// allow colSpan in tbodyspan = cell.colSpan - 1;if ( span > 0 ) {index = 0;while ( index <= span ) {// duplicate text (or not) to spanned columns// instead of setting duplicate span to empty string, use textExtraction to try to get a value// see http://stackoverflow.com/q/36449711/145346txt = c.duplicateSpan || index === 0 ?val :typeof c.textExtraction !== 'string' ?ts.getElementText( c, cell, cacheIndex + index ) || '' :'';rowData.raw[ cacheIndex + index ] = txt;cols[ cacheIndex + index ] = txt;index++;}cacheIndex += span;max += span;}}cacheIndex++;}// ensure rowData is always in the same location (after the last column)cols[ c.columns ] = rowData;cache.normalized[ cache.normalized.length ] = cols;}cache.colMax = colMax;// total up rows, not including child rowsc.totalRows += cache.normalized.length;
}if ( c.showProcessing ) {ts.isProcessing( table ); // remove processing icon}if ( c.debug ) {len = Math.min( 5, c.cache[ 0 ].normalized.length );console[ console.group ? 'group' : 'log' ]( 'Building cache for ' + c.totalRows +' rows (showing ' + len + ' rows in log) and ' + c.columns + ' columns' +ts.benchmark( cacheTime ) );val = {};for ( colIndex = 0; colIndex < c.columns; colIndex++ ) {for ( cacheIndex = 0; cacheIndex < len; cacheIndex++ ) {if ( !val[ 'row: ' + cacheIndex ] ) {val[ 'row: ' + cacheIndex ] = {};}val[ 'row: ' + cacheIndex ][ c.$headerIndexed[ colIndex ].text() ] =c.cache[ 0 ].normalized[ cacheIndex ][ colIndex ];}}console[ console.table ? 'table' : 'log' ]( val );if ( console.groupEnd ) { console.groupEnd(); }}if ( $.isFunction( callback ) ) {callback( table );}},
getColumnText : function( table, column, callback, rowFilter ) {table = $( table )[0];var tbodyIndex, rowIndex, cache, row, tbodyLen, rowLen, raw, parsed, $cell, result,hasCallback = typeof callback === 'function',allColumns = column === 'all',data = { raw : [], parsed: [], $cell: [] },c = table.config;if ( ts.isEmptyObject( c ) ) {if ( c.debug ) {console.warn( 'No cache found - aborting getColumnText function!' );}} else {tbodyLen = c.$tbodies.length;for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) {cache = c.cache[ tbodyIndex ].normalized;rowLen = cache.length;for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) {row = cache[ rowIndex ];if ( rowFilter && !row[ c.columns ].$row.is( rowFilter ) ) {continue;}result = true;parsed = ( allColumns ) ? row.slice( 0, c.columns ) : row[ column ];row = row[ c.columns ];raw = ( allColumns ) ? row.raw : row.raw[ column ];$cell = ( allColumns ) ? row.$row.children() : row.$row.children().eq( column );if ( hasCallback ) {result = callback({tbodyIndex : tbodyIndex,rowIndex : rowIndex,parsed : parsed,raw : raw,$row : row.$row,$cell : $cell});}if ( result !== false ) {data.parsed[ data.parsed.length ] = parsed;data.raw[ data.raw.length ] = raw;data.$cell[ data.$cell.length ] = $cell;}}}// return everythingreturn data;}},
/*██  ██ █████▄ █████▄ ▄████▄ ██████ ████████  ██ ██▄▄██ ██  ██ ██▄▄██   ██   ██▄▄██  ██ ██▀▀▀  ██  ██ ██▀▀██   ██   ██▀▀▀████▀ ██     █████▀ ██  ██   ██   ██████*/setHeadersCss : function( c ) {var indx, column,list = c.sortList,len = list.length,none = ts.css.sortNone + ' ' + c.cssNone,css = [ ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc ],cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ],aria = [ 'ascending', 'descending' ],// find the footer$extras = c.$table.find( 'tfoot tr' ).children( 'td, th' ).add( $( c.namespace + '_extra_headers' ) ).removeClass( css.join( ' ' ) ),// remove all header information$sorted = c.$headers.add( $( 'thead ' + c.namespace + '_extra_headers' ) ).removeClass( css.join( ' ' ) ).addClass( none ).attr( 'aria-sort', 'none' ).find( '.' + ts.css.icon ).removeClass( cssIcon.join( ' ' ) ).end();// add css none to all sortable headers$sorted.not( '.sorter-false' ).find( '.' + ts.css.icon ).addClass( cssIcon[ 2 ] );// add disabled css icon classif ( c.cssIconDisabled ) {$sorted.filter( '.sorter-false' ).find( '.' + ts.css.icon ).addClass( c.cssIconDisabled );}for ( indx = 0; indx < len; indx++ ) {// direction = 2 means reset!if ( list[ indx ][ 1 ] !== 2 ) {// multicolumn sorting updating - see #1005// .not(function(){}) needs jQuery 1.4// filter(function(i, el){}) <- el is undefined in jQuery v1.2.6$sorted = c.$headers.filter( function( i ) {// only include headers that are in the sortList (this includes colspans)var include = true,$el = c.$headers.eq( i ),col = parseInt( $el.attr( 'data-column' ), 10 ),end = col + c.$headers[ i ].colSpan;for ( ; col < end; col++ ) {include = include ? include || ts.isValueInArray( col, c.sortList ) > -1 : false;}return include;});
// choose the :last in case there are nested columns$sorted = $sorted.not( '.sorter-false' ).filter( '[data-column="' + list[ indx ][ 0 ] + '"]' + ( len === 1 ? ':last' : '' ) );if ( $sorted.length ) {for ( column = 0; column < $sorted.length; column++ ) {if ( !$sorted[ column ].sortDisabled ) {$sorted.eq( column ).removeClass( none ).addClass( css[ list[ indx ][ 1 ] ] ).attr( 'aria-sort', aria[ list[ indx ][ 1 ] ] ).find( '.' + ts.css.icon ).removeClass( cssIcon[ 2 ] ).addClass( cssIcon[ list[ indx ][ 1 ] ] );}}// add sorted class to footer & extra headers, if they existif ( $extras.length ) {$extras.filter( '[data-column="' + list[ indx ][ 0 ] + '"]' ).removeClass( none ).addClass( css[ list[ indx ][ 1 ] ] );}}}}// add verbose aria labelslen = c.$headers.length;for ( indx = 0; indx < len; indx++ ) {ts.setColumnAriaLabel( c, c.$headers.eq( indx ) );}},
// nextSort (optional), lets you disable next sort textsetColumnAriaLabel : function( c, $header, nextSort ) {if ( $header.length ) {var column = parseInt( $header.attr( 'data-column' ), 10 ),vars = c.sortVars[ column ],tmp = $header.hasClass( ts.css.sortAsc ) ?'sortAsc' :$header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone',txt = $.trim( $header.text() ) + ': ' + ts.language[ tmp ];if ( $header.hasClass( 'sorter-false' ) || nextSort === false ) {txt += ts.language.sortDisabled;} else {tmp = ( vars.count + 1 ) % vars.order.length;nextSort = vars.order[ tmp ];// if nextSorttxt += ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ];}$header.attr( 'aria-label', txt );}},
updateHeader : function( c ) {var index, isDisabled, $header, col,table = c.table,len = c.$headers.length;for ( index = 0; index < len; index++ ) {$header = c.$headers.eq( index );col = ts.getColumnData( table, c.headers, index, true );// add 'sorter-false' class if 'parser-false' is setisDisabled = ts.getData( $header, col, 'sorter' ) === 'false' || ts.getData( $header, col, 'parser' ) === 'false';ts.setColumnSort( c, $header, isDisabled );}},
setColumnSort : function( c, $header, isDisabled ) {var id = c.table.id;$header[ 0 ].sortDisabled = isDisabled;$header[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ).attr( 'aria-disabled', '' + isDisabled );// disable tab index on disabled cellsif ( c.tabIndex ) {if ( isDisabled ) {$header.removeAttr( 'tabindex' );} else {$header.attr( 'tabindex', '0' );}}// aria-controls - requires table IDif ( id ) {if ( isDisabled ) {$header.removeAttr( 'aria-controls' );} else {$header.attr( 'aria-controls', id );}}},
updateHeaderSortCount : function( c, list ) {var col, dir, group, indx, primary, temp, val, order,sortList = list || c.sortList,len = sortList.length;c.sortList = [];for ( indx = 0; indx < len; indx++ ) {val = sortList[ indx ];// ensure all sortList values are numeric - fixes #127col = parseInt( val[ 0 ], 10 );// prevents error if sorton array is wrongif ( col < c.columns ) {
// set order if not already defined - due to colspan header without associated header cell// adding this check prevents a javascript errorif ( !c.sortVars[ col ].order ) {if ( ts.getOrder( c.sortInitialOrder ) ) {order = c.sortReset ? [ 1, 0, 2 ] : [ 1, 0 ];} else {order = c.sortReset ? [ 0, 1, 2 ] : [ 0, 1 ];}c.sortVars[ col ].order = order;c.sortVars[ col ].count = 0;}
order = c.sortVars[ col ].order;dir = ( '' + val[ 1 ] ).match( /^(1|d|s|o|n)/ );dir = dir ? dir[ 0 ] : '';// 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)extswitch ( dir ) {case '1' : case 'd' : // descendingdir = 1;break;case 's' : // same direction (as primary column)// if primary sort is set to 's', make it ascendingdir = primary || 0;break;case 'o' :temp = order[ ( primary || 0 ) % order.length ];// opposite of primary column; but resets if primary resetsdir = temp === 0 ? 1 : temp === 1 ? 0 : 2;break;case 'n' :dir = order[ ( ++c.sortVars[ col ].count ) % order.length ];break;default : // ascendingdir = 0;break;}primary = indx === 0 ? dir : primary;group = [ col, parseInt( dir, 10 ) || 0 ];c.sortList[ c.sortList.length ] = group;dir = $.inArray( group[ 1 ], order ); // fixes issue #167c.sortVars[ col ].count = dir >= 0 ? dir : group[ 1 ] % order.length;}}},
updateAll : function( c, resort, callback ) {var table = c.table;table.isUpdating = true;ts.refreshWidgets( table, true, true );ts.buildHeaders( c );ts.bindEvents( table, c.$headers, true );ts.bindMethods( c );ts.commonUpdate( c, resort, callback );},
update : function( c, resort, callback ) {var table = c.table;table.isUpdating = true;// update sorting (if enabled/disabled)ts.updateHeader( c );ts.commonUpdate( c, resort, callback );},
// simple header update - see #989updateHeaders : function( c, callback ) {c.table.isUpdating = true;ts.buildHeaders( c );ts.bindEvents( c.table, c.$headers, true );ts.resortComplete( c, callback );},
updateCell : function( c, cell, resort, callback ) {// updateCell for child rows is a mess - we'll ignore them for now// eventually I'll break out the "update" row cache code to make everything consistentif ( $( cell ).closest( 'tr' ).hasClass( c.cssChildRow ) ) {console.warn('Tablesorter Warning! "updateCell" for child row content has been disabled, use "update" instead');return;}if ( ts.isEmptyObject( c.cache ) ) {// empty table, do an update instead - fixes #1099ts.updateHeader( c );ts.commonUpdate( c, resort, callback );return;}c.table.isUpdating = true;c.$table.find( c.selectorRemove ).remove();// get position from the domvar tmp, indx, row, icell, cache, len,$tbodies = c.$tbodies,$cell = $( cell ),// update cache - format: function( s, table, cell, cellIndex )// no closest in jQuery v1.2.6tbodyIndex = $tbodies.index( $.fn.closest ? $cell.closest( 'tbody' ) : $cell.parents( 'tbody' ).filter( ':first' ) ),tbcache = c.cache[ tbodyIndex ],$row = $.fn.closest ? $cell.closest( 'tr' ) : $cell.parents( 'tr' ).filter( ':first' );cell = $cell[ 0 ]; // in case cell is a jQuery object// tbody may not exist if update is initialized while tbody is removed for processingif ( $tbodies.length && tbodyIndex >= 0 ) {row = $tbodies.eq( tbodyIndex ).find( 'tr' ).not( '.' + c.cssChildRow ).index( $row );cache = tbcache.normalized[ row ];len = $row[ 0 ].cells.length;if ( len !== c.columns ) {// colspan in here somewhere!icell = 0;tmp = false;for ( indx = 0; indx < len; indx++ ) {if ( !tmp && $row[ 0 ].cells[ indx ] !== cell ) {icell += $row[ 0 ].cells[ indx ].colSpan;} else {tmp = true;}}} else {icell = $cell.index();}tmp = ts.getElementText( c, cell, icell ); // rawcache[ c.columns ].raw[ icell ] = tmp;tmp = ts.getParsedText( c, cell, icell, tmp );cache[ icell ] = tmp; // parsedif ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) {// update column max value (ignore sign)tbcache.colMax[ icell ] = Math.max( Math.abs( tmp ) || 0, tbcache.colMax[ icell ] || 0 );}tmp = resort !== 'undefined' ? resort : c.resort;if ( tmp !== false ) {// widgets will be reappliedts.checkResort( c, tmp, callback );} else {// don't reapply widgets is resort is false, just in case it causes// problems with element focusts.resortComplete( c, callback );}} else {if ( c.debug ) {console.error( 'updateCell aborted, tbody missing or not within the indicated table' );}c.table.isUpdating = false;}},
addRows : function( c, $row, resort, callback ) {var txt, val, tbodyIndex, rowIndex, rows, cellIndex, len, order,cacheIndex, rowData, cells, cell, span,// allow passing a row string if only one non-info tbody exists in the tablevalid = typeof $row === 'string' && c.$tbodies.length === 1 && /<tr/.test( $row || '' ),table = c.table;if ( valid ) {$row = $( $row );c.$tbodies.append( $row );} else if ( !$row ||// row is a jQuery object?!( $row instanceof jQuery ) ||// row contained in the table?( $.fn.closest ? $row.closest( 'table' )[ 0 ] : $row.parents( 'table' )[ 0 ] ) !== c.table ) {if ( c.debug ) {console.error( 'addRows method requires (1) a jQuery selector reference to rows that have already ' +'been added to the table, or (2) row HTML string to be added to a table with only one tbody' );}return false;}table.isUpdating = true;if ( ts.isEmptyObject( c.cache ) ) {// empty table, do an update instead - fixes #450ts.updateHeader( c );ts.commonUpdate( c, resort, callback );} else {rows = $row.filter( 'tr' ).attr( 'role', 'row' ).length;tbodyIndex = c.$tbodies.index( $row.parents( 'tbody' ).filter( ':first' ) );// fixes adding rows to an empty table - see issue #179if ( !( c.parsers && c.parsers.length ) ) {ts.setupParsers( c );}// add each rowfor ( rowIndex = 0; rowIndex < rows; rowIndex++ ) {cacheIndex = 0;len = $row[ rowIndex ].cells.length;order = c.cache[ tbodyIndex ].normalized.length;cells = [];rowData = {child : [],raw : [],$row : $row.eq( rowIndex ),order : order};// add each cellfor ( cellIndex = 0; cellIndex < len; cellIndex++ ) {cell = $row[ rowIndex ].cells[ cellIndex ];txt = ts.getElementText( c, cell, cacheIndex );rowData.raw[ cacheIndex ] = txt;val = ts.getParsedText( c, cell, cacheIndex, txt );cells[ cacheIndex ] = val;if ( ( c.parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) {// update column max value (ignore sign)c.cache[ tbodyIndex ].colMax[ cacheIndex ] =Math.max( Math.abs( val ) || 0, c.cache[ tbodyIndex ].colMax[ cacheIndex ] || 0 );}span = cell.colSpan - 1;if ( span > 0 ) {cacheIndex += span;}cacheIndex++;}// add the row data to the endcells[ c.columns ] = rowData;// update cachec.cache[ tbodyIndex ].normalized[ order ] = cells;}// resort using current settingsts.checkResort( c, resort, callback );}},
updateCache : function( c, callback, $tbodies ) {// rebuild parsersif ( !( c.parsers && c.parsers.length ) ) {ts.setupParsers( c, $tbodies );}// rebuild the cache mapts.buildCache( c, callback, $tbodies );},
// init flag (true) used by pager plugin to prevent widget application// renamed from appendToTableappendCache : function( c, init ) {var parsed, totalRows, $tbody, $curTbody, rowIndex, tbodyIndex, appendTime,table = c.table,wo = c.widgetOptions,$tbodies = c.$tbodies,rows = [],cache = c.cache;// empty table - fixes #206/#346if ( ts.isEmptyObject( cache ) ) {// run pager appender in case the table was just emptiedreturn c.appender ? c.appender( table, rows ) :table.isUpdating ? c.$table.triggerHandler( 'updateComplete', table ) : ''; // Fixes #532}if ( c.debug ) {appendTime = new Date();}for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) {$tbody = $tbodies.eq( tbodyIndex );if ( $tbody.length ) {// detach tbody for manipulation$curTbody = ts.processTbody( table, $tbody, true );parsed = cache[ tbodyIndex ].normalized;totalRows = parsed.length;for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) {rows[rows.length] = parsed[ rowIndex ][ c.columns ].$row;// removeRows used by the pager plugin; don't render if using ajax - fixes #411if ( !c.appender || ( c.pager && ( !c.pager.removeRows || !wo.pager_removeRows ) && !c.pager.ajax ) ) {$curTbody.append( parsed[ rowIndex ][ c.columns ].$row );}}// restore tbodyts.processTbody( table, $curTbody, false );}}if ( c.appender ) {c.appender( table, rows );}if ( c.debug ) {console.log( 'Rebuilt table' + ts.benchmark( appendTime ) );}// apply table widgets; but not before ajax completesif ( !init && !c.appender ) {ts.applyWidget( table );}if ( table.isUpdating ) {c.$table.triggerHandler( 'updateComplete', table );}},
commonUpdate : function( c, resort, callback ) {// remove rows/elements before updatec.$table.find( c.selectorRemove ).remove();// rebuild parsersts.setupParsers( c );// rebuild the cache mapts.buildCache( c );ts.checkResort( c, resort, callback );},
/*▄█████ ▄████▄ █████▄ ██████ ██ █████▄ ▄████▄▀█▄    ██  ██ ██▄▄██   ██   ██ ██  ██ ██ ▄▄▄   ▀█▄ ██  ██ ██▀██    ██   ██ ██  ██ ██ ▀███████▀ ▀████▀ ██  ██   ██   ██ ██  ██ ▀████▀*/initSort : function( c, cell, event ) {if ( c.table.isUpdating ) {// let any updates complete before initializing a sortreturn setTimeout( function(){ts.initSort( c, cell, event );}, 50 );}
var arry, indx, headerIndx, dir, temp, tmp, $header,notMultiSort = !event[ c.sortMultiSortKey ],table = c.table,len = c.$headers.length,// get current column indexcol = parseInt( $( cell ).attr( 'data-column' ), 10 ),order = c.sortVars[ col ].order;
// Only call sortStart if sorting is enabledc.$table.triggerHandler( 'sortStart', table );// get current column sort ordertmp = ( c.sortVars[ col ].count + 1 ) % order.length;c.sortVars[ col ].count = event[ c.sortResetKey ] ? 2 : tmp;// reset all sorts on non-current column - issue #30if ( c.sortRestart ) {for ( headerIndx = 0; headerIndx < len; headerIndx++ ) {$header = c.$headers.eq( headerIndx );tmp = parseInt( $header.attr( 'data-column' ), 10 );// only reset counts on columns that weren't just clicked on and if not included in a multisortif ( col !== tmp && ( notMultiSort || $header.hasClass( ts.css.sortNone ) ) ) {c.sortVars[ tmp ].count = -1;}}}// user only wants to sort on one columnif ( notMultiSort ) {// flush the sort listc.sortList = [];c.last.sortList = [];if ( c.sortForce !== null ) {arry = c.sortForce;for ( indx = 0; indx < arry.length; indx++ ) {if ( arry[ indx ][ 0 ] !== col ) {c.sortList[ c.sortList.length ] = arry[ indx ];}}}// add column to sort listdir = order[ c.sortVars[ col ].count ];if ( dir < 2 ) {c.sortList[ c.sortList.length ] = [ col, dir ];// add other columns if header spans across multipleif ( cell.colSpan > 1 ) {for ( indx = 1; indx < cell.colSpan; indx++ ) {c.sortList[ c.sortList.length ] = [ col + indx, dir ];// update count on columns in colSpanc.sortVars[ col + indx ].count = $.inArray( dir, order );}}}// multi column sorting} else {// get rid of the sortAppend before adding more - fixes issue #115 & #523c.sortList = $.extend( [], c.last.sortList );
// the user has clicked on an already sorted columnif ( ts.isValueInArray( col, c.sortList ) >= 0 ) {// reverse the sorting directionfor ( indx = 0; indx < c.sortList.length; indx++ ) {tmp = c.sortList[ indx ];if ( tmp[ 0 ] === col ) {// order.count seems to be incorrect when compared to cell.counttmp[ 1 ] = order[ c.sortVars[ col ].count ];if ( tmp[1] === 2 ) {c.sortList.splice( indx, 1 );c.sortVars[ col ].count = -1;}}}} else {// add column to sort list arraydir = order[ c.sortVars[ col ].count ];if ( dir < 2 ) {c.sortList[ c.sortList.length ] = [ col, dir ];// add other columns if header spans across multipleif ( cell.colSpan > 1 ) {for ( indx = 1; indx < cell.colSpan; indx++ ) {c.sortList[ c.sortList.length ] = [ col + indx, dir ];// update count on columns in colSpanc.sortVars[ col + indx ].count = $.inArray( dir, order );}}}}}// save sort before applying sortAppendc.last.sortList = $.extend( [], c.sortList );if ( c.sortList.length && c.sortAppend ) {arry = $.isArray( c.sortAppend ) ? c.sortAppend : c.sortAppend[ c.sortList[ 0 ][ 0 ] ];if ( !ts.isEmptyObject( arry ) ) {for ( indx = 0; indx < arry.length; indx++ ) {if ( arry[ indx ][ 0 ] !== col && ts.isValueInArray( arry[ indx ][ 0 ], c.sortList ) < 0 ) {dir = arry[ indx ][ 1 ];temp = ( '' + dir ).match( /^(a|d|s|o|n)/ );if ( temp ) {tmp = c.sortList[ 0 ][ 1 ];switch ( temp[ 0 ] ) {case 'd' :dir = 1;break;case 's' :dir = tmp;break;case 'o' :dir = tmp === 0 ? 1 : 0;break;case 'n' :dir = ( tmp + 1 ) % order.length;break;default:dir = 0;break;}}c.sortList[ c.sortList.length ] = [ arry[ indx ][ 0 ], dir ];}}}}// sortBegin event triggered immediately before the sortc.$table.triggerHandler( 'sortBegin', table );// setTimeout needed so the processing icon shows upsetTimeout( function() {// set css for headersts.setHeadersCss( c );ts.multisort( c );ts.appendCache( c );c.$table.triggerHandler( 'sortBeforeEnd', table );c.$table.triggerHandler( 'sortEnd', table );}, 1 );},
// sort multiple columnsmultisort : function( c ) { /*jshint loopfunc:true */var tbodyIndex, sortTime, colMax, rows, tmp,table = c.table,sorter = [],dir = 0,textSorter = c.textSorter || '',sortList = c.sortList,sortLen = sortList.length,len = c.$tbodies.length;if ( c.serverSideSorting || ts.isEmptyObject( c.cache ) ) {// empty table - fixes #206/#346return;}if ( c.debug ) { sortTime = new Date(); }// cache textSorter to optimize speedif ( typeof textSorter === 'object' ) {colMax = c.columns;while ( colMax-- ) {tmp = ts.getColumnData( table, textSorter, colMax );if ( typeof tmp === 'function' ) {sorter[ colMax ] = tmp;}}}for ( tbodyIndex = 0; tbodyIndex < len; tbodyIndex++ ) {colMax = c.cache[ tbodyIndex ].colMax;rows = c.cache[ tbodyIndex ].normalized;
rows.sort( function( a, b ) {var sortIndex, num, col, order, sort, x, y;// rows is undefined here in IE, so don't use it!for ( sortIndex = 0; sortIndex < sortLen; sortIndex++ ) {col = sortList[ sortIndex ][ 0 ];order = sortList[ sortIndex ][ 1 ];// sort direction, true = asc, false = descdir = order === 0;
if ( c.sortStable && a[ col ] === b[ col ] && sortLen === 1 ) {return a[ c.columns ].order - b[ c.columns ].order;}
// fallback to natural sort since it is more robustnum = /n/i.test( ts.getSortType( c.parsers, col ) );if ( num && c.strings[ col ] ) {// sort strings in numerical columnsif ( typeof ( ts.string[ c.strings[ col ] ] ) === 'boolean' ) {num = ( dir ? 1 : -1 ) * ( ts.string[ c.strings[ col ] ] ? -1 : 1 );} else {num = ( c.strings[ col ] ) ? ts.string[ c.strings[ col ] ] || 0 : 0;}// fall back to built-in numeric sort// var sort = $.tablesorter['sort' + s]( a[col], b[col], dir, colMax[col], table );sort = c.numberSorter ? c.numberSorter( a[ col ], b[ col ], dir, colMax[ col ], table ) :ts[ 'sortNumeric' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], num, colMax[ col ], col, c );} else {// set a & b depending on sort directionx = dir ? a : b;y = dir ? b : a;// text sort functionif ( typeof textSorter === 'function' ) {// custom OVERALL text sortersort = textSorter( x[ col ], y[ col ], dir, col, table );} else if ( typeof sorter[ col ] === 'function' ) {// custom text sorter for a SPECIFIC COLUMNsort = sorter[ col ]( x[ col ], y[ col ], dir, col, table );} else {// fall back to natural sortsort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], col, c );}}if ( sort ) { return sort; }}return a[ c.columns ].order - b[ c.columns ].order;});}if ( c.debug ) {console.log( 'Applying sort ' + sortList.toString() + ts.benchmark( sortTime ) );}},
resortComplete : function( c, callback ) {if ( c.table.isUpdating ) {c.$table.triggerHandler( 'updateComplete', c.table );}if ( $.isFunction( callback ) ) {callback( c.table );}},
checkResort : function( c, resort, callback ) {var sortList = $.isArray( resort ) ? resort : c.sortList,// if no resort parameter is passed, fallback to config.resort (true by default)resrt = typeof resort === 'undefined' ? c.resort : resort;// don't try to resort if the table is still processing// this will catch spamming of the updateCell methodif ( resrt !== false && !c.serverSideSorting && !c.table.isProcessing ) {if ( sortList.length ) {ts.sortOn( c, sortList, function() {ts.resortComplete( c, callback );}, true );} else {ts.sortReset( c, function() {ts.resortComplete( c, callback );ts.applyWidget( c.table, false );} );}} else {ts.resortComplete( c, callback );ts.applyWidget( c.table, false );}},
sortOn : function( c, list, callback, init ) {var table = c.table;c.$table.triggerHandler( 'sortStart', table );// update header count indexts.updateHeaderSortCount( c, list );// set css for headersts.setHeadersCss( c );// fixes #346if ( c.delayInit && ts.isEmptyObject( c.cache ) ) {ts.buildCache( c );}c.$table.triggerHandler( 'sortBegin', table );// sort the table and append it to the domts.multisort( c );ts.appendCache( c, init );c.$table.triggerHandler( 'sortBeforeEnd', table );c.$table.triggerHandler( 'sortEnd', table );ts.applyWidget( table );if ( $.isFunction( callback ) ) {callback( table );}},
sortReset : function( c, callback ) {c.sortList = [];ts.setHeadersCss( c );ts.multisort( c );ts.appendCache( c );var indx;for (indx = 0; indx < c.columns; indx++) {c.sortVars[ indx ].count = -1;}if ( $.isFunction( callback ) ) {callback( c.table );}},
getSortType : function( parsers, column ) {return ( parsers && parsers[ column ] ) ? parsers[ column ].type || '' : '';},
getOrder : function( val ) {// look for 'd' in 'desc' order; return truereturn ( /^d/i.test( val ) || val === 1 );},
// Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed)sortNatural : function( a, b ) {if ( a === b ) { return 0; }a = a.toString();b = b.toString();var aNum, bNum, aFloat, bFloat, indx, max,regex = ts.regex;// first try and sort Hex codesif ( regex.hex.test( b ) ) {aNum = parseInt( ( a || '' ).match( regex.hex ), 16 );bNum = parseInt( ( b || '' ).match( regex.hex ), 16 );if ( aNum < bNum ) { return -1; }if ( aNum > bNum ) { return 1; }}// chunk/tokenizeaNum = ( a || '' ).replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' );bNum = ( b || '' ).replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' );max = Math.max( aNum.length, bNum.length );// natural sorting through split numeric strings and default stringsfor ( indx = 0; indx < max; indx++ ) {// find floats not starting with '0', string or 0 if not definedaFloat = isNaN( aNum[ indx ] ) ? aNum[ indx ] || 0 : parseFloat( aNum[ indx ] ) || 0;bFloat = isNaN( bNum[ indx ] ) ? bNum[ indx ] || 0 : parseFloat( bNum[ indx ] ) || 0;// handle numeric vs string comparison - number < string - (Kyle Adams)if ( isNaN( aFloat ) !== isNaN( bFloat ) ) { return isNaN( aFloat ) ? 1 : -1; }// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'if ( typeof aFloat !== typeof bFloat ) {aFloat += '';bFloat += '';}if ( aFloat < bFloat ) { return -1; }if ( aFloat > bFloat ) { return 1; }}return 0;},
sortNaturalAsc : function( a, b, col, c ) {if ( a === b ) { return 0; }var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ];if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : -empty || -1; }if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : empty || 1; }return ts.sortNatural( a, b );},
sortNaturalDesc : function( a, b, col, c ) {if ( a === b ) { return 0; }var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ];if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : empty || 1; }if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : -empty || -1; }return ts.sortNatural( b, a );},
// basic alphabetical sortsortText : function( a, b ) {return a > b ? 1 : ( a < b ? -1 : 0 );},
// return text string value by adding up ascii value// so the text is somewhat sorted when using a digital sort// this is NOT an alphanumeric sortgetTextValue : function( val, num, max ) {if ( max ) {// make sure the text value is greater than the max numerical value (max)var indx,len = val ? val.length : 0,n = max + num;for ( indx = 0; indx < len; indx++ ) {n += val.charCodeAt( indx );}return num * n;}return 0;},
sortNumericAsc : function( a, b, num, max, col, c ) {if ( a === b ) { return 0; }var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ];if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : -empty || -1; }if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : empty || 1; }if ( isNaN( a ) ) { a = ts.getTextValue( a, num, max ); }if ( isNaN( b ) ) { b = ts.getTextValue( b, num, max ); }return a - b;},
sortNumericDesc : function( a, b, num, max, col, c ) {if ( a === b ) { return 0; }var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ];if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : empty || 1; }if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : -empty || -1; }if ( isNaN( a ) ) { a = ts.getTextValue( a, num, max ); }if ( isNaN( b ) ) { b = ts.getTextValue( b, num, max ); }return b - a;},
sortNumeric : function( a, b ) {return a - b;},
/*██ ██ ██ ██ █████▄ ▄████▄ ██████ ██████ ▄███████ ██ ██ ██ ██  ██ ██ ▄▄▄ ██▄▄     ██   ▀█▄██ ██ ██ ██ ██  ██ ██ ▀██ ██▀▀     ██      ▀█▄███████▀ ██ █████▀ ▀████▀ ██████   ██   █████▀*/addWidget : function( widget ) {if ( widget.id && !ts.isEmptyObject( ts.getWidgetById( widget.id ) ) ) {console.warn( '"' + widget.id + '" widget was loaded more than once!' );}ts.widgets[ ts.widgets.length ] = widget;},
hasWidget : function( $table, name ) {$table = $( $table );return $table.length && $table[ 0 ].config && $table[ 0 ].config.widgetInit[ name ] || false;},
getWidgetById : function( name ) {var indx, widget,len = ts.widgets.length;for ( indx = 0; indx < len; indx++ ) {widget = ts.widgets[ indx ];if ( widget && widget.id && widget.id.toLowerCase() === name.toLowerCase() ) {return widget;}}},
applyWidgetOptions : function( table ) {var indx, widget, wo,c = table.config,len = c.widgets.length;if ( len ) {for ( indx = 0; indx < len; indx++ ) {widget = ts.getWidgetById( c.widgets[ indx ] );if ( widget && widget.options ) {wo = $.extend( true, {}, widget.options );c.widgetOptions = $.extend( true, wo, c.widgetOptions );// add widgetOptions to defaults for option validator$.extend( true, ts.defaults.widgetOptions, widget.options );}}}},
addWidgetFromClass : function( table ) {var len, indx,c = table.config,// look for widgets to apply from table class// don't match from 'ui-widget-content'; use \S instead of \w to include widgets// with dashes in the name, e.g. "widget-test-2" extracts out "test-2"regex = '^' + c.widgetClass.replace( ts.regex.templateName, '(\\S+)+' ) + '$',widgetClass = new RegExp( regex, 'g' ),// split up table class (widget id's can include dashes) - stop using match// otherwise only one widget gets extracted, see #1109widgets = ( table.className || '' ).split( ts.regex.spaces );if ( widgets.length ) {len = widgets.length;for ( indx = 0; indx < len; indx++ ) {if ( widgets[ indx ].match( widgetClass ) ) {c.widgets[ c.widgets.length ] = widgets[ indx ].replace( widgetClass, '$1' );}}}},
applyWidgetId : function( table, id, init ) {table = $(table)[0];var applied, time, name,c = table.config,wo = c.widgetOptions,widget = ts.getWidgetById( id );if ( widget ) {name = widget.id;applied = false;// add widget name to option list so it gets reapplied after sorting, filtering, etcif ( $.inArray( name, c.widgets ) < 0 ) {c.widgets[ c.widgets.length ] = name;}if ( c.debug ) { time = new Date(); }
if ( init || !( c.widgetInit[ name ] ) ) {// set init flag first to prevent calling init more than once (e.g. pager)c.widgetInit[ name ] = true;if ( table.hasInitialized ) {// don't reapply widget options on tablesorter initts.applyWidgetOptions( table );}if ( typeof widget.init === 'function' ) {applied = true;if ( c.debug ) {console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' );}widget.init( table, widget, c, wo );}}if ( !init && typeof widget.format === 'function' ) {applied = true;if ( c.debug ) {console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' );}widget.format( table, c, wo, false );}if ( c.debug ) {if ( applied ) {console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time ) );if ( console.groupEnd ) { console.groupEnd(); }}}}},
applyWidget : function( table, init, callback ) {table = $( table )[ 0 ]; // in case this is called externallyvar indx, len, names, widget, time,c = table.config,widgets = [];// prevent numerous consecutive widget applicationsif ( init !== false && table.hasInitialized && ( table.isApplyingWidgets || table.isUpdating ) ) {return;}if ( c.debug ) { time = new Date(); }ts.addWidgetFromClass( table );// prevent "tablesorter-ready" from firing multiple times in a rowclearTimeout( c.timerReady );if ( c.widgets.length ) {table.isApplyingWidgets = true;// ensure unique widget idsc.widgets = $.grep( c.widgets, function( val, index ) {return $.inArray( val, c.widgets ) === index;});names = c.widgets || [];len = names.length;// build widget array & add priority as neededfor ( indx = 0; indx < len; indx++ ) {widget = ts.getWidgetById( names[ indx ] );if ( widget && widget.id ) {// set priority to 10 if not definedif ( !widget.priority ) { widget.priority = 10; }widgets[ indx ] = widget;} else if ( c.debug ) {console.warn( '"' + names[ indx ] + '" widget code does not exist!' );}}// sort widgets by prioritywidgets.sort( function( a, b ) {return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1;});// add/update selected widgetslen = widgets.length;if ( c.debug ) {console[ console.group ? 'group' : 'log' ]( 'Start ' + ( init ? 'initializing' : 'applying' ) + ' widgets' );}for ( indx = 0; indx < len; indx++ ) {widget = widgets[ indx ];if ( widget && widget.id ) {ts.applyWidgetId( table, widget.id, init );}}if ( c.debug && console.groupEnd ) { console.groupEnd(); }}c.timerReady = setTimeout( function() {table.isApplyingWidgets = false;$.data( table, 'lastWidgetApplication', new Date() );c.$table.triggerHandler( 'tablesorter-ready' );// callback executed on init onlyif ( !init && typeof callback === 'function' ) {callback( table );}if ( c.debug ) {widget = c.widgets.length;console.log( 'Completed ' +( init === true ? 'initializing ' : 'applying ' ) + widget +' widget' + ( widget !== 1 ? 's' : '' ) + ts.benchmark( time ) );}}, 10 );},
removeWidget : function( table, name, refreshing ) {table = $( table )[ 0 ];var index, widget, indx, len,c = table.config;// if name === true, add all widgets from $.tablesorter.widgetsif ( name === true ) {name = [];len = ts.widgets.length;for ( indx = 0; indx < len; indx++ ) {widget = ts.widgets[ indx ];if ( widget && widget.id ) {name[ name.length ] = widget.id;}}} else {// name can be either an array of widgets names,// or a space/comma separated list of widget namesname = ( $.isArray( name ) ? name.join( ',' ) : name || '' ).toLowerCase().split( /[\s,]+/ );}len = name.length;for ( index = 0; index < len; index++ ) {widget = ts.getWidgetById( name[ index ] );indx = $.inArray( name[ index ], c.widgets );// don't remove the widget from config.widget if refreshingif ( indx >= 0 && refreshing !== true ) {c.widgets.splice( indx, 1 );}if ( widget && widget.remove ) {if ( c.debug ) {console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[ index ] + '" widget' );}widget.remove( table, c, c.widgetOptions, refreshing );c.widgetInit[ name[ index ] ] = false;}}},
refreshWidgets : function( table, doAll, dontapply ) {table = $( table )[ 0 ]; // see issue #243var indx, widget,c = table.config,curWidgets = c.widgets,widgets = ts.widgets,len = widgets.length,list = [],callback = function( table ) {$( table ).triggerHandler( 'refreshComplete' );};// remove widgets not defined in config.widgets, unless doAll is truefor ( indx = 0; indx < len; indx++ ) {widget = widgets[ indx ];if ( widget && widget.id && ( doAll || $.inArray( widget.id, curWidgets ) < 0 ) ) {list[ list.length ] = widget.id;}}ts.removeWidget( table, list.join( ',' ), true );if ( dontapply !== true ) {// call widget init ifts.applyWidget( table, doAll || false, callback );if ( doAll ) {// apply widget formatts.applyWidget( table, false, callback );}} else {callback( table );}},
/*██  ██ ██████ ██ ██     ██ ██████ ██ ██████ ▄███████  ██   ██   ██ ██     ██   ██   ██ ██▄▄   ▀█▄██  ██   ██   ██ ██     ██   ██   ██ ██▀▀      ▀█▄▀████▀   ██   ██ ██████ ██   ██   ██ ██████ █████▀*/benchmark : function( diff ) {return ( ' (' + ( new Date().getTime() - diff.getTime() ) + ' ms)' );},// deprecated ts.loglog : function() {console.log( arguments );},
// $.isEmptyObject from jQuery v1.4isEmptyObject : function( obj ) {/*jshint forin: false */for ( var name in obj ) {return false;}return true;},
isValueInArray : function( column, arry ) {var indx,len = arry && arry.length || 0;for ( indx = 0; indx < len; indx++ ) {if ( arry[ indx ][ 0 ] === column ) {return indx;}}return -1;},
formatFloat : function( str, table ) {if ( typeof str !== 'string' || str === '' ) { return str; }// allow using formatFloat without a table; defaults to US number formatvar num,usFormat = table && table.config ? table.config.usNumberFormat !== false :typeof table !== 'undefined' ? table : true;if ( usFormat ) {// US Format - 1,234,567.89 -> 1234567.89str = str.replace( ts.regex.comma, '' );} else {// German Format = 1.234.567,89 -> 1234567.89// French Format = 1 234 567,89 -> 1234567.89str = str.replace( ts.regex.digitNonUS, '' ).replace( ts.regex.comma, '.' );}if ( ts.regex.digitNegativeTest.test( str ) ) {// make (#) into a negative number -> (10) = -10str = str.replace( ts.regex.digitNegativeReplace, '-$1' );}num = parseFloat( str );// return the text instead of zeroreturn isNaN( num ) ? $.trim( str ) : num;},
isDigit : function( str ) {// replace all unwanted chars and matchreturn isNaN( str ) ?ts.regex.digitTest.test( str.toString().replace( ts.regex.digitReplace, '' ) ) :str !== '';},
// computeTableHeaderCellIndexes from:// http://www.javascripttoolbox.com/lib/table/examples.php// http://www.javascripttoolbox.com/temp/table_cellindex.htmlcomputeColumnIndex : function( $rows, c ) {var i, j, k, l, cell, cells, rowIndex, rowSpan, colSpan, firstAvailCol,// total columns has been calculated, use it to set the matrixrowcolumns = c && c.columns || 0,matrix = [],matrixrow = new Array( columns );for ( i = 0; i < $rows.length; i++ ) {cells = $rows[ i ].cells;for ( j = 0; j < cells.length; j++ ) {cell = cells[ j ];rowIndex = cell.parentNode.rowIndex;rowSpan = cell.rowSpan || 1;colSpan = cell.colSpan || 1;if ( typeof matrix[ rowIndex ] === 'undefined' ) {matrix[ rowIndex ] = [];}// Find first available column in the first rowfor ( k = 0; k < matrix[ rowIndex ].length + 1; k++ ) {if ( typeof matrix[ rowIndex ][ k ] === 'undefined' ) {firstAvailCol = k;break;}}// jscs:disable disallowEmptyBlocksif ( columns && cell.cellIndex === firstAvailCol ) {// don't to anything} else if ( cell.setAttribute ) {// jscs:enable disallowEmptyBlocks// add data-column (setAttribute = IE8+)cell.setAttribute( 'data-column', firstAvailCol );} else {// remove once we drop support for IE7 - 1/12/2016$( cell ).attr( 'data-column', firstAvailCol );}for ( k = rowIndex; k < rowIndex + rowSpan; k++ ) {if ( typeof matrix[ k ] === 'undefined' ) {matrix[ k ] = [];}matrixrow = matrix[ k ];for ( l = firstAvailCol; l < firstAvailCol + colSpan; l++ ) {matrixrow[ l ] = 'x';}}}}return matrixrow.length;},
// automatically add a colgroup with col elements set to a percentage widthfixColumnWidth : function( table ) {table = $( table )[ 0 ];var overallWidth, percent, $tbodies, len, index,c = table.config,$colgroup = c.$table.children( 'colgroup' );// remove plugin-added colgroup, in case we need to refresh the widthsif ( $colgroup.length && $colgroup.hasClass( ts.css.colgroup ) ) {$colgroup.remove();}if ( c.widthFixed && c.$table.children( 'colgroup' ).length === 0 ) {$colgroup = $( '<colgroup class="' + ts.css.colgroup + '">' );overallWidth = c.$table.width();// only add col for visible columns - fixes #371$tbodies = c.$tbodies.find( 'tr:first' ).children( ':visible' );len = $tbodies.length;for ( index = 0; index < len; index++ ) {percent = parseInt( ( $tbodies.eq( index ).width() / overallWidth ) * 1000, 10 ) / 10 + '%';$colgroup.append( $( '<col>' ).css( 'width', percent ) );}c.$table.prepend( $colgroup );}},
// get sorter, string, empty, etc options for each column from// jQuery data, metadata, header option or header class name ('sorter-false')// priority = jQuery data > meta > headers option > header class namegetData : function( header, configHeader, key ) {var meta, cl4ss,val = '',$header = $( header );if ( !$header.length ) { return ''; }meta = $.metadata ? $header.metadata() : false;cl4ss = ' ' + ( $header.attr( 'class' ) || '' );if ( typeof $header.data( key ) !== 'undefined' ||typeof $header.data( key.toLowerCase() ) !== 'undefined' ) {// 'data-lockedOrder' is assigned to 'lockedorder'; but 'data-locked-order' is assigned to 'lockedOrder'// 'data-sort-initial-order' is assigned to 'sortInitialOrder'val += $header.data( key ) || $header.data( key.toLowerCase() );} else if ( meta && typeof meta[ key ] !== 'undefined' ) {val += meta[ key ];} else if ( configHeader && typeof configHeader[ key ] !== 'undefined' ) {val += configHeader[ key ];} else if ( cl4ss !== ' ' && cl4ss.match( ' ' + key + '-' ) ) {// include sorter class name 'sorter-text', etc; now works with 'sorter-my-custom-parser'val = cl4ss.match( new RegExp( '\\s' + key + '-([\\w-]+)' ) )[ 1 ] || '';}return $.trim( val );},
getColumnData : function( table, obj, indx, getCell, $headers ) {if ( typeof obj !== 'object' || obj === null ) {return obj;}table = $( table )[ 0 ];var $header, key,c = table.config,$cells = ( $headers || c.$headers ),// c.$headerIndexed is not defined initially$cell = c.$headerIndexed && c.$headerIndexed[ indx ] ||$cells.filter( '[data-column="' + indx + '"]:last' );if ( typeof obj[ indx ] !== 'undefined' ) {return getCell ? obj[ indx ] : obj[ $cells.index( $cell ) ];}for ( key in obj ) {if ( typeof key === 'string' ) {$header = $cell// header cell with class/id.filter( key )// find elements within the header cell with cell/id.add( $cell.find( key ) );if ( $header.length ) {return obj[ key ];}}}return;},
// *** Process table ***// add processing indicatorisProcessing : function( $table, toggle, $headers ) {$table = $( $table );var c = $table[ 0 ].config,// default to all headers$header = $headers || $table.find( '.' + ts.css.header );if ( toggle ) {// don't use sortList if custom $headers usedif ( typeof $headers !== 'undefined' && c.sortList.length > 0 ) {// get headers from the sortList$header = $header.filter( function() {// get data-column from attr to keep compatibility with jQuery 1.2.6return this.sortDisabled ?false :ts.isValueInArray( parseFloat( $( this ).attr( 'data-column' ) ), c.sortList ) >= 0;});}$table.add( $header ).addClass( ts.css.processing + ' ' + c.cssProcessing );} else {$table.add( $header ).removeClass( ts.css.processing + ' ' + c.cssProcessing );}},
// detach tbody but save the position// don't use tbody because there are portions that look for a tbody index (updateCell)processTbody : function( table, $tb, getIt ) {table = $( table )[ 0 ];if ( getIt ) {table.isProcessing = true;$tb.before( '<colgroup class="tablesorter-savemyplace"/>' );return $.fn.detach ? $tb.detach() : $tb.remove();}var holdr = $( table ).find( 'colgroup.tablesorter-savemyplace' );$tb.insertAfter( holdr );holdr.remove();table.isProcessing = false;},
clearTableBody : function( table ) {$( table )[ 0 ].config.$tbodies.children().detach();},
// used when replacing accented characters during sortingcharacterEquivalents : {'a' : '\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5', // áàâãäąå'A' : '\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5', // ÁÀÂÃÄĄÅ'c' : '\u00e7\u0107\u010d', // çćč'C' : '\u00c7\u0106\u010c', // ÇĆČ'e' : '\u00e9\u00e8\u00ea\u00eb\u011b\u0119', // éèêëěę'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6\u014d', // óòôõöō'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6\u014c', // ÓÒÔÕÖŌ'ss': '\u00df', // ß (s sharp)'SS': '\u1e9e', // ẞ (Capital sharp s)'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů'U' : '\u00da\u00d9\u00db\u00dc\u016e' // ÚÙÛÜŮ},
replaceAccents : function( str ) {var chr,acc = '[',eq = ts.characterEquivalents;if ( !ts.characterRegex ) {ts.characterRegexArray = {};for ( chr in eq ) {if ( typeof chr === 'string' ) {acc += eq[ chr ];ts.characterRegexArray[ chr ] = new RegExp( '[' + eq[ chr ] + ']', 'g' );}}ts.characterRegex = new RegExp( acc + ']' );}if ( ts.characterRegex.test( str ) ) {for ( chr in eq ) {if ( typeof chr === 'string' ) {str = str.replace( ts.characterRegexArray[ chr ], chr );}}}return str;},
validateOptions : function( c ) {var setting, setting2, typ, timer,// ignore options containing an arrayignore = 'headers sortForce sortList sortAppend widgets'.split( ' ' ),orig = c.originalSettings;if ( orig ) {if ( c.debug ) {timer = new Date();}for ( setting in orig ) {typ = typeof ts.defaults[setting];if ( typ === 'undefined' ) {console.warn( 'Tablesorter Warning! "table.config.' + setting + '" option not recognized' );} else if ( typ === 'object' ) {for ( setting2 in orig[setting] ) {typ = ts.defaults[setting] && typeof ts.defaults[setting][setting2];if ( $.inArray( setting, ignore ) < 0 && typ === 'undefined' ) {console.warn( 'Tablesorter Warning! "table.config.' + setting + '.' + setting2 + '" option not recognized' );}}}}if ( c.debug ) {console.log( 'validate options time:' + ts.benchmark( timer ) );}}},
// restore headersrestoreHeaders : function( table ) {var index, $cell,c = $( table )[ 0 ].config,$headers = c.$table.find( c.selectorHeaders ),len = $headers.length;// don't use c.$headers here in case header cells were swappedfor ( index = 0; index < len; index++ ) {$cell = $headers.eq( index );// only restore header cells if it is wrapped// because this is also used by the updateAll methodif ( $cell.find( '.' + ts.css.headerIn ).length ) {$cell.html( c.headerContent[ index ] );}}},
destroy : function( table, removeClasses, callback ) {table = $( table )[ 0 ];if ( !table.hasInitialized ) { return; }// remove all widgetsts.removeWidget( table, true, false );var events,$t = $( table ),c = table.config,debug = c.debug,$h = $t.find( 'thead:first' ),$r = $h.find( 'tr.' + ts.css.headerRow ).removeClass( ts.css.headerRow + ' ' + c.cssHeaderRow ),$f = $t.find( 'tfoot:first > tr' ).children( 'th, td' );if ( removeClasses === false && $.inArray( 'uitheme', c.widgets ) >= 0 ) {// reapply uitheme classes, in case we want to maintain appearance$t.triggerHandler( 'applyWidgetId', [ 'uitheme' ] );$t.triggerHandler( 'applyWidgetId', [ 'zebra' ] );}// remove widget added rows, just in case$h.find( 'tr' ).not( $r ).remove();// disable tablesorter - not using .unbind( namespace ) because namespacing was// added in jQuery v1.4.3 - see http://api.jquery.com/event.namespace/events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' +'appendCache updateCache applyWidgetId applyWidgets refreshWidgets removeWidget destroy mouseup mouseleave ' +'keypress sortBegin sortEnd resetToLoadState '.split( ' ' ).join( c.namespace + ' ' );$t.removeData( 'tablesorter' ).unbind( events.replace( ts.regex.spaces, ' ' ) );c.$headers.add( $f ).removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join( ' ' ) ).removeAttr( 'data-column' ).removeAttr( 'aria-label' ).attr( 'aria-disabled', 'true' );$r.find( c.selectorSort ).unbind( ( 'mousedown mouseup keypress '.split( ' ' ).join( c.namespace + ' ' ) ).replace( ts.regex.spaces, ' ' ) );ts.restoreHeaders( table );$t.toggleClass( ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false );$t.removeClass(c.namespace.slice(1));// clear flag in case the plugin is initialized againtable.hasInitialized = false;delete table.config.cache;if ( typeof callback === 'function' ) {callback( table );}if ( debug ) {console.log( 'tablesorter has been removed' );}}
};
$.fn.tablesorter = function( settings ) {return this.each( function() {var table = this,// merge & extend config optionsc = $.extend( true, {}, ts.defaults, settings, ts.instanceMethods );// save initial settingsc.originalSettings = settings;// create a table from data (build table widget)if ( !table.hasInitialized && ts.buildTable && this.nodeName !== 'TABLE' ) {// return the table (in case the original target is the table's container)ts.buildTable( table, c );} else {ts.setup( table, c );}});};
// set up debug logsif ( !( window.console && window.console.log ) ) {// access $.tablesorter.logs for browsers that don't have a console...ts.logs = [];/*jshint -W020 */console = {};console.log = console.warn = console.error = console.table = function() {var arg = arguments.length > 1 ? arguments : arguments[0];ts.logs[ ts.logs.length ] = { date: Date.now(), log: arg };};}
// add default parsersts.addParser({id : 'no-parser',is : function() {return false;},format : function() {return '';},type : 'text'});
ts.addParser({id : 'text',is : function() {return true;},format : function( str, table ) {var c = table.config;if ( str ) {str = $.trim( c.ignoreCase ? str.toLocaleLowerCase() : str );str = c.sortLocaleCompare ? ts.replaceAccents( str ) : str;}return str;},type : 'text'});
ts.regex.nondigit = /[^\w,. \-()]/g;ts.addParser({id : 'digit',is : function( str ) {return ts.isDigit( str );},format : function( str, table ) {var num = ts.formatFloat( ( str || '' ).replace( ts.regex.nondigit, '' ), table );return str && typeof num === 'number' ? num :str ? $.trim( str && table.config.ignoreCase ? str.toLocaleLowerCase() : str ) : str;},type : 'numeric'});
ts.regex.currencyReplace = /[+\-,. ]/g;ts.regex.currencyTest = /^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/;ts.addParser({id : 'currency',is : function( str ) {str = ( str || '' ).replace( ts.regex.currencyReplace, '' );// test for £$€¤¥¢return ts.regex.currencyTest.test( str );},format : function( str, table ) {var num = ts.formatFloat( ( str || '' ).replace( ts.regex.nondigit, '' ), table );return str && typeof num === 'number' ? num :str ? $.trim( str && table.config.ignoreCase ? str.toLocaleLowerCase() : str ) : str;},type : 'numeric'});
// too many protocols to add them all https://en.wikipedia.org/wiki/URI_scheme// now, this regex can be updated before initializationts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//;ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\/(www\.)?/;ts.addParser({id : 'url',is : function( str ) {return ts.regex.urlProtocolTest.test( str );},format : function( str ) {return str ? $.trim( str.replace( ts.regex.urlProtocolReplace, '' ) ) : str;},type : 'text'});
ts.regex.dash = /-/g;ts.regex.isoDate = /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/;ts.addParser({id : 'isoDate',is : function( str ) {return ts.regex.isoDate.test( str );},format : function( str, table ) {var date = str ? new Date( str.replace( ts.regex.dash, '/' ) ) : str;return date instanceof Date && isFinite( date ) ? date.getTime() : str;},type : 'numeric'});
ts.regex.percent = /%/g;ts.regex.percentTest = /(\d\s*?%|%\s*?\d)/;ts.addParser({id : 'percent',is : function( str ) {return ts.regex.percentTest.test( str ) && str.length < 15;},format : function( str, table ) {return str ? ts.formatFloat( str.replace( ts.regex.percent, '' ), table ) : str;},type : 'numeric'});
// added image parser to core v2.17.9ts.addParser({id : 'image',is : function( str, table, node, $node ) {return $node.find( 'img' ).length > 0;},format : function( str, table, cell ) {return $( cell ).find( 'img' ).attr( table.config.imgAttr || 'alt' ) || str;},parsed : true, // filter widget flagtype : 'text'});
ts.regex.dateReplace = /(\S)([AP]M)$/i; // used by usLongDate & time parserts.regex.usLongDateTest1 = /^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i;ts.regex.usLongDateTest2 = /^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i;ts.addParser({id : 'usLongDate',is : function( str ) {// two digit years are not allowed cross-browser// Jan 01, 2013 12:34:56 PM or 01 Jan 2013return ts.regex.usLongDateTest1.test( str ) || ts.regex.usLongDateTest2.test( str );},format : function( str, table ) {var date = str ? new Date( str.replace( ts.regex.dateReplace, '$1 $2' ) ) : str;return date instanceof Date && isFinite( date ) ? date.getTime() : str;},type : 'numeric'});
// testing for ##-##-#### or ####-##-##, so it's not perfect; time can be includedts.regex.shortDateTest = /(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/;// escaped "-" because JSHint in Firefox was showing it as an errorts.regex.shortDateReplace = /[\-.,]/g;// XXY covers MDY & DMY formatsts.regex.shortDateXXY = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/;ts.regex.shortDateYMD = /(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/;ts.convertFormat = function( dateString, format ) {dateString = ( dateString || '' ).replace( ts.regex.spaces, ' ' ).replace( ts.regex.shortDateReplace, '/' );if ( format === 'mmddyyyy' ) {dateString = dateString.replace( ts.regex.shortDateXXY, '$3/$1/$2' );} else if ( format === 'ddmmyyyy' ) {dateString = dateString.replace( ts.regex.shortDateXXY, '$3/$2/$1' );} else if ( format === 'yyyymmdd' ) {dateString = dateString.replace( ts.regex.shortDateYMD, '$1/$2/$3' );}var date = new Date( dateString );return date instanceof Date && isFinite( date ) ? date.getTime() : '';};
ts.addParser({id : 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd'is : function( str ) {str = ( str || '' ).replace( ts.regex.spaces, ' ' ).replace( ts.regex.shortDateReplace, '/' );return ts.regex.shortDateTest.test( str );},format : function( str, table, cell, cellIndex ) {if ( str ) {var c = table.config,$header = c.$headerIndexed[ cellIndex ],format = $header.length && $header.data( 'dateFormat' ) ||ts.getData( $header, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat' ) ||c.dateFormat;// save format because getData can be slow...if ( $header.length ) {$header.data( 'dateFormat', format );}return ts.convertFormat( str, format ) || str;}return str;},type : 'numeric'});
// match 24 hour time & 12 hours time + am/pm - see http://regexr.com/3c3tkts.regex.timeTest = /^(0?[1-9]|1[0-2]):([0-5]\d)(\s[AP]M)$|^((?:[01]\d|[2][0-4]):[0-5]\d)$/i;ts.regex.timeMatch = /(0?[1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)/i;ts.addParser({id : 'time',is : function( str ) {return ts.regex.timeTest.test( str );},format : function( str, table ) {// isolate time... ignore month, day and yearvar temp,timePart = ( str || '' ).match( ts.regex.timeMatch ),orig = new Date( str ),// no time component? default to 00:00 by leaving it out, but only if str is definedtime = str && ( timePart !== null ? timePart[ 0 ] : '00:00 AM' ),date = time ? new Date( '2000/01/01 ' + time.replace( ts.regex.dateReplace, '$1 $2' ) ) : time;if ( date instanceof Date && isFinite( date ) ) {temp = orig instanceof Date && isFinite( orig ) ? orig.getTime() : 0;// if original string was a valid date, add it to the decimal so the column sorts in some kind of order// luckily new Date() ignores the decimalsreturn temp ? parseFloat( date.getTime() + '.' + orig.getTime() ) : date.getTime();}return str;},type : 'numeric'});
ts.addParser({id : 'metadata',is : function() {return false;},format : function( str, table, cell ) {var c = table.config,p = ( !c.parserMetadataName ) ? 'sortValue' : c.parserMetadataName;return $( cell ).metadata()[ p ];},type : 'numeric'});
/*██████ ██████ █████▄ █████▄ ▄████▄  ▄█▀  ██▄▄   ██▄▄██ ██▄▄██ ██▄▄██▄█▀    ██▀▀   ██▀▀██ ██▀▀█  ██▀▀████████ ██████ █████▀ ██  ██ ██  ██*/// add default widgetsts.addWidget({id : 'zebra',priority : 90,format : function( table, c, wo ) {var $visibleRows, $row, count, isEven, tbodyIndex, rowIndex, len,child = new RegExp( c.cssChildRow, 'i' ),$tbodies = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody:not(.' + c.cssInfoBlock + ')' ) );for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) {// loop through the visible rowscount = 0;$visibleRows = $tbodies.eq( tbodyIndex ).children( 'tr:visible' ).not( c.selectorRemove );len = $visibleRows.length;for ( rowIndex = 0; rowIndex < len; rowIndex++ ) {$row = $visibleRows.eq( rowIndex );// style child rows the same way the parent row was styledif ( !child.test( $row[ 0 ].className ) ) { count++; }isEven = ( count % 2 === 0 );$row.removeClass( wo.zebra[ isEven ? 1 : 0 ] ).addClass( wo.zebra[ isEven ? 0 : 1 ] );}}},remove : function( table, c, wo, refreshing ) {if ( refreshing ) { return; }var tbodyIndex, $tbody,$tbodies = c.$tbodies,toRemove = ( wo.zebra || [ 'even', 'odd' ] ).join( ' ' );for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ){$tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody$tbody.children().removeClass( toRemove );ts.processTbody( table, $tbody, false ); // restore tbody}}});
})( jQuery );

 

posted @ 2017-07-27 15:19  velin  阅读(1441)  评论(0编辑  收藏  举报