├── .gitignore ├── README.md ├── dist ├── calendar.css └── calendar.js ├── gulpfile.js ├── index.html ├── package.json └── src ├── calendar.css └── calendar.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # build dir 7 | public 8 | 9 | # e2e test reports 10 | test/e2e/reports 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules 37 | jspm_packages 38 | 39 | # Optional npm cache directory 40 | .npm 41 | 42 | # Optional REPL history 43 | .node_repl_history 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Calendar 2 | A calendar component, based on jQuery. [Demo](//yscoder.github.io/Calendar/index.html) 3 | 4 | --- 5 | 6 | ## Depend 7 | 8 | ``` 9 | 10 | 11 | 12 | ``` 13 | 14 | ## Use 15 | 16 | ```html 17 |
18 | 19 | 24 | ``` 25 | 26 | or 27 | 28 | ```html 29 | 30 |
31 | 37 | ``` 38 | 39 | ## Options 40 | 41 | ```js 42 | { 43 | 44 | // width 45 | width: 280, 46 | 47 | // height, 48 | height: 280, 49 | 50 | // zIndex 51 | zIndex: 1, 52 | 53 | // set the trigger selector 54 | trigger: null, 55 | 56 | // offset position 57 | offset: [0, 1], 58 | 59 | // override class 60 | customClass: '', 61 | 62 | // set display view, optional date or month 63 | view: 'date', 64 | 65 | // set current date 66 | date: new Date(), 67 | 68 | // date format 69 | format: 'yyyy/mm/dd', 70 | 71 | // first day of the week 72 | startWeek: 0, 73 | 74 | // week format 75 | weekArray: ['日', '一', '二', '三', '四', '五', '六'], 76 | 77 | // month format 78 | monthArray: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], 79 | 80 | // optional date range 81 | // value: `[ start date[, end date] ]` 82 | // Fixed date range: [new Date(2016, 0, 1), new Date(2016, 11, 31)] or ['2016/1/1', '2016/12/1'] 83 | // Starting today: [new Date(), null] or [new Date()] 84 | selectedRang: null, 85 | 86 | // display data when mouse hover 87 | // value: `[{ date: String || Date, value: object }, ... ]` 88 | // example: [ { date: '2016/1/1', value: 'A new Year'} ] or [ { date: new Date(), value: 'What to do'} ] 89 | data: null, 90 | 91 | // data label format 92 | // No display is set to `false` 93 | label: '{d}\n{v}', 94 | 95 | // arrow characters 96 | prev: '<', 97 | next: '>', 98 | 99 | // callback function when view changed 100 | // params: view, y, m 101 | viewChange: $.noop, 102 | 103 | // callback function when selected 104 | onSelected: function (view, date, value) { 105 | // body... 106 | }, 107 | 108 | // callback function when mouseenter 109 | onMouseenter: $.noop, 110 | 111 | // callback function when closed 112 | onClose: $.noop 113 | } 114 | ``` 115 | 116 | # Methods 117 | 118 | ```js 119 | $element.calendar(method, value) 120 | ``` 121 | 122 | * `setDate`:Setting selected date. 123 | * `setData`:Setting hover data. -------------------------------------------------------------------------------- /dist/calendar.css: -------------------------------------------------------------------------------- 1 | .calendar-label,.calendar-label i{display:none;left:50%;position:absolute}.calendar{width:280px;height:330px}.calendar-modal{display:none;position:absolute;background:#fdfdfd;border:1px solid #e8e8e8}.calendar-modal .view{box-shadow:1px 2px 3px #ddd}.calendar-inner{position:relative;z-index:1;-webkit-perspective:1000;-moz-perspective:1000;perspective:1000;-webkit-transform:perspective(1000px);transform:perspective(1000px);-webkit-transform-style:preserve-3d;transform-style:preserve-3d}.calendar-views{-webkit-transform-style:preserve-3d;transform-style:preserve-3d;width:100%}.calendar .view{-webkit-backface-visibility:hidden;backface-visibility:hidden;position:absolute;top:0;left:0;background:#fff;-webkit-transition:.6s;transition:.6s}.calendar-d .view-month,.calendar-m .view-date{-webkit-transform:rotateY(180deg);transform:rotateY(180deg);visibility:hidden;z-index:1}.calendar-d .view-date,.calendar-m .view-month{-webkit-transform:rotateY(0);transform:rotateY(0);visibility:visible;z-index:2}.calendar-ct,.calendar-hd,.calendar-views .days,.calendar-views .week{overflow:hidden}.calendar .date-items li,.calendar .view,.calendar-arrow .prev,.calendar-display{float:left}.calendar-arrow,.calendar-arrow .next{float:right}.calendar-ct{height:280px}.calendar-hd{padding:10px 0;height:30px;line-height:30px}.calendar-display{font-size:28px;text-indent:10px}.view-month .calendar-hd{padding:10px}.calendar-arrow,.calendar-display{color:#ddd}.calendar li[disabled]{color:#bbb}.calendar li.new[disabled],.calendar li.old[disabled]{color:#eee}.calendar-arrow span:hover,.calendar-display .m,.calendar-display:hover,.calendar-views .week{color:#888}.calendar-views .days .new,.calendar-views .days .old{color:#ccc}.calendar-arrow span,.calendar-views .days li[data-calendar-day],.calendar-views .view-month li[data-calendar-month]{cursor:pointer}.calendar li[disabled]{cursor:not-allowed}.calendar-arrow{width:50px;margin-right:10px}.calendar-arrow span{font:500 26px sans-serif}.calendar ol li{position:relative;float:left;text-align:center;border-radius:50%}.calendar .days li,.calendar .week li{width:40px;height:40px;line-height:40px}.calendar .month-items li{width:70px;height:70px;line-height:70px}.calendar .days li[data-calendar-day]:hover,.calendar .view-month li[data-calendar-month]:hover{background:#eee}.calendar .calendar-views .now{color:#fff;background:#66be8c!important}.calendar .calendar-views .selected{color:#66be8c;background:#CDE9D9!important}.calendar .calendar-views .dot{position:absolute;left:50%;bottom:4px;margin-left:-2px;width:4px;height:4px;background:#66be8c;border-radius:50%}.calendar-views .now .dot{background:#fff}.calendar .date-items{width:300%;margin-left:-100%}.calendar-label{top:50%;z-index:2;padding:5px 10px;line-height:22px;color:#fff;background:#000;border-radius:3px;opacity:.7;filter:alpha(opacity=70)}.calendar-label i{bottom:-12px;width:0;height:0;margin-left:-3px;border:6px solid transparent;border-top-color:#000} -------------------------------------------------------------------------------- /dist/calendar.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @authors yusen 3 | * @date 2017-01-04 21:34:19 4 | * @github https://github.com/yscoder/Calendar 5 | */ 6 | !function(t,e){"function"==typeof define&&define.amd?define("calendar",["jquery"],e):"object"==typeof exports?module.exports=e(require("jquery")):e(t.jQuery)}(this,function(t){function e(t){return"[object Date]"===V.call(t)}function a(t){return"[object String]"===V.call(t)}function i(t){return t.getAttribute("class")||t.getAttribute("className")}function n(e,a){this.$element=t(e),this.options=t.extend({},t.fn.calendar.defaults,a),this.$element.addClass("calendar "+this.options.customClass),this.width=this.options.width,this.height=this.options.height,this.date=this.options.date,this.selectedRang=this.options.selectedRang,this.data=this.options.data,this.init()}var s={width:280,height:280,zIndex:1,trigger:null,offset:[0,1],customClass:"",view:"date",date:new Date,format:"yyyy/mm/dd",startWeek:0,weekArray:["日","一","二","三","四","五","六"],monthArray:["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"],selectedRang:null,data:null,label:"{d}\n{v}",prev:"<",next:">",viewChange:t.noop,onSelected:function(t,e,a){},onMouseenter:t.noop,onClose:t.noop},o="data-calendar-",r="["+o+"display-date]",h="["+o+"display-month]",l="["+o+"arrow-date]",d="["+o+"arrow-month]",c=o+"day",u=o+"month",p="disabled",m="markData",f={date:"calendar-d",month:"calendar-m"},g="old",v="new",D="now",w="selected",y='',M='{year}/{month}',$='style="width:{w}px;height:{h}px;line-height:{h}px"',x="
  • {wk}
  • ",b="
  • {value}
  • ',I="
  • {m}
  • ",k=['
    ','
    ','
    ','
    ','','{yyyy}/{mm}',"",'
    ','{prev}','{next}',"
    ","
    ",'
    ','
      {week}
    ','
      ',"
      ","
      ",'
      ','
      ','{yyyy}','
      ','{prev}','{next}',"
      ","
      ",'
        {month}
      ',"
      ","
      ","
      ",'

      HelloWorld

      '],V=Object.prototype.toString;String.prototype.repeat=function(t){return this.replace(/\{\w+\}/g,function(e){var a=e.replace(/\{|\}/g,"");return t[a]||""})},String.prototype.toDate=function(){var t=(new Date,this.replace(/\d/g,"").charAt(0)),e=this.split(t);return new Date(parseInt(e[0]),parseInt(e[1])-1,parseInt(e[2]))},Date.prototype.format=function(t){var e=this.getFullYear(),a=this.getMonth()+1,i=this.getDate();return t.replace("yyyy",e).replace("mm",a).replace("dd",i)},Date.prototype.isSame=function(t,a,i){if(e(t)){var n=t;t=n.getFullYear(),a=n.getMonth()+1,i=n.getDate()}return this.getFullYear()===t&&this.getMonth()+1===a&&this.getDate()===i},Date.prototype.add=function(t){this.setDate(this.getDate()+t)},Date.prototype.minus=function(t){this.setDate(this.getDate()-t)},Date.prototype.clearTime=function(t){return this.setHours(0),this.setSeconds(0),this.setMinutes(0),this.setMilliseconds(0),this},Date.isLeap=function(t){return t%100!==0&&t%4===0||t%400===0},Date.getDaysNum=function(t,e){var a=31;switch(e){case 2:a=this.isLeap(t)?29:28;break;case 4:case 6:case 9:case 11:a=30}return a},Date.getSiblingsMonth=function(t,e,a){var i=new Date(t,e-1);return i.setMonth(e-1+a),{y:i.getFullYear(),m:i.getMonth()+1}},Date.getPrevMonth=function(t,e,a){return this.getSiblingsMonth(t,e,0-(a||1))},Date.getNextMonth=function(t,e,a){return this.getSiblingsMonth(t,e,a||1)},Date.tryParse=function(t){return t?e(t)?t:t.toDate():t},n.prototype={constructor:n,getDayAction:function(t){var e=c;if(this.selectedRang){var a=Date.tryParse(this.selectedRang[0]),i=Date.tryParse(this.selectedRang[1]);(a&&ti.clearTime())&&(e=p)}return e},getDayData:function(t){var e,a=this.data;if(a)for(var i=0,n=a.length;i'));e(a)?(n=a.getFullYear(),s=a.getMonth()+1):(n=Number(a),s=Number(i)),o=new Date(n,s-1,1).getDay()||7,l=o-this.options.startWeek,r=Date.getDaysNum(n,s),h=Date.getPrevMonth(n,s),prevDaysNum=Date.getDaysNum(n,h.m),nextM=Date.getNextMonth(n,s);for(var c=1,u=2,p=3,m=0,f=prevDaysNum-l+1;f<=prevDaysNum;f++,m++)d.append(this.getDayItem(h.y,h.m,f,c));for(var g=1;g<=r;g++,m++)d.append(this.getDayItem(n,s,g,u));for(var v=1,D=42-m;v<=D;v++)d.append(this.getDayItem(nextM.y,nextM.m,v,p));return t("
    • ").width(this.options.width).append(d)},getWeekHtml:function(){for(var t=[],e=this.options.weekArray,a=this.options.startWeek,i=e.length,n=this.width/7,s=this.height/7,o=a;o"));var s=n.outerWidth(),o=n.outerHeight();n.css({left:t.pageX-s/2+"px",top:t.pageY-o-20+"px",zIndex:this.options.zIndex+1}).show()},hasLabel:function(){return!!this.options.label&&(t("body").append(this.$label),!0)},event:function(){var e=this,a=e.options.viewChange;e.$element.on("click",r,function(){var t=e.getDisDateValue();e.updateMonthView(t[0],t[1]),a("month",t[0],t[1])}).on("click",h,function(){var t=this.innerHTML;e.updateDateView(t),a("date",t)}),e.$element.on("click",l,function(){var t=e.getDisDateValue(),n=i(this),s=t[0],o=t[1],r=e.updateDateView(s,o,n,function(){a("date",r.y,r.m)})}).on("click",d,function(){var t=Number(e.$disMonth.html()),n=i(this);t="prev"===n?t-1:t+1,e.updateMonthView(t),a("month",t)}),e.$element.on("click","["+c+"]",function(){var a=parseInt(this.innerHTML),n=i(this),s=/new|old/.test(n)?n.match(/new|old/)[0]:"",o=e.selectedDay(a,s);e.options.onSelected.call(this,"date",o,t(this).data(m)),e.$trigger&&e.hide("date",o,t(this).data(m))}).on("click","["+u+"]",function(){var i=Number(e.$disMonth.html()),n=t(this).index()+1;e.updateDateView(i,n),a("date",i,n),e.options.onSelected.call(this,"month",new Date(i,n-1))}),e.$element.on("mouseenter","["+c+"]",function(a){var i=t(this),n=i.attr(c).toDate();e.hasLabel()&&i.data(m)&&e.showLabel(a,"date",n,i.data(m)),e.options.onMouseenter.call(this,"date",n,i.data(m))}).on("mouseleave","["+c+"]",function(){e.$label.hide()})},resize:function(){var t=this.width,e=this.height,a=this.$element.find(".calendar-hd").outerHeight();this.$element.width(t).height(e+a).find(".calendar-inner, .view").css("width",t+"px"),this.$element.find(".calendar-ct").width(t).height(e)},init:function(){this.fillStatic(),this.resize(),this.render(),this.view=this.options.view,this.setView(this.view),this.event()},setData:function(t){if(this.data=t,"date"===this.view){var e=this.getDisDateValue();this.fillDateItems(e[0],e[1])}else"month"===this.view&&this.updateMonthView(this.$disMonth.html())},setDate:function(t){var e=Date.tryParse(t);this.updateDateView(e.getFullYear(),e.getMonth()+1),this.selectedDay(e.getDate())},methods:function(t,e){if("[object Function]"===V.call(this[t]))return this[t].apply(this,e)}},t.fn.calendar=function(e){var i,s=this.data("calendar"),o=[].slice.call(arguments);return s?a(e)?(i=e,o.shift(),s.methods(i,o)):this:this.each(function(){return t(this).data("calendar",new n(this,e))})},t.fn.calendar.defaults=s}); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp') 2 | const cleanCss = require('gulp-clean-css') 3 | const uglify = require('gulp-uglify') 4 | const browserSync = require('browser-sync') 5 | 6 | const src = './src/' 7 | const dist = './dist/' 8 | 9 | gulp.task('css', () => { 10 | return gulp.src(src + '*.css') 11 | .pipe(cleanCss()) 12 | .pipe(gulp.dest(dist)) 13 | }) 14 | 15 | gulp.task('js', () => { 16 | return gulp.src(src + '*.js') 17 | .pipe(uglify({ 18 | preserveComments: 'license' 19 | })) 20 | .pipe(gulp.dest(dist)) 21 | }) 22 | 23 | gulp.task('default', ['css', 'js']) 24 | 25 | gulp.task('dev', () => { 26 | browserSync.init({ 27 | server: { 28 | baseDir: './' 29 | } 30 | }) 31 | 32 | gulp.watch(['index.html', src + '*.*'], browserSync.reload) 33 | }) -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Calendar demo 8 | 9 | 52 | 53 | 54 |
      55 |

      Inline calendar

      56 |
      57 | 58 |

      Trigger calendar

      59 | 60 |
      61 |

      62 |
      63 | 64 | 65 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flip-calendar-picker", 3 | "version": "1.3.0", 4 | "description": "A calendar component, based on jQuery.", 5 | "main": "./src/calendar.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/yscoder/Calendar.git" 9 | }, 10 | "keywords": [ 11 | "jquery", 12 | "calendar", 13 | "picker", 14 | "date" 15 | ], 16 | "author": "yusen", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/yscoder/Calendar/issues" 20 | }, 21 | "homepage": "https://github.com/yscoder/Calendar#readme", 22 | "devDependencies": { 23 | "browser-sync": "^2.18.2", 24 | "gulp": "^3.9.1", 25 | "gulp-clean-css": "^2.0.13", 26 | "gulp-uglify": "^2.0.0" 27 | } 28 | } -------------------------------------------------------------------------------- /src/calendar.css: -------------------------------------------------------------------------------- 1 | .calendar { 2 | width: 280px; 3 | height: 330px; 4 | } 5 | 6 | .calendar-modal { 7 | display: none; 8 | position: absolute; 9 | background: #fdfdfd; 10 | border: 1px solid #e8e8e8; 11 | } 12 | 13 | .calendar-modal .view { 14 | box-shadow: 1px 2px 3px #ddd; 15 | } 16 | 17 | .calendar-inner { 18 | position: relative; 19 | z-index: 1; 20 | -webkit-perspective: 1000; 21 | -moz-perspective: 1000; 22 | perspective: 1000; 23 | -webkit-transform: perspective(1000px); 24 | transform: perspective(1000px); 25 | -webkit-transform-style: preserve-3d; 26 | transform-style: preserve-3d; 27 | } 28 | 29 | .calendar-views { 30 | -webkit-transform-style: preserve-3d; 31 | transform-style: preserve-3d; 32 | } 33 | 34 | .calendar .view { 35 | -webkit-backface-visibility: hidden; 36 | backface-visibility: hidden; 37 | position: absolute; 38 | top: 0; 39 | left: 0; 40 | background: #fff; 41 | *overflow: hidden; 42 | -webkit-transition: .6s; 43 | transition: .6s; 44 | } 45 | 46 | .calendar-d .view-month, 47 | .calendar-m .view-date { 48 | -webkit-transform: rotateY(180deg); 49 | transform: rotateY(180deg); 50 | visibility: hidden; 51 | z-index: 1; 52 | } 53 | 54 | .calendar-d .view-date, 55 | .calendar-m .view-month { 56 | -webkit-transform: rotateY(0deg); 57 | transform: rotateY(0deg); 58 | visibility: visible; 59 | z-index: 2; 60 | } 61 | 62 | .calendar-ct, 63 | .calendar-hd, 64 | .calendar-views .week, 65 | .calendar-views .days { 66 | overflow: hidden; 67 | } 68 | 69 | .calendar-views { 70 | width: 100%; 71 | } 72 | 73 | .calendar .view, 74 | .calendar-display, 75 | .calendar-arrow .prev, 76 | .calendar .date-items li { 77 | float: left; 78 | } 79 | 80 | .calendar-arrow, 81 | .calendar-arrow .next { 82 | float: right; 83 | } 84 | 85 | .calendar-ct { 86 | height: 280px; 87 | } 88 | 89 | .calendar-hd { 90 | padding: 10px 0; 91 | height: 30px; 92 | line-height: 30px; 93 | } 94 | 95 | .calendar-display { 96 | font-size: 28px; 97 | text-indent: 10px; 98 | } 99 | 100 | .view-month .calendar-hd { 101 | padding: 10px; 102 | } 103 | 104 | .calendar-arrow, 105 | .calendar-display { 106 | color: #ddd; 107 | } 108 | 109 | .calendar li[disabled] { 110 | color: #bbb; 111 | } 112 | 113 | .calendar li.old[disabled], 114 | .calendar li.new[disabled] { 115 | color: #eee; 116 | } 117 | 118 | .calendar-display .m, 119 | .calendar-views .week, 120 | .calendar-display:hover, 121 | .calendar-arrow span:hover { 122 | color: #888; 123 | } 124 | 125 | .calendar-views .days .old, 126 | .calendar-views .days .new { 127 | color: #ccc 128 | } 129 | 130 | .calendar-arrow span, 131 | .calendar-views .days li[data-calendar-day], 132 | .calendar-views .view-month li[data-calendar-month] { 133 | cursor: pointer; 134 | } 135 | 136 | .calendar li[disabled] { 137 | cursor: not-allowed; 138 | } 139 | 140 | .calendar-arrow { 141 | width: 50px; 142 | margin-right: 10px; 143 | } 144 | 145 | .calendar-arrow span { 146 | font: 500 26px sans-serif; 147 | } 148 | 149 | .calendar ol li { 150 | position: relative; 151 | float: left; 152 | text-align: center; 153 | border-radius: 50%; 154 | } 155 | 156 | .calendar .week li, 157 | .calendar .days li { 158 | width: 40px; 159 | height: 40px; 160 | line-height: 40px; 161 | } 162 | 163 | .calendar .month-items li { 164 | width: 70px; 165 | height: 70px; 166 | line-height: 70px; 167 | } 168 | 169 | .calendar .days li[data-calendar-day]:hover, 170 | .calendar .view-month li[data-calendar-month]:hover { 171 | background: #eee; 172 | } 173 | 174 | .calendar .calendar-views .now { 175 | color: #fff; 176 | background: #66be8c!important; 177 | } 178 | 179 | .calendar .calendar-views .selected { 180 | color: #66be8c; 181 | background: #CDE9D9!important; 182 | } 183 | 184 | .calendar .calendar-views .dot { 185 | position: absolute; 186 | left: 50%; 187 | bottom: 4px; 188 | margin-left: -2px; 189 | width: 4px; 190 | height: 4px; 191 | background: #66be8c; 192 | border-radius: 50%; 193 | } 194 | 195 | .calendar-views .now .dot { 196 | background: #fff; 197 | } 198 | 199 | .calendar .date-items { 200 | width: 300%; 201 | margin-left: -100%; 202 | } 203 | 204 | .calendar-label { 205 | display: none; 206 | position: absolute; 207 | top: 50%; 208 | left: 50%; 209 | z-index: 2; 210 | padding: 5px 10px; 211 | line-height: 22px; 212 | color: #fff; 213 | background: #000; 214 | border-radius: 3px; 215 | opacity: .7; 216 | filter: alpha(opacity=70); 217 | } 218 | 219 | .calendar-label i { 220 | display: none; 221 | position: absolute; 222 | left: 50%; 223 | bottom: -12px; 224 | width: 0; 225 | height: 0; 226 | margin-left: -3px; 227 | border: 6px solid transparent; 228 | border-top-color: #000; 229 | } 230 | -------------------------------------------------------------------------------- /src/calendar.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @authors yusen 3 | * @date 2017-01-04 21:34:19 4 | * @github https://github.com/yscoder/Calendar 5 | */ 6 | (function (root, factory) { 7 | if (typeof define === 'function' && define.amd) { 8 | define('calendar', ['jquery'], factory); 9 | } else if (typeof exports === 'object') { 10 | module.exports = factory(require('jquery')); 11 | } else { 12 | factory(root.jQuery); 13 | } 14 | }(this, function ($) { 15 | 16 | // default config 17 | 18 | var defaults = { 19 | 20 | // 宽度 21 | width: 280, 22 | // 高度, 不包含头部,头部固定高度 23 | height: 280, 24 | 25 | zIndex: 1, 26 | 27 | // selector 28 | // 设置触发显示的元素,为null时默认显示 29 | trigger: null, 30 | 31 | // 偏移位置,可设正负值 32 | // trigger 设置时生效 33 | offset: [0, 1], 34 | 35 | // 自定义类,用于重写样式 36 | customClass: '', 37 | 38 | // 显示视图 39 | // 可选:date, month 40 | view: 'date', 41 | 42 | // 默认日期为当前日期 43 | date: new Date(), 44 | format: 'yyyy/mm/dd', 45 | 46 | // 一周的第一天 47 | // 0表示周日,依次类推 48 | startWeek: 0, 49 | 50 | // 星期格式 51 | weekArray: ['日', '一', '二', '三', '四', '五', '六'], 52 | 53 | // 月份格式 54 | monthArray: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], 55 | 56 | // 设置选择范围 57 | // 格式:[开始日期, 结束日期] 58 | // 开始日期为空,则无上限;结束日期为空,则无下限 59 | // 如设置2015年11月23日以前不可选:[new Date(), null] or ['2015/11/23'] 60 | selectedRang: null, 61 | 62 | // 日期关联数据 [{ date: string, value: object }, ... ] 63 | // 日期格式与 format 一致 64 | // 如 [ {date: '2015/11/23', value: '面试'} ] 65 | data: null, 66 | 67 | // 展示关联数据 68 | // 格式化参数:{m}视图,{d}日期,{v}value 69 | // 设置 false 表示不显示 70 | label: '{d}\n{v}', 71 | 72 | // 切换字符 73 | prev: '<', 74 | next: '>', 75 | 76 | // 切换视图 77 | // 参数:view, y, m 78 | viewChange: $.noop, 79 | 80 | // view: 视图 81 | // date: 不同视图返回不同的值 82 | // value: 日期关联数据 83 | onSelected: function (view, date, value) { 84 | // body... 85 | }, 86 | 87 | // 参数同上 88 | onMouseenter: $.noop, 89 | 90 | onClose: $.noop 91 | }, 92 | 93 | // static variable 94 | 95 | ACTION_NAMESPACE = 'data-calendar-', 96 | 97 | DISPLAY_VD = '[' + ACTION_NAMESPACE + 'display-date]', 98 | DISPLAY_VM = '[' + ACTION_NAMESPACE + 'display-month]', 99 | 100 | ARROW_DATE = '[' + ACTION_NAMESPACE + 'arrow-date]', 101 | ARROW_MONTH = '[' + ACTION_NAMESPACE + 'arrow-month]', 102 | 103 | ITEM_DAY = ACTION_NAMESPACE + 'day', 104 | ITEM_MONTH = ACTION_NAMESPACE + 'month', 105 | 106 | DISABLED = 'disabled', 107 | MARK_DATA = 'markData', 108 | 109 | VIEW_CLASS = { 110 | date: 'calendar-d', 111 | month: 'calendar-m' 112 | }, 113 | 114 | OLD_DAY_CLASS = 'old', 115 | NEW_DAY_CLASS = 'new', 116 | TODAY_CLASS = 'now', 117 | SELECT_CLASS = 'selected', 118 | MARK_DAY_HTML = '', 119 | DATE_DIS_TPL = '{year}/{month}', 120 | 121 | ITEM_STYLE = 'style="width:{w}px;height:{h}px;line-height:{h}px"', 122 | WEEK_ITEM_TPL = '
    • {wk}
    • ', 123 | DAY_ITEM_TPL = '
    • {value}
    • ', 124 | MONTH_ITEM_TPL = '
    • {m}
    • ', 125 | 126 | TEMPLATE = [ 127 | '
      ', 128 | '
      ', 129 | '
      ', 130 | '
      ', 131 | '', 132 | '{yyyy}/{mm}', 133 | '', 134 | '
      ', 135 | '{prev}', 136 | '{next}', 137 | '
      ', 138 | '
      ', 139 | '
      ', 140 | '
        {week}
      ', 141 | '
        ', 142 | '
        ', 143 | '
        ', 144 | '
        ', 145 | '
        ', 146 | '{yyyy}', 147 | '
        ', 148 | '{prev}', 149 | '{next}', 150 | '
        ', 151 | '
        ', 152 | '
          {month}
        ', 153 | '
        ', 154 | '
        ', 155 | '
        ', 156 | '

        HelloWorld

        ' 157 | ], 158 | OS = Object.prototype.toString; 159 | 160 | // utils 161 | 162 | function isDate(obj) { 163 | return OS.call(obj) === '[object Date]'; 164 | } 165 | 166 | function isString(obj) { 167 | return OS.call(obj) === '[object String]'; 168 | } 169 | 170 | 171 | function getClass(el) { 172 | return el.getAttribute('class') || el.getAttribute('className'); 173 | } 174 | 175 | // extension methods 176 | 177 | String.prototype.repeat = function (data) { 178 | return this.replace(/\{\w+\}/g, function (str) { 179 | var prop = str.replace(/\{|\}/g, ''); 180 | return data[prop] || ''; 181 | }); 182 | } 183 | 184 | String.prototype.toDate = function () { 185 | var dt = new Date(), 186 | dot = this.replace(/\d/g, '').charAt(0), 187 | arr = this.split(dot); 188 | 189 | return new Date(parseInt(arr[0]), parseInt(arr[1]) - 1, parseInt(arr[2])); 190 | } 191 | 192 | Date.prototype.format = function (exp) { 193 | var y = this.getFullYear(), 194 | m = this.getMonth() + 1, 195 | d = this.getDate(); 196 | 197 | return exp.replace('yyyy', y).replace('mm', m).replace('dd', d); 198 | } 199 | 200 | Date.prototype.isSame = function (y, m, d) { 201 | if (isDate(y)) { 202 | var dt = y; 203 | y = dt.getFullYear(); 204 | m = dt.getMonth() + 1; 205 | d = dt.getDate(); 206 | } 207 | return this.getFullYear() === y && this.getMonth() + 1 === m && this.getDate() === d; 208 | } 209 | 210 | Date.prototype.add = function (n) { 211 | this.setDate(this.getDate() + n); 212 | } 213 | 214 | Date.prototype.minus = function (n) { 215 | this.setDate(this.getDate() - n); 216 | } 217 | 218 | Date.prototype.clearTime = function (n) { 219 | this.setHours(0); 220 | this.setSeconds(0); 221 | this.setMinutes(0); 222 | this.setMilliseconds(0); 223 | return this; 224 | } 225 | 226 | Date.isLeap = function (y) { 227 | return (y % 100 !== 0 && y % 4 === 0) || (y % 400 === 0); 228 | } 229 | 230 | Date.getDaysNum = function (y, m) { 231 | var num = 31; 232 | 233 | switch (m) { 234 | case 2: 235 | num = this.isLeap(y) ? 29 : 28; 236 | break; 237 | case 4: 238 | case 6: 239 | case 9: 240 | case 11: 241 | num = 30; 242 | break; 243 | } 244 | return num; 245 | } 246 | 247 | Date.getSiblingsMonth = function (y, m, n) { 248 | var d = new Date(y, m - 1); 249 | d.setMonth(m - 1 + n); 250 | return { 251 | y: d.getFullYear(), 252 | m: d.getMonth() + 1 253 | }; 254 | } 255 | 256 | Date.getPrevMonth = function (y, m, n) { 257 | return this.getSiblingsMonth(y, m, 0 - (n || 1)); 258 | } 259 | 260 | Date.getNextMonth = function (y, m, n) { 261 | return this.getSiblingsMonth(y, m, n || 1); 262 | } 263 | 264 | Date.tryParse = function (obj) { 265 | if (!obj) { 266 | return obj; 267 | } 268 | return isDate(obj) ? obj : obj.toDate(); 269 | } 270 | 271 | 272 | // Calendar class 273 | 274 | function Calendar(element, options) { 275 | this.$element = $(element); 276 | this.options = $.extend({}, $.fn.calendar.defaults, options); 277 | this.$element.addClass('calendar ' + this.options.customClass); 278 | this.width = this.options.width; 279 | this.height = this.options.height; 280 | this.date = this.options.date; 281 | this.selectedRang = this.options.selectedRang; 282 | this.data = this.options.data; 283 | this.init(); 284 | } 285 | 286 | Calendar.prototype = { 287 | constructor: Calendar, 288 | getDayAction: function (day) { 289 | var action = ITEM_DAY; 290 | if (this.selectedRang) { 291 | var start = Date.tryParse(this.selectedRang[0]), 292 | end = Date.tryParse(this.selectedRang[1]); 293 | 294 | if ((start && day < start.clearTime()) || (end && day > end.clearTime())) { 295 | action = DISABLED; 296 | } 297 | } 298 | 299 | return action; 300 | }, 301 | getDayData: function (day) { 302 | var ret, data = this.data; 303 | 304 | if (data) { 305 | 306 | for (var i = 0, len = data.length; i < len; i++) { 307 | var item = data[i]; 308 | 309 | if (day.isSame(Date.tryParse(item.date))) { 310 | ret = item.value; 311 | } 312 | } 313 | } 314 | 315 | return ret; 316 | }, 317 | getDayItem: function (y, m, d, f) { 318 | var dt = this.date, 319 | idt = new Date(y, m - 1, d), 320 | data = { 321 | w: this.width / 7, 322 | h: this.height / 7, 323 | value: d 324 | }, 325 | markData, 326 | $item; 327 | 328 | var selected = dt.isSame(y, m, d) ? SELECT_CLASS : ''; 329 | if (f === 1) { 330 | data['class'] = OLD_DAY_CLASS; 331 | } else if (f === 3) { 332 | data['class'] = NEW_DAY_CLASS; 333 | } else { 334 | data['class'] = ''; 335 | } 336 | 337 | if (dt.isSame(y, m, d)) { 338 | data['class'] += ' ' + TODAY_CLASS; 339 | } 340 | 341 | data.date = idt.format(this.options.format); 342 | data.action = this.getDayAction(idt); 343 | markData = this.getDayData(idt); 344 | 345 | $item = $(DAY_ITEM_TPL.repeat(data)); 346 | 347 | if (markData) { 348 | $item.data(MARK_DATA, markData); 349 | $item.html(d + MARK_DAY_HTML); 350 | } 351 | 352 | return $item; 353 | }, 354 | getDaysHtml: function (y, m) { 355 | var year, month, firstWeek, daysNum, prevM, prevDiff, 356 | dt = this.date, 357 | $days = $('
          '); 358 | 359 | if (isDate(y)) { 360 | year = y.getFullYear(); 361 | month = y.getMonth() + 1; 362 | } else { 363 | year = Number(y); 364 | month = Number(m); 365 | } 366 | 367 | firstWeek = new Date(year, month - 1, 1).getDay() || 7; 368 | prevDiff = firstWeek - this.options.startWeek; 369 | daysNum = Date.getDaysNum(year, month); 370 | prevM = Date.getPrevMonth(year, month); 371 | prevDaysNum = Date.getDaysNum(year, prevM.m); 372 | nextM = Date.getNextMonth(year, month); 373 | // month flag 374 | var PREV_FLAG = 1, 375 | CURR_FLAG = 2, 376 | NEXT_FLAG = 3, 377 | count = 0; 378 | 379 | for (var p = prevDaysNum - prevDiff + 1; p <= prevDaysNum; p++ , count++) { 380 | 381 | $days.append(this.getDayItem(prevM.y, prevM.m, p, PREV_FLAG)); 382 | } 383 | 384 | for (var c = 1; c <= daysNum; c++ , count++) { 385 | $days.append(this.getDayItem(year, month, c, CURR_FLAG)); 386 | } 387 | 388 | for (var n = 1, nl = 42 - count; n <= nl; n++) { 389 | 390 | $days.append(this.getDayItem(nextM.y, nextM.m, n, NEXT_FLAG)); 391 | } 392 | 393 | return $('
        1. ').width(this.options.width).append($days); 394 | }, 395 | getWeekHtml: function () { 396 | var week = [], 397 | weekArray = this.options.weekArray, 398 | start = this.options.startWeek, 399 | len = weekArray.length, 400 | w = this.width / 7, 401 | h = this.height / 7; 402 | 403 | for (var i = start; i < len; i++) { 404 | week.push(WEEK_ITEM_TPL.repeat({ 405 | w: w, 406 | h: h, 407 | wk: weekArray[i] 408 | })); 409 | } 410 | 411 | for (var j = 0; j < start; j++) { 412 | week.push(WEEK_ITEM_TPL.repeat({ 413 | w: w, 414 | h: h, 415 | wk: weekArray[j] 416 | })); 417 | } 418 | 419 | return week.join(''); 420 | }, 421 | getMonthHtml: function () { 422 | var monthArray = this.options.monthArray, 423 | month = [], 424 | w = this.width / 4, 425 | h = this.height / 4, 426 | i = 0; 427 | 428 | for (; i < 12; i++) { 429 | month.push(MONTH_ITEM_TPL.repeat({ 430 | w: w, 431 | h: h, 432 | m: monthArray[i] 433 | })); 434 | } 435 | 436 | return month.join(''); 437 | }, 438 | setMonthAction: function (y) { 439 | var m = this.date.getMonth() + 1; 440 | 441 | this.$monthItems.children().removeClass(TODAY_CLASS); 442 | if (y === this.date.getFullYear()) { 443 | this.$monthItems.children().eq(m - 1).addClass(TODAY_CLASS); 444 | } 445 | }, 446 | fillStatic: function () { 447 | var staticData = { 448 | prev: this.options.prev, 449 | next: this.options.next, 450 | week: this.getWeekHtml(), 451 | month: this.getMonthHtml() 452 | }; 453 | 454 | this.$element.html(TEMPLATE.join('').repeat(staticData)); 455 | }, 456 | updateDisDate: function (y, m) { 457 | this.$disDate.html(DATE_DIS_TPL.repeat({ 458 | year: y, 459 | month: m 460 | })); 461 | }, 462 | updateDisMonth: function (y) { 463 | this.$disMonth.html(y); 464 | }, 465 | fillDateItems: function (y, m) { 466 | var ma = [ 467 | Date.getPrevMonth(y, m), { 468 | y: y, 469 | m: m 470 | }, 471 | Date.getNextMonth(y, m) 472 | ]; 473 | 474 | this.$dateItems.html(''); 475 | for (var i = 0; i < 3; i++) { 476 | var $item = this.getDaysHtml(ma[i].y, ma[i].m); 477 | this.$dateItems.append($item); 478 | } 479 | 480 | }, 481 | hide: function (view, date, data) { 482 | this.$trigger.val(date.format(this.options.format)); 483 | this.options.onClose.call(this, view, date, data); 484 | this.$element.hide(); 485 | }, 486 | setPosition: function () { 487 | var post = this.$trigger.offset(); 488 | var offs = this.options.offset; 489 | 490 | this.$element.css({ 491 | left: (post.left + offs[0]) + 'px', 492 | top: (post.top + this.$trigger.outerHeight() + offs[1]) + 'px' 493 | }) 494 | }, 495 | trigger: function () { 496 | 497 | this.$trigger = $(this.options.trigger); 498 | 499 | var _this = this, 500 | $this = _this.$element; 501 | 502 | $this.addClass('calendar-modal').css('zIndex', _this.options.zIndex); 503 | 504 | $(document).click(function (e) { 505 | if (_this.$trigger[0] != e.target && !$.contains($this[0], e.target)) { 506 | $this.hide(); 507 | } 508 | }).on('click', this.options.trigger, function () { 509 | this.$trigger = $(this); 510 | _this.setPosition(); 511 | $this.show(); 512 | }) 513 | 514 | $(window).resize(function () { 515 | _this.setPosition(); 516 | }); 517 | }, 518 | render: function () { 519 | this.$week = this.$element.find('.week'); 520 | this.$dateItems = this.$element.find('.date-items'); 521 | this.$monthItems = this.$element.find('.month-items'); 522 | this.$label = this.$element.find('.calendar-label'); 523 | this.$disDate = this.$element.find(DISPLAY_VD); 524 | this.$disMonth = this.$element.find(DISPLAY_VM); 525 | 526 | var y = this.date.getFullYear(), 527 | m = this.date.getMonth() + 1; 528 | 529 | this.updateDisDate(y, m); 530 | this.updateMonthView(y); 531 | 532 | this.fillDateItems(y, m); 533 | 534 | this.options.trigger && this.trigger(); 535 | 536 | }, 537 | setView: function (view) { 538 | this.$element.removeClass(VIEW_CLASS.date + ' ' + VIEW_CLASS.month) 539 | .addClass(VIEW_CLASS[view]); 540 | this.view = view; 541 | }, 542 | updateDateView: function (y, m, dirc, cb) { 543 | m = m || this.date.getMonth() + 1; 544 | 545 | var _this = this, 546 | $dis = this.$dateItems, 547 | exec = { 548 | prev: function () { 549 | var pm = Date.getPrevMonth(y, m), 550 | ppm = Date.getPrevMonth(y, m, 2), 551 | $prevItem = _this.getDaysHtml(ppm.y, ppm.m); 552 | 553 | m = pm.m; 554 | y = pm.y; 555 | 556 | $dis.animate({ 557 | marginLeft: 0 558 | }, 300, 'swing', function () { 559 | $dis.children(':last').remove(); 560 | $dis.prepend($prevItem).css('margin-left', '-100%'); 561 | 562 | $.isFunction(cb) && cb.call(_this); 563 | }); 564 | }, 565 | next: function () { 566 | var nm = Date.getNextMonth(y, m), 567 | nnm = Date.getNextMonth(y, m, 2), 568 | $nextItem = _this.getDaysHtml(nnm.y, nnm.m); 569 | 570 | m = nm.m; 571 | y = nm.y; 572 | 573 | $dis.animate({ 574 | marginLeft: '-200%' 575 | }, 300, 'swing', function () { 576 | $dis.children(':first').remove(); 577 | $dis.append($nextItem).css('margin-left', '-100%'); 578 | 579 | $.isFunction(cb) && cb.call(_this); 580 | }); 581 | 582 | } 583 | }; 584 | 585 | 586 | if (dirc) { 587 | exec[dirc](); 588 | } else { 589 | this.fillDateItems(y, m); 590 | } 591 | 592 | this.updateDisDate(y, m); 593 | 594 | this.setView('date'); 595 | 596 | return { 597 | y: y, 598 | m: m 599 | }; 600 | }, 601 | updateMonthView: function (y) { 602 | this.updateDisMonth(y); 603 | this.setMonthAction(y); 604 | this.setView('month'); 605 | }, 606 | getDisDateValue: function () { 607 | var arr = this.$disDate.html().split('/'), 608 | y = Number(arr[0]), 609 | m = Number(arr[1].match(/\d{1,2}/)[0]); 610 | 611 | return [y, m]; 612 | }, 613 | selectedDay: function (d, type) { 614 | var arr = this.getDisDateValue(), 615 | y = arr[0], 616 | m = arr[1], 617 | toggleClass = function () { 618 | this.$dateItems.children(':eq(1)') 619 | .find('[' + ITEM_DAY + ']:not(.' + NEW_DAY_CLASS + ', .' + OLD_DAY_CLASS + ')') 620 | .removeClass(SELECT_CLASS) 621 | .filter(function (index) { 622 | return parseInt(this.innerHTML) === d; 623 | }).addClass(SELECT_CLASS); 624 | }; 625 | 626 | if (type) { 627 | var ret = this.updateDateView(y, m, { 628 | 'old': 'prev', 629 | 'new': 'next' 630 | }[type], toggleClass); 631 | y = ret.y; 632 | m = ret.m; 633 | this.options.viewChange('date', y, m); 634 | } else { 635 | toggleClass.call(this); 636 | } 637 | 638 | return new Date(y, m - 1, d); 639 | }, 640 | showLabel: function (event, view, date, data) { 641 | var $lbl = this.$label; 642 | 643 | $lbl.find('p').html(this.options.label.repeat({ 644 | m: view, 645 | d: date.format(this.options.format), 646 | v: data 647 | }).replace(/\n/g, '
          ')); 648 | 649 | var w = $lbl.outerWidth(), 650 | h = $lbl.outerHeight(); 651 | 652 | $lbl.css({ 653 | left: (event.pageX - w / 2) + 'px', 654 | top: (event.pageY - h - 20) + 'px', 655 | zIndex: this.options.zIndex + 1 656 | }).show(); 657 | }, 658 | hasLabel: function () { 659 | if (this.options.label) { 660 | $('body').append(this.$label); 661 | return true; 662 | } 663 | return false; 664 | }, 665 | event: function () { 666 | var _this = this, 667 | vc = _this.options.viewChange; 668 | 669 | // view change 670 | _this.$element.on('click', DISPLAY_VD, function () { 671 | var arr = _this.getDisDateValue(); 672 | _this.updateMonthView(arr[0], arr[1]); 673 | 674 | vc('month', arr[0], arr[1]); 675 | 676 | }).on('click', DISPLAY_VM, function () { 677 | var y = this.innerHTML; 678 | 679 | _this.updateDateView(y); 680 | vc('date', y); 681 | }); 682 | 683 | // arrow 684 | _this.$element.on('click', ARROW_DATE, function () { 685 | var arr = _this.getDisDateValue(), 686 | type = getClass(this), 687 | y = arr[0], 688 | m = arr[1]; 689 | 690 | var d = _this.updateDateView(y, m, type, function () { 691 | vc('date', d.y, d.m); 692 | }); 693 | 694 | }).on('click', ARROW_MONTH, function () { 695 | 696 | var y = Number(_this.$disMonth.html()), 697 | type = getClass(this); 698 | 699 | y = type === 'prev' ? y - 1 : y + 1; 700 | _this.updateMonthView(y); 701 | vc('month', y); 702 | }); 703 | 704 | // selected 705 | _this.$element.on('click', '[' + ITEM_DAY + ']', function () { 706 | var d = parseInt(this.innerHTML), 707 | cls = getClass(this), 708 | type = /new|old/.test(cls) ? cls.match(/new|old/)[0] : ''; 709 | 710 | var day = _this.selectedDay(d, type); 711 | 712 | _this.options.onSelected.call(this, 'date', day, $(this).data(MARK_DATA)); 713 | 714 | _this.$trigger && _this.hide('date', day, $(this).data(MARK_DATA)); 715 | 716 | }).on('click', '[' + ITEM_MONTH + ']', function () { 717 | var y = Number(_this.$disMonth.html()), 718 | m = $(this).index() + 1; 719 | 720 | _this.updateDateView(y, m); 721 | vc('date', y, m); 722 | _this.options.onSelected.call(this, 'month', new Date(y, m - 1)); 723 | }); 724 | 725 | // hover 726 | _this.$element.on('mouseenter', '[' + ITEM_DAY + ']', function (e) { 727 | var $this = $(this), 728 | day = $this.attr(ITEM_DAY).toDate(); 729 | 730 | if (_this.hasLabel() && $this.data(MARK_DATA)) { 731 | _this.showLabel(e, 'date', day, $this.data(MARK_DATA)); 732 | } 733 | 734 | _this.options.onMouseenter.call(this, 'date', day, $this.data(MARK_DATA)); 735 | }).on('mouseleave', '[' + ITEM_DAY + ']', function () { 736 | _this.$label.hide(); 737 | }); 738 | }, 739 | resize: function () { 740 | var w = this.width, 741 | h = this.height, 742 | hdH = this.$element.find('.calendar-hd').outerHeight(); 743 | 744 | this.$element.width(w).height(h + hdH) 745 | .find('.calendar-inner, .view') 746 | .css('width', w + 'px'); 747 | 748 | this.$element.find('.calendar-ct').width(w).height(h); 749 | 750 | }, 751 | init: function () { 752 | 753 | this.fillStatic(); 754 | this.resize(); 755 | this.render(); 756 | this.view = this.options.view; 757 | this.setView(this.view); 758 | this.event(); 759 | }, 760 | setData: function (data) { 761 | this.data = data; 762 | 763 | if (this.view === 'date') { 764 | var d = this.getDisDateValue(); 765 | this.fillDateItems(d[0], d[1]); 766 | } else if (this.view === 'month') { 767 | this.updateMonthView(this.$disMonth.html()); 768 | } 769 | }, 770 | setDate: function (date) { 771 | var dateObj = Date.tryParse(date); 772 | this.updateDateView(dateObj.getFullYear(), dateObj.getMonth() + 1); 773 | this.selectedDay(dateObj.getDate()); 774 | }, 775 | methods: function (name, args) { 776 | if (OS.call(this[name]) === '[object Function]') { 777 | return this[name].apply(this, args); 778 | } 779 | } 780 | }; 781 | 782 | $.fn.calendar = function (options) { 783 | var calendar = this.data('calendar'), 784 | fn, 785 | args = [].slice.call(arguments); 786 | 787 | if (!calendar) { 788 | return this.each(function () { 789 | return $(this).data('calendar', new Calendar(this, options)); 790 | }); 791 | } 792 | if (isString(options)) { 793 | fn = options; 794 | args.shift(); 795 | return calendar.methods(fn, args); 796 | } 797 | 798 | return this; 799 | } 800 | 801 | $.fn.calendar.defaults = defaults; 802 | 803 | })); 804 | --------------------------------------------------------------------------------