zebra-datepicker-rails 1.9.5 → 1.9.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a77bd1eb0c0a1dcd5ed11abbaa57590863a3e0be
4
- data.tar.gz: 35745f4232c07389409729121a0e6a648ee305c2
2
+ SHA256:
3
+ metadata.gz: d6273b6b21ad75b9b6225fc2965a7c8a8785cb1a66879e3addc9884bd30ea1df
4
+ data.tar.gz: 8f8629963e0daac76127243e7c27d53a9a2d5d4f1cf3335f859f33b3e55b3a59
5
5
  SHA512:
6
- metadata.gz: 2a646b12d55c5109a20ff312e8ed40d1c7544982a17cb1262bca13d23e2403d3d0806471124dc4eeddb44825c042bdbbdd0348fc87c4d218339bf2b2b8e51e48
7
- data.tar.gz: 8c34aa6784f24a8cd36e5790534cfd5ecc2010a3e33db8afaf5b77ef4704aacace6dde1fdb4cb5e29bdef1e8c120bc2f442a06ec72b336b3358480453623eb81
6
+ metadata.gz: 98d73cad37adfb4db95f145c10e9c506a4069476dbb750658b0d54e08e53d4b2f4fbf11f00d61cf5112e147959fcad2af7218a705b4b6192e7794eed3039f703
7
+ data.tar.gz: 274d1a1966d1516d2c2d522199853ce993e6915062f0273098de9fbdee06ed2887dd1adfb297d6e2c41a2b12b658f5fdaa4abafd6ec5784fe38146aa75f76ff9
data/Rakefile CHANGED
@@ -4,7 +4,8 @@ desc "Change css url calls to use sass-rails' asset-url"
4
4
  task :change_css_url do
5
5
  Dir.chdir('vendor/assets/stylesheets/zebra-datepicker/')
6
6
  Dir.glob('*.scss').each do |filename|
7
- content = File.read(filename).gsub!(" url('../images/", " asset-url('zebra-datepicker/")
7
+ template = File.basename(filename, '.scss')
8
+ content = File.read(filename).gsub!(' url("', ' asset-url("zebra-datepicker/' + template + '/')
8
9
  file = File.open(filename, 'w')
9
10
  file.write(content)
10
11
  file.close
@@ -14,17 +15,17 @@ end
14
15
  desc 'Copy Assets from Zebra_Datepicker'
15
16
  task :copy do
16
17
  {
17
- 'public/css/bootstrap.css' => 'vendor/assets/stylesheets/zebra-datepicker/bootstrap.scss',
18
- 'public/css/default.css' => 'vendor/assets/stylesheets/zebra-datepicker/default.scss',
19
- 'public/css/metallic.css' => 'vendor/assets/stylesheets/zebra-datepicker/metallic.scss',
18
+ 'dist/css/bootstrap/zebra_datepicker.css' => 'vendor/assets/stylesheets/zebra-datepicker/bootstrap.scss',
19
+ 'dist/css/default/zebra_datepicker.css' => 'vendor/assets/stylesheets/zebra-datepicker/default.scss',
20
+ 'dist/css/metallic/zebra_datepicker.css' => 'vendor/assets/stylesheets/zebra-datepicker/metallic.scss',
20
21
 
21
- 'public/images/metallic/' => 'vendor/assets/images/zebra-datepicker/',
22
- 'public/images/calendar-disabled.png' => 'vendor/assets/images/zebra-datepicker/',
23
- 'public/images/calendar.png' => 'vendor/assets/images/zebra-datepicker/',
22
+ 'dist/css/bootstrap/icons.png' => 'vendor/assets/images/zebra-datepicker/bootstrap/icons.png',
23
+ 'dist/css/default/icons.png' => 'vendor/assets/images/zebra-datepicker/default/icons.png',
24
+ 'dist/css/metallic/icons.png' => 'vendor/assets/images/zebra-datepicker/metallic/icons.png',
24
25
 
25
- 'public/javascript/zebra_datepicker.js' => 'vendor/assets/javascripts/zebra-datepicker/',
26
- 'public/javascript/zebra_datepicker.src.js' => 'vendor/assets/javascripts/zebra-datepicker/'
26
+ 'dist/zebra_datepicker.src.js' => 'vendor/assets/javascripts/zebra-datepicker/zebra_datepicker.js'
27
27
  }.each do |src, dest|
28
+ FileUtils.mkdir_p File.dirname(dest)
28
29
  FileUtils.cp_r("Zebra_Datepicker/#{src}", dest)
29
30
  end
30
31
  end
@@ -1,5 +1,5 @@
1
1
  module ZebraDatepickerRails
2
2
  module Rails
3
- VERSION = "1.9.5"
3
+ VERSION = "1.9.7"
4
4
  end
5
5
  end
@@ -1 +1,3690 @@
1
- !function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){"use strict";a.Zebra_DatePicker=function(b,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q={always_visible:!1,container:a("body"),custom_classes:!1,days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],days_abbr:!1,default_position:"above",direction:0,disabled_dates:!1,enabled_dates:!1,first_day_of_week:1,format:"Y-m-d",header_captions:{days:"F, Y",months:"Y",years:"Y1 - Y2"},header_navigation:["&#171;","&#187;"],icon_position:"right",inside:!0,lang_clear_date:"Clear date",months:["January","February","March","April","May","June","July","August","September","October","November","December"],months_abbr:!1,offset:[5,-5],open_icon_only:!1,pair:!1,readonly_element:!0,select_other_months:!1,show_clear_date:0,show_icon:!0,show_other_months:!0,show_select_today:"Today",show_week_number:!1,start_date:!1,strict:!1,view:"days",weekend_days:[0,6],zero_pad:!1,onChange:null,onClear:null,onOpen:null,onClose:null,onSelect:null},R={},S=this;S.settings={};var T=a(b),U=function(b){if(N=Math.floor(65536*(1+Math.random())).toString(16),!b){S.settings=a.extend({},Q,c),R.readonly=T.attr("readonly"),R.style=T.attr("style");for(var y in T.data())0===y.indexOf("zdp_")&&(y=y.replace(/^zdp\_/,""),void 0!==Q[y]&&(S.settings[y]="pair"==y?a(T.data("zdp_"+y)):T.data("zdp_"+y)))}S.settings.readonly_element&&T.attr("readonly","readonly");var E={days:["d","j","D"],months:["F","m","M","n","t"],years:["o","Y","y"]},F=!1,G=!1,U=!1,X=null;for(X in E)a.each(E[X],function(a,b){S.settings.format.indexOf(b)>-1&&("days"==X?F=!0:"months"==X?G=!0:"years"==X&&(U=!0))});H=F&&G&&U?["years","months","days"]:!F&&G&&U?["years","months"]:F&&G&&!U?["months","days"]:F||G||!U?F||!G||U?["years","months","days"]:["months"]:["years"],-1==a.inArray(S.settings.view,H)&&(S.settings.view=H[H.length-1]),x=[],w=[],O={},P=[];var Y;for(var Z in S.settings.custom_classes)S.settings.custom_classes.hasOwnProperty(Z)&&P.push(Z);for(var $=0;$<2+P.length;$++)Y=0===$?S.settings.disabled_dates:1==$?S.settings.enabled_dates:S.settings.custom_classes[P[$-2]],a.isArray(Y)&&Y.length>0&&a.each(Y,function(){for(var b=this.split(" "),c=0;4>c;c++){b[c]||(b[c]="*"),b[c]=b[c].indexOf(",")>-1?b[c].split(","):new Array(b[c]);for(var d=0;d<b[c].length;d++)if(b[c][d].indexOf("-")>-1){var e=b[c][d].match(/^([0-9]+)\-([0-9]+)/);if(null!==e){for(var f=ja(e[1]);f<=ja(e[2]);f++)-1==a.inArray(f,b[c])&&b[c].push(f+"");b[c].splice(d,1)}}for(d=0;d<b[c].length;d++)b[c][d]=isNaN(ja(b[c][d]))?b[c][d]:ja(b[c][d])}0===$?x.push(b):1==$?w.push(b):(void 0===O[P[$-2]]&&(O[P[$-2]]=[]),O[P[$-2]].push(b))});var _,aa,ba=new Date,ea=S.settings.reference_date?S.settings.reference_date:T.data("zdp_reference_date")&&void 0!==T.data("zdp_reference_date")?T.data("zdp_reference_date"):ba;if(z=void 0,A=void 0,o=ea.getMonth(),l=ba.getMonth(),p=ea.getFullYear(),m=ba.getFullYear(),q=ea.getDate(),n=ba.getDate(),S.settings.direction===!0)z=ea;else if(S.settings.direction===!1)A=ea,D=A.getMonth(),C=A.getFullYear(),B=A.getDate();else if(!a.isArray(S.settings.direction)&&da(S.settings.direction)&&ja(S.settings.direction)>0||a.isArray(S.settings.direction)&&((_=V(S.settings.direction[0]))||S.settings.direction[0]===!0||da(S.settings.direction[0])&&S.settings.direction[0]>0)&&((aa=V(S.settings.direction[1]))||S.settings.direction[1]===!1||da(S.settings.direction[1])&&S.settings.direction[1]>=0))z=_?_:new Date(p,o,q+ja(a.isArray(S.settings.direction)?S.settings.direction[0]===!0?0:S.settings.direction[0]:S.settings.direction)),o=z.getMonth(),p=z.getFullYear(),q=z.getDate(),aa&&+aa>=+z?A=aa:!aa&&S.settings.direction[1]!==!1&&a.isArray(S.settings.direction)&&(A=new Date(p,o,q+ja(S.settings.direction[1]))),A&&(D=A.getMonth(),C=A.getFullYear(),B=A.getDate());else if(!a.isArray(S.settings.direction)&&da(S.settings.direction)&&ja(S.settings.direction)<0||a.isArray(S.settings.direction)&&(S.settings.direction[0]===!1||da(S.settings.direction[0])&&S.settings.direction[0]<0)&&((_=V(S.settings.direction[1]))||da(S.settings.direction[1])&&S.settings.direction[1]>=0))A=new Date(p,o,q+ja(a.isArray(S.settings.direction)?S.settings.direction[0]===!1?0:S.settings.direction[0]:S.settings.direction)),D=A.getMonth(),C=A.getFullYear(),B=A.getDate(),_&&+A>+_?z=_:!_&&a.isArray(S.settings.direction)&&(z=new Date(C,D,B-ja(S.settings.direction[1]))),z&&(o=z.getMonth(),p=z.getFullYear(),q=z.getDate());else if(a.isArray(S.settings.disabled_dates)&&S.settings.disabled_dates.length>0)for(var ha in x)if("*"==x[ha][0]&&"*"==x[ha][1]&&"*"==x[ha][2]&&"*"==x[ha][3]){var la=[];if(a.each(w,function(){var a=this;"*"!=a[2][0]&&la.push(parseInt(a[2][0]+("*"==a[1][0]?"12":ia(a[1][0],2))+("*"==a[0][0]?"*"==a[1][0]?"31":new Date(a[2][0],a[1][0],0).getDate():ia(a[0][0],2)),10))}),la.sort(),la.length>0){var na=(la[0]+"").match(/([0-9]{4})([0-9]{2})([0-9]{2})/);p=parseInt(na[1],10),o=parseInt(na[2],10)-1,q=parseInt(na[3],10)}break}if(ca(p,o,q)){for(;ca(p);)z?(p++,o=0):(p--,o=11);for(;ca(p,o);)z?(o++,q=1):(o--,q=new Date(p,o+1,0).getDate()),o>11?(p++,o=0,q=1):0>o&&(p--,o=11,q=new Date(p,o+1,0).getDate());for(;ca(p,o,q);)z?q++:q--,ba=new Date(p,o,q),p=ba.getFullYear(),o=ba.getMonth(),q=ba.getDate();ba=new Date(p,o,q),p=ba.getFullYear(),o=ba.getMonth(),q=ba.getDate()}var oa=V(T.val()||(S.settings.start_date?S.settings.start_date:""));if(oa&&S.settings.strict&&ca(oa.getFullYear(),oa.getMonth(),oa.getDate())&&T.val(""),b||void 0===z&&void 0===oa||ka(void 0!==oa?oa:z),!S.settings.always_visible){if(!b){if(S.settings.show_icon){"firefox"==ma.name&&T.is('input[type="text"]')&&"inline"==T.css("display")&&T.css("display","inline-block");var pa=a('<span class="Zebra_DatePicker_Icon_Wrapper"></span>').css({display:T.css("display"),position:"static"==T.css("position")?"relative":T.css("position"),"float":T.css("float"),top:T.css("top"),right:T.css("right"),bottom:T.css("bottom"),left:T.css("left")});"block"==T.css("display")&&pa.css("width",T.outerWidth(!0)),T.wrap(pa).css({position:"relative",top:"auto",right:"auto",bottom:"auto",left:"auto"}),f=a('<button type="button" class="Zebra_DatePicker_Icon'+("disabled"==T.attr("disabled")?" Zebra_DatePicker_Icon_Disabled":"")+'">Pick a date</button>'),S.icon=f,I=S.settings.open_icon_only?f:f.add(T)}else I=T;I.bind("click.Zebra_DatePicker_"+N,function(a){a.preventDefault(),T.attr("disabled")||(e.hasClass("dp_visible")?S.hide():S.show())}),!S.settings.readonly_element&&S.settings.pair&&T.bind("blur.Zebra_DatePicker_"+N,function(){var b;(b=V(a(this).val()))&&!ca(b.getFullYear(),b.getMonth(),b.getDate())&&ka(b)}),void 0!==f&&f.insertAfter(T)}if(void 0!==f){f.attr("style",""),S.settings.inside&&f.addClass("Zebra_DatePicker_Icon_Inside_"+("right"==S.settings.icon_position?"Right":"Left"));var qa=T.outerWidth(),ra=T.outerHeight(),sa=parseInt(T.css("marginLeft"),10)||0,ta=parseInt(T.css("marginTop"),10)||0,ua=(f.outerWidth(),f.outerHeight()),va=parseInt(f.css("marginLeft"),10)||0;parseInt(f.css("marginRight"),10)||0;S.settings.inside?(f.css("top",ta+(ra-ua)/2),"right"==S.settings.icon_position?f.css("right",0):f.css("left",0)):f.css({top:ta+(ra-ua)/2,left:sa+qa+va}),f.removeClass(" Zebra_DatePicker_Icon_Disabled"),"disabled"==T.attr("disabled")&&f.addClass("Zebra_DatePicker_Icon_Disabled")}}if(L=S.settings.show_select_today!==!1&&a.inArray("days",H)>-1&&!ca(m,l,n)?S.settings.show_select_today:!1,b)return a(".dp_previous",e).html(S.settings.header_navigation[0]),a(".dp_next",e).html(S.settings.header_navigation[1]),a(".dp_clear",e).html(S.settings.lang_clear_date),void a(".dp_today",e).html(S.settings.show_select_today);a(window).bind("resize.Zebra_DatePicker_"+N+", orientationchange.Zebra_DatePicker_"+N,function(){S.hide(),void 0!==f&&(clearTimeout(M),M=setTimeout(function(){S.update()},100))});var wa='<div class="Zebra_DatePicker"><table class="dp_header"><tr><td class="dp_previous">'+S.settings.header_navigation[0]+'</td><td class="dp_caption">&#032;</td><td class="dp_next">'+S.settings.header_navigation[1]+'</td></tr></table><table class="dp_daypicker"></table><table class="dp_monthpicker"></table><table class="dp_yearpicker"></table><table class="dp_footer"><tr><td class="dp_today"'+(S.settings.show_clear_date!==!1?' style="width:50%"':"")+">"+L+'</td><td class="dp_clear"'+(L!==!1?' style="width:50%"':"")+">"+S.settings.lang_clear_date+"</td></tr></table></div>";e=a(wa),S.datepicker=e,g=a("table.dp_header",e),h=a("table.dp_daypicker",e),i=a("table.dp_monthpicker",e),j=a("table.dp_yearpicker",e),K=a("table.dp_footer",e),J=a("td.dp_today",K),k=a("td.dp_clear",K),S.settings.always_visible?T.attr("disabled")||(S.settings.always_visible.append(e),S.show()):S.settings.container.append(e),e.delegate("td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_week_number)","mouseover",function(){a(this).addClass("dp_hover")}).delegate("td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_week_number)","mouseout",function(){a(this).removeClass("dp_hover")}),W(a("td",g)),a(".dp_previous",g).bind("click",function(){"months"==d?s--:"years"==d?s-=12:--r<0&&(r=11,s--),fa()}),a(".dp_caption",g).bind("click",function(){d="days"==d?a.inArray("months",H)>-1?"months":a.inArray("years",H)>-1?"years":"days":"months"==d?a.inArray("years",H)>-1?"years":a.inArray("days",H)>-1?"days":"months":a.inArray("days",H)>-1?"days":a.inArray("months",H)>-1?"months":"years",fa()}),a(".dp_next",g).bind("click",function(){"months"==d?s++:"years"==d?s+=12:12==++r&&(r=0,s++),fa()}),h.delegate("td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_week_number)","click",function(){S.settings.select_other_months&&a(this).attr("class")&&null!==(na=a(this).attr("class").match(/date\_([0-9]{4})(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])/))?ga(na[1],na[2]-1,na[3],"days",a(this)):ga(s,r,ja(a(this).html()),"days",a(this))}),i.delegate("td:not(.dp_disabled)","click",function(){var b=a(this).attr("class").match(/dp\_month\_([0-9]+)/);r=ja(b[1]),-1==a.inArray("days",H)?ga(s,r,1,"months",a(this)):(d="days",S.settings.always_visible&&T.val(""),fa())}),j.delegate("td:not(.dp_disabled)","click",function(){s=ja(a(this).html()),-1==a.inArray("months",H)?ga(s,1,1,"years",a(this)):(d="months",S.settings.always_visible&&T.val(""),fa())}),a(J).bind("click",function(b){b.preventDefault(),ga(m,l,n,"days",a(".dp_current",h)),S.settings.always_visible&&S.show(),S.hide()}),a(k).bind("click",function(b){b.preventDefault(),T.val(""),S.settings.always_visible?(t=null,u=null,v=null,a("td.dp_selected",e).removeClass("dp_selected")):(t=null,u=null,v=null,r=null,s=null),S.hide(),S.settings.onClear&&"function"==typeof S.settings.onClear&&S.settings.onClear.call(T,T)}),S.settings.always_visible||(a(document).bind("mousedown.Zebra_DatePicker_"+N+", touchstart.Zebra_DatePicker_"+N,function(b){if(e.hasClass("dp_visible")){if(S.settings.show_icon&&a(b.target).get(0)===f.get(0))return!0;0===a(b.target).parents().filter(".Zebra_DatePicker").length&&S.hide()}}),a(document).bind("keyup.Zebra_DatePicker_"+N,function(a){e.hasClass("dp_visible")&&27==a.which&&S.hide()})),fa()};S.clear_date=function(){a(k).trigger("click")},S.destroy=function(){void 0!==S.icon&&S.icon.remove(),S.datepicker.remove(),S.settings.show_icon&&!S.settings.always_visible&&T.unwrap(),T.unbind("click.Zebra_DatePicker_"+N),T.unbind("blur.Zebra_DatePicker_"+N),a(document).unbind("keyup.Zebra_DatePicker_"+N),a(document).unbind("mousedown.Zebra_DatePicker_"+N),a(window).unbind("resize.Zebra_DatePicker_"+N),a(window).unbind("orientationchange.Zebra_DatePicker_"+N),T.removeData("Zebra_DatePicker"),T.attr("readonly",R.readonly?!0:!1),T.attr("style",R.style?R.style:"")},S.hide=function(){S.settings.always_visible||(ba("hide"),e.removeClass("dp_visible").addClass("dp_hidden"),S.settings.onClose&&"function"==typeof S.settings.onClose&&S.settings.onClose.call(T,T))},S.set_date=function(a){var b;(b=V(a))&&!ca(b.getFullYear(),b.getMonth(),b.getDate())&&(T.val(a),ka(b))},S.show=function(){d=S.settings.view;var b=V(T.val()||(S.settings.start_date?S.settings.start_date:""));if(b?(u=b.getMonth(),r=b.getMonth(),v=b.getFullYear(),s=b.getFullYear(),t=b.getDate(),ca(v,u,t)&&(S.settings.strict&&T.val(""),r=o,s=p)):(r=o,s=p),fa(),S.settings.always_visible)e.removeClass("dp_hidden").addClass("dp_visible");else{if(S.settings.container.is("body")){var c=e.outerWidth(),g=e.outerHeight(),h=(void 0!==f?f.offset().left+f.outerWidth(!0):T.offset().left+T.outerWidth(!0))+S.settings.offset[0],i=(void 0!==f?f.offset().top:T.offset().top)-g+S.settings.offset[1],j=a(window).width(),k=a(window).height(),l=a(window).scrollTop(),m=a(window).scrollLeft();"below"==S.settings.default_position&&(i=(void 0!==f?f.offset().top:T.offset().top)+S.settings.offset[1]),h+c>m+j&&(h=m+j-c),m>h&&(h=m),i+g>l+k&&(i=l+k-g),l>i&&(i=l),e.css({left:h,top:i})}else e.css({left:0,top:0});e.removeClass("dp_hidden").addClass("dp_visible"),ba()}S.settings.onOpen&&"function"==typeof S.settings.onOpen&&S.settings.onOpen.call(T,T)},S.update=function(b){S.original_direction&&(S.original_direction=S.direction),S.settings=a.extend(S.settings,b),U(!0)};var V=function(b){if(b+="",""!==a.trim(b)){for(var c=X(S.settings.format),d=["d","D","j","l","N","S","w","F","m","M","n","Y","y"],e=[],f=[],g=null,h=null,i=0;i<d.length;i++)(g=c.indexOf(d[i]))>-1&&e.push({character:d[i],position:g});if(e.sort(function(a,b){return a.position-b.position}),a.each(e,function(a,b){switch(b.character){case"d":f.push("0[1-9]|[12][0-9]|3[01]");break;case"D":f.push("[a-z]{3}");break;case"j":f.push("[1-9]|[12][0-9]|3[01]");break;case"l":f.push("[a-z]+");break;case"N":f.push("[1-7]");break;case"S":f.push("st|nd|rd|th");break;case"w":f.push("[0-6]");break;case"F":f.push("[a-z]+");break;case"m":f.push("0[1-9]|1[012]+");break;case"M":f.push("[a-z]{3}");break;case"n":f.push("[1-9]|1[012]");break;case"Y":f.push("[0-9]{4}");break;case"y":f.push("[0-9]{2}")}}),f.length&&(e.reverse(),a.each(e,function(a,b){c=c.replace(b.character,"("+f[f.length-a-1]+")")}),f=new RegExp("^"+c+"$","ig"),h=f.exec(b))){var j,k=new Date,l=1,m=k.getMonth()+1,n=k.getFullYear(),o=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],p=["January","February","March","April","May","June","July","August","September","October","November","December"],q=!0;if(e.reverse(),a.each(e,function(b,c){if(!q)return!0;switch(c.character){case"m":case"n":m=ja(h[b+1]);break;case"d":case"j":l=ja(h[b+1]);break;case"D":case"l":case"F":case"M":j="D"==c.character||"l"==c.character?S.settings.days:S.settings.months,q=!1,a.each(j,function(a,d){if(q)return!0;if(h[b+1].toLowerCase()==d.substring(0,"D"==c.character||"M"==c.character?3:d.length).toLowerCase()){switch(c.character){case"D":h[b+1]=o[a].substring(0,3);break;case"l":h[b+1]=o[a];break;case"F":h[b+1]=p[a],m=a+1;break;case"M":h[b+1]=p[a].substring(0,3),m=a+1}q=!0}});break;case"Y":n=ja(h[b+1]);break;case"y":n="19"+ja(h[b+1])}}),q){var r=new Date(n,(m||1)-1,l||1);if(r.getFullYear()==n&&r.getDate()==(l||1)&&r.getMonth()==(m||1)-1)return r}}return!1}},W=function(a){"firefox"==ma.name?a.css("MozUserSelect","none"):"explorer"==ma.name?a.bind("selectstart",function(){return!1}):a.mousedown(function(){return!1})},X=function(a){return a.replace(/([-.,*+?^${}()|[\]\/\\])/g,"\\$1")},Y=function(b){for(var c="",d=b.getDate(),e=b.getDay(),f=S.settings.days[e],g=b.getMonth()+1,h=S.settings.months[g-1],i=b.getFullYear()+"",j=0;j<S.settings.format.length;j++){var k=S.settings.format.charAt(j);switch(k){case"y":i=i.substr(2);case"Y":c+=i;break;case"m":g=ia(g,2);case"n":c+=g;break;case"M":h=a.isArray(S.settings.months_abbr)&&void 0!==S.settings.months_abbr[g-1]?S.settings.months_abbr[g-1]:S.settings.months[g-1].substr(0,3);case"F":c+=h;break;case"d":d=ia(d,2);case"j":c+=d;break;case"D":f=a.isArray(S.settings.days_abbr)&&void 0!==S.settings.days_abbr[e]?S.settings.days_abbr[e]:S.settings.days[e].substr(0,3);case"l":c+=f;break;case"N":e++;case"w":c+=e;break;case"S":c+=d%10==1&&"11"!=d?"st":d%10==2&&"12"!=d?"nd":d%10==3&&"13"!=d?"rd":"th";break;default:c+=k}}return c},Z=function(){var b=new Date(s,r+1,0).getDate(),c=new Date(s,r,1).getDay(),d=new Date(s,r,0).getDate(),e=c-S.settings.first_day_of_week;e=0>e?7+e:e,ea(S.settings.header_captions.days);var f="<tr>";S.settings.show_week_number&&(f+="<th>"+S.settings.show_week_number+"</th>");for(var g=0;7>g;g++)f+="<th>"+(a.isArray(S.settings.days_abbr)&&void 0!==S.settings.days_abbr[(S.settings.first_day_of_week+g)%7]?S.settings.days_abbr[(S.settings.first_day_of_week+g)%7]:S.settings.days[(S.settings.first_day_of_week+g)%7].substr(0,2))+"</th>";for(f+="</tr><tr>",g=0;42>g;g++){g>0&&g%7===0&&(f+="</tr><tr>"),g%7===0&&S.settings.show_week_number&&(f+='<td class="dp_week_number">'+la(new Date(s,r,g-e+1))+"</td>");var i=g-e+1;if(S.settings.select_other_months&&(e>g||i>b)){var j=new Date(s,r,i),k=j.getFullYear(),o=j.getMonth(),p=j.getDate();j=k+ia(o+1,2)+ia(p,2)}if(e>g)f+='<td class="'+(S.settings.select_other_months&&!ca(k,o,p)?"dp_not_in_month_selectable date_"+j:"dp_not_in_month")+'">'+(S.settings.select_other_months||S.settings.show_other_months?ia(d-e+g+1,S.settings.zero_pad?2:0):"&nbsp;")+"</td>";else if(i>b)f+='<td class="'+(S.settings.select_other_months&&!ca(k,o,p)?"dp_not_in_month_selectable date_"+j:"dp_not_in_month")+'">'+(S.settings.select_other_months||S.settings.show_other_months?ia(i-b,S.settings.zero_pad?2:0):"&nbsp;")+"</td>";else{var q=(S.settings.first_day_of_week+g)%7,w="",x=aa(s,r,i);ca(s,r,i)?(a.inArray(q,S.settings.weekend_days)>-1?w="dp_weekend_disabled":w+=" dp_disabled",r==l&&s==m&&n==i&&(w+=" dp_disabled_current"),""!=x&&(w+=" "+x+"_disabled")):(a.inArray(q,S.settings.weekend_days)>-1&&(w="dp_weekend"),r==u&&s==v&&t==i&&(w+=" dp_selected"),r==l&&s==m&&n==i&&(w+=" dp_current"),""!=x&&(w+=" "+x)),f+="<td"+(""!==w?' class="'+a.trim(w)+'"':"")+">"+((S.settings.zero_pad?ia(i,2):i)||"&nbsp;")+"</td>"}}f+="</tr>",h.html(a(f)),S.settings.always_visible&&(E=a("td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month, .dp_week_number)",h)),h.show()},$=function(){ea(S.settings.header_captions.months);for(var b="<tr>",c=0;12>c;c++){c>0&&c%3===0&&(b+="</tr><tr>");var d="dp_month_"+c;ca(s,c)?d+=" dp_disabled":u!==!1&&u==c&&s==v?d+=" dp_selected":l==c&&m==s&&(d+=" dp_current"),b+='<td class="'+a.trim(d)+'">'+(a.isArray(S.settings.months_abbr)&&void 0!==S.settings.months_abbr[c]?S.settings.months_abbr[c]:S.settings.months[c].substr(0,3))+"</td>"}b+="</tr>",i.html(a(b)),S.settings.always_visible&&(F=a("td:not(.dp_disabled)",i)),i.show()},_=function(){ea(S.settings.header_captions.years);for(var b="<tr>",c=0;12>c;c++){c>0&&c%3===0&&(b+="</tr><tr>");var d="";ca(s-7+c)?d+=" dp_disabled":v&&v==s-7+c?d+=" dp_selected":m==s-7+c&&(d+=" dp_current"),b+="<td"+(""!==a.trim(d)?' class="'+a.trim(d)+'"':"")+">"+(s-7+c)+"</td>"}b+="</tr>",j.html(a(b)),S.settings.always_visible&&(G=a("td:not(.dp_disabled)",j)),j.show()},aa=function(b,c,d){var e,f,g;"undefined"!=typeof c&&(c+=1);for(f in P)if(e=P[f],g=!1,a.isArray(O[e])&&a.each(O[e],function(){if(!g){var f=this;if((a.inArray(b,f[2])>-1||a.inArray("*",f[2])>-1)&&("undefined"!=typeof c&&a.inArray(c,f[1])>-1||a.inArray("*",f[1])>-1)&&("undefined"!=typeof d&&a.inArray(d,f[0])>-1||a.inArray("*",f[0])>-1)){if("*"==f[3])return g=e;var h=new Date(b,c-1,d).getDay();if(a.inArray(h,f[3])>-1)return g=e}}}),g)return g;return g||""},ba=function(b){if("explorer"==ma.name&&6==ma.version){if(!y){var c=ja(e.css("zIndex"))-1;y=a("<iframe>",{src:'javascript:document.write("")',scrolling:"no",frameborder:0,css:{zIndex:c,position:"absolute",top:-1e3,left:-1e3,width:e.outerWidth(),height:e.outerHeight(),filter:"progid:DXImageTransform.Microsoft.Alpha(opacity=0)",display:"none"}}),a("body").append(y)}switch(b){case"hide":y.hide();break;default:var d=e.offset();y.css({top:d.top,left:d.left,display:"block"})}}},ca=function(b,c,d){if(!(void 0!==b&&!isNaN(b)||void 0!==c&&!isNaN(c)||void 0!==d&&!isNaN(d)))return!1;if(1e3>b)return!0;if(a.isArray(S.settings.direction)||0!==ja(S.settings.direction)){var e=ja(ha(b,"undefined"!=typeof c?ia(c,2):"","undefined"!=typeof d?ia(d,2):"")),f=(e+"").length;if(8==f&&("undefined"!=typeof z&&e<ja(ha(p,ia(o,2),ia(q,2)))||"undefined"!=typeof A&&e>ja(ha(C,ia(D,2),ia(B,2)))))return!0;if(6==f&&("undefined"!=typeof z&&e<ja(ha(p,ia(o,2)))||"undefined"!=typeof A&&e>ja(ha(C,ia(D,2)))))return!0;if(4==f&&("undefined"!=typeof z&&p>e||"undefined"!=typeof A&&e>C))return!0}"undefined"!=typeof c&&(c+=1);var g=!1,h=!1;return a.isArray(x)&&x.length&&a.each(x,function(){if(!g){var e=this;if((a.inArray(b,e[2])>-1||a.inArray("*",e[2])>-1)&&("undefined"!=typeof c&&a.inArray(c,e[1])>-1||a.inArray("*",e[1])>-1)&&("undefined"!=typeof d&&a.inArray(d,e[0])>-1||a.inArray("*",e[0])>-1)){if("*"==e[3])return g=!0;var f=new Date(b,c-1,d).getDay();if(a.inArray(f,e[3])>-1)return g=!0}}}),w&&a.each(w,function(){if(!h){var e=this;if((a.inArray(b,e[2])>-1||a.inArray("*",e[2])>-1)&&(h=!0,"undefined"!=typeof c))if(h=!0,a.inArray(c,e[1])>-1||a.inArray("*",e[1])>-1){if("undefined"!=typeof d)if(h=!0,a.inArray(d,e[0])>-1||a.inArray("*",e[0])>-1){if("*"==e[3])return h=!0;var f=new Date(b,c-1,d).getDay();if(a.inArray(f,e[3])>-1)return h=!0;h=!1}else h=!1}else h=!1}}),w&&h?!1:x&&g?!0:!1},da=function(a){return(a+"").match(/^\-?[0-9]+$/)?!0:!1},ea=function(b){!isNaN(parseFloat(r))&&isFinite(r)&&(b=b.replace(/\bm\b|\bn\b|\bF\b|\bM\b/,function(b){switch(b){case"m":return ia(r+1,2);case"n":return r+1;case"F":return S.settings.months[r];case"M":return a.isArray(S.settings.months_abbr)&&void 0!==S.settings.months_abbr[r]?S.settings.months_abbr[r]:S.settings.months[r].substr(0,3);default:return b}})),!isNaN(parseFloat(s))&&isFinite(s)&&(b=b.replace(/\bY\b/,s).replace(/\by\b/,(s+"").substr(2)).replace(/\bY1\b/i,s-7).replace(/\bY2\b/i,s+4)),a(".dp_caption",g).html(b)},fa=function(){if(""===h.text()||"days"==d){if(""===h.text()){S.settings.always_visible||e.css("left",-1e3),e.css("visibility","visible"),Z();var b=h.outerWidth(),c=h.outerHeight();i.css({width:b,height:c}),j.css({width:b,height:c}),g.css("width",b),K.css("width",b),e.css("visibility","").addClass("dp_hidden")}else Z();i.hide(),j.hide()}else"months"==d?($(),h.hide(),j.hide()):"years"==d&&(_(),h.hide(),i.hide());if(S.settings.onChange&&"function"==typeof S.settings.onChange&&void 0!==d){var f="days"==d?h.find("td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month)"):"months"==d?i.find("td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month)"):j.find("td:not(.dp_disabled, .dp_weekend_disabled, .dp_not_in_month)");f.each(function(){var b;"days"==d?a(this).hasClass("dp_not_in_month_selectable")?(b=a(this).attr("class").match(/date\_([0-9]{4})(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])/),a(this).data("date",b[1]+"-"+b[2]+"-"+b[3])):a(this).data("date",s+"-"+ia(r+1,2)+"-"+ia(ja(a(this).text()),2)):"months"==d?(b=a(this).attr("class").match(/dp\_month\_([0-9]+)/),a(this).data("date",s+"-"+ia(ja(b[1])+1,2))):a(this).data("date",ja(a(this).text()))}),S.settings.onChange.call(T,d,f,T)}K.show(),S.settings.show_clear_date===!0||0===S.settings.show_clear_date&&""!==T.val()||S.settings.always_visible&&S.settings.show_clear_date!==!1?(k.show(),L?(J.css("width","50%"),k.css("width","50%")):(J.hide(),k.css("width","100%"))):(k.hide(),L?J.show().css("width","100%"):K.hide())},ga=function(a,b,c,d,e){var f=new Date(a,b,c,12,0,0),g="days"==d?E:"months"==d?F:G,h=Y(f);T.val(h),S.settings.always_visible&&(u=f.getMonth(),r=f.getMonth(),v=f.getFullYear(),s=f.getFullYear(),t=f.getDate(),g.removeClass("dp_selected"),e.addClass("dp_selected"),"days"==d&&e.hasClass("dp_not_in_month_selectable")&&S.show()),S.hide(),ka(f),S.settings.onSelect&&"function"==typeof S.settings.onSelect&&S.settings.onSelect.call(T,h,a+"-"+ia(b+1,2)+"-"+ia(c,2),f,T,la(f)),T.focus()},ha=function(){for(var a="",b=0;b<arguments.length;b++)a+=arguments[b]+"";return a},ia=function(a,b){for(a+="";a.length<b;)a="0"+a;return a},ja=function(a){return parseInt(a,10)},ka=function(b){S.settings.pair&&a.each(S.settings.pair,function(){var c=a(this);if(c.data&&c.data("Zebra_DatePicker")){var d=c.data("Zebra_DatePicker");d.update({reference_date:b,direction:0===d.settings.direction?1:d.settings.direction}),d.settings.always_visible&&d.show()}else c.data("zdp_reference_date",b)})},la=function(a){var b,c,d,e,f,g,h,i,j,k=a.getFullYear(),l=a.getMonth()+1,m=a.getDate();return 3>l?(b=k-1,c=(b/4|0)-(b/100|0)+(b/400|0),d=((b-1)/4|0)-((b-1)/100|0)+((b-1)/400|0),e=c-d,f=0,g=m-1+31*(l-1)):(b=k,c=(b/4|0)-(b/100|0)+(b/400|0),d=((b-1)/4|0)-((b-1)/100|0)+((b-1)/400|0),e=c-d,f=e+1,g=m+((153*(l-3)+2)/5|0)+58+e),h=(b+c)%7,m=(g+h-f)%7,i=g+3-m,j=0>i?53-((h-e)/5|0):i>364+e?1:(i/7|0)+1},ma={init:function(){this.name=this.searchString(this.dataBrowser)||"",this.version=this.searchVersion(navigator.userAgent)||this.searchVersion(navigator.appVersion)||""},searchString:function(a){for(var b=0;b<a.length;b++){var c=a[b].string,d=a[b].prop;if(this.versionSearchString=a[b].versionSearch||a[b].identity,c){if(-1!=c.indexOf(a[b].subString))return a[b].identity}else if(d)return a[b].identity}},searchVersion:function(a){var b=a.indexOf(this.versionSearchString);if(-1!=b)return parseFloat(a.substring(b+this.versionSearchString.length+1))},dataBrowser:[{string:navigator.userAgent,subString:"Firefox",identity:"firefox"},{string:navigator.userAgent,subString:"MSIE",identity:"explorer",versionSearch:"MSIE"}]};ma.init(),U()},a.fn.Zebra_DatePicker=function(b){return this.each(function(){void 0!==a(this).data("Zebra_DatePicker")&&a(this).data("Zebra_DatePicker").destroy();var c=new a.Zebra_DatePicker(this,b);a(this).data("Zebra_DatePicker",c)})}});
1
+ /**
2
+ * Zebra_DatePicker
3
+ *
4
+ * Zebra_DatePicker is a small, compact and highly configurable date picker plugin for jQuery
5
+ *
6
+ * Read more {@link https://github.com/stefangabos/Zebra_Datepicker/ here}
7
+ *
8
+ * @author Stefan Gabos <contact@stefangabos.ro>
9
+ * @version 1.9.7 (last revision: December 03, 2017)
10
+ * @copyright (c) 2011 - 2017 Stefan Gabos
11
+ * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU LESSER GENERAL PUBLIC LICENSE
12
+ * @package Zebra_DatePicker
13
+ */
14
+ (function(factory) {
15
+
16
+ 'use strict';
17
+
18
+ // AMD
19
+ if (typeof define === 'function' && define.amd) define(['jquery'], factory);
20
+
21
+ // CommonJS
22
+ else if (typeof exports === 'object') factory(require('jquery'));
23
+
24
+ // browser globals
25
+ else factory(jQuery);
26
+
27
+ }(function($) {
28
+
29
+ 'use strict';
30
+
31
+ $.Zebra_DatePicker = function(element, options) {
32
+
33
+ var defaults = {
34
+
35
+ // setting this property to a jQuery element, will result in the date picker being always visible, the indicated
36
+ // element being the date picker's container;
37
+ //
38
+ // setting this to boolean TRUE will keep will result in the date picker not closing when selecting a
39
+ // date but only when the user clicks outside the date picker.
40
+ //
41
+ // note that when a date format is used that also involves time, this property will be automatically
42
+ // set to TRUE!
43
+ //
44
+ // default is FALSE
45
+ always_visible: false,
46
+
47
+ // by default, the date picker is injected into the <body>; use this property to tell the library to inject
48
+ // the date picker into a custom element - useful when you want the date picker to open at a specific position
49
+ //
50
+ // must be a jQuery element
51
+ //
52
+ // default is $('body')
53
+ container: $('body'),
54
+
55
+ // dates that should have custom classes applied to them
56
+ // an object in the form of
57
+ // {
58
+ // 'myclass1': [dates_to_apply_the_custom_class_to],
59
+ // 'myclass2': [dates_to_apply_the_custom_class_to]
60
+ // }
61
+ // where "dates_to_apply_the_custom_class_to" is an array of dates in the same format as required for
62
+ // "disabled_dates" property.
63
+ //
64
+ // custom classes will be applied *only* in the day picker view and not on month/year views!
65
+ // also note that the class name will have the "_disabled" suffix added if the day the class is applied to
66
+ // is disabled
67
+ //
68
+ // in order for the styles in your custom classes to be applied, make sure you are using the following syntax:
69
+ //
70
+ // .Zebra_DatePicker .dp_daypicker td.myclass1 { .. }
71
+ // .Zebra_DatePicker .dp_daypicker td.myclass1_disabled { .. }
72
+ //
73
+ // default is FALSE, no custom classes
74
+ custom_classes: false,
75
+
76
+ // days of the week; Sunday to Saturday
77
+ days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
78
+
79
+ // by default, the abbreviated name of a day consists of the first 2 letters from the day's full name;
80
+ // while this is common for most languages, there are also exceptions for languages like Thai, Loa, Myanmar,
81
+ // etc. where this is not correct; for these cases, specify an array with the abbreviations to be used for
82
+ // the 7 days of the week; leave it FALSE to use the first 2 letters of a day's name as the abbreviation.
83
+ //
84
+ // default is FALSE
85
+ days_abbr: false,
86
+
87
+ // the position of the date picker relative to the element it is attached to. note that, regardless of this
88
+ // setting, the date picker's position will be automatically adjusted to fit in the viewport, if needed.
89
+ //
90
+ // possible values are "above" and "below"
91
+ //
92
+ // default is "above"
93
+ default_position: 'above',
94
+
95
+ // direction of the calendar
96
+ //
97
+ // a positive or negative integer: n (a positive integer) creates a future-only calendar beginning at n days
98
+ // after today; -n (a negative integer); if n is 0, the calendar has no restrictions. use boolean true for
99
+ // a future-only calendar starting with today and use boolean false for a past-only calendar ending today.
100
+ //
101
+ // you may also set this property to an array with two elements in the following combinations:
102
+ //
103
+ // - first item is boolean TRUE (calendar starts today), an integer > 0 (calendar starts n days after
104
+ // today), or a valid date given in the format defined by the "format" attribute, using English for
105
+ // month names (calendar starts at the specified date), and the second item is boolean FALSE (the calendar
106
+ // has no ending date), an integer > 0 (calendar ends n days after the starting date), or a valid date
107
+ // given in the format defined by the "format" attribute, using English for month names, and which occurs
108
+ // after the starting date (calendar ends at the specified date)
109
+ //
110
+ // - first item is boolean FALSE (calendar ends today), an integer < 0 (calendar ends n days before today),
111
+ // or a valid date given in the format defined by the "format" attribute, using English for month names
112
+ // (calendar ends at the specified date), and the second item is an integer > 0 (calendar ends n days
113
+ // before the ending date), or a valid date given in the format defined by the "format" attribute, using
114
+ // English for month names and which occurs before the starting date (calendar starts at the specified
115
+ // date)
116
+ //
117
+ // [1, 7] - calendar starts tomorrow and ends seven days after that
118
+ // [true, 7] - calendar starts today and ends seven days after that
119
+ // ['2013-01-01', false] - calendar starts on January 1st 2013 and has no ending date ("format" is YYYY-MM-DD)
120
+ // [false, '2012-01-01'] - calendar ends today and starts on January 1st 2012 ("format" is YYYY-MM-DD)
121
+ //
122
+ // note that "disabled_dates" property will still apply!
123
+ //
124
+ // default is 0 (no restrictions)
125
+ direction: 0,
126
+
127
+ // an array of disabled dates in the following format: 'day month year weekday' where "weekday" is optional
128
+ // and can be 0-6 (Saturday to Sunday); the syntax is similar to cron's syntax: the values are separated by
129
+ // spaces and may contain * (asterisk) - (dash) and , (comma) delimiters:
130
+ //
131
+ // ['1 1 2012'] would disable January 1, 2012;
132
+ // ['* 1 2012'] would disable all days in January 2012;
133
+ // ['1-10 1 2012'] would disable January 1 through 10 in 2012;
134
+ // ['1,10 1 2012'] would disable January 1 and 10 in 2012;
135
+ // ['1-10,20,22,24 1-3 *'] would disable 1 through 10, plus the 22nd and 24th of January through March for every year;
136
+ // ['* * * 0,6'] would disable all Saturdays and Sundays;
137
+ // ['01 07 2012', '02 07 2012', '* 08 2012'] would disable 1st and 2nd of July 2012, and all of August of 2012
138
+ //
139
+ // default is FALSE, no disabled dates
140
+ //
141
+ // DISABLING ALL DATES AND NOT SPECIFYING AT LEAST ONE ENABLED DATE WILL SEND THE SCRIPT INTO AN INFINITE
142
+ // LOOP SEARCHING FOR AN ENABLED DATE TO DISPLAY!
143
+ disabled_dates: false,
144
+
145
+ // an array of enabled dates in the same format as required for "disabled_dates" property.
146
+ // to be used together with the "disabled_dates" property by first setting the "disabled_dates" property to
147
+ // something like "[* * * *]" (which will disable everything) and the setting the "enabled_dates" property to,
148
+ // say, "[* * * 0,6]" to enable just weekends.
149
+ enabled_dates: false,
150
+
151
+ // an array of selectable hours.
152
+ // default is FALSE, all hours are selectable.
153
+ enabled_hours: false,
154
+
155
+ // an array of selectable minutes.
156
+ // default is FALSE, all minutes are selectable.
157
+ enabled_minutes: false,
158
+
159
+ // an array of selectable seconds.
160
+ // default is FALSE, all seconds are selectable.
161
+ enabled_seconds: false,
162
+
163
+ // week's starting day
164
+ //
165
+ // valid values are 0 to 6, Sunday to Saturday
166
+ //
167
+ // default is 1, Monday
168
+ first_day_of_week: 1,
169
+
170
+ // format of the returned date
171
+ //
172
+ // accepts the following characters for date formatting: d, D, j, l, N, w, S, F, m, M, n, Y, y, h, H,
173
+ // g, G, i, s, a, A borrowing the syntax from PHP's "date" function.
174
+ //
175
+ // note that when setting a date format without days ('d', 'j'), the users will be able to select only years
176
+ // and months, and when setting a format without months and days ('F', 'm', 'M', 'n', 'd', 'j'), the
177
+ // users will be able to select only years; likewise, when setting a date format with just months ('F', 'm',
178
+ // 'M', 'n') or just years ('Y', 'y'), users will be able to select only months and years, respectively.
179
+ //
180
+ // setting a format that also involves time (h, H, g, G, i, s, a, A) will automatically enable the time
181
+ // picker.
182
+ //
183
+ // also note that the value of the "view" property (see below) may be overridden if it is the case: a value of
184
+ // "days" for the "view" property makes no sense if the date format doesn't allow the selection of days.
185
+ //
186
+ // default is Y-m-d
187
+ format: 'Y-m-d',
188
+
189
+ // captions in the datepicker's header, for the 3 possible views: days, months, years
190
+ //
191
+ // for each of the 3 views the following special characters may be used borrowing from PHP's "date" function's
192
+ // syntax: m, n, F, M, y and Y; any of these will be replaced at runtime with the appropriate date fragment,
193
+ // depending on the currently viewed date. two more special characters are also available Y1 and Y2 (upper
194
+ // case representing years with 4 digits, lowercase representing years with 2 digits) which represent
195
+ // "currently selected year - 7" and "currently selected year + 4" and which only make sense used in the
196
+ // "years" view.
197
+ //
198
+ // even though any of these special characters may be used in any of the 3 views, you should use m, n, F, M
199
+ // for the "days" view and y, Y, Y1, Y2, y1, y2 for the "months" and "years" view or you may get unexpected
200
+ // results!
201
+ //
202
+ // Text and HTML can also be used, and will be rendered as it is, as in the example below (the library is
203
+ // smart enough to not replace special characters when used in words or HTML tags):
204
+ //
205
+ // header_captions: {
206
+ // 'days': 'Departure:<br>F, Y',
207
+ // 'months': 'Departure:<br>Y',
208
+ // 'years': 'Departure:<br>Y1 - Y2'
209
+ // }
210
+ //
211
+ // Default is
212
+ //
213
+ // header_captions: {
214
+ // 'days': 'F, Y',
215
+ // 'months': 'Y',
216
+ // 'years': 'Y1 - Y2'
217
+ // }
218
+ header_captions: {
219
+ days: 'F, Y',
220
+ months: 'Y',
221
+ years: 'Y1 - Y2'
222
+ },
223
+
224
+ // icon's position
225
+ // accepted values are "left" and "right"
226
+ //
227
+ // default is "right"
228
+ icon_position: 'right',
229
+
230
+ // should the icon for opening the datepicker be inside the element?
231
+ // if set to FALSE, the icon will be placed to the right of the parent element, while if set to TRUE it will
232
+ // be placed to the right of the parent element, but *inside* the element itself
233
+ //
234
+ // default is TRUE
235
+ inside: true,
236
+
237
+ // the caption for the "Clear" button
238
+ lang_clear_date: 'Clear date',
239
+
240
+ // months names
241
+ months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
242
+
243
+ // by default, the abbreviated name of a month consists of the first 3 letters from the month's full name;
244
+ // while this is common for most languages, there are also exceptions for languages like Thai, Loa, Myanmar,
245
+ // etc. where this is not correct; for these cases, specify an array with the abbreviations to be used for
246
+ // the months of the year; leave it FALSE to use the first 3 letters of a month's name as the abbreviation.
247
+ //
248
+ // default is FALSE
249
+ months_abbr: false,
250
+
251
+ // HTML to be used for previous/next and up/down buttons, in that order
252
+ //
253
+ // default is ['&#9664;', '&#9654;', '&#9650;', '&#9660;']
254
+ navigation: ['&#9664;', '&#9654;', '&#9650;', '&#9660;'],
255
+
256
+ // the offset, in pixels (x, y), to shift the date picker's position relative to the top-right of the icon
257
+ // that toggles the date picker or, if the icon is disabled, relative to the top-right corner of the element
258
+ // the plugin is attached to.
259
+ //
260
+ // note that this only applies if the position of element relative to the browser's viewport doesn't require
261
+ // the date picker to be placed automatically so that it is visible!
262
+ //
263
+ // default is [5, -5]
264
+ offset: [5, -5],
265
+
266
+ // set whether the date picker should be shown *only* when interacting with the icon
267
+ // note that if you also set the "show_icon" property to FALSE, you will not be able to show the date picker anymore!
268
+ //
269
+ // default is FALSE
270
+ open_icon_only: false,
271
+
272
+ // set this property to TRUE if you want the date picker to be shown when the parent element (if
273
+ // "open_icon_only" is not set to FALSE) or the associated calendar icon (if "show_icon" is set to TRUE)
274
+ // receive focus.
275
+ //
276
+ // default is FALSE
277
+ open_on_focus: false,
278
+
279
+ // if set as a jQuery element with a Zebra_DatePicker attached, that particular date picker will use the
280
+ // current date picker's value as starting date
281
+ // note that the rules set in the "direction" property will still apply, only that the reference date will
282
+ // not be the current system date but the value selected in the current date picker
283
+ // default is FALSE (not paired with another date picker)
284
+ pair: false,
285
+
286
+ // should the element the calendar is attached to, be read-only?
287
+ // if set to TRUE, a date can be set only through the date picker and cannot be entered manually
288
+ //
289
+ // default is TRUE
290
+ readonly_element: true,
291
+
292
+ // should days from previous and/or next month be selectable when visible?
293
+ // note that if the value of this property is set to TRUE, the value of "show_other_months" will be considered
294
+ // TRUE regardless of the actual value!
295
+ //
296
+ // default is FALSE
297
+ select_other_months: false,
298
+
299
+ // should the "Clear date" button be visible?
300
+ //
301
+ // accepted values are:
302
+ //
303
+ // - 0 (zero) - the button for clearing a previously selected date is shown only if a previously selected date
304
+ // already exists; this means that if the input the date picker is attached to is empty, and the user selects
305
+ // a date for the first time, this button will not be visible; once the user picked a date and opens the date
306
+ // picker again, this time the button will be visible.
307
+ //
308
+ // - TRUE will make the button visible all the time
309
+ //
310
+ // - FALSE will disable the button
311
+ //
312
+ // default is "0" (without quotes)
313
+ show_clear_date: 0,
314
+
315
+ // should a calendar icon be added to the elements the plugin is attached to?
316
+ //
317
+ // default is TRUE
318
+ show_icon: true,
319
+
320
+ // should days from previous and/or next month be visible?
321
+ //
322
+ // default is TRUE
323
+ show_other_months: true,
324
+
325
+ // should the "Today" button be visible?
326
+ // setting it to anything but boolean FALSE will enable the button and will use the property's value as
327
+ // caption for the button; setting it to FALSE will disable the button
328
+ //
329
+ // default is "Today"
330
+ show_select_today: 'Today',
331
+
332
+ // should an extra column be shown, showing the number of each week?
333
+ // anything other than FALSE will enable this feature, and use the given value as column title
334
+ // i.e. show_week_number: 'Wk' would enable this feature and have "Wk" as the column's title
335
+ //
336
+ // default is FALSE
337
+ show_week_number: false,
338
+
339
+ // a default date to start the date picker with
340
+ // must be specified in the format defined by the "format" property, or it will be ignored!
341
+ // note that this value is used only if there is no value in the field the date picker is attached to!
342
+ //
343
+ // default is FALSE
344
+ start_date: false,
345
+
346
+ // should default values, in the input field the date picker is attached to, be deleted if they are not valid
347
+ // according to "direction" and/or "disabled_dates"?
348
+ //
349
+ // default is FALSE
350
+ strict: false,
351
+
352
+ // how should the date picker start; valid values are "days", "months" and "years"
353
+ // note that the date picker is always cycling days-months-years when clicking in the date picker's header,
354
+ // and years-months-days when selecting dates (unless one or more of the views are missing due to the date's
355
+ // format)
356
+ //
357
+ // also note that the value of the "view" property may be overridden if the date's format requires so! (i.e.
358
+ // "days" for the "view" property makes no sense if the date format doesn't allow the selection of days)
359
+ //
360
+ // default is "days"
361
+ view: 'days',
362
+
363
+ // days of the week that are considered "weekend days"
364
+ // valid values are 0 to 6, Sunday to Saturday
365
+ //
366
+ // default values are 0 and 6 (Saturday and Sunday)
367
+ weekend_days: [0, 6],
368
+
369
+ // when set to TRUE, day numbers < 10 will be prefixed with 0; set to FALSE if you don't want that
370
+ //
371
+ // default is TRUE
372
+ zero_pad: false,
373
+
374
+ // callback function to be executed whenever the user changes the view (days/months/years), as well as when
375
+ // the user navigates by clicking on the "next"/"previous" icons in any of the views;
376
+ //
377
+ // the callback function called by this event takes 3 arguments - the first argument represents the current
378
+ // view (can be "days", "months" or "years"), the second argument represents an array containing the "active"
379
+ // elements (not disabled) from the view, as jQuery elements, allowing for easy customization and interaction
380
+ // with particular cells in the date picker's view, while the third argument is a reference to the element
381
+ // the date picker is attached to, as a jQuery object (deprecated - use the "this" keyword inside the callback
382
+ // function to refer to the element the date picker is attached to)
383
+ //
384
+ // for simplifying searching for particular dates, each element in the second argument will also have a
385
+ // "date" data attribute whose format depends on the value of the "view" argument:
386
+ // - YYYY-MM-DD for elements in the "days" view
387
+ // - YYYY-MM for elements in the "months" view
388
+ // - YYYY for elements in the "years" view
389
+ //
390
+ // the "this" keyword inside the callback function refers to the element the date picker is attached to!
391
+ onChange: null,
392
+
393
+ // callback function to be executed when the user clicks the "Clear" button
394
+ // the callback function takes a single argument:
395
+ // - a reference to the element the date picker is attached to, as a jQuery object (deprecated - use the
396
+ // "this" keyword inside the callback function to refer to the element the date picker is attached to)
397
+ //
398
+ // the "this" keyword inside the callback function refers to the element the date picker is attached to!
399
+ onClear: null,
400
+
401
+ // callback function to be executed when the date picker is shown
402
+ // the callback function takes a single argument:
403
+ // - a reference to the element the date picker is attached to, as a jQuery object (deprecated - use the
404
+ // "this" keyword inside the callback function to refer to the element the date picker is attached to)
405
+ //
406
+ // the "this" keyword inside the callback function refers to the element the date picker is attached to!
407
+ onOpen: null,
408
+
409
+ // callback function to be executed when the date picker is closed, but only when the "always_visible"
410
+ // property is set to FALSE
411
+ // the callback function takes a single argument:
412
+ // - a reference to the element the date picker is attached to, as a jQuery object (deprecated - use the
413
+ // "this" keyword inside the callback function to refer to the element the date picker is attached to)
414
+ //
415
+ // the "this" keyword inside the callback function refers to the element the date picker is attached to!
416
+ onClose: null,
417
+
418
+ // callback function to be executed when a date is selected
419
+ // the callback function takes 5 arguments:
420
+ // - the date in the format specified by the "format" attribute;
421
+ // - the date in YYYY-MM-DD format
422
+ // - the date as a JavaScript Date object
423
+ // - a reference to the element the date picker is attached to, as a jQuery object (deprecated - use the
424
+ // "this" keyword inside the callback function to refer to the element the date picker is attached to)
425
+ // - the ISO 8601 week number of the selected date
426
+ //
427
+ // the "this" keyword inside the callback function refers to the element the date picker is attached to!
428
+ onSelect: null
429
+
430
+ },
431
+
432
+ // private properties
433
+ cleardate, clickables, confirm_selection, current_system_day, current_system_month, current_system_year,
434
+ custom_class_names = [], custom_classes = {}, datepicker, daypicker, daypicker_cells, default_day,
435
+ default_month, default_year, disabled_dates = [], enabled_dates = [], end_date, first_selectable_day,
436
+ first_selectable_month, first_selectable_year, footer, header, icon, last_selectable_day, last_selectable_month,
437
+ last_selectable_year, monthpicker, monthpicker_cells, original_attributes = {}, selected_hour, selected_minute,
438
+ selected_second, selected_ampm, view_toggler, selected_month, selected_year, selecttoday, shim,
439
+ show_select_today, start_date, timeout, timepicker, timepicker_config, uniqueid = '', yearpicker, yearpicker_cells,
440
+ view, views;
441
+
442
+ var plugin = this;
443
+
444
+ plugin.settings = {};
445
+
446
+ // the jQuery version of the element
447
+ // "element" (without the $) will point to the DOM element
448
+ var $element = $(element);
449
+
450
+ /**
451
+ * Constructor method. Initializes the date picker.
452
+ *
453
+ * @return void
454
+ */
455
+ var init = function(update) {
456
+
457
+ var
458
+
459
+ // the characters that may be present in the date format and that represent days, months, years, hours,
460
+ // minutes and seconds
461
+ date_chars = {
462
+ days: ['d', 'j', 'D'],
463
+ months: ['F', 'm', 'M', 'n', 't'],
464
+ years: ['o', 'Y', 'y'],
465
+ hours: ['G', 'g', 'H', 'h'],
466
+ minutes: ['i'],
467
+ seconds: ['s'],
468
+ ampm: ['A', 'a']
469
+ },
470
+
471
+ // some defaults
472
+ type = null, data, dates, k, l;
473
+
474
+ // generate a random ID for each date picker (we'll use this if later a certain date picker is destroyed to
475
+ // remove related events)
476
+ // the code is taken from http://stackoverflow.com/a/105074
477
+ for (k = 0; k < 3; k++) uniqueid += Math.floor((1 + Math.random()) * 0x10000).toString(16);
478
+
479
+ // unless we're not just updating settings
480
+ if (!update) {
481
+
482
+ // merge default settings with user-settings (
483
+ plugin.settings = $.extend({}, defaults, options);
484
+
485
+ // preserve some of element's original attributes
486
+ original_attributes['readonly'] = $element.attr('readonly');
487
+ original_attributes['style'] = $element.attr('style');
488
+
489
+ // iterate through the element's data attributes (if any)
490
+ for (data in $element.data())
491
+
492
+ // if data attribute's name starts with "zdp_"
493
+ if (data.indexOf('zdp_') === 0) {
494
+
495
+ // remove the "zdp_" prefix
496
+ data = data.replace(/^zdp\_/, '');
497
+
498
+ // if such a property exists
499
+ if (undefined !== defaults[data])
500
+
501
+ // update the property's value
502
+ // (note that for the "pair" property we need to convert the property to an element)
503
+ plugin.settings[data] = (data === 'pair' ? $($element.data('zdp_' + data)) : $element.data('zdp_' + data));
504
+
505
+ }
506
+
507
+ }
508
+
509
+ // if the element should be read-only, set the "readonly" attribute
510
+ if (plugin.settings.readonly_element) $element.attr('readonly', 'readonly');
511
+
512
+ // assume there's no timepicker
513
+ timepicker_config = false;
514
+
515
+ // the views the user can cycle through
516
+ views = [];
517
+
518
+ // determine the views the user can cycle through, depending on the format
519
+ // that is, if the format doesn't contain the day, the user will be able to cycle only through years and months,
520
+ // whereas if the format doesn't contain months nor days, the user will only be able to select years
521
+
522
+ // iterate through all the character blocks
523
+ for (type in date_chars)
524
+
525
+ // iterate through the characters of each block
526
+ $.each(date_chars[type], function(index, character) {
527
+
528
+ var i, max;
529
+
530
+ // if current character exists in the "format" property
531
+ if (plugin.settings.format.indexOf(character) > -1)
532
+
533
+ // if user can cycle through the "days" view
534
+ if (type === 'days') views.push('days');
535
+
536
+ // if user can cycle through the "months" view
537
+ else if (type === 'months') views.push('months');
538
+
539
+ // if user can cycle through the "years" view
540
+ else if (type === 'years') views.push('years');
541
+
542
+ // if time is available in the date's format
543
+ else if (type === 'hours' || type === 'minutes' || type === 'seconds' || type === 'ampm') {
544
+
545
+ // if variable is not yet initialized
546
+ if (!timepicker_config) {
547
+
548
+ // initialize the variable now
549
+ timepicker_config = {is12hour: false};
550
+
551
+ // users may access the "time" view
552
+ views.push('time');
553
+
554
+ }
555
+
556
+ // if hours are available in the date's format
557
+ if (type === 'hours') {
558
+
559
+ // selectable hours (12 or 24) depending on the format
560
+ if (character === 'g' || character == 'h') {
561
+
562
+ max = 12;
563
+
564
+ // set a flag telling that the hour is 12 hour format
565
+ timepicker_config.is12hour = true;
566
+
567
+ } else max = 24;
568
+
569
+ timepicker_config.hours = [];
570
+
571
+ // iterate through valid hours
572
+ for (i = (max === 12 ? 1 : 0); i < (max === 12 ? 13 : max); i++)
573
+
574
+ // and add them to the lookup array if a user-defined list of values doesn't exist, or if the value is in that list
575
+ if (!$.isArray(plugin.settings.enabled_hours) || $.inArray(i, plugin.settings.enabled_hours) > -1) timepicker_config.hours.push(i);
576
+
577
+ // if minutes are available in the date's format
578
+ } else if (type === 'minutes') {
579
+
580
+ timepicker_config.minutes = [];
581
+
582
+ // iterate through valid minutes
583
+ for (i = 0; i < 60; i++)
584
+
585
+ // and add them to the lookup array if a user-defined list of values doesn't exist, or if the value is in that list
586
+ if (!$.isArray(plugin.settings.enabled_minutes) || $.inArray(i, plugin.settings.enabled_minutes) > -1) timepicker_config.minutes.push(i);
587
+
588
+ // if seconds are available in the date's format
589
+ } else if (type === 'seconds') {
590
+
591
+ timepicker_config.seconds = [];
592
+
593
+ // iterate through valid minutes
594
+ for (i = 0; i < 60; i++)
595
+
596
+ // and add them to the lookup array if a user-defined list of values doesn't exist, or if the value is in that list
597
+ if (!$.isArray(plugin.settings.enabled_seconds) || $.inArray(i, plugin.settings.enabled_seconds) > -1) timepicker_config.seconds.push(i);
598
+
599
+ // if am/pm is available in the date's format
600
+ } else
601
+
602
+ // pre-fill the array of selectable seconds
603
+ timepicker_config.ampm = ['am', 'pm'];
604
+
605
+ }
606
+
607
+ });
608
+
609
+ // if invalid format (no days, no months, no years) use the default where the user is able to cycle through
610
+ // all the views, except time
611
+ if (views.length === 0) views = ['years', 'months', 'days'];
612
+
613
+ // if the starting view is not amongst the views the user can cycle through, set the correct starting view
614
+ if ($.inArray(plugin.settings.view, views) === -1) plugin.settings.view = views[views.length - 1];
615
+
616
+ // parse the rules for disabling dates and turn them into arrays of arrays
617
+
618
+ for (k in plugin.settings.custom_classes) if (plugin.settings.custom_classes.hasOwnProperty(k)) custom_class_names.push(k);
619
+
620
+ // it's the same logic for preparing the enabled/disable dates, as well as dates that have custom classes
621
+ for (l = 0; l < 2 + custom_class_names.length; l++) {
622
+
623
+ // first time we're doing disabled dates,
624
+ if (l === 0) dates = plugin.settings.disabled_dates;
625
+
626
+ // second time we're doing enabled_dates
627
+ else if (l === 1) dates = plugin.settings.enabled_dates;
628
+
629
+ // otherwise, we're doing dates that will have custom classes
630
+ else dates = plugin.settings.custom_classes[custom_class_names[l - 2]];
631
+
632
+ // if we have a non-empty array
633
+ if ($.isArray(dates) && dates.length > 0)
634
+
635
+ // iterate through the rules
636
+ $.each(dates, function() {
637
+
638
+ // split the values in rule by white space
639
+ var rules = this.split(' '), i, j, k, limits;
640
+
641
+ // there can be a maximum of 4 rules (days, months, years and, optionally, day of the week)
642
+ for (i = 0; i < 4; i++) {
643
+
644
+ // if one of the values is not available
645
+ // replace it with a * (wildcard)
646
+ if (!rules[i]) rules[i] = '*';
647
+
648
+ // if rule contains a comma, create a new array by splitting the rule by commas
649
+ // if there are no commas create an array containing the rule's string
650
+ rules[i] = (rules[i].indexOf(',') > -1 ? rules[i].split(',') : new Array(rules[i]));
651
+
652
+ // iterate through the items in the rule
653
+ for (j = 0; j < rules[i].length; j++)
654
+
655
+ // if item contains a dash (defining a range)
656
+ if (rules[i][j].indexOf('-') > -1) {
657
+
658
+ // get the lower and upper limits of the range
659
+ limits = rules[i][j].match(/^([0-9]+)\-([0-9]+)/);
660
+
661
+ // if range is valid
662
+ if (null !== limits) {
663
+
664
+ // iterate through the range
665
+ for (k = to_int(limits[1]); k <= to_int(limits[2]); k++)
666
+
667
+ // if value is not already among the values of the rule
668
+ // add it to the rule
669
+ if ($.inArray(k, rules[i]) === -1) rules[i].push(k + '');
670
+
671
+ // remove the range indicator
672
+ rules[i].splice(j, 1);
673
+
674
+ }
675
+
676
+ }
677
+
678
+ // iterate through the items in the rule
679
+ // and make sure that numbers are numbers
680
+ for (j = 0; j < rules[i].length; j++) rules[i][j] = (isNaN(to_int(rules[i][j])) ? rules[i][j] : to_int(rules[i][j]));
681
+
682
+ }
683
+
684
+ // add to the correct list of processed rules
685
+ // first time we're doing disabled dates,
686
+ if (l === 0) disabled_dates.push(rules);
687
+
688
+ // second time we're doing enabled_dates
689
+ else if (l === 1) enabled_dates.push(rules);
690
+
691
+ // otherwise, we're doing the dates to which custom classes need to be applied
692
+ else {
693
+
694
+ if (undefined === custom_classes[custom_class_names[l - 2]]) custom_classes[custom_class_names[l - 2]] = [];
695
+ custom_classes[custom_class_names[l - 2]].push(rules);
696
+
697
+ }
698
+
699
+ });
700
+
701
+ }
702
+
703
+ var
704
+
705
+ // cache the current system date
706
+ date = new Date(),
707
+
708
+ // when the date picker's starting date depends on the value of another date picker, this value will be
709
+ // set by the other date picker
710
+ // this value will be used as base for all calculations (if not set, will be the same as the current
711
+ // system date)
712
+ reference_date = (!plugin.settings.reference_date ? ($element.data('zdp_reference_date') && undefined !== $element.data('zdp_reference_date') ? $element.data('zdp_reference_date') : date) : plugin.settings.reference_date),
713
+
714
+ tmp_start_date, tmp_end_date;
715
+
716
+ // reset these values here as this method might be called more than once during a date picker's lifetime
717
+ // (when the selectable dates depend on the values from another date picker)
718
+ start_date = undefined; end_date = undefined;
719
+
720
+ // extract the date parts
721
+ // also, save the current system month/day/year - we'll use them to highlight the current system date
722
+ first_selectable_month = reference_date.getMonth();
723
+ current_system_month = date.getMonth();
724
+ first_selectable_year = reference_date.getFullYear();
725
+ current_system_year = date.getFullYear();
726
+ first_selectable_day = reference_date.getDate();
727
+ current_system_day = date.getDate();
728
+
729
+ // check if the calendar has any restrictions
730
+
731
+ // calendar is future-only, starting today
732
+ // it means we have a starting date (the current system date), but no ending date
733
+ if (plugin.settings.direction === true) start_date = reference_date;
734
+
735
+ // calendar is past only, ending today
736
+ else if (plugin.settings.direction === false) {
737
+
738
+ // it means we have an ending date (the reference date), but no starting date
739
+ end_date = reference_date;
740
+
741
+ // extract the date parts
742
+ last_selectable_month = end_date.getMonth();
743
+ last_selectable_year = end_date.getFullYear();
744
+ last_selectable_day = end_date.getDate();
745
+
746
+ } else if (
747
+
748
+ // if direction is not given as an array and the value is an integer > 0
749
+ (!$.isArray(plugin.settings.direction) && is_integer(plugin.settings.direction) && to_int(plugin.settings.direction) > 0) ||
750
+
751
+ // or direction is given as an array
752
+ ($.isArray(plugin.settings.direction) && (
753
+
754
+ // and first entry is a valid date
755
+ (tmp_start_date = check_date(plugin.settings.direction[0])) ||
756
+ // or a boolean TRUE
757
+ plugin.settings.direction[0] === true ||
758
+ // or an integer > 0
759
+ (is_integer(plugin.settings.direction[0]) && plugin.settings.direction[0] > 0)
760
+
761
+ ) && (
762
+
763
+ // and second entry is a valid date
764
+ (tmp_end_date = check_date(plugin.settings.direction[1])) ||
765
+ // or a boolean FALSE
766
+ plugin.settings.direction[1] === false ||
767
+ // or integer >= 0
768
+ (is_integer(plugin.settings.direction[1]) && plugin.settings.direction[1] >= 0)
769
+
770
+ ))
771
+
772
+ ) {
773
+
774
+ // if an exact starting date was given, use that as a starting date
775
+ if (tmp_start_date) start_date = tmp_start_date;
776
+
777
+ // otherwise
778
+ else
779
+
780
+ // figure out the starting date
781
+ // use the Date object to normalize the date
782
+ // for example, 2011 05 33 will be transformed to 2011 06 02
783
+ start_date = new Date(
784
+ first_selectable_year,
785
+ first_selectable_month,
786
+ first_selectable_day + (!$.isArray(plugin.settings.direction) ? to_int(plugin.settings.direction) : to_int(plugin.settings.direction[0] === true ? 0 : plugin.settings.direction[0]))
787
+ );
788
+
789
+ // re-extract the date parts
790
+ first_selectable_month = start_date.getMonth();
791
+ first_selectable_year = start_date.getFullYear();
792
+ first_selectable_day = start_date.getDate();
793
+
794
+ // if an exact ending date was given and the date is after the starting date, use that as a ending date
795
+ if (tmp_end_date && +tmp_end_date >= +start_date) end_date = tmp_end_date;
796
+
797
+ // if have information about the ending date
798
+ else if (!tmp_end_date && plugin.settings.direction[1] !== false && $.isArray(plugin.settings.direction))
799
+
800
+ // figure out the ending date
801
+ // use the Date object to normalize the date
802
+ // for example, 2011 05 33 will be transformed to 2011 06 02
803
+ end_date = new Date(
804
+ first_selectable_year,
805
+ first_selectable_month,
806
+ first_selectable_day + to_int(plugin.settings.direction[1])
807
+ );
808
+
809
+ // if a valid ending date exists
810
+ if (end_date) {
811
+
812
+ // extract the date parts
813
+ last_selectable_month = end_date.getMonth();
814
+ last_selectable_year = end_date.getFullYear();
815
+ last_selectable_day = end_date.getDate();
816
+
817
+ }
818
+
819
+ } else if (
820
+
821
+ // if direction is not given as an array and the value is an integer < 0
822
+ (!$.isArray(plugin.settings.direction) && is_integer(plugin.settings.direction) && to_int(plugin.settings.direction) < 0) ||
823
+
824
+ // or direction is given as an array
825
+ ($.isArray(plugin.settings.direction) && (
826
+
827
+ // and first entry is boolean FALSE
828
+ plugin.settings.direction[0] === false ||
829
+ // or an integer < 0
830
+ (is_integer(plugin.settings.direction[0]) && plugin.settings.direction[0] < 0)
831
+
832
+ ) && (
833
+
834
+ // and second entry is a valid date
835
+ (tmp_start_date = check_date(plugin.settings.direction[1])) ||
836
+ // or an integer >= 0
837
+ (is_integer(plugin.settings.direction[1]) && plugin.settings.direction[1] >= 0)
838
+
839
+ ))
840
+
841
+ ) {
842
+
843
+ // figure out the ending date
844
+ // use the Date object to normalize the date
845
+ // for example, 2011 05 33 will be transformed to 2011 06 02
846
+ end_date = new Date(
847
+ first_selectable_year,
848
+ first_selectable_month,
849
+ first_selectable_day + (!$.isArray(plugin.settings.direction) ? to_int(plugin.settings.direction) : to_int(plugin.settings.direction[0] === false ? 0 : plugin.settings.direction[0]))
850
+ );
851
+
852
+ // re-extract the date parts
853
+ last_selectable_month = end_date.getMonth();
854
+ last_selectable_year = end_date.getFullYear();
855
+ last_selectable_day = end_date.getDate();
856
+
857
+ // if an exact starting date was given, and the date is before the ending date, use that as a starting date
858
+ if (tmp_start_date && +tmp_start_date < +end_date) start_date = tmp_start_date;
859
+
860
+ // if have information about the starting date
861
+ else if (!tmp_start_date && $.isArray(plugin.settings.direction))
862
+
863
+ // figure out the staring date
864
+ // use the Date object to normalize the date
865
+ // for example, 2011 05 33 will be transformed to 2011 06 02
866
+ start_date = new Date(
867
+ last_selectable_year,
868
+ last_selectable_month,
869
+ last_selectable_day - to_int(plugin.settings.direction[1])
870
+ );
871
+
872
+ // if a valid starting date exists
873
+ if (start_date) {
874
+
875
+ // extract the date parts
876
+ first_selectable_month = start_date.getMonth();
877
+ first_selectable_year = start_date.getFullYear();
878
+ first_selectable_day = start_date.getDate();
879
+
880
+ }
881
+
882
+ // if there are disabled dates
883
+ } else if ($.isArray(plugin.settings.disabled_dates) && plugin.settings.disabled_dates.length > 0)
884
+
885
+ // iterate through the rules for disabling dates
886
+ for (var interval in disabled_dates)
887
+
888
+ // only if there is a rule that disables *everything*
889
+ if (disabled_dates[interval][0] === '*' && disabled_dates[interval][1] === '*' && disabled_dates[interval][2] === '*' && disabled_dates[interval][3] === '*') {
890
+
891
+ var tmpDates = [];
892
+
893
+ // iterate through the rules for enabling dates
894
+ // looking for the minimum/maximum selectable date (if it's the case)
895
+ $.each(enabled_dates, function() {
896
+
897
+ var rule = this;
898
+
899
+ // if the rule doesn't apply to all years
900
+ if (rule[2][0] !== '*')
901
+
902
+ // format date and store it in our stack
903
+ tmpDates.push(parseInt(
904
+ rule[2][0] +
905
+ (rule[1][0] === '*' ? '12' : str_pad(rule[1][0], 2)) +
906
+ (rule[0][0] === '*' ? (rule[1][0] === '*' ? '31' : new Date(rule[2][0], rule[1][0], 0).getDate()) : str_pad(rule[0][0], 2)), 10));
907
+
908
+ });
909
+
910
+ // sort dates ascending
911
+ tmpDates.sort();
912
+
913
+ // if we have any rules
914
+ if (tmpDates.length > 0) {
915
+
916
+ // get date parts
917
+ var matches = (tmpDates[0] + '').match(/([0-9]{4})([0-9]{2})([0-9]{2})/);
918
+
919
+ // assign the date parts to the appropriate variables
920
+ first_selectable_year = parseInt(matches[1], 10);
921
+ first_selectable_month = parseInt(matches[2], 10) - 1;
922
+ first_selectable_day = parseInt(matches[3], 10);
923
+
924
+ }
925
+
926
+ // don't look further
927
+ break;
928
+
929
+ }
930
+
931
+ // if first selectable date exists but is disabled, find the actual first selectable date
932
+ if (is_disabled(first_selectable_year, first_selectable_month, first_selectable_day)) {
933
+
934
+ // loop until we find the first selectable year
935
+ while (is_disabled(first_selectable_year))
936
+
937
+ // if calendar is past-only,
938
+ if (!start_date) {
939
+
940
+ // decrement the year
941
+ first_selectable_year--;
942
+
943
+ // because we've changed years, reset the month to December
944
+ first_selectable_month = 11;
945
+
946
+ // otherwise
947
+ } else {
948
+
949
+ // increment the year
950
+ first_selectable_year++;
951
+
952
+ // because we've changed years, reset the month to January
953
+ first_selectable_month = 0;
954
+
955
+ }
956
+
957
+ // loop until we find the first selectable month
958
+ while (is_disabled(first_selectable_year, first_selectable_month)) {
959
+
960
+ // if calendar is past-only
961
+ if (!start_date) {
962
+
963
+ // decrement the month
964
+ first_selectable_month--;
965
+
966
+ // because we've changed months, reset the day to the last day of the month
967
+ first_selectable_day = new Date(first_selectable_year, first_selectable_month + 1, 0).getDate();
968
+
969
+ // otherwise
970
+ } else {
971
+
972
+ // increment the month
973
+ first_selectable_month++;
974
+
975
+ // because we've changed months, reset the day to the first day of the month
976
+ first_selectable_day = 1;
977
+
978
+ }
979
+
980
+ // if we moved to a following year
981
+ if (first_selectable_month > 11) {
982
+
983
+ // increment the year
984
+ first_selectable_year++;
985
+
986
+ // reset the month to January
987
+ first_selectable_month = 0;
988
+
989
+ // because we've changed months, reset the day to the first day of the month
990
+ first_selectable_day = 1;
991
+
992
+ // if we moved to a previous year
993
+ } else if (first_selectable_month < 0) {
994
+
995
+ // decrement the year
996
+ first_selectable_year--;
997
+
998
+ // reset the month to December
999
+ first_selectable_month = 11;
1000
+
1001
+ // because we've changed months, reset the day to the last day of the month
1002
+ first_selectable_day = new Date(first_selectable_year, first_selectable_month + 1, 0).getDate();
1003
+
1004
+ }
1005
+
1006
+ }
1007
+
1008
+ // loop until we find the first selectable day
1009
+ while (is_disabled(first_selectable_year, first_selectable_month, first_selectable_day)) {
1010
+
1011
+ // if calendar is past-only, decrement the day
1012
+ if (!start_date) first_selectable_day--;
1013
+
1014
+ // otherwise, increment the day
1015
+ else first_selectable_day++;
1016
+
1017
+ // use the Date object to normalize the date
1018
+ // for example, 2011 05 33 will be transformed to 2011 06 02
1019
+ date = new Date(first_selectable_year, first_selectable_month, first_selectable_day);
1020
+
1021
+ // re-extract date parts from the normalized date
1022
+ // as we use them in the current loop
1023
+ first_selectable_year = date.getFullYear();
1024
+ first_selectable_month = date.getMonth();
1025
+ first_selectable_day = date.getDate();
1026
+
1027
+ }
1028
+
1029
+ // use the Date object to normalize the date
1030
+ // for example, 2011 05 33 will be transformed to 2011 06 02
1031
+ date = new Date(first_selectable_year, first_selectable_month, first_selectable_day);
1032
+
1033
+ // re-extract date parts from the normalized date
1034
+ // as we use them in the current loop
1035
+ first_selectable_year = date.getFullYear();
1036
+ first_selectable_month = date.getMonth();
1037
+ first_selectable_day = date.getDate();
1038
+
1039
+ }
1040
+
1041
+ // get the default date, from the element, and check if it represents a valid date, according to the required format
1042
+ var default_date = check_date($element.val() || (plugin.settings.start_date ? plugin.settings.start_date : ''));
1043
+
1044
+ // if there is a default date, date picker is in "strict" mode, and the default date is disabled
1045
+ if (default_date && plugin.settings.strict && is_disabled(default_date.getFullYear(), default_date.getMonth(), default_date.getDate()))
1046
+
1047
+ // clear the value of the parent element
1048
+ $element.val('');
1049
+
1050
+ // updates value for the date picker whose starting date depends on the selected date (if any)
1051
+ if (!update && (undefined !== start_date || undefined !== default_date))
1052
+ update_dependent(undefined !== default_date ? default_date : start_date);
1053
+
1054
+ // if date picker is not always visible in a container
1055
+ if (!(plugin.settings.always_visible instanceof jQuery)) {
1056
+
1057
+ // if we're just creating the date picker
1058
+ if (!update) {
1059
+
1060
+ // if a calendar icon should be added to the element the plugin is attached to, create the icon now
1061
+ if (plugin.settings.show_icon) {
1062
+
1063
+ // strangely, in Firefox 21+ (or maybe even earlier) input elements have their "display" property
1064
+ // set to "inline" instead of "inline-block" as do all the other browsers.
1065
+ // because this behavior brakes the positioning of the icon, we'll set the "display" property to
1066
+ // "inline-block" before anything else;
1067
+ if (browser.name === 'firefox' && $element.is('input[type="text"]') && $element.css('display') === 'inline') $element.css('display', 'inline-block');
1068
+
1069
+ // we create a wrapper for the parent element so that we can later position the icon
1070
+ // also, make sure the wrapper inherits some important css properties of the parent element
1071
+ var icon_wrapper = $('<span class="Zebra_DatePicker_Icon_Wrapper"></span>').css({
1072
+ display: $element.css('display'),
1073
+ position: $element.css('position') === 'static' ? 'relative' : $element.css('position'),
1074
+ float: $element.css('float'),
1075
+ top: $element.css('top'),
1076
+ right: $element.css('right'),
1077
+ bottom: $element.css('bottom'),
1078
+ left: $element.css('left')
1079
+ });
1080
+
1081
+ // if parent element has its "display" property set to "block"
1082
+ // the wrapper has to have its "width" set
1083
+ if ($element.css('display') === 'block') icon_wrapper.css('width', $element.outerWidth(true));
1084
+
1085
+ // put wrapper around the element
1086
+ // also, make sure we set some important css properties for it
1087
+ $element.wrap(icon_wrapper).css({
1088
+ position: 'relative',
1089
+ top: 'auto',
1090
+ right: 'auto',
1091
+ bottom: 'auto',
1092
+ left: 'auto'
1093
+ });
1094
+
1095
+ // create the actual calendar icon (show a disabled icon if the element is disabled)
1096
+ icon = $('<button type="button" class="Zebra_DatePicker_Icon' + ($element.attr('disabled') === 'disabled' ? ' Zebra_DatePicker_Icon_Disabled' : '') + '">Pick a date</button>');
1097
+
1098
+ // a reference to the icon, as a global property
1099
+ plugin.icon = icon;
1100
+
1101
+ // the date picker will open when clicking both the icon and the element the plugin is attached to
1102
+ // (or the icon only, if set so)
1103
+ clickables = plugin.settings.open_icon_only ? icon : icon.add($element);
1104
+
1105
+ // if calendar icon is not visible, the date picker will open when clicking the element
1106
+ } else clickables = $element;
1107
+
1108
+ // attach the "click" and, if required, the "focus" event to the clickable elements (icon and/or element)
1109
+ clickables.on('click.Zebra_DatePicker_' + uniqueid + (plugin.settings.open_on_focus ? ' focus.Zebra_DatePicker_' + uniqueid : ''), function() {
1110
+
1111
+ // if date picker is not visible and element is not disabled
1112
+ if (datepicker.hasClass('dp_hidden') && !$element.attr('disabled'))
1113
+
1114
+ // show the date picker
1115
+ plugin.show();
1116
+
1117
+ });
1118
+
1119
+ // attach a keydown event to the clickable elements (icon and/or element)
1120
+ clickables.on('keydown.Zebra_DatePicker_' + uniqueid, function(e) {
1121
+
1122
+ // if "Tab" key was pressed and the date picker is visible
1123
+ if (e.keyCode === 9 && !datepicker.hasClass('dp_hidden'))
1124
+
1125
+ // hide the date picker
1126
+ plugin.hide();
1127
+
1128
+ });
1129
+
1130
+ // if users can manually enter dates and a pair date element exists
1131
+ if (!plugin.settings.readonly_element && plugin.settings.pair)
1132
+
1133
+ // whenever the element looses focus
1134
+ $element.on('blur.Zebra_DatePicker_' + uniqueid, function() {
1135
+
1136
+ var date;
1137
+
1138
+ // if a valid date was entered, update the paired date picker
1139
+ if ((date = check_date($(this).val())) && !is_disabled(date.getFullYear(), date.getMonth(), date.getDate())) update_dependent(date);
1140
+
1141
+ });
1142
+
1143
+ // if icon exists, inject it into the DOM, right after the parent element (and inside the wrapper)
1144
+ if (undefined !== icon) icon.insertAfter($element);
1145
+
1146
+ }
1147
+
1148
+ // if calendar icon exists
1149
+ if (undefined !== icon) {
1150
+
1151
+ // needed when updating: remove any inline style set previously by library,
1152
+ // so we get the right values below
1153
+ icon.attr('style', '');
1154
+
1155
+ // if calendar icon is to be placed *inside* the element
1156
+ // add an extra class to the icon
1157
+ if (plugin.settings.inside) icon.addClass('Zebra_DatePicker_Icon_Inside_' + (plugin.settings.icon_position === 'right' ? 'Right' : 'Left'));
1158
+
1159
+ var
1160
+
1161
+ // get element's width and height (including margins)
1162
+ element_width = $element.outerWidth(),
1163
+ element_height = $element.outerHeight(),
1164
+ element_margin_left = parseInt($element.css('marginLeft'), 10) || 0,
1165
+ element_margin_top = parseInt($element.css('marginTop'), 10) || 0,
1166
+
1167
+ // get icon's width, height and margins
1168
+ icon_width = icon.outerWidth(),
1169
+ icon_height = icon.outerHeight(),
1170
+ icon_margin_left = parseInt(icon.css('marginLeft'), 10) || 0,
1171
+ icon_margin_right = parseInt(icon.css('marginRight'), 10) || 0;
1172
+
1173
+ // if icon is to be placed *inside* the element
1174
+ // position the icon accordingly
1175
+ if (plugin.settings.inside) {
1176
+
1177
+ // set icon's top
1178
+ icon.css('top', element_margin_top + ((element_height - icon_height) / 2));
1179
+
1180
+ // place icon to the right or to the left, according to the settings
1181
+ if (plugin.settings.icon_position === 'right') icon.css('right', 0);
1182
+ else icon.css('left', 0);
1183
+
1184
+ // if icon is to be placed to the right of the element
1185
+ // position the icon accordingly
1186
+ } else
1187
+
1188
+ icon.css({
1189
+ top: element_margin_top + ((element_height - icon_height) / 2),
1190
+ left: element_margin_left + element_width + icon_margin_left
1191
+ });
1192
+
1193
+ // assume the datepicker is not disabled
1194
+ icon.removeClass(' Zebra_DatePicker_Icon_Disabled');
1195
+
1196
+ // if element the datepicker is attached to became disabled, disable the calendar icon, too
1197
+ if ($element.attr('disabled') === 'disabled') icon.addClass('Zebra_DatePicker_Icon_Disabled');
1198
+
1199
+ }
1200
+
1201
+ }
1202
+
1203
+ // if the "Today" button is to be shown and it makes sense to be shown
1204
+ // (the "days" view is available and "today" is not a disabled date)
1205
+ show_select_today = (plugin.settings.show_select_today !== false && $.inArray('days', views) > -1 && !is_disabled(current_system_year, current_system_month, current_system_day) ? plugin.settings.show_select_today : false);
1206
+
1207
+ // if we just needed to recompute the things above
1208
+ if (update) {
1209
+
1210
+ // make sure we update these strings, in case they've changed
1211
+ $('.dp_previous', datepicker).html(plugin.settings.navigation[0]);
1212
+ $('.dp_next', datepicker).html(plugin.settings.navigation[1]);
1213
+ $('.dp_time_controls_increase .dp_time_control', datepicker).html(plugin.settings.navigation[2]);
1214
+ $('.dp_time_controls_decrease .dp_time_control', datepicker).html(plugin.settings.navigation[3]);
1215
+ $('.dp_clear', datepicker).html(plugin.settings.lang_clear_date);
1216
+ $('.dp_today', datepicker).html(plugin.settings.show_select_today);
1217
+
1218
+ // don't go further
1219
+ return;
1220
+
1221
+ }
1222
+
1223
+ // update icon/date picker position on resize and/or changing orientation
1224
+ $(window).on('resize.Zebra_DatePicker_' + uniqueid + ', orientationchange.Zebra_DatePicker_' + uniqueid, function() {
1225
+
1226
+ // hide the date picker
1227
+ plugin.hide();
1228
+
1229
+ // if the icon is visible, update its position as the parent element might have changed position
1230
+ if (icon !== undefined) {
1231
+
1232
+ // we use timeouts so that we do not call the "update" method on *every* step of the resize event
1233
+
1234
+ // clear a previously set timeout
1235
+ clearTimeout(timeout);
1236
+
1237
+ // set timeout again
1238
+ timeout = setTimeout(function() {
1239
+
1240
+ // update the date picker
1241
+ plugin.update();
1242
+
1243
+ }, 100);
1244
+
1245
+ }
1246
+
1247
+ });
1248
+
1249
+ // generate the container that will hold everything
1250
+ var html = '' +
1251
+ '<div class="Zebra_DatePicker">' +
1252
+ '<table class="dp_header dp_actions">' +
1253
+ '<tr>' +
1254
+ '<td class="dp_previous">' + plugin.settings.navigation[0] + '</td>' +
1255
+ '<td class="dp_caption"></td>' +
1256
+ '<td class="dp_next">' + plugin.settings.navigation[1] + '</td>' +
1257
+ '</tr>' +
1258
+ '</table>' +
1259
+ '<table class="dp_daypicker' + (plugin.settings.show_week_number ? ' dp_week_numbers' : '') + ' dp_body"></table>' +
1260
+ '<table class="dp_monthpicker dp_body"></table>' +
1261
+ '<table class="dp_yearpicker dp_body"></table>' +
1262
+ '<table class="dp_timepicker dp_body"></table>' +
1263
+ '<table class="dp_footer dp_actions"><tr>' +
1264
+ '<td class="dp_today">' + show_select_today + '</td>' +
1265
+ '<td class="dp_clear">' + plugin.settings.lang_clear_date + '</td>' +
1266
+ '<td class="dp_view_toggler dp_icon"></td>' +
1267
+ '<td class="dp_confirm dp_icon"></td>' +
1268
+ '</tr></table>' +
1269
+ '</div>';
1270
+
1271
+ // create a jQuery object out of the HTML above and create a reference to it
1272
+ datepicker = $(html);
1273
+
1274
+ // create references to the different parts of the date picker
1275
+ header = $('table.dp_header', datepicker);
1276
+ daypicker = $('table.dp_daypicker', datepicker);
1277
+ monthpicker = $('table.dp_monthpicker', datepicker);
1278
+ yearpicker = $('table.dp_yearpicker', datepicker);
1279
+ timepicker = $('table.dp_timepicker', datepicker);
1280
+ footer = $('table.dp_footer', datepicker);
1281
+ selecttoday = $('td.dp_today', footer);
1282
+ cleardate = $('td.dp_clear', footer);
1283
+ view_toggler = $('td.dp_view_toggler', footer);
1284
+ confirm_selection = $('td.dp_confirm', footer);
1285
+
1286
+ // if date picker is not always visible in a container
1287
+ if (!(plugin.settings.always_visible instanceof jQuery))
1288
+
1289
+ // inject the container into the DOM
1290
+ plugin.settings.container.append(datepicker);
1291
+
1292
+ // otherwise, if element is not disabled
1293
+ else if (!$element.attr('disabled')) {
1294
+
1295
+ // inject the date picker into the designated container element
1296
+ plugin.settings.always_visible.append(datepicker);
1297
+
1298
+ // and make it visible right away
1299
+ plugin.show();
1300
+
1301
+ }
1302
+
1303
+ // add the mouseover/mousevents to all to the date picker's cells
1304
+ // except those that are not selectable
1305
+ datepicker
1306
+ .on('mouseover', 'td:not(.dp_disabled)', function() {
1307
+ $(this).addClass('dp_hover');
1308
+ })
1309
+ .on('mouseout', 'td:not(.dp_disabled)', function() {
1310
+ $(this).removeClass('dp_hover');
1311
+ });
1312
+
1313
+ // prevent text selection (prevent accidental select when user clicks too fast)
1314
+ disable_text_select(datepicker);
1315
+
1316
+ // event for when clicking the "previous" button
1317
+ $('.dp_previous', header).on('click', function() {
1318
+
1319
+ // if view is "months"
1320
+ // decrement year by one
1321
+ if (view === 'months') selected_year--;
1322
+
1323
+ // if view is "years"
1324
+ // decrement years by 12
1325
+ else if (view === 'years') selected_year -= 12;
1326
+
1327
+ // if view is "days"
1328
+ // decrement the month and
1329
+ // if month is out of range
1330
+ else if (--selected_month < 0) {
1331
+
1332
+ // go to the last month of the previous year
1333
+ selected_month = 11;
1334
+ selected_year--;
1335
+
1336
+ }
1337
+
1338
+ // generate the appropriate view
1339
+ manage_views();
1340
+
1341
+ });
1342
+
1343
+ // attach a click event to the caption in header
1344
+ $('.dp_caption', header).on('click', function() {
1345
+
1346
+ // if current view is "days", take the user to the next view, depending on the format
1347
+ if (view === 'days') view = ($.inArray('months', views) > -1 ? 'months' : ($.inArray('years', views) > -1 ? 'years' : 'days'));
1348
+
1349
+ // if current view is "months", take the user to the next view, depending on the format
1350
+ else if (view === 'months') view = ($.inArray('years', views) > -1 ? 'years' : ($.inArray('days', views) > -1 ? 'days' : 'months'));
1351
+
1352
+ // if current view is "years", take the user to the next view, depending on the format
1353
+ else view = ($.inArray('days', views) > -1 ? 'days' : ($.inArray('months', views) > -1 ? 'months' : 'years'));
1354
+
1355
+ // generate the appropriate view
1356
+ manage_views();
1357
+
1358
+ });
1359
+
1360
+ // event for when clicking the "next" button
1361
+ $('.dp_next', header).on('click', function() {
1362
+
1363
+ // if view is "months"
1364
+ // increment year by 1
1365
+ if (view === 'months') selected_year++;
1366
+
1367
+ // if view is "years"
1368
+ // increment years by 12
1369
+ else if (view === 'years') selected_year += 12;
1370
+
1371
+ // if view is "days"
1372
+ // increment the month and
1373
+ // if month is out of range
1374
+ else if (++selected_month === 12) {
1375
+
1376
+ // go to the first month of the next year
1377
+ selected_month = 0;
1378
+ selected_year++;
1379
+
1380
+ }
1381
+
1382
+ // generate the appropriate view
1383
+ manage_views();
1384
+
1385
+ });
1386
+
1387
+ // attach a click event for the cells in the day picker
1388
+ daypicker.on('click', 'td:not(.dp_disabled)', function() {
1389
+
1390
+ var matches;
1391
+
1392
+ // if other months are selectable and currently clicked cell contains a class with the cell's date
1393
+ if (plugin.settings.select_other_months && $(this).attr('class') && null !== (matches = $(this).attr('class').match(/date\_([0-9]{4})(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])/)))
1394
+
1395
+ // use the stored date
1396
+ select_date(matches[1], matches[2] - 1, matches[3], 'days', $(this));
1397
+
1398
+ // put selected date in the element the plugin is attached to, and hide the date picker
1399
+ else select_date(selected_year, selected_month, to_int($(this).html()), 'days', $(this));
1400
+
1401
+ });
1402
+
1403
+ // attach a click event for the cells in the month picker
1404
+ monthpicker.on('click', 'td:not(.dp_disabled)', function() {
1405
+
1406
+ // get the month we've clicked on
1407
+ var matches = $(this).attr('class').match(/dp\_month\_([0-9]+)/);
1408
+
1409
+ // set the selected month
1410
+ selected_month = to_int(matches[1]);
1411
+
1412
+ // if user can select only years and months
1413
+ if ($.inArray('days', views) === -1)
1414
+
1415
+ // put selected date in the element the plugin is attached to, and hide the date picker
1416
+ select_date(selected_year, selected_month, 1, 'months', $(this));
1417
+
1418
+ else {
1419
+
1420
+ // direct the user to the "days" view
1421
+ view = 'days';
1422
+
1423
+ // if date picker is always visible
1424
+ // empty the value in the text box the date picker is attached to
1425
+ if (plugin.settings.always_visible) $element.val('');
1426
+
1427
+ // generate the appropriate view
1428
+ manage_views();
1429
+
1430
+ }
1431
+
1432
+ });
1433
+
1434
+ // attach a click event for the cells in the year picker
1435
+ yearpicker.on('click', 'td:not(.dp_disabled)', function() {
1436
+
1437
+ // set the selected year
1438
+ selected_year = to_int($(this).html());
1439
+
1440
+ // if user can select only years
1441
+ if ($.inArray('months', views) === -1)
1442
+
1443
+ // put selected date in the element the plugin is attached to, and hide the date picker
1444
+ select_date(selected_year, 1, 1, 'years', $(this));
1445
+
1446
+ else {
1447
+
1448
+ // direct the user to the "months" view
1449
+ view = 'months';
1450
+
1451
+ // if date picker is always visible
1452
+ // empty the value in the text box the date picker is attached to
1453
+ if (plugin.settings.always_visible) $element.val('');
1454
+
1455
+ // generate the appropriate view
1456
+ manage_views();
1457
+
1458
+ }
1459
+
1460
+ });
1461
+
1462
+ // function to execute when the "Today" button is clicked
1463
+ selecttoday.on('click', function(e) {
1464
+
1465
+ // date might have changed since we opened the date picker, so always use the current date
1466
+ var date = new Date;
1467
+
1468
+ e.preventDefault();
1469
+
1470
+ // select the current date
1471
+ select_date(date.getFullYear(), date.getMonth(), date.getDate(), 'days', $('.dp_current', daypicker));
1472
+
1473
+ });
1474
+
1475
+ // function to execute when the "Clear" button is clicked
1476
+ cleardate.on('click', function(e) {
1477
+
1478
+ e.preventDefault();
1479
+
1480
+ // clear the element's value
1481
+ $element.val('');
1482
+
1483
+ // reset these values
1484
+ default_day = null; default_month = null; default_year = null;
1485
+
1486
+ // if date picker is not always visible
1487
+ if (!plugin.settings.always_visible) {
1488
+
1489
+ // reset these values
1490
+ selected_month = null; selected_year = null;
1491
+
1492
+ // if date picker is always visible
1493
+ } else
1494
+
1495
+ // remove the "selected" class from all cells that have it
1496
+ $('td.dp_selected', datepicker).removeClass('dp_selected');
1497
+
1498
+ // give the focus back to the parent element
1499
+ $element.focus();
1500
+
1501
+ // hide the date picker
1502
+ plugin.hide();
1503
+
1504
+ // if a callback function exists for when clearing a date
1505
+ if (plugin.settings.onClear && typeof plugin.settings.onClear === 'function')
1506
+
1507
+ // execute the callback function and pass as argument the element the plugin is attached to
1508
+ plugin.settings.onClear.call($element, $element);
1509
+
1510
+ });
1511
+
1512
+ // function to execute when the clock/calendar button is clicked in the footer
1513
+ view_toggler.on('click', function() {
1514
+
1515
+ // if we're not in the time picker mode
1516
+ if (view !== 'time') {
1517
+
1518
+ // switch to time picker mode
1519
+ view = 'time';
1520
+ manage_views();
1521
+
1522
+ // if we are already in the time picker mode,
1523
+ // switch back to the standard view
1524
+ // (let the click on the header's caption handle things)
1525
+ } else $('.dp_caption', header).trigger('click');
1526
+
1527
+ });
1528
+
1529
+ // when the "confirm selection" button is clicked, hide the date picker
1530
+ // (visible only when in the "time" view)
1531
+ confirm_selection.on('click', function() {
1532
+
1533
+ // as users may click this before making any adjustments to time, simulate time adjustment so that
1534
+ // a value is selected
1535
+ $('.dp_time_controls_increase td').trigger('click');
1536
+ $('.dp_time_controls_decrease td').trigger('click');
1537
+
1538
+ plugin.hide();
1539
+
1540
+ });
1541
+
1542
+ // handle value increases on the time picker
1543
+ datepicker.on('click', '.dp_time_controls_increase td, .dp_time_controls_decrease td', function() {
1544
+
1545
+ var
1546
+
1547
+ // are we increasing or decreasing values?
1548
+ increase = $(this).parent('.dp_time_controls_increase').length > 0,
1549
+
1550
+ // figure out what we're increasing (hour, minutes, seconds, ampm)
1551
+ matches = $(this).attr('class').match(/dp\_time\_([^\s]+)/i),
1552
+ value_container = $('.dp_time_segments .dp_time_' + matches[1] + (matches[1] !== 'ampm' ? 's' : ''), timepicker),
1553
+
1554
+ // the current value (strip the zeros in front)
1555
+ value = value_container.text().toLowerCase(),
1556
+
1557
+ // the array with allowed values
1558
+ lookup = timepicker_config[matches[1] + (matches[1] !== 'ampm' ? 's' : '')],
1559
+
1560
+ // the current value's position in the array of allowed values
1561
+ current_value_position = lookup.indexOf(matches[1] !== 'ampm' ? parseInt(value, 10) : value),
1562
+
1563
+ // the next value's position in the lookup array
1564
+ next_value_position = current_value_position === -1 ? 0 : (increase ? (current_value_position + 1 >= lookup.length ? 0 : current_value_position + 1) : (current_value_position - 1 < 0 ? lookup.length - 1 : current_value_position - 1)),
1565
+
1566
+ default_date;
1567
+
1568
+ // increase/decrease the required value according to the values in the lookup array
1569
+ if (matches[1] === 'hour') selected_hour = lookup[next_value_position];
1570
+ else if (matches[1] === 'minute') selected_minute = lookup[next_value_position];
1571
+ else if (matches[1] === 'second') selected_second = lookup[next_value_position];
1572
+ else selected_ampm = lookup[next_value_position];
1573
+
1574
+ // if a default day is not available and the "start_date" property is set
1575
+ if (!default_day && plugin.settings.start_date) {
1576
+
1577
+ // check if "start_date" is valid according to the format
1578
+ default_date = check_date(plugin.settings.start_date);
1579
+
1580
+ // ...and if it is, extract the day from there
1581
+ if (default_date) default_day = default_date.getDate();
1582
+
1583
+ }
1584
+
1585
+ // if still no value, use the first selectable day
1586
+ if (!default_day) default_day = first_selectable_day;
1587
+
1588
+ // set the new value
1589
+ value_container.text(str_pad(lookup[next_value_position], 2).toUpperCase());
1590
+
1591
+ // update the value in the element
1592
+ select_date(selected_year, selected_month, default_day);
1593
+
1594
+ });
1595
+
1596
+ // if date picker is not always visible in a container
1597
+ if (!(plugin.settings.always_visible instanceof jQuery)) {
1598
+
1599
+ // whenever anything is clicked on the page
1600
+ $(document).on('mousedown.Zebra_DatePicker_' + uniqueid + ' touchstart.Zebra_DatePicker_' + uniqueid, function(e) {
1601
+
1602
+ // if
1603
+ if (
1604
+
1605
+ // date picker is visible
1606
+ !datepicker.hasClass('dp_hidden') &&
1607
+ (
1608
+ // date picker opens only on interacting with the icon, icon exists, but it is not the clicked element
1609
+ (plugin.settings.open_icon_only && plugin.icon && $(e.target).get(0) !== plugin.icon.get(0)) ||
1610
+
1611
+ // date picker doesn't open only on interacting with the icon but the clicked element it's not the icon nor the parent element
1612
+ (!plugin.settings.open_icon_only && $(e.target).get(0) !== $element.get(0) && (!plugin.icon || $(e.target).get(0) !== plugin.icon.get(0)))
1613
+
1614
+ ) &&
1615
+
1616
+ // and the click is not inside the calendar
1617
+ $(e.target).parents().filter('.Zebra_DatePicker').length === 0
1618
+
1619
+ // hide the date picker
1620
+ ) plugin.hide(true);
1621
+
1622
+ });
1623
+
1624
+ // whenever a key is pressed on the page
1625
+ $(document).on('keyup.Zebra_DatePicker_' + uniqueid, function(e) {
1626
+
1627
+ // if the date picker is visible
1628
+ // and the pressed key is ESC
1629
+ // hide the date picker
1630
+ if (!datepicker.hasClass('dp_hidden') && e.which === 27) plugin.hide();
1631
+
1632
+ });
1633
+
1634
+ }
1635
+
1636
+ // last thing is to pre-render some of the date picker right away
1637
+ manage_views();
1638
+
1639
+ };
1640
+
1641
+ /**
1642
+ * Clears the selected date.
1643
+ *
1644
+ * @return void
1645
+ */
1646
+ plugin.clear_date = function() {
1647
+
1648
+ $(cleardate).trigger('click');
1649
+
1650
+ };
1651
+
1652
+ /**
1653
+ * Destroys the date picker.
1654
+ *
1655
+ * @return void
1656
+ */
1657
+ plugin.destroy = function() {
1658
+
1659
+ // if the calendar icon exists
1660
+ if (undefined !== plugin.icon) {
1661
+
1662
+ // remove associated event handlers
1663
+ plugin.icon.off('click.Zebra_DatePicker_' + uniqueid);
1664
+ plugin.icon.off('focus.Zebra_DatePicker_' + uniqueid);
1665
+ plugin.icon.off('keydown.Zebra_DatePicker_' + uniqueid);
1666
+
1667
+ // remove the icon itself
1668
+ plugin.icon.remove();
1669
+
1670
+ }
1671
+
1672
+ // remove all events attached to the datepicker
1673
+ // (these are the ones for increasing/decreasing values in the time picker)
1674
+ datepicker.off();
1675
+
1676
+ // remove the calendar
1677
+ datepicker.remove();
1678
+
1679
+ // if calendar icon was shown and the date picker was not always visible in a container,
1680
+ // also remove the wrapper used for positioning it
1681
+ if (plugin.settings.show_icon && !(plugin.settings.always_visible instanceof jQuery)) $element.unwrap();
1682
+
1683
+ // remove associated event handlers from the element
1684
+ $element.off('blur.Zebra_DatePicker_' + uniqueid);
1685
+ $element.off('click.Zebra_DatePicker_' + uniqueid);
1686
+ $element.off('focus.Zebra_DatePicker_' + uniqueid);
1687
+ $element.off('keydown.Zebra_DatePicker_' + uniqueid);
1688
+ $element.off('mousedown.Zebra_DatePicker_' + uniqueid);
1689
+
1690
+ // remove associated event handlers from the document
1691
+ $(document).off('keyup.Zebra_DatePicker_' + uniqueid);
1692
+ $(document).off('mousedown.Zebra_DatePicker_' + uniqueid);
1693
+ $(document).off('touchstart.Zebra_DatePicker_' + uniqueid);
1694
+ $(window).off('resize.Zebra_DatePicker_' + uniqueid);
1695
+ $(window).off('orientationchange.Zebra_DatePicker_' + uniqueid);
1696
+
1697
+ // remove association with the element
1698
+ $element.removeData('Zebra_DatePicker');
1699
+
1700
+ // restore element's modified attributes
1701
+ $element.attr('readonly', original_attributes['readonly']);
1702
+ $element.attr('style', original_attributes['style'] ? original_attributes['style'] : '');
1703
+
1704
+ };
1705
+
1706
+ /**
1707
+ * Hides the date picker.
1708
+ *
1709
+ * @return void
1710
+ */
1711
+ plugin.hide = function(outside) {
1712
+
1713
+ // if date picker is not always visible or we clicked outside the date picker
1714
+ // (the "outside" argument is TRUE when clicking outside the date picker and the "always_visible" is set to boolean TRUE)
1715
+ if (!plugin.settings.always_visible || outside) {
1716
+
1717
+ // hide the iFrameShim in Internet Explorer 6
1718
+ iframeShim('hide');
1719
+
1720
+ // hide the date picker
1721
+ datepicker.addClass('dp_hidden');
1722
+
1723
+ // if a callback function exists for when hiding the date picker
1724
+ if (plugin.settings.onClose && typeof plugin.settings.onClose === 'function')
1725
+
1726
+ // execute the callback function and pass as argument the element the plugin is attached to
1727
+ plugin.settings.onClose.call($element, $element);
1728
+
1729
+ }
1730
+
1731
+ };
1732
+
1733
+ /**
1734
+ * Set the date picker's value
1735
+ *
1736
+ * Must be in the format set by the "format" property!
1737
+ *
1738
+ * @return void
1739
+ */
1740
+ plugin.set_date = function(date) {
1741
+
1742
+ var dateObj;
1743
+
1744
+ // if a valid date was entered, and date is not disabled
1745
+ if ((dateObj = check_date(date)) && !is_disabled(dateObj.getFullYear(), dateObj.getMonth(), dateObj.getDate())) {
1746
+
1747
+ // set the element's value
1748
+ $element.val(date);
1749
+
1750
+ // update the paired date picker (if any)
1751
+ update_dependent(dateObj);
1752
+
1753
+ }
1754
+
1755
+ };
1756
+
1757
+ /**
1758
+ * Shows the date picker.
1759
+ *
1760
+ * @return void
1761
+ */
1762
+ plugin.show = function() {
1763
+
1764
+ // always show the view defined in settings
1765
+ view = plugin.settings.view;
1766
+
1767
+ // get the default date, from the element, and check if it represents a valid date, according to the required format
1768
+ var default_date = check_date($element.val() || (plugin.settings.start_date ? plugin.settings.start_date : '')),
1769
+ current_date;
1770
+
1771
+ // if the value represents a valid date
1772
+ if (default_date) {
1773
+
1774
+ // extract the date parts
1775
+ // we'll use these to highlight the default date in the date picker and as starting point to
1776
+ // what year and month to start the date picker with
1777
+ // why separate values? because selected_* will change as user navigates within the date picker
1778
+ default_month = default_date.getMonth();
1779
+ selected_month = default_date.getMonth();
1780
+ default_year = default_date.getFullYear();
1781
+ selected_year = default_date.getFullYear();
1782
+ default_day = default_date.getDate();
1783
+
1784
+ // if the default date represents a disabled date
1785
+ if (is_disabled(default_year, default_month, default_day)) {
1786
+
1787
+ // if date picker is in "strict" mode, clear the value of the parent element
1788
+ if (plugin.settings.strict) $element.val('');
1789
+
1790
+ // the calendar will start with the first selectable year/month
1791
+ selected_month = first_selectable_month;
1792
+ selected_year = first_selectable_year;
1793
+
1794
+ }
1795
+
1796
+ // if a default value is not available, or value does not represent a valid date
1797
+ } else {
1798
+
1799
+ // the calendar will start with the first selectable year/month
1800
+ selected_month = first_selectable_month;
1801
+ selected_year = first_selectable_year;
1802
+
1803
+ }
1804
+
1805
+ // whatever the case, if time picker is enabled
1806
+ if (timepicker_config) {
1807
+
1808
+ // if a default date is available, use the time from there
1809
+ if (default_date) current_date = default_date;
1810
+
1811
+ // use current system time otherwise
1812
+ else current_date = new Date();
1813
+
1814
+ // extract time parts from it
1815
+ selected_hour = current_date.getHours();
1816
+ selected_minute = current_date.getMinutes();
1817
+ selected_second = current_date.getSeconds();
1818
+ selected_ampm = (selected_hour >= 12 ? 'pm' : 'am');
1819
+
1820
+ // if hour is in 12 hour format
1821
+ if (timepicker_config.is12hour)
1822
+
1823
+ // convert it to the correct value
1824
+ selected_hour = (selected_hour % 12 === 0 ? 12 : selected_hour % 12);
1825
+
1826
+ // make sure that the default values are withing the allowed range, if a range is defined
1827
+ if ($.isArray(plugin.settings.enabled_hours) && $.inArray(selected_hour, plugin.settings.enabled_hours) === -1) selected_hour = plugin.settings.enabled_hours[0];
1828
+ if ($.isArray(plugin.settings.enabled_minutes) && $.inArray(selected_minute, plugin.settings.enabled_minutes) === -1) selected_minute = plugin.settings.enabled_minutes[0];
1829
+ if ($.isArray(plugin.settings.enabled_seconds) && $.inArray(selected_second, plugin.settings.enabled_seconds) === -1) selected_second = plugin.settings.enabled_seconds[0];
1830
+
1831
+ }
1832
+
1833
+ // generate the appropriate view
1834
+ manage_views();
1835
+
1836
+ // if date picker is not always visible in a container, and the calendar icon is visible
1837
+ if (!(plugin.settings.always_visible instanceof jQuery)) {
1838
+
1839
+ // if date picker is to be injected into the <body>
1840
+ if (plugin.settings.container.is('body')) {
1841
+
1842
+ var
1843
+
1844
+ // get the date picker width and height
1845
+ datepicker_width = datepicker.outerWidth(),
1846
+ datepicker_height = datepicker.outerHeight(),
1847
+
1848
+ // compute the date picker's default left and top
1849
+ // this will be computed relative to the icon's top-right corner (if the calendar icon exists), or
1850
+ // relative to the element's top-right corner otherwise, to which the offsets given at initialization
1851
+ // are added/subtracted
1852
+ left = (undefined !== icon ? icon.offset().left + icon.outerWidth(true) : $element.offset().left + $element.outerWidth(true)) + plugin.settings.offset[0],
1853
+ top = (undefined !== icon ? icon.offset().top : $element.offset().top) - datepicker_height + plugin.settings.offset[1],
1854
+
1855
+ // get browser window's width and height
1856
+ window_width = $(window).width(),
1857
+ window_height = $(window).height(),
1858
+
1859
+ // get browser window's horizontal and vertical scroll offsets
1860
+ window_scroll_top = $(window).scrollTop(),
1861
+ window_scroll_left = $(window).scrollLeft();
1862
+
1863
+ if (plugin.settings.default_position === 'below')
1864
+ top = (undefined !== icon ? icon.offset().top : $element.offset().top) + plugin.settings.offset[1];
1865
+
1866
+ // if date picker is outside the viewport, adjust its position so that it is visible
1867
+ if (left + datepicker_width > window_scroll_left + window_width) left = window_scroll_left + window_width - datepicker_width;
1868
+ if (left < window_scroll_left) left = window_scroll_left;
1869
+
1870
+ if (top + datepicker_height > window_scroll_top + window_height) top = window_scroll_top + window_height - datepicker_height;
1871
+ if (top < window_scroll_top) top = window_scroll_top;
1872
+
1873
+ // make the date picker visible
1874
+ datepicker.css({
1875
+ left: left,
1876
+ top: top
1877
+ });
1878
+
1879
+ // if date picker is to be injected into a custom container element
1880
+ } else
1881
+
1882
+ datepicker.css({
1883
+ left: 0,
1884
+ top: 0
1885
+ });
1886
+
1887
+ // fade-in the date picker
1888
+ // for Internet Explorer < 9 show the date picker instantly or fading alters the font's weight
1889
+ datepicker.removeClass('dp_hidden');
1890
+
1891
+ // show the iFrameShim in Internet Explorer 6
1892
+ iframeShim();
1893
+
1894
+ // if date picker is always visible, show it
1895
+ } else datepicker.removeClass('dp_hidden');
1896
+
1897
+ // if a callback function exists for when showing the date picker
1898
+ if (plugin.settings.onOpen && typeof plugin.settings.onOpen === 'function')
1899
+
1900
+ // execute the callback function and pass as argument the element the plugin is attached to
1901
+ plugin.settings.onOpen.call($element, $element);
1902
+
1903
+ };
1904
+
1905
+ /**
1906
+ * Updates the configuration options given as argument
1907
+ *
1908
+ * @param object values An object containing any number of configuration options to be updated
1909
+ *
1910
+ * @return void
1911
+ */
1912
+ plugin.update = function(values) {
1913
+
1914
+ // if original direction not saved, save it now
1915
+ if (plugin.original_direction) plugin.original_direction = plugin.direction;
1916
+
1917
+ // update configuration options
1918
+ plugin.settings = $.extend(plugin.settings, values);
1919
+
1920
+ // reinitialize the object with the new options
1921
+ init(true);
1922
+
1923
+ };
1924
+
1925
+ /**
1926
+ * Checks if a string represents a valid date according to the format defined by the "format" property.
1927
+ *
1928
+ * @param string str_date A string representing a date, formatted accordingly to the "format" property.
1929
+ * For example, if "format" is "Y-m-d" the string should look like "2011-06-01"
1930
+ *
1931
+ * @return mixed Returns a JavaScript Date object if string represents a valid date according
1932
+ * formatted according to the "format" property, or FALSE otherwise.
1933
+ *
1934
+ * @access private
1935
+ */
1936
+ var check_date = function(str_date) {
1937
+
1938
+ // treat argument as a string
1939
+ str_date += '';
1940
+
1941
+ // if value is given
1942
+ if ($.trim(str_date) !== '') {
1943
+
1944
+ var
1945
+
1946
+ // prepare the format by removing white space from it
1947
+ // and also escape characters that could have special meaning in a regular expression
1948
+ format = escape_regexp(plugin.settings.format),
1949
+
1950
+ // allowed characters in date's format
1951
+ format_chars = ['d', 'D', 'j', 'l', 'N', 'S', 'w', 'F', 'm', 'M', 'n', 'Y', 'y', 'G', 'g', 'H', 'h', 'i', 's', 'a', 'A'],
1952
+
1953
+ // "matches" will contain the characters defining the date's format
1954
+ matches = [],
1955
+
1956
+ // "regexp" will contain the regular expression built for each of the characters used in the date's format
1957
+ regexp = [],
1958
+
1959
+ // "position" will contain the position of the character found in the date's format
1960
+ position = null,
1961
+
1962
+ // "segments" will contain the matches of the regular expression
1963
+ segments = null;
1964
+
1965
+ // iterate through the allowed characters in date's format
1966
+ for (var i = 0; i < format_chars.length; i++)
1967
+
1968
+ // if character is found in the date's format
1969
+ if ((position = format.indexOf(format_chars[i])) > -1)
1970
+
1971
+ // save it, alongside the character's position
1972
+ matches.push({
1973
+ character: format_chars[i],
1974
+ position: position
1975
+ });
1976
+
1977
+ // sort characters defining the date's format based on their position, ascending
1978
+ matches.sort(function(a, b) { return a.position - b.position; });
1979
+
1980
+ // iterate through the characters defining the date's format
1981
+ $.each(matches, function(index, match) {
1982
+
1983
+ // add to the array of regular expressions, based on the character
1984
+ switch (match.character) {
1985
+
1986
+ case 'd': regexp.push('0[1-9]|[12][0-9]|3[01]'); break;
1987
+ case 'D': regexp.push('[a-z]{3}'); break;
1988
+ case 'j': regexp.push('[1-9]|[12][0-9]|3[01]'); break;
1989
+ case 'l': regexp.push('[a-z]+'); break;
1990
+ case 'N': regexp.push('[1-7]'); break;
1991
+ case 'S': regexp.push('st|nd|rd|th'); break;
1992
+ case 'w': regexp.push('[0-6]'); break;
1993
+ case 'F': regexp.push('[a-z]+'); break;
1994
+ case 'm': regexp.push('0[1-9]|1[012]'); break;
1995
+ case 'M': regexp.push('[a-z]{3}'); break;
1996
+ case 'n': regexp.push('[1-9]|1[012]'); break;
1997
+ case 'Y': regexp.push('[0-9]{4}'); break;
1998
+ case 'y': regexp.push('[0-9]{2}'); break;
1999
+ case 'G': regexp.push('[1-9]|1[0-9]|2[0123]'); break;
2000
+ case 'g': regexp.push('[0-9]|1[012]'); break;
2001
+ case 'H': regexp.push('0[0-9]|1[0-9]|2[0123]'); break;
2002
+ case 'h': regexp.push('0[0-9]|1[012]'); break;
2003
+ case 'i': regexp.push('0[0-9]|[12345][0-9]'); break;
2004
+ case 's': regexp.push('0[0-9]|[12345][0-9]'); break;
2005
+ case 'a': regexp.push('am|pm'); break;
2006
+ case 'A': regexp.push('AM|PM'); break;
2007
+
2008
+ }
2009
+
2010
+ });
2011
+
2012
+ // if we have an array of regular expressions
2013
+ if (regexp.length) {
2014
+
2015
+ // we will replace characters in the date's format in reversed order
2016
+ matches.reverse();
2017
+
2018
+ // iterate through the characters in date's format
2019
+ $.each(matches, function(index, match) {
2020
+
2021
+ // replace each character with the appropriate regular expression
2022
+ format = format.replace(match.character, '(' + regexp[regexp.length - index - 1] + ')');
2023
+
2024
+ });
2025
+
2026
+ // the final regular expression
2027
+ regexp = new RegExp('^' + format + '$', 'ig');
2028
+
2029
+ // if regular expression was matched
2030
+ if ((segments = regexp.exec(str_date))) {
2031
+
2032
+ // check if date is a valid date (i.e. there's no February 31)
2033
+
2034
+ var tmpdate = new Date(),
2035
+ original_day = 1,
2036
+ original_month = tmpdate.getMonth() + 1,
2037
+ original_year = tmpdate.getFullYear(),
2038
+ original_hours = tmpdate.getHours(),
2039
+ original_minutes = tmpdate.getMinutes(),
2040
+ original_seconds = tmpdate.getSeconds(),
2041
+ original_ampm,
2042
+ english_days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
2043
+ english_months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
2044
+ iterable,
2045
+
2046
+ // by default, we assume the date is valid
2047
+ valid = true;
2048
+
2049
+ // reverse back the characters in the date's format
2050
+ matches.reverse();
2051
+
2052
+ // iterate through the characters in the date's format
2053
+ $.each(matches, function(index, match) {
2054
+
2055
+ // if the date is not valid, don't look further
2056
+ if (!valid) return true;
2057
+
2058
+ // based on the character
2059
+ switch (match.character) {
2060
+
2061
+ case 'm':
2062
+ case 'n':
2063
+
2064
+ // extract the month from the value entered by the user
2065
+ original_month = to_int(segments[index + 1]);
2066
+
2067
+ break;
2068
+
2069
+ case 'd':
2070
+ case 'j':
2071
+
2072
+ // extract the day from the value entered by the user
2073
+ original_day = to_int(segments[index + 1]);
2074
+
2075
+ break;
2076
+
2077
+ case 'D':
2078
+ case 'l':
2079
+ case 'F':
2080
+ case 'M':
2081
+
2082
+ // if day is given as day name, we'll check against the names in the used language
2083
+ if (match.character === 'D' || match.character === 'l') iterable = plugin.settings.days;
2084
+
2085
+ // if month is given as month name, we'll check against the names in the used language
2086
+ else iterable = plugin.settings.months;
2087
+
2088
+ // by default, we assume the day or month was not entered correctly
2089
+ valid = false;
2090
+
2091
+ // iterate through the month/days in the used language
2092
+ $.each(iterable, function(key, value) {
2093
+
2094
+ // if month/day was entered correctly, don't look further
2095
+ if (valid) return true;
2096
+
2097
+ // if month/day was entered correctly
2098
+ if (segments[index + 1].toLowerCase() === value.substring(0, (match.character === 'D' || match.character === 'M' ? 3 : value.length)).toLowerCase()) {
2099
+
2100
+ // extract the day/month from the value entered by the user
2101
+ switch (match.character) {
2102
+
2103
+ case 'D': segments[index + 1] = english_days[key].substring(0, 3); break;
2104
+ case 'l': segments[index + 1] = english_days[key]; break;
2105
+ case 'F': segments[index + 1] = english_months[key]; original_month = key + 1; break;
2106
+ case 'M': segments[index + 1] = english_months[key].substring(0, 3); original_month = key + 1; break;
2107
+
2108
+ }
2109
+
2110
+ // day/month value is valid
2111
+ valid = true;
2112
+
2113
+ }
2114
+
2115
+ });
2116
+
2117
+ break;
2118
+
2119
+ case 'Y':
2120
+
2121
+ // extract the year from the value entered by the user
2122
+ original_year = to_int(segments[index + 1]);
2123
+
2124
+ break;
2125
+
2126
+ case 'y':
2127
+
2128
+ // extract the year from the value entered by the user
2129
+ original_year = '19' + to_int(segments[index + 1]);
2130
+
2131
+ break;
2132
+
2133
+ case 'G':
2134
+ case 'H':
2135
+ case 'g':
2136
+ case 'h':
2137
+
2138
+ // extract the hours from the value entered by the user
2139
+ original_hours = to_int(segments[index + 1]);
2140
+ break;
2141
+
2142
+ case 'i':
2143
+
2144
+ // extract the minutes from the value entered by the user
2145
+ original_minutes = to_int(segments[index + 1]);
2146
+ break;
2147
+
2148
+ case 's':
2149
+
2150
+ // extract the seconds from the value entered by the user
2151
+ original_seconds = to_int(segments[index + 1]);
2152
+ break;
2153
+
2154
+ case 'a':
2155
+ case 'A':
2156
+
2157
+ // extract the seconds from the value entered by the user
2158
+ original_ampm = segments[index + 1].toLowerCase();
2159
+ break;
2160
+
2161
+ }
2162
+
2163
+ });
2164
+
2165
+ // if everything is ok so far
2166
+ if (valid) {
2167
+
2168
+ // generate a Date object using the values entered by the user
2169
+ // (handle also the case when original_month and/or original_day are undefined - i.e date format is "Y-m" or "Y")
2170
+ var date = new Date(original_year, (original_month || 1) - 1, original_day || 1, original_hours + (original_ampm === 'pm' ? 12 : 0), original_minutes, original_seconds);
2171
+
2172
+ // if, after that, the date is the same as the date entered by the user
2173
+ if (date.getFullYear() === original_year && date.getDate() === (original_day || 1) && date.getMonth() === ((original_month || 1) - 1))
2174
+
2175
+ // return the date as JavaScript date object
2176
+ return date;
2177
+
2178
+ }
2179
+
2180
+ }
2181
+
2182
+ }
2183
+
2184
+ // if script gets this far, return false as something must've went wrong
2185
+ return false;
2186
+
2187
+ }
2188
+
2189
+ };
2190
+
2191
+ /**
2192
+ * Prevents the possibility of selecting text on a given element. Used on the "previous" and "next" buttons
2193
+ * where text might get accidentally selected when user quickly clicks on the buttons.
2194
+ *
2195
+ * Code by http://chris-barr.com/index.php/entry/disable_text_selection_with_jquery/
2196
+ *
2197
+ * @param jQuery Element el A jQuery element on which to prevents text selection.
2198
+ *
2199
+ * @return void
2200
+ *
2201
+ * @access private
2202
+ */
2203
+ var disable_text_select = function(el) {
2204
+
2205
+ // if browser is Firefox
2206
+ if (browser.name === 'firefox') el.css('MozUserSelect', 'none');
2207
+
2208
+ // if browser is Internet Explorer
2209
+ else if (browser.name === 'explorer') $(document).on('selectstart', el, function() { return false; });
2210
+
2211
+ // for the other browsers
2212
+ else el.mousedown(function() { return false; });
2213
+
2214
+ };
2215
+
2216
+ /**
2217
+ * Escapes special characters in a string, preparing it for use in a regular expression.
2218
+ *
2219
+ * @param string str The string in which special characters should be escaped.
2220
+ *
2221
+ * @return string Returns the string with escaped special characters.
2222
+ *
2223
+ * @access private
2224
+ */
2225
+ var escape_regexp = function(str) {
2226
+
2227
+ // return string with special characters escaped
2228
+ return str.replace(/([-.,*+?^${}()|[\]\/\\])/g, '\\$1');
2229
+
2230
+ };
2231
+
2232
+ /**
2233
+ * Formats a JavaScript date object to the format specified by the "format" property.
2234
+ * Code taken from http://electricprism.com/aeron/calendar/
2235
+ *
2236
+ * @param date date A valid JavaScript date object
2237
+ *
2238
+ * @return string Returns a string containing the formatted date
2239
+ *
2240
+ * @access private
2241
+ */
2242
+ var format = function(date) {
2243
+
2244
+ var result = '',
2245
+
2246
+ // extract parts of the date:
2247
+ // day number, 1 - 31
2248
+ j = date.getDate(),
2249
+
2250
+ // day of the week, 0 - 6, Sunday - Saturday
2251
+ w = date.getDay(),
2252
+
2253
+ // the name of the day of the week Sunday - Saturday
2254
+ l = plugin.settings.days[w],
2255
+
2256
+ // the month number, 1 - 12
2257
+ n = date.getMonth() + 1,
2258
+
2259
+ // the month name, January - December
2260
+ f = plugin.settings.months[n - 1],
2261
+
2262
+ // the year (as a string)
2263
+ y = date.getFullYear() + '',
2264
+
2265
+ // the hour, 0-23
2266
+ h = date.getHours(),
2267
+
2268
+ // the hour in 12 hours format
2269
+ h12 = h % 12 === 0 ? 12 : h % 12,
2270
+
2271
+ // the minute, 0-59
2272
+ m = date.getMinutes(),
2273
+
2274
+ // the second, 0-59
2275
+ s = date.getSeconds(),
2276
+
2277
+ // am/pm
2278
+ a = (h >= 12 ? 'pm' : 'am'),
2279
+
2280
+ i, chr;
2281
+
2282
+ // iterate through the characters in the format
2283
+ for (i = 0; i < plugin.settings.format.length; i++) {
2284
+
2285
+ // extract the current character
2286
+ chr = plugin.settings.format.charAt(i);
2287
+
2288
+ // see what character it is
2289
+ switch (chr) {
2290
+
2291
+ // year as two digits
2292
+ case 'y': y = y.substr(2);
2293
+
2294
+ // year as four digits
2295
+ // falls through
2296
+ case 'Y': result += y; break;
2297
+
2298
+ // month number, prefixed with 0
2299
+ case 'm': n = str_pad(n, 2);
2300
+
2301
+ // month number, not prefixed with 0
2302
+ // falls through
2303
+ case 'n': result += n; break;
2304
+
2305
+ // month name, three letters
2306
+ case 'M': f = ($.isArray(plugin.settings.months_abbr) && undefined !== plugin.settings.months_abbr[n - 1] ? plugin.settings.months_abbr[n - 1] : plugin.settings.months[n - 1].substr(0, 3));
2307
+
2308
+ // full month name
2309
+ // falls through
2310
+ case 'F': result += f; break;
2311
+
2312
+ // day number, prefixed with 0
2313
+ case 'd': j = str_pad(j, 2);
2314
+
2315
+ // day number not prefixed with 0
2316
+ // falls through
2317
+ case 'j': result += j; break;
2318
+
2319
+ // day name, three letters
2320
+ case 'D': l = ($.isArray(plugin.settings.days_abbr) && undefined !== plugin.settings.days_abbr[w] ? plugin.settings.days_abbr[w] : plugin.settings.days[w].substr(0, 3));
2321
+
2322
+ // full day name
2323
+ // falls through
2324
+ case 'l': result += l; break;
2325
+
2326
+ // ISO-8601 numeric representation of the day of the week, 1 - 7
2327
+ case 'N': w++;
2328
+
2329
+ // day of the week, 0 - 6
2330
+ // falls through
2331
+ case 'w': result += w; break;
2332
+
2333
+ // English ordinal suffix for the day of the month, 2 characters
2334
+ // (st, nd, rd or th (works well with j))
2335
+ case 'S':
2336
+
2337
+ if (j % 10 === 1 && j !== '11') result += 'st';
2338
+
2339
+ else if (j % 10 === 2 && j !== '12') result += 'nd';
2340
+
2341
+ else if (j % 10 === 3 && j !== '13') result += 'rd';
2342
+
2343
+ else result += 'th';
2344
+
2345
+ break;
2346
+
2347
+ // hour in 12 hours format, without leading zeros
2348
+ case 'g': result += h12; break;
2349
+
2350
+ // hour in 12 hours format, with leading zeros
2351
+ case 'h': result += str_pad(h12, 2); break;
2352
+
2353
+ // hour in 24 hours format, without leading zeros
2354
+ case 'G': result += h; break;
2355
+
2356
+ // hour in 24 hours format, with leading zeros
2357
+ case 'H': result += str_pad(h, 2); break;
2358
+
2359
+ // minutes, with leading zeros
2360
+ case 'i': result += str_pad(m, 2); break;
2361
+
2362
+ // seconds, with leading zeros
2363
+ case 's': result += str_pad(s, 2); break;
2364
+
2365
+ // am/pm, lowercase
2366
+ case 'a': result += a; break;
2367
+
2368
+ // am/pm, uppercase
2369
+ case 'A': result += a.toUpperCase(); break;
2370
+
2371
+ // this is probably the separator
2372
+ default: result += chr;
2373
+
2374
+ }
2375
+
2376
+ }
2377
+
2378
+ // return formated date
2379
+ return result;
2380
+
2381
+ };
2382
+
2383
+ /**
2384
+ * Generates the day picker view, and displays it
2385
+ *
2386
+ * @return void
2387
+ *
2388
+ * @access private
2389
+ */
2390
+ var generate_daypicker = function() {
2391
+
2392
+ var
2393
+
2394
+ // get the number of days in the selected month
2395
+ days_in_month = new Date(selected_year, selected_month + 1, 0).getDate(),
2396
+
2397
+ // get the selected month's starting day (from 0 to 6)
2398
+ first_day = new Date(selected_year, selected_month, 1).getDay(),
2399
+
2400
+ // how many days are there in the previous month
2401
+ days_in_previous_month = new Date(selected_year, selected_month, 0).getDate(),
2402
+
2403
+ // how many days are there to be shown from the previous month
2404
+ days_from_previous_month = first_day - plugin.settings.first_day_of_week,
2405
+
2406
+ i, html, day, real_date, real_year, real_month, real_day, weekday, class_name, custom_class_name, is_weekend;
2407
+
2408
+ // the final value of how many days are there to be shown from the previous month
2409
+ days_from_previous_month = days_from_previous_month < 0 ? 7 + days_from_previous_month : days_from_previous_month;
2410
+
2411
+ // manage header caption and enable/disable navigation buttons if necessary
2412
+ manage_header(plugin.settings.header_captions['days']);
2413
+
2414
+ // start generating the HTML
2415
+ html = '<tr>';
2416
+
2417
+ // if a column featuring the number of the week is to be shown
2418
+ if (plugin.settings.show_week_number)
2419
+
2420
+ // column title
2421
+ html += '<th>' + plugin.settings.show_week_number + '</th>';
2422
+
2423
+ // name of week days
2424
+ // show the abbreviated day names (or only the first two letters of the full name if no abbreviations are specified)
2425
+ // and also, take in account the value of the "first_day_of_week" property
2426
+ for (i = 0; i < 7; i++)
2427
+
2428
+ html += '<th>' + ($.isArray(plugin.settings.days_abbr) && undefined !== plugin.settings.days_abbr[(plugin.settings.first_day_of_week + i) % 7] ? plugin.settings.days_abbr[(plugin.settings.first_day_of_week + i) % 7] : plugin.settings.days[(plugin.settings.first_day_of_week + i) % 7].substr(0, 2)) + '</th>';
2429
+
2430
+ html += '</tr><tr>';
2431
+
2432
+ // the calendar shows a total of 42 days
2433
+ for (i = 0; i < 42; i++) {
2434
+
2435
+ // seven days per row
2436
+ if (i > 0 && i % 7 === 0) html += '</tr><tr>';
2437
+
2438
+ // if week number is to be shown
2439
+ if (i % 7 === 0 && plugin.settings.show_week_number)
2440
+
2441
+ // show ISO 8601 week number
2442
+ html += '<th>' + getWeekNumber(new Date(selected_year, selected_month, (i - days_from_previous_month + 1))) + '</th>';
2443
+
2444
+ // the number of the day in month
2445
+ day = (i - days_from_previous_month + 1);
2446
+
2447
+ // if dates in previous/next month can be selected, and this is one of those days
2448
+ if (plugin.settings.select_other_months && (i < days_from_previous_month || day > days_in_month)) {
2449
+
2450
+ // use the Date object to normalize the date
2451
+ // for example, 2011 05 33 will be transformed to 2011 06 02
2452
+ real_date = new Date(selected_year, selected_month, day);
2453
+ real_year = real_date.getFullYear();
2454
+ real_month = real_date.getMonth();
2455
+ real_day = real_date.getDate();
2456
+
2457
+ // extract normalized date parts and merge them
2458
+ real_date = real_year + str_pad(real_month + 1, 2) + str_pad(real_day, 2);
2459
+
2460
+ }
2461
+
2462
+ // get the week day (0 to 6, Sunday to Saturday)
2463
+ weekday = (plugin.settings.first_day_of_week + i) % 7;
2464
+
2465
+ // is day on a weekend?
2466
+ is_weekend = ($.inArray(weekday, plugin.settings.weekend_days) > -1);
2467
+
2468
+ // if this is a day from the previous month
2469
+ if (i < days_from_previous_month)
2470
+
2471
+ html += '<td class="dp_not_in_month ' + (is_weekend ? 'dp_weekend ' : '') + (plugin.settings.select_other_months && !is_disabled(real_year, real_month, real_day) ? 'date_' + real_date : 'dp_disabled') + '">' + (plugin.settings.select_other_months || plugin.settings.show_other_months ? str_pad(days_in_previous_month - days_from_previous_month + i + 1, plugin.settings.zero_pad ? 2 : 0) : '&nbsp;') + '</td>';
2472
+
2473
+ // if this is a day from the next month
2474
+ else if (day > days_in_month)
2475
+
2476
+ html += '<td class="dp_not_in_month ' + (is_weekend ? 'dp_weekend ' : '') + (plugin.settings.select_other_months && !is_disabled(real_year, real_month, real_day) ? 'date_' + real_date : 'dp_disabled') + '">' + (plugin.settings.select_other_months || plugin.settings.show_other_months ? str_pad(day - days_in_month, plugin.settings.zero_pad ? 2 : 0) : '&nbsp;') + '</td>';
2477
+
2478
+ // if this is a day from the current month
2479
+ else {
2480
+
2481
+ class_name = '';
2482
+
2483
+ // custom class, if any
2484
+ custom_class_name = get_custom_class(selected_year, selected_month, day);
2485
+
2486
+ // if day is in weekend
2487
+ if (is_weekend) class_name = ' dp_weekend';
2488
+
2489
+ // highlight the current system date
2490
+ if (selected_month === current_system_month && selected_year === current_system_year && current_system_day === day) class_name += ' dp_current';
2491
+
2492
+ // apply custom class, if a custom class exists
2493
+ if (custom_class_name !== '') class_name += ' ' + custom_class_name;
2494
+
2495
+ // highlight the currently selected date
2496
+ if (selected_month === default_month && selected_year === default_year && default_day === day) class_name += ' dp_selected';
2497
+
2498
+ // if date needs to be disabled
2499
+ if (is_disabled(selected_year, selected_month, day)) class_name += ' dp_disabled';
2500
+
2501
+ // print the day of the month (if "day" is NaN, use an empty string instead)
2502
+ html += '<td' + (class_name !== '' ? ' class="' + class_name.trim() + '"' : '') + '>' + ((plugin.settings.zero_pad ? str_pad(day, 2) : day) || '&nbsp;') + '</td>';
2503
+
2504
+ }
2505
+
2506
+ }
2507
+
2508
+ // wrap up generating the day picker
2509
+ html += '</tr>';
2510
+
2511
+ // inject the day picker into the DOM
2512
+ daypicker.html($(html));
2513
+
2514
+ // if date picker is always visible
2515
+ if (plugin.settings.always_visible)
2516
+
2517
+ // cache all the cells
2518
+ // (we need them so that we can easily remove the "dp_selected" class from all of them when user selects a date)
2519
+ daypicker_cells = $('td:not(.dp_disabled)', daypicker);
2520
+
2521
+ // make the day picker visible
2522
+ daypicker.show();
2523
+
2524
+ };
2525
+
2526
+ /**
2527
+ * Generates the month picker view, and displays it
2528
+ *
2529
+ * @return void
2530
+ *
2531
+ * @access private
2532
+ */
2533
+ var generate_monthpicker = function() {
2534
+
2535
+ // manage header caption and enable/disable navigation buttons if necessary
2536
+ manage_header(plugin.settings.header_captions['months']);
2537
+
2538
+ // start generating the HTML
2539
+ var html = '<tr>', i, class_name;
2540
+
2541
+ // iterate through all the months
2542
+ for (i = 0; i < 12; i++) {
2543
+
2544
+ // three month per row
2545
+ if (i > 0 && i % 3 === 0) html += '</tr><tr>';
2546
+
2547
+ class_name = 'dp_month_' + i;
2548
+
2549
+ // if month needs to be disabled
2550
+ if (is_disabled(selected_year, i)) class_name += ' dp_disabled';
2551
+
2552
+ // else, if a date is already selected and this is that particular month, highlight it
2553
+ else if (default_month !== false && default_month === i && selected_year === default_year) class_name += ' dp_selected';
2554
+
2555
+ // else, if this the current system month, highlight it
2556
+ else if (current_system_month === i && current_system_year === selected_year) class_name += ' dp_current';
2557
+
2558
+ // first three letters of the month's name
2559
+ html += '<td class="' + $.trim(class_name) + '">' + ($.isArray(plugin.settings.months_abbr) && undefined !== plugin.settings.months_abbr[i] ? plugin.settings.months_abbr[i] : plugin.settings.months[i].substr(0, 3)) + '</td>';
2560
+
2561
+ }
2562
+
2563
+ // wrap up
2564
+ html += '</tr>';
2565
+
2566
+ // inject into the DOM
2567
+ monthpicker.html($(html));
2568
+
2569
+ // if date picker is always visible
2570
+ if (plugin.settings.always_visible)
2571
+
2572
+ // cache all the cells
2573
+ // (we need them so that we can easily remove the "dp_selected" class from all of them when user selects a month)
2574
+ monthpicker_cells = $('td:not(.dp_disabled)', monthpicker);
2575
+
2576
+ // make the month picker visible
2577
+ monthpicker.show();
2578
+
2579
+ };
2580
+
2581
+ /**
2582
+ * Generates the time picker view, and displays it
2583
+ *
2584
+ * @return void
2585
+ *
2586
+ * @access private
2587
+ */
2588
+ var generate_timepicker = function() {
2589
+
2590
+ var html;
2591
+
2592
+ // the HTML
2593
+ html = '<tr class="dp_time_controls_increase">' +
2594
+ (timepicker_config.hours ? '<td class="dp_time_hour dp_time_control">' + plugin.settings.navigation[2] + '</td>' : '') +
2595
+ (timepicker_config.minutes ? '<td class="dp_time_minute dp_time_control">' + plugin.settings.navigation[2] + '</td>' : '') +
2596
+ (timepicker_config.seconds ? '<td class="dp_time_second dp_time_control">' + plugin.settings.navigation[2] + '</td>' : '') +
2597
+ (timepicker_config.ampm ? '<td class="dp_time_ampm dp_time_control">' + plugin.settings.navigation[2] + '</td>' : '') +
2598
+ '</tr>';
2599
+
2600
+ html += '<tr class="dp_time_segments">';
2601
+
2602
+ if (timepicker_config.hours) html += '<td class="dp_time_hours dp_disabled' + (timepicker_config.minutes || timepicker_config.seconds || timepicker_config.ampm ? ' dp_time_separator' : '') + '"><div>' + str_pad(selected_hour, 2) + '</div></td>';
2603
+ if (timepicker_config.minutes) html += '<td class="dp_time_minutes dp_disabled' + (timepicker_config.seconds || timepicker_config.ampm ? ' dp_time_separator' : '') + '"><div>' + str_pad(selected_minute, 2) + '</div></td>';
2604
+ if (timepicker_config.seconds) html += '<td class="dp_time_seconds dp_disabled' + (timepicker_config.ampm ? ' dp_time_separator' : '') + '"><div>' + str_pad(selected_second, 2) + '</div></td>';
2605
+ if (timepicker_config.ampm) html += '<td class="dp_time_ampm dp_disabled">' + selected_ampm.toUpperCase() + '</td>';
2606
+
2607
+ html += '</tr>';
2608
+
2609
+ html += '<tr class="dp_time_controls_decrease">' +
2610
+ (timepicker_config.hours ? '<td class="dp_time_hour dp_time_control">' + plugin.settings.navigation[3] + '</td>' : '') +
2611
+ (timepicker_config.minutes ? '<td class="dp_time_minute dp_time_control">' + plugin.settings.navigation[3] + '</td>' : '') +
2612
+ (timepicker_config.seconds ? '<td class="dp_time_second dp_time_control">' + plugin.settings.navigation[3] + '</td>' : '') +
2613
+ (timepicker_config.ampm ? '<td class="dp_time_ampm dp_time_control">' + plugin.settings.navigation[3] + '</td>' : '') +
2614
+ '</tr>';
2615
+
2616
+ // inject into the DOM
2617
+ timepicker.html($(html));
2618
+
2619
+ // make the time picker visible
2620
+ timepicker.show();
2621
+
2622
+ }
2623
+
2624
+ /**
2625
+ * Generates the year picker view, and displays it
2626
+ *
2627
+ * @return void
2628
+ *
2629
+ * @access private
2630
+ */
2631
+ var generate_yearpicker = function() {
2632
+
2633
+ // manage header caption and enable/disable navigation buttons if necessary
2634
+ manage_header(plugin.settings.header_captions['years']);
2635
+
2636
+ // start generating the HTML
2637
+ var html = '<tr>', i, class_name;
2638
+
2639
+ // we're showing 9 years at a time, current year in the middle
2640
+ for (i = 0; i < 12; i++) {
2641
+
2642
+ // three years per row
2643
+ if (i > 0 && i % 3 === 0) html += '</tr><tr>';
2644
+
2645
+ class_name = '';
2646
+
2647
+ // if year needs to be disabled
2648
+ if (is_disabled(selected_year - 7 + i)) class_name += ' dp_disabled';
2649
+
2650
+ // else, if a date is already selected and this is that particular year, highlight it
2651
+ else if (default_year && default_year === selected_year - 7 + i) class_name += ' dp_selected';
2652
+
2653
+ // else, if this is the current system year, highlight it
2654
+ else if (current_system_year === (selected_year - 7 + i)) class_name += ' dp_current';
2655
+
2656
+ // first three letters of the month's name
2657
+ html += '<td' + ($.trim(class_name) !== '' ? ' class="' + $.trim(class_name) + '"' : '') + '>' + (selected_year - 7 + i) + '</td>';
2658
+
2659
+ }
2660
+
2661
+ // wrap up
2662
+ html += '</tr>';
2663
+
2664
+ // inject into the DOM
2665
+ yearpicker.html($(html));
2666
+
2667
+ // if date picker is always visible
2668
+ if (plugin.settings.always_visible)
2669
+
2670
+ // cache all the cells
2671
+ // (we need them so that we can easily remove the "dp_selected" class from all of them when user selects a year)
2672
+ yearpicker_cells = $('td:not(.dp_disabled)', yearpicker);
2673
+
2674
+ // make the year picker visible
2675
+ yearpicker.show();
2676
+
2677
+ };
2678
+
2679
+ /**
2680
+ * Return the name of a custom class to be applied to the given date.
2681
+ *
2682
+ * @return string The name of a custom class to be applied to the given date, or an empty string if no custom
2683
+ * class needs to be applied.
2684
+ *
2685
+ * @param integer year The year to check
2686
+ * @param integer month The month to check
2687
+ * @param integer day The day to check
2688
+ *
2689
+ * @access private
2690
+ */
2691
+ var get_custom_class = function(year, month, day) {
2692
+
2693
+ var class_name, i, found;
2694
+
2695
+ // if month is given as argument, increment it (as JavaScript uses 0 for January, 1 for February...)
2696
+ if (typeof month !== 'undefined') month = month + 1;
2697
+
2698
+ // iterate through the custom classes
2699
+ for (i in custom_class_names) {
2700
+
2701
+ // the class name we're currently checking
2702
+ class_name = custom_class_names[i]; found = false;
2703
+
2704
+ // if there are any custom classes defined
2705
+ if ($.isArray(custom_classes[class_name]))
2706
+
2707
+ // iterate through the rules for which the custom class to be applied
2708
+ $.each(custom_classes[class_name], function() {
2709
+
2710
+ // if a custom class needs to be applied to the date we're checking, don't look further
2711
+ if (found) return;
2712
+
2713
+ var rule = this, weekday;
2714
+
2715
+ // if the rules apply for the current year
2716
+ if ($.inArray(year, rule[2]) > -1 || $.inArray('*', rule[2]) > -1)
2717
+
2718
+ // if the rules apply for the current month
2719
+ if ((typeof month !== 'undefined' && $.inArray(month, rule[1]) > -1) || $.inArray('*', rule[1]) > -1)
2720
+
2721
+ // if the rules apply for the current day
2722
+ if ((typeof day !== 'undefined' && $.inArray(day, rule[0]) > -1) || $.inArray('*', rule[0]) > -1) {
2723
+
2724
+ // if custom class is to be applied whatever the day
2725
+ // don't look any further
2726
+ if (rule[3].indexOf('*') > -1) return (found = class_name);
2727
+
2728
+ // get the weekday
2729
+ weekday = new Date(year, month - 1, day).getDay();
2730
+
2731
+ // if custom class is to be applied to weekday
2732
+ // don't look any further
2733
+ if ($.inArray(weekday, rule[3]) > -1) return (found = class_name);
2734
+
2735
+ }
2736
+
2737
+ });
2738
+
2739
+ // if a custom class needs to be applied to the date we're checking, don't look further
2740
+ if (found) return found;
2741
+
2742
+ }
2743
+
2744
+ // return what we've found
2745
+ return found || '';
2746
+
2747
+ };
2748
+
2749
+ /**
2750
+ * Generates an iFrame shim in Internet Explorer 6 so that the date picker appears above select boxes.
2751
+ *
2752
+ * @return void
2753
+ *
2754
+ * @access private
2755
+ */
2756
+ var iframeShim = function(action) {
2757
+
2758
+ var zIndex, offset;
2759
+
2760
+ // this is necessary only if browser is Internet Explorer 6
2761
+ if (browser.name === 'explorer' && browser.version === 6) {
2762
+
2763
+ // if the iFrame was not yet created
2764
+ // "undefined" evaluates as FALSE
2765
+ if (!shim) {
2766
+
2767
+ // the iFrame has to have the element's zIndex minus 1
2768
+ zIndex = to_int(datepicker.css('zIndex')) - 1;
2769
+
2770
+ // create the iFrame
2771
+ shim = $('<iframe>', {
2772
+ src: 'javascript:document.write("")',
2773
+ scrolling: 'no',
2774
+ frameborder: 0,
2775
+ css: {
2776
+ zIndex: zIndex,
2777
+ position: 'absolute',
2778
+ top: -1000,
2779
+ left: -1000,
2780
+ width: datepicker.outerWidth(),
2781
+ height: datepicker.outerHeight(),
2782
+ filter: 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)',
2783
+ display: 'none'
2784
+ }
2785
+ });
2786
+
2787
+ // inject iFrame into DOM
2788
+ $('body').append(shim);
2789
+
2790
+ }
2791
+
2792
+ // what do we need to do
2793
+ switch (action) {
2794
+
2795
+ // hide the iFrame?
2796
+ case 'hide':
2797
+
2798
+ // set the iFrame's display property to "none"
2799
+ shim.hide();
2800
+
2801
+ break;
2802
+
2803
+ // show the iFrame?
2804
+ default:
2805
+
2806
+ // get date picker top and left position
2807
+ offset = datepicker.offset();
2808
+
2809
+ // position the iFrame shim right underneath the date picker
2810
+ // and set its display to "block"
2811
+ shim.css({
2812
+ top: offset.top,
2813
+ left: offset.left,
2814
+ display: 'block'
2815
+ });
2816
+
2817
+ }
2818
+
2819
+ }
2820
+
2821
+ };
2822
+
2823
+ /**
2824
+ * Checks if, according to the restrictions of the calendar and/or the values defined by the "disabled_dates"
2825
+ * property, a day, a month or a year needs to be disabled.
2826
+ *
2827
+ * @param integer year The year to check
2828
+ * @param integer month The month to check
2829
+ * @param integer day The day to check
2830
+ *
2831
+ * @return boolean Returns TRUE if the given value is not disabled or FALSE otherwise
2832
+ *
2833
+ * @access private
2834
+ */
2835
+ var is_disabled = function(year, month, day) {
2836
+
2837
+ var now, len, disabled, enabled;
2838
+
2839
+ // don't check bogus values
2840
+ if ((undefined === year || isNaN(year)) && (undefined === month || isNaN(month)) && (undefined === day || isNaN(day))) return false;
2841
+
2842
+ // this date picker cannot handle years before 1000, so we return false in this case
2843
+ else if (year < 1000) return true;
2844
+
2845
+ // if calendar has direction restrictions
2846
+ if (!(!$.isArray(plugin.settings.direction) && to_int(plugin.settings.direction) === 0)) {
2847
+
2848
+ // normalize and merge arguments then transform the result to an integer
2849
+ now = to_int(str_concat(year, (typeof month !== 'undefined' ? str_pad(month, 2) : ''), (typeof day !== 'undefined' ? str_pad(day, 2) : '')));
2850
+
2851
+ // get the length of the argument
2852
+ len = (now + '').length;
2853
+
2854
+ // if we're checking days
2855
+ if (len === 8 && (
2856
+
2857
+ // day is before the first selectable date
2858
+ (typeof start_date !== 'undefined' && now < to_int(str_concat(first_selectable_year, str_pad(first_selectable_month, 2), str_pad(first_selectable_day, 2)))) ||
2859
+
2860
+ // or day is after the last selectable date
2861
+ (typeof end_date !== 'undefined' && now > to_int(str_concat(last_selectable_year, str_pad(last_selectable_month, 2), str_pad(last_selectable_day, 2))))
2862
+
2863
+ // day needs to be disabled
2864
+ )) return true;
2865
+
2866
+ // if we're checking months
2867
+ else if (len === 6 && (
2868
+
2869
+ // month is before the first selectable month
2870
+ (typeof start_date !== 'undefined' && now < to_int(str_concat(first_selectable_year, str_pad(first_selectable_month, 2)))) ||
2871
+
2872
+ // or day is after the last selectable date
2873
+ (typeof end_date !== 'undefined' && now > to_int(str_concat(last_selectable_year, str_pad(last_selectable_month, 2))))
2874
+
2875
+ // month needs to be disabled
2876
+ )) return true;
2877
+
2878
+ // if we're checking years
2879
+ else if (len === 4 && (
2880
+
2881
+ // year is before the first selectable year
2882
+ (typeof start_date !== 'undefined' && now < first_selectable_year) ||
2883
+
2884
+ // or day is after the last selectable date
2885
+ (typeof end_date !== 'undefined' && now > last_selectable_year)
2886
+
2887
+ // year needs to be disabled
2888
+ )) return true;
2889
+
2890
+ }
2891
+
2892
+ // if month is given as argument, increment it (as JavaScript uses 0 for January, 1 for February...)
2893
+ if (typeof month !== 'undefined') month = month + 1;
2894
+
2895
+ // by default, we assume the day/month/year is not enabled nor disabled
2896
+ disabled = false, enabled = false;
2897
+
2898
+ // if there are rules for disabling dates
2899
+ if ($.isArray(disabled_dates) && disabled_dates.length)
2900
+
2901
+ // iterate through the rules for disabling dates
2902
+ $.each(disabled_dates, function() {
2903
+
2904
+ // if the date is to be disabled, don't look any further
2905
+ if (disabled) return;
2906
+
2907
+ var rule = this, weekday;
2908
+
2909
+ // if the rules apply for the current year
2910
+ if ($.inArray(year, rule[2]) > -1 || $.inArray('*', rule[2]) > -1)
2911
+
2912
+ // if the rules apply for the current month
2913
+ if ((typeof month !== 'undefined' && $.inArray(month, rule[1]) > -1) || $.inArray('*', rule[1]) > -1)
2914
+
2915
+ // if the rules apply for the current day
2916
+ if ((typeof day !== 'undefined' && $.inArray(day, rule[0]) > -1) || $.inArray('*', rule[0]) > -1) {
2917
+
2918
+ // if day is to be disabled whatever the day
2919
+ // don't look any further
2920
+ if (rule[3].indexOf('*') > -1) return (disabled = true);
2921
+
2922
+ // get the weekday
2923
+ weekday = new Date(year, month - 1, day).getDay();
2924
+
2925
+ // if weekday is to be disabled
2926
+ // don't look any further
2927
+ if ($.inArray(weekday, rule[3]) > -1) return (disabled = true);
2928
+
2929
+ }
2930
+
2931
+ });
2932
+
2933
+ // if there are rules that explicitly enable dates
2934
+ if (enabled_dates)
2935
+
2936
+ // iterate through the rules for enabling dates
2937
+ $.each(enabled_dates, function() {
2938
+
2939
+ // if the date is to be enabled, don't look any further
2940
+ if (enabled) return;
2941
+
2942
+ var rule = this, weekday;
2943
+
2944
+ // if the rules apply for the current year
2945
+ if ($.inArray(year, rule[2]) > -1 || $.inArray('*', rule[2]) > -1) {
2946
+
2947
+ // the year is enabled
2948
+ enabled = true;
2949
+
2950
+ // if we're also checking months
2951
+ if (typeof month !== 'undefined') {
2952
+
2953
+ // we assume the month is enabled
2954
+ enabled = true;
2955
+
2956
+ // if the rules apply for the current month
2957
+ if ($.inArray(month, rule[1]) > -1 || $.inArray('*', rule[1]) > -1) {
2958
+
2959
+ // if we're also checking days
2960
+ if (typeof day !== 'undefined') {
2961
+
2962
+ // we assume the day is enabled
2963
+ enabled = true;
2964
+
2965
+ // if the rules apply for the current day
2966
+ if ($.inArray(day, rule[0]) > -1 || $.inArray('*', rule[0]) > -1) {
2967
+
2968
+ // if day is to be enabled whatever the day
2969
+ // don't look any further
2970
+ if (rule[3].indexOf('*') > -1) return (enabled = true);
2971
+
2972
+ // get the weekday
2973
+ weekday = new Date(year, month - 1, day).getDay();
2974
+
2975
+ // if weekday is to be enabled
2976
+ // don't look any further
2977
+ if ($.inArray(weekday, rule[3]) > -1) return (enabled = true);
2978
+
2979
+ // if we get this far, it means the day is not enabled
2980
+ enabled = false;
2981
+
2982
+ // if day is not enabled
2983
+ } else enabled = false;
2984
+
2985
+ }
2986
+
2987
+ // if month is not enabled
2988
+ } else enabled = false;
2989
+
2990
+ }
2991
+
2992
+ }
2993
+
2994
+ });
2995
+
2996
+ // if checked date is enabled, return false
2997
+ if (enabled_dates && enabled) return false;
2998
+
2999
+ // if checked date is disabled return false
3000
+ else if (disabled_dates && disabled) return true;
3001
+
3002
+ // if script gets this far it means that the day/month/year doesn't need to be disabled
3003
+ return false;
3004
+
3005
+ };
3006
+
3007
+ /**
3008
+ * Checks whether a value is an integer number.
3009
+ *
3010
+ * @param mixed value Value to check
3011
+ *
3012
+ * @return Returns TRUE if the value represents an integer number, or FALSE otherwise
3013
+ *
3014
+ * @access private
3015
+ */
3016
+ var is_integer = function(value) {
3017
+
3018
+ // return TRUE if value represents an integer number, or FALSE otherwise
3019
+ return (value + '').match(/^\-?[0-9]+$/);
3020
+
3021
+ };
3022
+
3023
+ /**
3024
+ * Sets the caption in the header of the date picker and enables or disables navigation buttons when necessary.
3025
+ *
3026
+ * @param string caption String that needs to be displayed in the header
3027
+ *
3028
+ * @return void
3029
+ *
3030
+ * @access private
3031
+ */
3032
+ var manage_header = function(caption) {
3033
+
3034
+ // if "selected_month" has a value
3035
+ // $.isNumeric is available only from jQuery 1.7 - thanks to birla for the fix!
3036
+ if (!isNaN(parseFloat(selected_month)) && isFinite(selected_month))
3037
+
3038
+ caption = caption.replace(/\bm\b|\bn\b|\bF\b|\bM\b/, function(match) {
3039
+
3040
+ switch (match) {
3041
+
3042
+ // month number, prefixed with 0
3043
+ case 'm':
3044
+ return str_pad(selected_month + 1, 2);
3045
+
3046
+ // month number, not prefixed with 0
3047
+ case 'n':
3048
+ return selected_month + 1;
3049
+
3050
+ // full month name
3051
+ case 'F':
3052
+ return plugin.settings.months[selected_month];
3053
+
3054
+ // month name, three letters
3055
+ case 'M':
3056
+ return ($.isArray(plugin.settings.months_abbr) && undefined !== plugin.settings.months_abbr[selected_month] ? plugin.settings.months_abbr[selected_month] : plugin.settings.months[selected_month].substr(0, 3));
3057
+
3058
+ // unknown replace
3059
+ default:
3060
+ return match;
3061
+
3062
+ }
3063
+
3064
+ });
3065
+
3066
+ // if "selected_year" has a value
3067
+ // $.isNumeric is available only from jQuery 1.7 - thanks to birla for the fix!
3068
+ if (!isNaN(parseFloat(selected_year)) && isFinite(selected_year))
3069
+
3070
+ // replace year-related patterns
3071
+ caption =
3072
+
3073
+ caption
3074
+
3075
+ // year as four digits
3076
+ .replace(/\bY\b/, selected_year)
3077
+
3078
+ // year as two digits
3079
+ .replace(/\by\b/, (selected_year + '').substr(2))
3080
+
3081
+ // lower limit of year as two or four digits
3082
+ .replace(/\bY1\b/i, selected_year - 7)
3083
+
3084
+ // upper limit of year as two or four digits
3085
+ .replace(/\bY2\b/i, selected_year + 4);
3086
+
3087
+ // update the caption in the header
3088
+ $('.dp_caption', header).html(caption);
3089
+
3090
+ };
3091
+
3092
+ /**
3093
+ * Shows the appropriate view (days, months or years) according to the current value of the "view" property.
3094
+ *
3095
+ * @return void
3096
+ *
3097
+ * @access private
3098
+ */
3099
+ var manage_views = function() {
3100
+
3101
+ var height, elements;
3102
+
3103
+ // if the day picker was not yet generated
3104
+ if (daypicker.text() === '' || view === 'days') {
3105
+
3106
+ // if the day picker was not yet generated
3107
+ if (daypicker.text() === '') {
3108
+
3109
+ // if date picker is not always visible in a container
3110
+ if (!(plugin.settings.always_visible instanceof jQuery))
3111
+
3112
+ // temporarily set the date picker's left outside of view
3113
+ // so that we can later grab its width and height
3114
+ datepicker.css('left', -1000);
3115
+
3116
+ // temporarily make the date picker visible
3117
+ // so that we can later grab its width and height
3118
+ datepicker.removeClass('hidden');
3119
+
3120
+ // generate the day picker
3121
+ generate_daypicker();
3122
+
3123
+ // jQuery rounds values returned by outerWidth and outerHeight
3124
+ // therefore, if we can get the un-rounded values, get those
3125
+ // get the day picker's width and height
3126
+ if (typeof daypicker[0].getBoundingClientRect !== 'undefined') height = daypicker[0].getBoundingClientRect().height;
3127
+
3128
+ // if "getBoundingClientRect" is not available
3129
+ // get the day picker's height
3130
+ else height = daypicker.outerHeight(true);
3131
+
3132
+ // make the month picker have the same size as the day picker
3133
+ monthpicker.css('height', height);
3134
+
3135
+ // make the year picker have the same size as the day picker
3136
+ yearpicker.css('height', height);
3137
+
3138
+ // make the time picker have the same size as the day picker
3139
+ timepicker.css('height', height + header.outerHeight(true));
3140
+
3141
+ // set the container's width so all the views have 100% width
3142
+ datepicker.css('width', datepicker.outerWidth());
3143
+
3144
+ // // we have to set this now or Chrome will make the datepicker extend to the full width of the screen...
3145
+ // $('.dp_caption', header).css('width', '100%');
3146
+
3147
+ // hide the date picker again
3148
+ datepicker.addClass('dp_hidden');
3149
+
3150
+ // if the day picker was previously generated at least once
3151
+ // generate the day picker
3152
+ } else generate_daypicker();
3153
+
3154
+ // show header
3155
+ header.show();
3156
+
3157
+ // hide the year and the month pickers
3158
+ monthpicker.hide();
3159
+ yearpicker.hide();
3160
+
3161
+ // hide time-picker related elements
3162
+ timepicker.hide();
3163
+ view_toggler.hide();
3164
+ confirm_selection.hide();
3165
+
3166
+ // if the time picker is enabled, show the clock icon
3167
+ if (timepicker_config) view_toggler.show().removeClass('dp_calendar');
3168
+
3169
+ // if the view is "months"
3170
+ } else if (view === 'months') {
3171
+
3172
+ // generate the month picker
3173
+ generate_monthpicker();
3174
+
3175
+ // hide the day and the year pickers
3176
+ daypicker.hide();
3177
+ yearpicker.hide();
3178
+
3179
+ // hide time-picker related elements
3180
+ timepicker.hide();
3181
+ view_toggler.hide();
3182
+ confirm_selection.hide();
3183
+
3184
+ // if the view is "years"
3185
+ } else if (view === 'years') {
3186
+
3187
+ // generate the year picker
3188
+ generate_yearpicker();
3189
+
3190
+ // hide the day and the month pickers
3191
+ daypicker.hide();
3192
+ monthpicker.hide();
3193
+
3194
+ // hide time-picker related elements
3195
+ timepicker.hide();
3196
+ view_toggler.hide();
3197
+ confirm_selection.hide();
3198
+
3199
+ // if the view is "time"
3200
+ } else if (view === 'time') {
3201
+
3202
+ // generate the time picker
3203
+ generate_timepicker();
3204
+
3205
+ // if the "time" view is the only available view
3206
+ if (views.length === 1) {
3207
+
3208
+ // hide the time picker toggler button
3209
+ view_toggler.hide();
3210
+
3211
+ // show the confirmation button
3212
+ confirm_selection.show();
3213
+
3214
+ // if the "time" view is not the only available view
3215
+ } else {
3216
+
3217
+ // time picker toggler button, but change the icon
3218
+ view_toggler.show().addClass('dp_calendar');
3219
+
3220
+ // if no date is selected
3221
+ // hide the confirmation button
3222
+ if ($element.val() === '') confirm_selection.hide();
3223
+
3224
+ // show the confirmation button
3225
+ else confirm_selection.show();
3226
+
3227
+ }
3228
+
3229
+ // hide the header, day, month and year pickers
3230
+ header.hide();
3231
+ daypicker.hide();
3232
+ monthpicker.hide();
3233
+ yearpicker.hide();
3234
+
3235
+ }
3236
+
3237
+ // if a callback function exists for when navigating through months/years
3238
+ if (view !== 'time' && plugin.settings.onChange && typeof plugin.settings.onChange === 'function' && undefined !== view) {
3239
+
3240
+ // get the "active" elements in the view (ignoring the disabled ones)
3241
+ elements = (view === 'days' ?
3242
+ daypicker.find('td:not(.dp_disabled)') :
3243
+ (view === 'months' ?
3244
+ monthpicker.find('td:not(.dp_disabled)') :
3245
+ yearpicker.find('td:not(.dp_disabled)')));
3246
+
3247
+ // iterate through the active elements
3248
+ // and attach a "date" data attribute to each element in the form of
3249
+ // YYYY-MM-DD if the view is "days"
3250
+ // YYYY-MM if the view is "months"
3251
+ // YYYY if the view is "years"
3252
+ // so it's easy to identify elements in the list
3253
+ elements.each(function() {
3254
+
3255
+ var matches;
3256
+
3257
+ // if view is "days"
3258
+ if (view === 'days')
3259
+
3260
+ // if date is from a next/previous month and is selectable
3261
+ if ($(this).hasClass('dp_not_in_month') && !$(this).hasClass('dp_disabled')) {
3262
+
3263
+ // extract date from the attached class
3264
+ matches = $(this).attr('class').match(/date\_([0-9]{4})(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])/);
3265
+
3266
+ // attach a "date" data attribute to each element in the form of of YYYY-MM-DD for easily identifying sought elements
3267
+ $(this).data('date', matches[1] + '-' + matches[2] + '-' + matches[3]);
3268
+
3269
+ // if date is from the currently selected month
3270
+ } else
3271
+
3272
+ // attach a "date" data attribute to each element in the form of of YYYY-MM-DD for easily identifying sought elements
3273
+ $(this).data('date', selected_year + '-' + str_pad(selected_month + 1, 2) + '-' + str_pad(to_int($(this).text()), 2));
3274
+
3275
+ // if view is "months"
3276
+ else if (view === 'months') {
3277
+
3278
+ // get the month's number for the element's class
3279
+ matches = $(this).attr('class').match(/dp\_month\_([0-9]+)/);
3280
+
3281
+ // attach a "date" data attribute to each element in the form of of YYYY-MM for easily identifying sought elements
3282
+ $(this).data('date', selected_year + '-' + str_pad(to_int(matches[1]) + 1, 2));
3283
+
3284
+ // if view is "years"
3285
+ } else
3286
+
3287
+ // attach a "date" data attribute to each element in the form of of YYYY for easily identifying sought elements
3288
+ $(this).data('date', to_int($(this).text()));
3289
+
3290
+ });
3291
+
3292
+ // execute the callback function and send as arguments the current view, the elements in the view, and
3293
+ // the element the plugin is attached to
3294
+ plugin.settings.onChange.call($element, view, elements, $element);
3295
+
3296
+ }
3297
+
3298
+ // assume the footer is visible
3299
+ footer.show();
3300
+
3301
+ // if we are in the "time" view and there are more views available
3302
+ if (view === 'time' && views.length > 1) {
3303
+
3304
+ // hide the "Today" and the "Clear" buttons
3305
+ selecttoday.hide();
3306
+ cleardate.hide();
3307
+
3308
+ // for the other cases
3309
+ } else {
3310
+
3311
+ // assume both the "Today" and "Clear" buttons are visible
3312
+ selecttoday.show();
3313
+ cleardate.show();
3314
+
3315
+ // if the button for clearing a previously selected date needs to be visible all the time,
3316
+ // or the "Clear" button needs to be shown only when a date was previously selected, and now it's the case,
3317
+ // or the date picker is always visible and the "Clear" button was not explicitly disabled
3318
+ if (
3319
+ plugin.settings.show_clear_date === true ||
3320
+ (plugin.settings.show_clear_date === 0 && $element.val() !== '') ||
3321
+ (plugin.settings.always_visible && plugin.settings.show_clear_date !== false)
3322
+ )
3323
+
3324
+ // if the "Today" button is visible
3325
+ if (show_select_today) {
3326
+
3327
+ // show it, and set it's width to 50% of the available space
3328
+ selecttoday.css('width', '50%');
3329
+
3330
+ // the "Clear date" button only takes up 50% of the available space
3331
+ cleardate.css('width', '50%');
3332
+
3333
+ // if the "Today" button is not visible
3334
+ } else {
3335
+
3336
+ // hide the "Today" button
3337
+ selecttoday.hide();
3338
+
3339
+ // the "Clear date" button takes up 100% of the available space
3340
+ // unless the time picker is available, in which case take up 50% of the available space
3341
+ cleardate.css('width', views.indexOf('time') > -1 ? '50%' : '100%');
3342
+
3343
+ }
3344
+
3345
+ // otherwise
3346
+ else {
3347
+
3348
+ // hide the "Clear" button
3349
+ cleardate.hide();
3350
+
3351
+ // if the "Today" button is visible, it will now take up all the available space
3352
+ if (show_select_today) selecttoday.css('width', '100%');
3353
+
3354
+ // if the "Today" button should not be visible
3355
+ else {
3356
+
3357
+ // hide the "Today" button
3358
+ selecttoday.hide();
3359
+
3360
+ // if there's also no timepicker view, hide the footer entirely
3361
+ if (!timepicker_config || (view !== 'time' && view !== 'days')) footer.hide();
3362
+
3363
+ }
3364
+
3365
+ }
3366
+
3367
+ }
3368
+
3369
+ };
3370
+
3371
+ /**
3372
+ * Puts the specified date in the element the plugin is attached to, and hides the date picker.
3373
+ *
3374
+ * @param integer year The year
3375
+ *
3376
+ * @param integer month The month
3377
+ *
3378
+ * @param integer day The day
3379
+ *
3380
+ * @param string rview The view from where the method was called (the referrer view)
3381
+ *
3382
+ * @param object cell The element that was clicked
3383
+ *
3384
+ * @return void
3385
+ *
3386
+ * @access private
3387
+ */
3388
+ var select_date = function(year, month, day, rview, cell) {
3389
+
3390
+ var
3391
+
3392
+ // construct a new date object from the arguments
3393
+ default_date = new Date(year, month, day, (timepicker_config && timepicker_config.hours ? selected_hour + (timepicker_config.ampm && selected_ampm === 'pm' ? 12 : 0) : 12), (timepicker_config && timepicker_config.minutes ? selected_minute : 0), (timepicker_config && timepicker_config.seconds ? selected_second : 0)),
3394
+
3395
+ // pointer to the cells in the current view
3396
+ view_cells = (rview === 'days' ? daypicker_cells : (rview === 'months' ? monthpicker_cells : yearpicker_cells)),
3397
+
3398
+ // the selected date, formatted correctly
3399
+ selected_value = format(default_date);
3400
+
3401
+ // set the currently selected and formated date as the value of the element the plugin is attached to
3402
+ $element.val(selected_value);
3403
+
3404
+ // if date picker is always visible or time picker is available
3405
+ if (plugin.settings.always_visible || timepicker_config) {
3406
+
3407
+ // extract the date parts and reassign values to these variables
3408
+ // so that everything will be correctly highlighted
3409
+ default_month = default_date.getMonth();
3410
+ selected_month = default_date.getMonth();
3411
+ default_year = default_date.getFullYear();
3412
+ selected_year = default_date.getFullYear();
3413
+ default_day = default_date.getDate();
3414
+
3415
+ // if "cell" is available (it isn't when called from increasing/decreasing values the time picker)
3416
+ if (cell && view_cells) {
3417
+
3418
+ // remove the "selected" class from all cells in the current view
3419
+ view_cells.removeClass('dp_selected');
3420
+
3421
+ // add the "selected" class to the currently selected cell
3422
+ cell.addClass('dp_selected');
3423
+
3424
+ // if we're on the "days" view and days from other months are selectable and one of those days was
3425
+ // selected, repaint the datepicker so it will take us to the selected month
3426
+ if (rview === 'days' && cell.hasClass('dp_not_in_month') && !cell.hasClass('dp_disabled')) plugin.show();
3427
+
3428
+ }
3429
+
3430
+ }
3431
+
3432
+ // if format contains time, switch to the time picker view
3433
+ if (timepicker_config) {
3434
+
3435
+ view = 'time';
3436
+ manage_views();
3437
+
3438
+ // if format doesn't contain time
3439
+ } else {
3440
+
3441
+ // move focus to the element the plugin is attached to
3442
+ $element.focus();
3443
+
3444
+ // hide the date picker
3445
+ plugin.hide();
3446
+
3447
+ }
3448
+
3449
+ // updates value for the date picker whose starting date depends on the selected date (if any)
3450
+ update_dependent(default_date);
3451
+
3452
+ // if a callback function exists for when selecting a date
3453
+ if (plugin.settings.onSelect && typeof plugin.settings.onSelect === 'function')
3454
+
3455
+ // execute the callback function
3456
+ // make "this" inside the callback function refer to the element the date picker is attached to
3457
+ plugin.settings.onSelect.call($element, selected_value, year + '-' + str_pad(month + 1, 2) + '-' + str_pad(day, 2), default_date, $element, getWeekNumber(default_date));
3458
+
3459
+ };
3460
+
3461
+ /**
3462
+ * Concatenates any number of arguments and returns them as string.
3463
+ *
3464
+ * @return string Returns the concatenated values.
3465
+ *
3466
+ * @access private
3467
+ */
3468
+ var str_concat = function() {
3469
+
3470
+ var str = '', i;
3471
+
3472
+ // concatenate as string
3473
+ for (i = 0; i < arguments.length; i++) str += (arguments[i] + '');
3474
+
3475
+ // return the concatenated values
3476
+ return str;
3477
+
3478
+ };
3479
+
3480
+ /**
3481
+ * Left-pad a string to a certain length with zeroes.
3482
+ *
3483
+ * @param string str The string to be padded.
3484
+ *
3485
+ * @param integer len The length to which the string must be padded
3486
+ *
3487
+ * @return string Returns the string left-padded with leading zeroes
3488
+ *
3489
+ * @access private
3490
+ */
3491
+ var str_pad = function(str, len) {
3492
+
3493
+ // make sure argument is a string
3494
+ str += '';
3495
+
3496
+ // pad with leading zeroes until we get to the desired length
3497
+ while (str.length < len) str = '0' + str;
3498
+
3499
+ // return padded string
3500
+ return str;
3501
+
3502
+ };
3503
+
3504
+ /**
3505
+ * Returns the integer representation of a string
3506
+ *
3507
+ * @return int Returns the integer representation of the string given as argument
3508
+ *
3509
+ * @access private
3510
+ */
3511
+ var to_int = function(str) {
3512
+
3513
+ // return the integer representation of the string given as argument
3514
+ return parseInt(str, 10);
3515
+
3516
+ };
3517
+
3518
+ /**
3519
+ * Updates the paired date picker (whose starting date depends on the value of the current date picker)
3520
+ *
3521
+ * @param date date A JavaScript date object representing the currently selected date
3522
+ *
3523
+ * @return void
3524
+ *
3525
+ * @access private
3526
+ */
3527
+ var update_dependent = function(date) {
3528
+
3529
+ // if the pair element exists
3530
+ if (plugin.settings.pair)
3531
+
3532
+ // iterate through the pair elements (as there may be more than just one)
3533
+ $.each(plugin.settings.pair, function() {
3534
+
3535
+ var $pair = $(this), dp;
3536
+
3537
+ // chances are that in the beginning the pair element doesn't have the Zebra_DatePicker attached to it yet
3538
+ // (as the "start" element is usually created before the "end" element)
3539
+ // so we'll have to rely on "data" to send the starting date to the pair element
3540
+
3541
+ // therefore, if Zebra_DatePicker is not yet attached
3542
+ if (!($pair.data && $pair.data('Zebra_DatePicker')))
3543
+
3544
+ // set the starting date like this
3545
+ $pair.data('zdp_reference_date', date);
3546
+
3547
+ // if Zebra_DatePicker is attached to the pair element
3548
+ else {
3549
+
3550
+ // reference the date picker object attached to the other element
3551
+ dp = $pair.data('Zebra_DatePicker');
3552
+
3553
+ // update the other date picker's starting date
3554
+ // the value depends on the original value of the "direction" attribute
3555
+ // (also, if the pair date picker does not have a direction, set it to 1)
3556
+ dp.update({
3557
+ reference_date: date,
3558
+ direction: dp.settings.direction === 0 ? 1 : dp.settings.direction
3559
+ });
3560
+
3561
+ // if the other date picker is always visible, update the visuals now
3562
+ if (dp.settings.always_visible) dp.show();
3563
+
3564
+ }
3565
+
3566
+ });
3567
+
3568
+ };
3569
+
3570
+ /**
3571
+ * Calculate the ISO 8601 week number for a given date.
3572
+ *
3573
+ * Code is based on the algorithm at http://www.tondering.dk/claus/cal/week.php#calcweekno
3574
+ */
3575
+ var getWeekNumber = function(date) {
3576
+
3577
+ var y = date.getFullYear(),
3578
+ m = date.getMonth() + 1,
3579
+ d = date.getDate(),
3580
+ a, b, c, s, e, f, g, n, w;
3581
+
3582
+ // If month jan. or feb.
3583
+ if (m < 3) {
3584
+
3585
+ a = y - 1;
3586
+ b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0);
3587
+ c = ((a - 1) / 4 | 0) - ((a - 1) / 100 | 0) + ((a - 1) / 400 | 0);
3588
+ s = b - c;
3589
+ e = 0;
3590
+ f = d - 1 + 31 * (m - 1);
3591
+
3592
+ // If month mar. through dec.
3593
+ } else {
3594
+
3595
+ a = y;
3596
+ b = (a / 4 | 0) - (a / 100 | 0) + (a / 400 | 0);
3597
+ c = ((a - 1) / 4 | 0) - ((a - 1) / 100 | 0) + ((a - 1) / 400 | 0);
3598
+ s = b - c;
3599
+ e = s + 1;
3600
+ f = d + ((153 * (m - 3) + 2) / 5 | 0) + 58 + s;
3601
+
3602
+ }
3603
+
3604
+ g = (a + b) % 7;
3605
+ // ISO Weekday (0 is monday, 1 is tuesday etc.)
3606
+ d = (f + g - e) % 7;
3607
+ n = f + 3 - d;
3608
+
3609
+ if (n < 0) w = 53 - ((g - s) / 5 | 0);
3610
+
3611
+ else if (n > 364 + s) w = 1;
3612
+
3613
+ else w = (n / 7 | 0) + 1;
3614
+
3615
+ return w;
3616
+
3617
+ };
3618
+
3619
+ // since with jQuery 1.9.0 the $.browser object was removed, we rely on this piece of code from
3620
+ // http://www.quirksmode.org/js/detect.html to detect the browser
3621
+ var browser = {
3622
+ init: function() {
3623
+ this.name = this.searchString(this.dataBrowser) || '';
3624
+ this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || '';
3625
+ },
3626
+ searchString: function(data) {
3627
+ var i, dataString, dataProp;
3628
+
3629
+ for (i = 0; i < data.length; i++) {
3630
+ dataString = data[i].string;
3631
+ dataProp = data[i].prop;
3632
+ this.versionSearchString = data[i].versionSearch || data[i].identity;
3633
+ if (dataString) {
3634
+ if (dataString.indexOf(data[i].subString) !== -1)
3635
+ return data[i].identity;
3636
+ } else if (dataProp)
3637
+ return data[i].identity;
3638
+ }
3639
+ },
3640
+ searchVersion: function(dataString) {
3641
+ var index = dataString.indexOf(this.versionSearchString);
3642
+
3643
+ if (index === -1) return;
3644
+
3645
+ return parseFloat(dataString.substring(index + this.versionSearchString.length + 1));
3646
+ },
3647
+ dataBrowser: [
3648
+ {
3649
+ string: navigator.userAgent,
3650
+ subString: 'Firefox',
3651
+ identity: 'firefox'
3652
+ },
3653
+ {
3654
+ string: navigator.userAgent,
3655
+ subString: 'MSIE',
3656
+ identity: 'explorer',
3657
+ versionSearch: 'MSIE'
3658
+ }
3659
+ ]
3660
+ };
3661
+
3662
+ browser.init();
3663
+
3664
+ // initialize the plugin
3665
+ init();
3666
+
3667
+ };
3668
+
3669
+ $.fn.Zebra_DatePicker = function(options) {
3670
+
3671
+ // iterate through all the elements to which we need to attach the date picker to
3672
+ return this.each(function() {
3673
+
3674
+ // if element has a date picker already attached
3675
+ if (undefined !== $(this).data('Zebra_DatePicker'))
3676
+
3677
+ // remove the attached date picker
3678
+ $(this).data('Zebra_DatePicker').destroy();
3679
+
3680
+ // create an instance of the plugin
3681
+ var plugin = new $.Zebra_DatePicker(this, options);
3682
+
3683
+ // save a reference to the newly created object
3684
+ $(this).data('Zebra_DatePicker', plugin);
3685
+
3686
+ });
3687
+
3688
+ };
3689
+
3690
+ }));