text: /**
   * @DateTime.js
   * @author xunxuzhang
   * @version
   * Created: 15-07-03
   */
  
  (function (global, factory) {
      if (typeof define === 'function' && (define.amd || define.cmd)) {
          define(factory);
      } else {
          global.DateTime = factory();
      }
  }(this, function (require, exports, module) {
  	if (typeof require == 'function') {
  		require('common/ui/Follow');
  	} else if (!$().follow) {
  		if (window.console) {
  			console.error('need Follow.js');
  		}
  		return {};
  	}
  
  	/**
       * 日期，时间选择器
  	 * 基于HTML5时间类输入框
  	 * new DateTime(trigger, options);
  	 * @trigger 触发的元素，可以是文本框也可以是文本框容器(父级)
  	 * @options 可选参数
       */
  	
  	var prefixDate = 'ui-date-',
  		prefixRange = 'ui-range-', 
  		prefixDay = 'ui-day-', 
  		prefixYear = 'ui-year-', 
  		prefixMonth = 'ui-month-',
  		prefixHour = 'ui-hour-',
  		prefixMinute = 'ui-minute-';
  	
  	var SELECTED= 'selected', ACTIVE = 'active';
  	
  	var regDate = /\-|\//g;
  
  	// 拓展方法之字符串变时间对象
  	String.prototype.toDate = function() {
  		var year, month, day;
  		var arrDate = this.split(regDate);
  		year = arrDate[0] * 1;
  		month = arrDate[1] || 1;
  		day = arrDate[2] || 1;
  		// 年份需要是数值字符串
  		if (!year) {
  			return new Date();
  		}
  		return new Date(year, month-1, day);
  	};
  	// 日期对象变成年月日数组
  	Date.prototype.toArray = function() {
  		var year = this.getFullYear(), month = this.getMonth() + 1, date = this.getDate();
  		if (month < 10) {
  			month = '0' + month;
  		}
  		if (date < 10) {
  			date = '0' + date;
  		}
  		return [year, month, date];
  	};
  	
  	var DateTime = function(trigger, options) {
  		if (!trigger || !trigger.length) return this;
  		
  		// 默认参数
  		var defaults = {
  			value: '',               // 文本框初始值，优先从文本框获取。
  			type: 'auto',            // 浮层面板类型，可以是'date'(2015-01-01), 'year'(2015), 'month'(2015-01), 'time'(12:00), 'date-range'(2015-01-01 至 2015-06-01)
  			min: 'auto',             // 时间范围最小值，优先从文本框获取。'auto'表示无最小限制。
          	max: 'auto',             // 时间范围最大值，优先从文本框获取。'auto'表示无最大限制。
          	trigger: ['change'],     // 文本框值修改时候触发的文本框事件
  			onShow: $.noop,
  			onHide: $.noop	
  		};
  		
  		var params = $.extend({}, defaults, options || {});
  		
  		// 找到文本输入框
  		var input = null;
  		if (trigger.get(0).type) {
  			input = trigger;
  			trigger = input.parent();
  		} else {
  			input = trigger.find('input');	
  		}
  		// 如果没有时间类输入框，则byebye
  		if (input.length == 0) return this;
  		
  		// readonly
  		input.prop('readonly', true);
  
  		// hover, active style
  		input.parent().hover(function() {
  			$(this).addClass('hover');
  		}, function() {
  			$(this).removeClass('hover');
  		});
  		
  		// 时间选择的类型
  		var type = params.type;
  		if (type == 'auto') {
  			type = input.attr('type') || 'date';	
  		}
  		//if (type == 'date') {
  			// Chrome新版无法通过CSS修改连接符
  			// 因此重置type
  			//try {input.attr('type', '_date_')}catch(e){};	
  		//}
  		
  		// 插入下拉箭头
  		var id = input.attr('id');
  		if (!id) {
  			// 如果没有id, 创建随机id
  			// 下拉箭头需要
  			id = type + (Math.random() + '').replace('0.', '');
  			input.attr('id', id);
  		}
  		// 下拉元素
  		$('<label for="'+ id +'"></label>').addClass(prefixDate + 'arrow')
  		// 插入到文本框的后面
  		.insertAfter(input);
  		
  		// 初始值
  		var initValue = input.val();
  		if (initValue == '' && params.value) {
  			input.val(params.value);
  			initValue = params.value;
  		}
  		
  		// 初始值转换成时间值
  		switch (type) {
  			case 'date': case 'year': case 'month': {
  				// 日期
  				var initDate = initValue.toDate(), arrDate = initDate.toArray();
  				if (initValue == '') {
  					// 赋值今日				
  					if (type == 'date') {									
  						input.val(arrDate.join('-'));
  					} else if (type == 'year') {
  						input.val(arrDate[0]);
  					} else if (type == 'month') {
  						input.val(arrDate.slice(0, 2).join('-'));
  					}
  				}
  				
  				this[SELECTED] = arrDate;  // eg. [2015,07,20]
  
  				break;	
  			}
  			case 'time': case 'hour': case 'minute': {
  				var arrTime = initValue.split(':');
  				// 时间
  				var hour = arrTime[0], minute = arrTime[1];
  				// 这里的处理情况如下
  				// 1. 空值
  				// 2. 小时数不是数值
  				// 3. 小时数不在0~24之间
  				// 以上均认为时间是00:00
  				if (initValue == '' || !(hour < 24 && hour > 0)) {
  					hour = '00';
  					minute = '00';
  				} else {
  					// 分钟无论如何都要是正常值
  					if (!(minute > 0 && minute < 60) || type == 'hour') {
  						minute = '00';
  					} else if (minute.length == 1) {
  						minute = '0' + minute;
  					}
  					// 补0是必要的
  					if (hour.length == 1) {
  						hour = '0' + hour;
  					}				
  				}
  				input.val([hour, minute].join(':'));
  				
  				this[SELECTED] = [hour, minute];
  
  				break;	
  			}
  			case 'date-range': case 'month-range': {
  				// 日期范围
  				var beginDate = new Date(), endDate = new Date();
  				// 前后时间字符串
  				var arrRange = initValue.split(' ');
  				// 有如下一些情况：
  				// 1. 空，则选择范围就是今日
  				// 2. 只有一个时间，则选择范围只这个时间到今天这个范围
  				// 3. 其他就是正常的
  				if (initValue != '' && arrRange.length == 1) {
  					var someDate = arrRange[0].toDate();
  					if (someDate.getTime() > beginDate.getTime()) {
  						endDate = someDate;
  					} else {
  						beginDate = someDate;
  					}
  				} else {
  					beginDate = arrRange[0].toDate();
  					endDate = arrRange[arrRange.length - 1].toDate();
  				}
  				// 赋值
  				var arrBegin = beginDate.toArray(), arrEnd = endDate.toArray();
  				if (type == 'date-range') {
  					input.val(arrBegin.join('-') + ' 至 ' + arrEnd.join('-'));
  				} else {
  					input.val(arrBegin.slice(0,2).join('-') + ' 至 ' + arrEnd.slice(0,2).join('-'));
  				}
  								
  				// 存储
  				this[SELECTED] = [arrBegin, arrEnd];
  
  				break;
  			}
  
  		}
  		
  		var self = this;
  
  		var container = $('<div></div>').addClass(prefixDate + 'container').delegate('a', 'click', function() {
  			// 各个分支中可能会用到的变量
  			var year = 0, month = 0, date = 0, hour = 0;
  
  			// 各种事件
  			switch (container.attr('data-type')) {
  				case 'date': {
  					// 日期选择主体
  					// 1. 前后月份选择
  					if (/prev|next/.test(this.className)) {
  						month = $(this).attr('data-month');
  						self[SELECTED][1] = month * 1;
  
  						// 日期和月份要匹配，例如，不能出现4月31日
  						var monthDay = self._monthDay(self[SELECTED]);
  
  						var day = self[SELECTED][2], 
  							dayOverflow = container.data('dayOverflow'),
  							dayMax = (function() {
  								if (month - 1 < 0) {
  									return monthDay[11];
  								} else if (month > monthDay.length) {
  									return monthDay[0];
  								}
  								return monthDay[month - 1];
  							})();
  
  						// 例如，我们超出日期是31日，如果月份可以满足31日，使用31日
  						if (dayOverflow) {
  							self[SELECTED][2] = Math.min(dayOverflow, dayMax);
  						} else if (self[SELECTED][2] > dayMax) {
  							self[SELECTED][2] = dayMax;
  
  							// 这里是对体验的升级，
  							// 虽然下月份变成了30号，但是再回来时候，原来的31号变成了30号
  							// 不完美，我们需要处理下
  							// 通过一个变量记住，点击item项的时候，移除
  							// 且只在第一次的时候记住
  							// 因为28,29,30,31都可能出现，每次记忆会混乱
  							container.data('dayOverflow', day);
  						}
  
  						self[SELECTED] = self[SELECTED].join('-').toDate().toArray();
  
  						// 刷新
  						self.date();
  
  						// 如果在时间范围内
  						if (container.find('.' + SELECTED).get(0).href)	{
  							self.val();
  						}
  					} else if (/item/.test(this.className)) {
  						// 选择某日期啦
  						date = this.innerHTML;
  						if (/\D/.test(date)) {
  							// 今天
  							self[SELECTED] = new Date().toArray();
  						} else {
  							if (date < 10) date = '0' + date;
  							// 修改全局
  							self[SELECTED][2] = date;
  						}
  						
  						// 赋值
  						self.val();
  						// 隐藏
  						self.hide();
  
  						container.removeData('dayOverflow');
  					} else if ($(this).attr('data-type') == 'month') {
  						// 切换到年份选择
  						self.month();
  					}
  					break;
  				}
  				case 'date-range': {
  					// 区域选择
  					// 1. 前后月份选择
  					if (/prev|next/.test(this.className)) {
  						month = $(this).attr('data-month') * 1;
  
  						var arrRange = self.el.container.data('date') || self[SELECTED][0];
  												
  						// 跟其他面板不同，这里只刷新，点击确定再赋值
  						self.el.container.data('date', new Date(arrRange[0], month - 1, /*arrRange[2]*/1).toArray());
  						// 之前arrRange[2]存在跨多月风险，尤其31号的日子
  						// 刷新
  						self['date-range']();
  					} else if (/item/.test(this.className)) {
  						// 选择某日期
  						// 获得选中的年月日
  						year = $(this).attr('data-year');
  						month = $(this).attr('data-month');
  						date = this.innerHTML;
  						if (month < 10) month = '0' + month;
  						if (date < 10) date = '0' + date;
  						// 根据选中状态决定新的状态
  						var range = self[SELECTED];
  						if (range[0].join() == range[1].join()) {
  							// 如果之前前后日期一样，说明只选中了一个日期
  							// 根据前后顺序改变其中一个日期
  							if (year + month + date > range[0].join('')) {
  								// 新时间靠后
  								range[1] = [year, month, date];
  							} else {
  								range[0] = [year, month, date];
  							}
  						} else {
  							// 如果前后时间不一样，说明现在有范围
  							// 则取消范围，变成单选
  							range = [[year, month, date], [year, month, date]];
  						}
  						self[SELECTED] = range;
  						self['date-range']();
  					} else if (/button/.test(this.className)) {
  						var typeButton = $(this).attr('data-type');
  						if (typeButton == 'ensure') {
  							// 点击确定按钮
  							// 赋值
  							self.val();
  							// 修改存储值
  							self._rangeSelected = self[SELECTED];
  							// 关闭浮层
  							self.hide();
  						} else if (typeButton == 'cancel') {
  							// 重置选中值
  							if (self._rangeSelected) {
  								self[SELECTED] = self._rangeSelected;
  							}
  							// 关闭浮层
  							self.hide();
  						}
  					}
  					break;
  				}
  				case 'month-range': {
  					// 区域选择
  					// 1. 前后年份选择
  					if (/prev|next/.test(this.className)) {
  						year = $(this).attr('data-year') * 1;
  
  						var arrRange = self.el.container.data('date') || self[SELECTED][0];
  												
  						// 跟其他面板不同，这里只刷新，点击确定再赋值
  						self.el.container.data('date', new Date(year, arrRange[1], 1).toArray());
  						// 刷新
  						self['month-range']();
  					} else if (/item/.test(this.className)) {
  						// 选择某日期
  						// 获得选中的年月日
  						year = $(this).attr('data-year');
  						month = $(this).attr('data-value');
  						date = '01';
  						// 根据选中状态决定新的状态
  						var range = self[SELECTED];
  						if (range[0].join() == range[1].join()) {
  							// 如果之前前后日期一样，说明只选中了一个日期
  							// 根据前后顺序改变其中一个日期
  							if (year + month + date > range[0].join('')) {
  								// 新时间靠后
  								range[1] = [year, month, date];
  							} else {
  								range[0] = [year, month, date];
  							}
  						} else {
  							// 如果前后时间不一样，说明现在有范围
  							// 则取消范围，变成单选
  							range = [[year, month, date], [year, month, date]];
  						}
  						self[SELECTED] = range;
  						self['month-range']();
  					} else if (/button/.test(this.className)) {
  						var typeButton = $(this).attr('data-type');
  						if (typeButton == 'ensure') {
  							// 点击确定按钮
  							// 赋值
  							self.val();
  							// 修改存储值
  							self._rangeSelected = self[SELECTED];
  							// 关闭浮层
  							self.hide();
  						} else if (typeButton == 'cancel') {
  							// 重置选中值
  							if (self._rangeSelected) {
  								self[SELECTED] = self._rangeSelected;
  							}
  							// 关闭浮层
  							self.hide();
  						}
  					}
  
  					break;
  				}
  				case 'month': {
  					// 选择月份，可能从年份，也可能从日期过来
  					// 1. 前后年份
  					if (/prev|next/.test(this.className)) {
  						year = $(this).attr('data-year');
  						// 修改当前选中的年份数
  						self[SELECTED][0] = year * 1;
  						// 刷新
  						self.month();
  						// 文本框赋值
  						// 如果在区域内状态
  						if (container.find('.' + SELECTED).get(0).href)	{
  							self.val();
  						}		
  					} else if (/item/.test(this.className)) {
  						// value实际上是月份两位数值
  						var value = $(this).attr('data-value');
  						if (value) {
  							self[SELECTED][1] = value;
  						} else {
  							// 今月，只改变年月为今年和今月
  							var arrToday = new Date().toArray();
  							self[SELECTED][0] = arrToday[0];
  							self[SELECTED][1] = arrToday[1];
  						}
  			
  						// 赋值
  						self.val();
  
  						// 根据是否是月份输入框，决定是面板切换，还是关闭
  						if (self.type == 'month') {							
  							// 隐藏
  							self.hide();
  						} else {
  							self.date();
  						}
  					} else if ($(this).attr('data-type') == 'year') {
  						// 切换到年份选择
  						self.year();
  					}
  					break;
  				}
  				case 'year': {
  					// 选择年份，可能从月份过来，也可能直接打开
  					// 1. 前后12年翻页
  					if (/prev|next/.test(this.className)) {
  						year = $(this).attr('data-year')
  						// 修改当前选中的年份数
  						self[SELECTED][0] = year * 1;
  						// 刷新
  						self.year();
  						// 文本框赋值
  						// 如果在区域内状态
  						if (container.find('.' + SELECTED).get(0).href)	{
  							self.val();							
  						}		
  					}  else if (/item/.test(this.className)) {
  						if (this.innerHTML == '今年') {
  							self[SELECTED][0] = new Date().getFullYear();
  						} else {
  							self[SELECTED][0] = this.innerHTML * 1;
  						}
  
  						// 赋值
  						self.val();
  						// 如果是年份选择输入框
  						if (self.type == 'year') {
  							// 隐藏
  							self.hide();
  						} else {
  							// 回到月份面板
  							self.month();
  						}
  					}
  
  					break;
  				}
  				case 'minute': {
  					// 选择分钟，可以是minute类型，或者time类型, 但不包括hour类型
  					// 1. 前后翻页
  					if (/prev|next/.test(this.className)) {
  						hour = $(this).attr('data-hour')
  						if (hour.length == 1) {
  							hour = '0' + hour;
  						}
  						// 修改当前选中的小时数
  						self[SELECTED][0] = hour;
  						
  						// 刷新
  						self.minute();
  						// 文本框赋值
  						// 如果在区域内状态
  						if (container.find('.' + SELECTED).attr('href'))	{
  							self.val();
  						}	
  					} else if (/item/.test(this.className)) {
  						// 确定选择时间
  						self[SELECTED] = this.innerHTML.split(':');
  						self.val();
  						self.hide();
  					} else if ($(this).attr('data-type') == 'hour') {
  						// 切换到小时选择
  						self.hour();
  					}
  					break;
  				} 
  				case 'hour': {
  					if (/item/.test(this.className)) {
  						// 修改选中的小时
  						self[SELECTED][0] = this.innerHTML.split(':')[0];
  						// 赋值
  						self.val();
  
  						// 如果是从分钟模式切换过来，则切换回去，否则，直接隐藏
  						if (self.type == 'hour') {
  							self.hide();
  						} else {
  							self.minute();
  						}
  					}
  				}
  			}
  
  
  		});
  
  		this.el = {};
  		this.el.container = container;
  		this.el.trigger = trigger;
  		this.el.input = input;
  		
  		this.type = type;
  
  		// 最大小值的处理在各个种类的面板中执行
  		// 以便支持动态时间修改
  		// 例如，选择A时间，然后设置B时间的min值为A时间
  		this.max = params.max;
  		this.min = params.min;
  		
  		// 回调方法
  		this.callback = {
  			show: params.onShow,
  			hide: params.onHide,
  			trigger: params.trigger
  		};
  		
  		trigger.click($.proxy(function(event) {
  			if (!this.display) {
  				this.show();	
  			} else {
  				this.hide();	
  			}
  			event.preventDefault();
  		}, this));
  		
  		
  		// 时间范围选择点击页面空白区域不会隐藏
  		$(document).mouseup($.proxy(function(event) {
  			// 点击页面空白区域，隐藏
  			var target = event && event.target, container = this.el.container.get(0);
  			if (target && 
  				trigger.get(0) != target && trigger.get(0).contains(target) == false && 
  				container != target && container.contains(target) == false
  			) {
  				this.hide();
  			}
  		}, this));
  		
  		// IE9+ 前后分页图标使用内联SVG, 以便支持retina屏幕
  		this.svg = window.addEventListener? 
  			'<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200"><path d="M85.876,100.5l49.537-50.526c4.089-4.215,4.089-11.049,0-15.262 c-4.089-4.218-10.719-4.218-14.808,0L63.586,92.868c-4.089,4.215-4.089,11.049,0,15.264l57.018,58.156 c4.089,4.215,10.719,4.215,14.808,0c4.089-4.215,4.089-11.049,0-15.262L85.876,100.5z"/></svg>'
  			: '';
  	
  
  		return this;
  		
  	};
  
  	// 赋值
  	DateTime.prototype.format = function() {
  		// 根据当前value值修改实例对象缓存的选中值
  		// 特殊情况一般不处理
  		var type = this.type;
  		
  		var initValue = this.el.input.val();
  		if (initValue == '') return this;
  
  		switch (type) {
  			case 'date': case 'year': case 'month': {
  				// 日期
  				var initDate = initValue.toDate(), arrDate = initDate.toArray();
  				this[SELECTED] = arrDate;  // eg. [2015,07,20]
  
  				break;	
  			}
  			case 'time': case 'hour': case 'minute': {
  				var arrTime = initValue.split(':');
  				// 时间
  				var hour = arrTime[0], minute = arrTime[1];
  				// 补0
  				if (arrTime.length == 2) {
  					// 分钟无论如何都要是正常值
  					if (!(minute > 0 && minute < 60) || type == 'hour') {
  						minute = '00';
  					} else if (minute.length == 1) {
  						minute = '0' + minute;
  					}
  					// 补0是必要的
  					if (hour.length == 1) {
  						hour = '0' + hour;
  					}
  
  					this.el.input.val([hour, minute].join(':'));
  
  					this[SELECTED] = [hour, minute];
  				}				
  
  				break;	
  			}
  			case 'date-range': case 'month-range': {
  				// 日期范围
  				var beginDate = new Date(), endDate = new Date();
  				// 前后时间字符串
  				var arrRange = initValue.split(' ');
  				if (arrRange.length == 3) {					
  					beginDate = arrRange[0].toDate();
  					endDate = arrRange[arrRange.length - 1].toDate();
  					// 存储
  					this[SELECTED] = [beginDate.toArray(), endDate.toArray()];
  				}				
  				break;
  			}
  		}
  		return this;
  	};
  
  	// 赋值
  	DateTime.prototype.val = function() {
  		var input = this.el.input, selected = this[SELECTED], value = input.val();
  
  		switch (this.type) {
  			case 'date': {
  				input.val(selected.join('-'));
  				break;
  			}
  			case 'month': {
  				input.val(selected.slice(0,2).join('-'));
  				break;
  			}
  			case 'year': {
  				input.val(selected[0]);
  				break;
  			}
  			case 'date-range': {
  				input.val(selected[0].join('-') + ' 至 ' + selected[1].join('-'));
  				break;
  			}
  			case 'month-range': {
  				input.val(selected[0].slice(0,2).join('-') + ' 至 ' + selected[1].slice(0,2).join('-'));
  				break;
  			}
  			case 'time':  case 'minute': {
  				input.val(selected.join(':'));
  				break;
  			}
  			case 'hour': {
  				input.val(selected[0] + ':00');
  				break;
  			}
  		}
  
  		if (input.val() != value) {
  			if ($.isArray(this.callback.trigger)) {
  				$.each(this.callback.trigger, function(i, eventType) {
  					input.trigger(eventType);
  				});
  			} else {
  				input.trigger(this.callback.trigger);
  			}
  			
  		}
  		return this;
  	};
  
  	// 返回日历HTML字符串等数据的私有方法
  	// 因date和range浮层主结构类似，因此这里公用下
  	DateTime.prototype._calendar = function(date) {
  		var html = '';
  		// 根据当前日期数据返回
  		// eg. [2015,'02', 23]
  		var arrDate = date;
  
  		// 文本框元素比较常用，使用局部变量，一是节约文件大小，二是性能更好！
  		var input = this.el.input;
  
  		// 类型
  		var type = this.type;
  
  		// 最大月份和最小月份
  		var min = input.attr('min') || this.min, max = input.attr('max') || this.max;
  		// 这里使用日期对象(时间戳)做比对
  		var arr = $.map([min, max], function(min_max, index) {
  			if (typeof min_max == 'string' && /^\d{8}$/.test(min_max.replace(regDate, '')) == true) {
  				// 这里对字符串进行比较简单的合法性判别
  				// 自己人使用不会乱来的
  				min_max = min_max.toDate();
  			} else if (typeof min_max != 'object' || !min_max.getTime) {
  				min_max = index? new Date(9999,0,1): new Date(0,0,1);
  			}
  
  			return min_max;
  		});
  		min = arr[0];
  		max = arr[1];
  		
  		var chinese = ['日', '一', '二', '三', '四', '五', '六'];
  		var monthDay = this._monthDay(arrDate);
  
  
  		// 目前日期对象
  		var currentDate = arrDate.join('-').toDate();
  
  		// 3 星期几七大罪
  		html = html + '<div class="'+ prefixDay +'x">' + (function() {
  			var htmlDay = '';
  			$.each(chinese, function(index, value) {
  				htmlDay = htmlDay + '<span class="'+ prefixDay +'item">'+ value +'</span>';
  			});
  			return htmlDay;
  		})() +'</div>';
  
  		// 4. 日期详细
  		//  4.1 首先算出今月1号是星期几
  		var newDate = arrDate.join('-').toDate(), dayFirst = 0;
  		newDate.setDate(1);
  		if (newDate.getDate() == 2) {
  			newDate.setDate(0);
  		}
  		// 每月的第一天是星期几
  		dayFirst = newDate.getDay();
  		// 上个月是几月份
  		var lastMonth = newDate.getMonth() - 1;
  		if (lastMonth < 0) lastMonth = 11;
  
  		var htmlData = 'data-year="'+ arrDate[0] +'" data-month="'+ (newDate.getMonth() + 1) +'"';
  
  		html = html + '<div class="'+ prefixDate +'body">' + (function() {
  			var htmlDate = '', cl = '';
  			for (var tr=0; tr<6; tr++) {
  				htmlDate = htmlDate + '<div class="'+ prefixDate +'tr">';
  					for (var td=0; td<7; td++) {
  						// 类名
  						cl = prefixDate + 'item col' + td;
  
  						// 由于range选择和date选择UI上有比较大大差异
  						// 为了可读性以及后期维护
  						// 这里就不耦合在一起，而是分开处理
  						if (type == 'date') {
  							// 第一行上个月一些日期补全
  							if (tr == 0 && td < dayFirst) {
  								htmlDate = htmlDate + '<span class="'+ cl +'">'+ (monthDay[lastMonth] - dayFirst + td + 1) +'</span>';
  							} else {
  								var dayNow = tr * 7 + td - dayFirst + 1;
  								// 如果没有超过这个月末
  								if (dayNow <= monthDay[newDate.getMonth()]) {
  									// 这个日子对应的时间对象
  									var dateNow = new Date(arrDate[0], newDate.getMonth(), dayNow);
  									// 如果日子匹配
  									if (currentDate.getDate() == dayNow) {
  										cl = cl + ' ' + SELECTED;
  									}
  									// 如果在日期范围内
  									// 直接使用时间对象 Date 类作比较
  									if (dateNow >= min && dateNow <= max) {
  										htmlDate = htmlDate + '<a href="javascript:;" '+ htmlData +' class="'+ cl +'">'+ dayNow +'</a>';
  									} else {
  										htmlDate = htmlDate + '<span class="'+ cl +'">'+ dayNow +'</span>';
  									}
  									
  								} else {
  									htmlDate = htmlDate + '<span class="'+ cl +'">'+ (dayNow - monthDay[newDate.getMonth()]) +'</span>';
  								}
  							}	
  						} else if (type == 'date-range') {
  
  							// 非当前月部分使用空格补全
  							if (tr == 0 && td < dayFirst) {
  								htmlDate = htmlDate + '<span class="'+ cl +'">&nbsp;</span>';
  							} else {
  								var dayNow = tr * 7 + td - dayFirst + 1;
  								// 如果没有超过这个月末
  								if (dayNow <= monthDay[newDate.getMonth()]) {
  									// 这个日子对应的时间对象
  									var dateNow = new Date(arrDate[0], newDate.getMonth(), dayNow);
  
  									// range选择的匹配规则如下：
  									// 1. 获得已经选中到时间范围
  									// 2. 起始时间和结束时间是方框表示
  									// 3. 之间的时间是选中表示
  									var dateBegin = this[SELECTED][0].join('-').toDate(),
  										dateEnd   = this[SELECTED][1].join('-').toDate();
  
  									// 各个时间的时间戳
  									var timeNow = dateNow.getTime(), timeBegin = dateBegin.getTime(), timeEnd = dateEnd.getTime();
  
  									if (timeNow >= timeBegin && timeNow <= timeEnd) {
  										// 在时间范围之内
  										cl = cl + ' ' + SELECTED;
  										// 跟开始时间一样
  										if (timeNow == timeBegin) {
  											cl = cl + ' ' + prefixDate + 'begin';
  										}
  										// 跟结束时间一样
  										if (timeNow == timeEnd) {
  											cl = cl + ' ' + prefixDate + 'end';
  										}
  										// 今月的第一天还是最后一天
  										if (dayNow == 1) {
  											cl = cl + ' ' + prefixDate + 'first';
  										} else if (dayNow == monthDay[newDate.getMonth()]) {
  											cl = cl + ' ' + prefixDate + 'last';
  										}
  									}					
  
  									// 如果在日期范围内
  									// 直接使用时间对象 Date 类作比较
  									if (dateNow >= min && dateNow <= max) {
  										htmlDate = htmlDate + '<a href="javascript:;" '+ htmlData +' class="'+ cl +'">'+ dayNow +'</a>';
  									} else {
  										htmlDate = htmlDate + '<span class="'+ cl +'">'+ dayNow +'</span>';
  									}
  									
  								} else {
  									htmlDate = htmlDate + '<span class="'+ cl +'">&nbsp;</span>';
  								}
  							}	
  						}
  					}
  				htmlDate += '</div>';
  			}
  			return htmlDate;
  		}).call(this) + '</div>';
  
  		return {
  			monthDay: monthDay,
  			html: html,
  			min: min,
  			max: max
  		};
  	};
  
  	// 当前日期下这一年每月最大的日期数目
  	DateTime.prototype._monthDay = function(date) {
  		var arrDate = date;
  		if ($.isArray(date) == false) {
  			arrDate = date.toArray();
  		}
  
  		var monthDay = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  
  		// 如果是闰年
  		if (((arrDate[0] % 4 == 0) && (arrDate[0] % 100 != 0)) || (arrDate[0] % 400 == 0)) {
  			monthDay[1] = 29;
  		}
  
  		return monthDay;
  	},
  	
  	// 上个月同日期
  	DateTime.prototype._datePrevMonth = function(date) {
  		// add on 2016-03-31 
  		// 原先的加减月份的跨月计算是有些问题的
  		// 因为例如31号的下一月，系统会自动变成下下个月的1号
  		var arrDate = date;
  		if ($.isArray(date) == false) {
  			// 日期数组化
  			arrDate = date.toArray();
  		}
  
  		var month = arrDate[1]*1, monthDay = this._monthDay(arrDate);
  
  		if (month == 1) {
  			// 上个月是前一年的12月
  			// 12月有31号，无压力
  			return [arrDate[0]-1, 12, arrDate[2]].join('-').toDate();
  		}
  		
  		// 其他月份不跨年
  		// 如果上一个月的最后1天小于本月的最后1天
  		// 嘿嘿嘿嘿
  		// 年不变，月减一，日期下个月
  		if (monthDay[month-2] < arrDate[2]) {
  			return [arrDate[0], month-1, monthDay[month-2]].join('-').toDate();
  		}
  
  		// 年不变，月减一，日期不变
  		return [arrDate[0], month-1, arrDate[2]].join('-').toDate();
  
  	},
  	// 下个月
  	DateTime.prototype._dateNextMonth = function(date) {
  		var arrDate = date;
  		if ($.isArray(date) == false) {
  			// 日期数组化
  			arrDate = date.toArray();
  		}
  
  		var month = arrDate[1]*1, monthDay = this._monthDay(arrDate);
  
  		if (month == 12) {
  			// 下个月跨年了
  			// 1月份也有31天，无压力
  			return [arrDate[0]+1, 1, arrDate[2]].join('-').toDate();
  		}
  
  		// 其他月份不跨年
  		// 如果下个月最后1天小于本月最后1天，例如，3月31日
  		if (monthDay[month] < arrDate[2]) {
  			return [arrDate[0], month+1, monthDay[month]].join('-').toDate();
  		}
  
  		// 其他时候正常
  		return [arrDate[0], month+1, arrDate[2]].join('-').toDate();
  	},
  
  	// 选择日期
  	DateTime.prototype.date = function() {
  		var arrDate = this[SELECTED];
  		
  		// 目前日期对象
  		var currentDate = arrDate.join('-').toDate(),
  			// 上一个月
  		    prevMonth = arrDate[1] - 1 
  			// 下一个月
  			nextMonth = arrDate[1] * 1 + 1;
  
  		var obj = this._calendar(arrDate);
  
  		// 选择日期的完整HTML代码
  		// 1. 日期专属类名容器
  		var html = '<div class="'+ prefixDate +'x">';
  		// 2. 头部月份切换
  		html = html + '<div class="'+ prefixDate +'head">';
  		// 根据前后月份是否在范围之外，决定使用的标签类型
  		// span标签则是禁用态，本组件全部都是如此
  		// 2.1 上个月
  		// datePrevMonth指上个月日期
  		var datePrevMonth = this._datePrevMonth(arrDate),
  		// prevMonth指上个月
  		prevMonthGet = datePrevMonth.getMonth(),
  		prevYearGet = datePrevMonth.getFullYear();
  
  		//if (datePrevMonth >= obj.min && datePrevMonth <= obj.max) {
  			// add on 2015-12-01
  			// 原来的判断逻辑有问题
  			// 只要上个月份的最大日期比最小限制大就可以了
  		if (new Date(prevYearGet, prevMonthGet, obj.monthDay[prevMonthGet]) >= obj.min && datePrevMonth <= obj.max) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'prev" data-month="'+ prevMonth +'">'+ this.svg +'</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'prev">'+ this.svg +'</span>';
  		}
  
  		// 2.2 下个月
  		var dateNextMonth = this._dateNextMonth(arrDate),
  			nextMonthGet = dateNextMonth.getMonth(),
  			nextYearGet = dateNextMonth.getFullYear();
  		//if (dateNextMonth >= obj.min && dateNextMonth <= obj.max) {
  			// add on 2015-12-01
  			// 经反馈，如果当前日期是30号，但是最大日期是下月1号，则下个月不能进入
  			// 这是有问题的
  			// 原因是此时的dateNextMonth > obj.max
  			// 因此判断应该从月初
  		if (dateNextMonth >= obj.min && new Date(nextYearGet, nextMonthGet, 1) <= obj.max) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'next" data-month="'+ nextMonth +'">'+ this.svg +'</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'next">'+ this.svg +'</span>';
  		}
  
  		//   头部月份公用结束 
  		html = html + '<a href="javascript:" class="'+ prefixDate +'switch" data-type="month">'+ arrDate.slice(0,2).join('-') +'</a>\
  		</div>';
  		
  		// 3. 主体内容来自_calendar()方法
  		html += obj.html;
  
  		// 今天
  		// 首先，今天要在时间范围内
  		if (new Date() >= obj.min && new Date() <= obj.max) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'item ' + prefixDate +'now">今天</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'item ' + prefixDate +'now">今天</span>';
  		}
  
  
  		// 容器闭合标签
  		html += '</div>';
  		
  		this.el.container.attr('data-type', 'date').html(html);
  		
  		return this;
  	};
  
  	// 选择日期范围
  	DateTime.prototype['date-range'] = function() {
  		var arrDates = this[SELECTED];
  		// 当前起始日期
  		// 默认（第一次）打开使用选中日期
  		// 如果有，使用面板存储月份
  		// 因为range选择不是即时更新文本框
  		var arrDate = this.el.container.data('date') || arrDates[0];
  		this.el.container.data('date', arrDate);
  		// 前一个月
  		var prevMonth = arrDate[1] - 1,
  		// 后一个月
  		nextMonth = arrDate[1] * 1 + 1;
  
  
  		// 根据起始时间创建新的时间对象
  		// var dateBegin = arrDate.join('-').toDate();
  
  		// 含时间范围和对应月份日历HTML的对象
  		var obj = this._calendar(arrDate);
  
  		// 选择时间范围完整HTML代码
  		// 1. 同样的，range容器
  		var html = '<div class="'+ prefixRange +'x">';
  		// 2. 头部
  		html = html + '<div class="'+ prefixDate +'head">\
  			<div class="'+ prefixDate +'half">';
  		//  2.1 上一个月箭头
  		var datePrevMonth = new Date(arrDate[0], prevMonth - 1, arrDate[2]);
  		if (datePrevMonth >= obj.min && datePrevMonth <= obj.max) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'prev" data-month="'+ prevMonth +'">'+ this.svg +'</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'prev">'+ this.svg +'</span>';
  		}
  		// 今月月份显示
  		html = html + '<span class="'+ prefixDate +'switch">'+ new Date(arrDate[0], prevMonth, arrDate[2]).toArray().slice(0,2).join('-') +'</span>\
  		</div>\
  		<div class="'+ prefixDate +'half">';
  
  		// 2.2 下下个月
  		var dateNextMonth = new Date(arrDate[0], arrDate[1] , /*arrDate[2]*/1), dateAfterMonth = new Date(arrDate[0], nextMonth, arrDate[2]);
  
  		// 例如是3月31日
  		// 此时 arrDate[1] '03'
  		// nextMonth 是 4
  
  		// 此时原来的实现是由bug的
  		// 因为下个月没有4月31日，于是，就会变成5月
  		// 因此arrDate[2]应使用1代替
  
  		// 下个月的当前日期在合理范围之内，则使用该月
  		if (dateAfterMonth >= obj.min && dateAfterMonth <= obj.max) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'next" data-month="'+ nextMonth +'">'+ this.svg +'</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'next">'+ this.svg +'</span>';
  		}
  
  		// 
  
  		// 下月月份显示	
  		html = html + '<span class="'+ prefixDate +'switch">'+ dateNextMonth.toArray().slice(0,2).join('-') +'</span>\
  		</div>';
  
  		// 头部闭合
  		html += '</div>';
  
  		// 3. 两个主体列表
  		// 这里要嵌套一层range特有的body
  		html = html + '<div class="'+ prefixRange +'body">' + 
  		// 根据_calendar()方法创建两个月份日历
  			'<div class="'+ prefixDate +'half">' + obj.html + '</div>' +
  			'<div class="'+ prefixDate +'half">' + this._calendar(dateNextMonth.toArray()).html + '</div>' +
  		// 主体标签闭合
  		'</div>';
  
  		// 4. 确定与取消按钮
  		html = html + '<div class="'+ prefixRange +'footer">' +
  			'<a href="javascript:;" class="ui-button ui-button-primary" data-type="ensure">确定</a>' +
  			'<a href="javascript:;" class="ui-button" data-type="cancel">取消</a>' +
  		'</div>';
  
  		// 容器闭合标签
  		html += '</div>';
  		
  		this.el.container.attr('data-type', 'date-range').html(html);
  		
  		return this;
  	};
  	
  
  	// 月份组装
  	DateTime.prototype._month = function(arrDate) {
  		// 文本框元素比较常用，使用局部变量，一是节约文件大小，二是性能更好！
  		var input = this.el.input;
  
  		// 最大月份和最小月份
  		var min = input.attr('min') || this.min, max = input.attr('max') || this.max;
  		// 这里使用年月组合做比对
  		var arr = $.map([min, max], function(min_max, index) {
  			if (typeof min_max == 'object' && min_max.getTime) {
  				min_max = min_max.toArray().slice(0,2).join('');
  			} else if (typeof min_max == 'string' && /\D/.test(min_max.replace(regDate, '')) == false) {
  				// 这里对字符串进行比较简单的合法性判别
  				// 自己人使用不会乱来的
  				min_max = min_max.replace(regDate, '').slice(0, 6);
  			} else {
  				min_max = index? '999912': '000000';
  			}
  			return min_max;
  		});
  		min = arr[0];
  		max = arr[1];
  		
  		var chinese = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'];
  
  		// 年份
  		var year = arrDate[0] * 1;
  
  		// 类型
  		var type = this.type;
  
  		var html = '<div class="'+ prefixMonth +'body">'+ (function() {
  			var htmlDate = '', cl = '', month = '';
  			for (var i=1; i<=12; i+=1) {
  				// 合法格式字符串
  				if (i < 10) {
  					month = '0' + i;
  				} else {
  					month = i + '';
  				}
  
  				// 基本类名
  				cl = prefixDate +'item';
  
  				if (type == 'month') {					
  					if (i == arrDate[1]) {
  						// 选中态的类名
  						cl = cl + ' ' + SELECTED;
  					}
  				} else if (type == 'month-range') {
  					// range选择的匹配规则如下：
  					// 1. 获得已经选中到时间范围
  					// 2. 起始时间和结束时间是选中表示
  					// 3. 之间的时间也是选中表示
  					var strBegin = this[SELECTED][0].slice(0,2).join(''),
  						strEnd   = this[SELECTED][1].slice(0,2).join('');
  					var strNow = year + month;	
  					if (strNow >= strBegin && strNow <= strEnd)	 {
  						cl = cl + ' ' + SELECTED;
  					}
  				}
  				// 是否在范围以内
  				if (year + month >= min && year + month <= max) {
  					htmlDate = htmlDate + '<a href="javascript:" class="'+ cl +'" data-year="'+ year +'" data-value="'+ month +'">'+ chinese[i-1] +'月</a>';	
  				} else {
  					htmlDate = htmlDate + '<span class="'+ cl +'" data-value="'+ month +'">'+ chinese[i-1] +'月</span>';
  				}
  
  			}
  			return htmlDate;
  		}).call(this) +
  		'</div>';
  
  		return {
  			html: html,
  			min: min,
  			max: max
  		};
  	};
  
  	// 选择月份
  	DateTime.prototype.month = function() {
  		var arrDate = this[SELECTED];
  
  		var obj = this._month(arrDate);
  
  		var min = obj.min, max = obj.max;
  
  		// 选择月份的完整HTML代码
  		// 1. month专属类名容器
  		var html = '<div class="'+ prefixMonth +'x">';
  		// 2. 月份切换的头部
  		var year = arrDate[0] * 1;
  		//    为什么呢？因为年份的范围是当前年份前面6个，后面5个
  		//    例如，假设今年是2015年，则头部年份范围是2009-2020
  		html = html + '<div class="'+ prefixDate +'head">';
  		//    2.1 是否还有上一年
  		if (year-1 >= Math.floor(min/100) && year-1 <= Math.floor(max/100)) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'prev" data-year="'+ (year-1) +'">'+ this.svg +'</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'prev">'+ this.svg +'</span>';
  		}
  		//    2.2 是否还有下一年
  		if (year+1 >= Math.floor(min/100) && year+1 <= Math.floor(max/100)) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'next" data-year="'+ (year+1) +'">'+ this.svg +'</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'next">'+ this.svg +'</span>';
  		}
  		// 头部结束
  		html = html + '<a href="javascript:" class="'+ prefixDate +'switch" data-type="year">'+ year +'</a>\
  		</div>';
  		// 3. 月份切换主体列表
  		html = html + obj.html;
  
  		// 今月
  		// 首先，今月要在时间范围内
  		var thisYearMonth = new Date().toArray().slice(0,2).join('');
  		if (thisYearMonth >= min && thisYearMonth <= max) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'item ' + prefixDate +'now">今月</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'item ' + prefixDate +'now">今月</span>';
  		}
  
  		// 容器闭合标签
  		html += '</div>';
  		
  		this.el.container.attr('data-type', 'month').html(html);
  		
  		return this;
  	};
  
  
  	// 选择月份范围
  	DateTime.prototype['month-range'] = function() {
  		var arrDates = this[SELECTED];
  		// 当前起始日期
  		// 默认（第一次）打开使用选中日期
  		// 如果有，使用面板存储月份
  		// 因为range选择不是即时更新文本框
  		var arrDate = this.el.container.data('date') || arrDates[0];
  		this.el.container.data('date', arrDate);
  		// 前一年
  		var prevYear = arrDate[0]*1 - 1,
  		// 后一个年
  		nextYear = arrDate[0]*1 + 1;
  
  		// 含时间范围和对应月份日历HTML的对象
  		var obj = this._month(arrDate);
  		// 最大年份
  		var maxYear = obj.max.slice(0,4), minYear = obj.min.slice(0,4);
  
  		// 选择时间范围完整HTML代码
  		// 1. 同样的，range容器
  		var html = '<div class="'+ prefixRange +'x">';
  		// 2. 头部
  		html = html + '<div class="'+ prefixDate +'head">\
  			<div class="'+ prefixDate +'half">';
  		//  2.1 上一年箭头
  		if (prevYear >= minYear && prevYear <= maxYear) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'prev" data-year="'+ prevYear +'">'+ this.svg +'</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'prev">'+ this.svg +'</span>';
  		}
  		// 今年年份显示
  		html = html + '<span class="'+ prefixDate +'switch">'+ arrDate[0] +'</span>\
  		</div>\
  		<div class="'+ prefixDate +'half">';
  
  		// 2.2 下一年
  		if (nextYear >= minYear && nextYear < maxYear) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'next" data-year="'+ nextYear +'">'+ this.svg +'</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'next">'+ this.svg +'</span>';
  		}
  
  		// 下月月份显示	
  		html = html + '<span class="'+ prefixDate +'switch">'+ nextYear +'</span>\
  		</div>';
  
  		// 头部闭合
  		html += '</div>';
  
  		// 3. 两个主体列表
  		// 这里要嵌套一层range特有的body
  		html = html + '<div class="'+ prefixRange +'body">' + 
  		// 根据_calendar()方法创建两个月份日历
  			'<div class="'+ prefixDate +'half">' + obj.html + '</div>' +
  			'<div class="'+ prefixDate +'half">' + this._month([nextYear, arrDate[1], arrDate[2]]).html + '</div>' +
  		// 主体标签闭合
  		'</div>';
  
  		// 4. 确定与取消按钮
  		html = html + '<div class="'+ prefixRange +'footer">' +
  			'<a href="javascript:;" class="ui-button ui-button-primary" data-type="ensure">确定</a>' +
  			'<a href="javascript:;" class="ui-button" data-type="cancel">取消</a>' +
  		'</div>';
  
  		// 容器闭合标签
  		html += '</div>';
  		
  		this.el.container.attr('data-type', 'month-range').html(html);
  
  		return this;
  	}
  
  
  	// 选择年份
  	DateTime.prototype.year = function() {
  		var arrDate = this[SELECTED];
  
  		// 文本框元素比较常用，使用局部变量，一是节约文件大小，二是性能更好！
  		var input = this.el.input;
  
  		// 最小年份和最大年份
  		var min = input.attr('min') || this.min, max = input.attr('max') || this.max;
  
  
  		// 用户可能使用时间对象
  		if (typeof min == 'object' && min.getFullYear) {
  			min = min.getFullYear();
  		} else if (typeof min == 'string' && /\D/.test(min.replace(regDate, '')) == false) {
  			// 这里对字符串进行比较简单的合法性判别
  			// 自己人使用不会乱来的
  			min = min.toDate().getFullYear();
  		} else {
  			min = 0;
  		}
  		if (typeof max == 'object' && max.getFullYear) {
  			max = max.getFullYear();
  		} else if (typeof max == 'string' && /\D/.test(max.replace(regDate, '')) == false) {
  			max = max.toDate().getFullYear();
  		} else {
  			max = 9999;
  		}
  
  		// 选择年份的完整HTML代码
  		// 1. 同样的，year专属类名容器
  		var html = '<div class="'+ prefixYear +'x">';
  		// 2. 头部的年份切换，一切就是12年
  		//    有必要先知道当前的年份
  		var year = arrDate[0];
  		//    为什么呢？因为年份的范围是当前年份前面6个，后面5个
  		//    例如，假设今年是2015年，则头部年份范围是2009-2020
  		//    左右切换是没有限制的
  		html = html + '<div class="'+ prefixDate +'head">';
  		//    年份不是你想翻就能翻到
  		//    2.1 上一个12年
  		if (year-12 >= min && year-12 <= max) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'prev" data-year="'+ (year-12) +'">'+ this.svg +'</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'prev">'+ this.svg +'</span>';
  		}
  		//    2.2 下一个12年
  		if (year+12 >= min && year+12 <= max) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'next" data-year="'+ (year+12) +'">'+ this.svg +'</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'next">'+ this.svg +'</span>';
  		}
  		// year选择是顶级类别，一定是不可点击的
  		html = html + '<span class="'+ prefixDate +'switch">'+ [year-6, year+5].join('-') +'</span></div>';
  
  		// 3. 年份选择的主体
  		html = html + '<div class="'+ prefixYear +'body">'+ (function() {
  			var htmlDate = '', cl = '';
  			for (var i=year-6; i<year+6; i+=1) {
  				// 选中态的类名
  				cl = prefixDate +'item';
  				if (i == year) {
  					cl = cl + ' ' + SELECTED;
  				}
  
  				// 是否在范围以内
  				if (i >= min && i <= max) {
  					htmlDate = htmlDate + '<a href="javascript:" class="'+ cl +'">'+ i +'</a>';	
  				} else {
  					htmlDate = htmlDate + '<span class="'+ cl +'">'+ i +'</span>';
  				}
  			}
  			return htmlDate;
  		})() +
  		'</div>';
  
  		// 今年
  		// 首先，今年要在时间范围内
  		var thisYear = new Date().getFullYear();
  		if (thisYear >= min && thisYear <= max) {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'item ' + prefixDate +'now">今年</a>';
  		} else {
  			html = html + '<span class="'+ prefixDate +'item ' + prefixDate +'now">今年</span>';
  		}
  		
  
  		// 头部标签闭合
  		html += '</div>';
  
  		// 容器闭合标签
  		html += '</div>';
  		
  		this.el.container.attr('data-type', 'year').html(html);
  		
  		return this;
  
  	};
  
  	// 选择小时
  	DateTime.prototype.hour = function() {
  		var arrTime = this[SELECTED];
  
  		// 文本框元素比较常用，使用局部变量，一是节约文件大小，二是性能更好！
  		var input = this.el.input;
  
  		// 时间选择的小时间隔，默认是1小时
  		var step = input.attr('step') * 1;
  		if (this.type != 'hour' || !step || step < 1) {
  			step = 1;
  		} else {
  			step = Math.round(step);
  		}
  
  		// 最小时间，和最大时间
  		// 这里只比较小时
  		var min = (input.attr('min') || this.min.toString()).split(':')[0], max = (input.attr('max') || this.max.toString()).split(':')[0];
  
  		if (/\D/.test(min)) {
  			min = 0;
  		} else {
  			min = min * 1;
  		}
  		if (/\D/.test(max)) {
  			max = 24;
  		} else {
  			max = max * 1;
  		}
  
  		// 选择小时的完整HTML
  		// 1. 同样的，专有容器
  		var html = '<div class="'+ prefixHour +'x">';
  		// 2. 小时没有头部切换，直接列表们
  		html = html + '<div class="'+ prefixHour +'body">'+ (function() {
  			var htmlTime = '', hour = '', cl = '';
  			for (var i=0; i<24; i+=step) {
  				hour = i + '';
  				if (hour.length == 1) {
  					hour = '0' + hour;
  				}
  
  				// 选中态的类名
  				cl = prefixDate +'item';
  				if (hour == arrTime[0]) {
  					cl = cl + ' ' + SELECTED;
  				}
  
  				// 是否在范围以内
  				if (hour >= min && hour <= max) {
  					htmlTime = htmlTime + '<a href="javascript:" class="'+ cl +'">'+ hour +':00</a>';	
  				} else {
  					htmlTime = htmlTime + '<span class="'+ cl +'">'+ hour +':00</span>';
  				}
  			}
  			return htmlTime;
  		})() +
  		'</div>';
  
  
  		// 容器闭合标签
  		html += '</div>';
  		
  		this.el.container.attr('data-type', 'hour').html(html);
  		
  		return this;
  	};
  	// 选择分钟
  	DateTime.prototype.minute = function() {
  		var arrTime = this[SELECTED];
  		
  		// 文本框元素比较常用，使用局部变量，一是节约文件大小，二是性能更好！
  		var input = this.el.input;
  		// 分钟时间间隔, 默认为5分钟
  		var step = input.attr('step') * 1 || 5;
  
  		// 最小时间，和最大时间
  		// 跟小时不同，这里必须符合00:00格式
  		// 由于格式固定，我们直接去除':'后的值进行比较
  		// 例如：10:20 → 1020
  		var min = input.attr('min') || (this.min + ''), max = input.attr('max') || (this.max + '');
  		if (min == 'auto' || /\D/.test(min.replace(':', '')) || min.split(':').length != 2) {
  			min = 0;
  		} else {
  			min = min.replace(':', '') * 1;
  		}
  		if (max == 'auto' || /\D/.test(max.replace(':', '')) || max.split(':').length != 2) {
  			max = 2359;
  		} else {
  			max = max.replace(':', '') * 1;
  		}
  
  		// 选择分钟的完整HTML
  		// 1. 外部的容器，含有专有容器类名，可以重置内部的一些样式
  		var html = '<div class="'+ prefixMinute +'x">';
  		// 2. 头部小时的左右切换
  		var hour = arrTime[0] * 1;
  		//   首先是公共部分
  		html = html + '<div class="'+ prefixDate +'head">';
  
  
  		//   2.1 可不可往前翻
  		if (hour <= Math.floor(min / 100)) {
  			html = html + '<span class="'+ prefixDate +'prev">'+ this.svg +'</span>';
  		} else {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'prev" data-hour="'+ (hour-1) +'">'+ this.svg +'</a>';
  		}
  		// 2.2 可不可以往后翻
  		if (hour >= Math.floor(max / 100)) {
  			html = html + '<span class="'+ prefixDate +'next">'+ this.svg +'</span>';
  		} else {
  			html = html + '<a href="javascript:" class="'+ prefixDate +'next" data-hour="'+ (hour+1) +'">'+ this.svg +'</a>';
  		}
  
  		// 头部结束的公共html部分
  		html = html + '<a href="javascript:" class="'+ prefixDate +'switch" data-type="hour">'+ arrTime[0] +':00</a></div>';
  
  		// 3. 分钟主体
  		html = html + '<div class="'+ prefixMinute +'body">'+ (function() {
  			var htmlTime = '', minute = '', cl = '';
  			for (var i=0; i<60; i+=step) {
  				minute = i + '';
  				if (minute.length == 1) {
  					minute = '0' + minute;
  				}
  
  				// 基本样式
  				cl = prefixDate +'item';
  				
  
  				// 是否在时间范围内
  				if ((arrTime[0] + minute) * 1 >= min && (arrTime[0] + minute) * 1<= max) {
  					// 选中态
  					if (minute == arrTime[1]) {
  						cl = cl + ' ' + SELECTED;
  					}
  					htmlTime = htmlTime + '<a href="javascript:" class="'+ cl +'">'+ [arrTime[0], minute].join(':') +'</a>';
  				} else {
  					htmlTime = htmlTime + '<span class="'+ cl +'">'+ [arrTime[0], minute].join(':') +'</span>';
  				}
  			}
  			return htmlTime;
  		})() +
  		'</div>';
  		
  		// 容器闭合标签
  		html += '</div>';
  		
  		this.el.container.attr('data-type', 'minute').html(html);
  		
  		return this;
  	};
  	
  	
  	
  	DateTime.prototype.show = function() {
  		var container = this.el.container;
  
  		// 根据value更新SELECTED
  		this.format();
  
  		// 不同的类名显示不同的内容
  		if (this.type == 'time') {
  			this.minute();
  		} else if (this.type == 'date-range') {
  			// 存储当前日期范围数据，以便取消的时候可以正确还原
  			if (!this._rangeSelected) {
  				this._rangeSelected = this[SELECTED];
  			}
  			this['date-range']();
  		} else if (this.type == 'month-range') {
  			// 存储当前日期范围数据，以便取消的时候可以正确还原
  			if (!this._rangeSelected) {
  				this._rangeSelected = this[SELECTED];
  			}
  			this['month-range']();
  		} else if (this[this.type]) {
  			this[this.type]();
  		} else {
  			this.date();
  		}
  		if ($.contains($(document.body), container) == false) {
  			$(document.body).append(container);
  		}
  		
  		container.show().follow(this.el.trigger.addClass(ACTIVE), {
  			position: "4-1"	
  		});
  		
  		if ($.isFunction(this.callback.show)) {
  			this.callback.show.call(this, this.el.input, container);
  		}
  		this.display = true;
  		
  		return this;
  	};
  	
  	DateTime.prototype.hide = function() {
  		this.el.container.hide();
  		this.el.trigger.removeClass(ACTIVE);
  		if ($.isFunction(this.callback.hide)) {
  			this.callback.hide.call(this, this.el.input, this.el.container);
  		}
  		this.display = false;
  		return this;
  	};
  	
  	// 下面为拓展的jQuery包装器方法
  	$.fn.dateTime = function(options) {
  		return $(this).each(function() {
  			if (!$(this).data('dateTime')) {
  				$(this).data('dateTime', new DateTime($(this), options));
  			}
  		});
  	};
  
  	return DateTime;
  }));
  