zebra-datepicker-rails 1.9.5 → 1.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }));