使用avalon MVVM框架打造jquery ui的日历

使用avalon MVVM框架打造jquery ui的日历

我直接把jquery datepicker的结构抄过来,类名也照搬。于是一个换肤的日历就诞生了。

< div  ms-controller = "datepicker" >
 
     < div  id = "ui-datepicker-div"  class = "ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"  style = "display:block" >
         < div  class = "ui-datepicker-header ui-widget-header ui-helper-clearfix ui-corner-all" >
             < a  class = "ui-datepicker-prev ui-corner-all"  title = "Prev"
                ms-click = "prevMonth"
                ms-hover = "ui-state-hover"
                ms-hover-1 = "ui-datepicker-prev-hover"
                >
                 < span  class = "ui-icon ui-icon-circle-triangle-w" >Prev</ span ></ a >
             < a  class = "ui-datepicker-next ui-corner-all"   title = "Next"
                ms-click = "nextMonth"
                ms-hover = "ui-state-hover"
                ms-hover-1 = "ui-datepicker-next-hover"
                >
                 < span  class = "ui-icon ui-icon-circle-triangle-e" >Next</ span ></ a >
             < div  class = "ui-datepicker-title" >
                 < select   ms-each-month = "$months"  ms-if = "changeMonth"  ms-model = "currentMonth"  >
                     < option  value = "{{month}}"  ms-selected = "currentMonth == month" >{{month+1}}月</ option >
                 </ select >
                 < select   ms-each-year = "candidateYears"  ms-if = "changeYear"  ms-model = "currentYear"  >
                     < option  value = "{{year}}"  ms-selected = "currentYear == year" >{{year}}年</ option >
                 </ select >
                 {{title}}
             </ div >
         </ div >
         < table  class = "ui-datepicker-calendar"  >
             < thead >
                 < tr  ms-each-date = "$weeks" >
                     < th  ms-class-ui-datepicker-week-end = "$first" >
                         < span  title = "星期{{date}}" >{{date}}</ span >
                     </ th >
                 </ tr >
             </ thead >
             < tbody  ms-each-week = "currentWeeks"  ms-click = "selectDay" >
                 < tr  ms-each-day = "week" >
                     < td  ms-class-ui-datepicker-other-month = "Number(day.split('-')[1]) != currentMonth"
                         ms-class-ui-datepicker-week-end = "$first || $last"
                         ms-class-ui-state-disabled = "day.split('-')[3] == 1"
                         ms-class-ui-datepicker-unselectable = "day.split('-')[3] == 1"
                         >
                         < a  class = "ui-state-default"   ms-title = 'currentMonth'  href = "#"
                            ms-if = "showOtherMonths || Number(day.split('-')[1]) == currentMonth"
                            ms-hover = "ui-state-hover"
                            ms-class-ui-state-highlight = "isToday"
                            >{{day.split('-')[2]}} </ a >
                     </ td >
             </ tbody >
         </ table >
         < div  class = "ui-datepicker-buttonpane ui-widget-content"  ms-if = "showButtonPanel" >
             < button  type = "button"  class = "ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all"
                     ms-hover = "ui-state-hover"
                     ms-click = "backToday"
                     >Today</ button >
             < button  type = "button"  class = "ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all"
                     ms-hover = "ui-state-hover"
                     >Done</ button >
         </ div >
     </ div >
     < link  rel = "stylesheet"  href = "http://code.jquery.com/ui/1.10.3/themes/{{theme}}/jquery-ui.css"  >
     < div >< input  type = "radio"  ms-model = "changeMonth"  />可选择月份</ div >
     < div >< input  type = "radio"  ms-model = "changeYear"  />可选择年份</ div >
     < div >< input  type = "radio"  ms-model = "showButtonPanel"  />显示按钮面板</ div >
     < div >< input  type = "radio"  ms-model = "showOtherMonths"  />显示其他月份的日期</ div >
     < div >< select  ms-model = "theme" >
             < option  value = 'start' >start</ option >
             < option  value = 'smoothness' >smoothness</ option >
             < option  value = 'flick' >flick</ option >
             < option  value = 'sunny' >sunny</ option >
             < option  value = 'excite-bike' >excite bike</ option >
             < option  value = 'black-tie' >black tie</ option >
             < option  value = 'trontastic' >trontastic</ option >
             < option  value = 'swanky-purse' >swanky purse</ option >
             < option  value = 'le-frog' >le frog</ option >
             < option  value = 'blitzer' >blitzer</ option >
             < option  value = 'dot-luv' >dot luv</ option >
             < option  value = 'mint-choc' >mint-choc</ option >
             < option  value = 'hot-sneaks' >hot sneaks</ option >
             < option  value = 'south-street' > south street</ option >
             < option  value = 'humanity' >humanity</ option >
             < option  value = 'vader' >vader</ option >
             < option  value = 'eggplant' >eggplant</ option >
             < option  value = 'cupertino' >cupertino</ option >
             < option  value = 'overcast' >overcast</ option >
         </ select >你喜欢的皮肤</ div >
     < p >你选择的日期为 {{selectedDate | date('yyyy-MM-dd')}}</ p >
 
</ div >

上面的结构分两部分,最上的日历,下面的一些表单元素用于控制日历的配置。

avalon.ready( function () {
    avalon.define( "datepicker" , function (vm) {
         //配置
         vm.changeYear = false
         vm.changeMonth = false
         vm.minDate = new  Date(2013, 3, 25);
         //vm.maxDate
         vm.showOtherMonths = false ;
         vm.showButtonPanel = false ;
         //当前时间
         vm.selectedDate = new  Date;
         vm.currentDate = new  Date;
         vm.currentMonth = vm.currentDate.getMonth();
         vm.currentYear = vm.currentDate.getFullYear();
         vm.currentWeeks = getWeeks(vm.currentDate);
         //显示顶部的年份与月份
         vm.title = {
             get: function () {
                 var  format = "" ;
                 if  (! this .changeYear && this .changeMonth) {
                     format = "yyyy年" ;
                 } else  if  ( this .changeYear && ! this .changeMonth) {
                     format = "MMMM" ;
                 } else  if  (! this .changeYear && ! this .changeMonth) {
                     format = "MMMM yyyy年" ;
                 }
                 return  format && avalon.filters.date( this .currentDate, format);
             }
         };
         //星期显示
         vm.$weeks = "日一二三四五六" .split( "" );
         //月份下拉菜单
         vm.$months = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
         //当月的日期
         function  isDisabled(time) {
             var  disabled = false ;
             if  (vm.minDate) {
                 disabled = time < vm.minDate;
             }
             if  (disabled && vm.maxDate) {
                 disabled = time > vm.maxDate;
             }
             return  disabled - 0;
         }
         function   getWeeks(cur) {
             vm.currentDate = cur;
             var  year = cur.getFullYear();
             var  month = cur.getMonth(); //得到今天是几月(0 ~ 11)
             var  date = cur.getDate();  //得到今天是几号 (1 ~ 31)
             cur.setMonth(month + 1); //改为下一个月,
             //由于日期是1 ~ 31, 0则是退到上一个月的最后一天,于是得到这个月的总天数
             cur.setDate(0);
             var  num = cur.getDate();
             var  next = 6 - cur.getDay();
             var  dates = avalon.range(1, num + 1);
             dates = dates.map( function (d) {
                 var  time = new  Date(year, month, d)
                 return  [year, month, d, isDisabled(time)].join( "-" );
             });
             cur.setMonth(month);
             cur.setDate(1); //得到当月的第一天
             var  prev = cur.getDay(); //0 ~ 6
             cur.setDate(date); //还原
             for  ( var  i = 0; i < prev; i++) { //补上上一个月的日期
                 cur = new  Date(year, month, -1 * i)
                 dates.unshift([year, cur.getMonth(), cur.getDate(), isDisabled(cur)].join( "-" ))
             }
             for  ( var  i = 0; i < next; i++) { //补上下一个月的日期
                 cur = new  Date(year, month + 1, i + 1)
                 dates.push([year, cur.getMonth(), cur.getDate(), isDisabled(cur)].join( "-" ))
             }
             var  ret = [];
             while  (dates.length) { //每行七个分组
                 ret.push(dates.splice(0, 7));
             }
             return  ret; //一个三维数组
         }
 
         //取得当年的前后20年
         function  getYears() {
             var  y = vm.currentYear;
             return  avalon.range(y - 10, y + 10);
         }
         vm.candidateYears = getYears();
         //点击事件
         vm.theme = "start" ;
         vm.nextMonth = function (e) {
             e.preventDefault()
             var  d = vm.currentDate;
             var  m = d.getMonth();
             d.setMonth(m + 1);
             m = d.getMonth();
             if  (m === 0) {
                 var  y = d.getFullYear();
                 vm.currentYear = y;
             }
             vm.currentMonth = m;
         };
         //点击事件
         vm.prevMonth = function (e) {
             e.preventDefault()
             var  d = vm.currentDate;
             var  m = d.getMonth();
             d.setMonth(m - 1);
             m = d.getMonth();
             if  (m === 11) {
                 var  y = d.getFullYear();
                 vm.currentYear = y;
             }
             vm.currentMonth = m;
         };
         //侦听
         vm.$watch( "currentMonth" , function (val) {
             var  d = vm.currentDate;
             d.setMonth(val);
             vm.currentWeeks = getWeeks(d);
             vm.title = NaN;
         });
 
         vm.$watch( "currentYear" , function (val) {
             var  d = vm.currentDate;
             d.setFullYear(val)
             vm.currentWeeks = getWeeks(d);
             vm.title = NaN;
         });
         //高亮当前选中的日期
         vm.selectDay = function (e) {
             var  el = e.target;
             e.preventDefault()
             if  (el.tagName === "A"  && el.parentNode.tagName === "TD"  && !/disabled/.test(el.className)) {
                 vm.selected = el.innerHTML;
                 var  d = el.$scope.day.split( '-' )
                 vm.selectedDate = new  Date(d[0], d[1], d[2]);
             }
         };
 
         //高亮今天的日期
         var  today = new  Date;
         var  atoday = [today.getFullYear(), today.getMonth(), today.getDate()];
         vm.isToday = function () {
             var  day = this .$scope.day;
             return  day.slice(0, day.lastIndexOf( "-" )) === atoday.join( "-" );
         };
         vm.backToday = function () {
             vm.currentMonth = atoday[1];
             vm.currentYear = atoday[0];
         }
     });
     avalon.scan();
});

这个JS代码比起先前的更清晰,放弃使用$fire这个危险的操作,建议大家也不要用它。因为如果没有严格的值变动检测, 这很容易引起无限递归。 现在avalon已经完全重用现有的节点,不会像过去那样每次都清空然后又添加。因此性能更好。

Prev Next
5月 2013年
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
可选择月份
可选择年份
显示按钮面板
显示其他月份的日期
选择你喜欢的皮肤(注:这些样式都受到博客园染指了)

你选择的日期为 2013-05-10

所有JS代码不到150行,就能涵盖jquery ui datepicker(2000多行)的绝大多数功能。如果努力一点,把模板也封装一下,其他功能也跟进,最多也是500行的规模。可谓MVVM的威力。而且这样写JS,可读性非常好,思路不会像着jQuery那样跟着CSS表达式——“这个元素是在哪里,该添加类名还是移除类名……”

 
 
 
标签:  javascript

你可能感兴趣的:(JavaScript)