diff --git a/src/Framework/Framework/Compilation/Javascript/JavascriptTranslatableMethodCollection.cs b/src/Framework/Framework/Compilation/Javascript/JavascriptTranslatableMethodCollection.cs index 96f9f1e2a2..d1ff0557c9 100644 --- a/src/Framework/Framework/Compilation/Javascript/JavascriptTranslatableMethodCollection.cs +++ b/src/Framework/Framework/Compilation/Javascript/JavascriptTranslatableMethodCollection.cs @@ -308,6 +308,24 @@ private void AddDefaultToStringTranslations() .Invoke(args[0].WithAnnotation(ShouldBeObservableAnnotation.Instance), args[1]) .WithAnnotation(ResultIsObservableAnnotation.Instance) )); + AddMethodTranslator(() => default(TimeSpan).ToString(), new GenericMethodCompiler( + args => new JsIdentifierExpression("dotvvm").Member("globalize").Member("bindingTimeOnlyToString") + .WithAnnotation(new GlobalizeResourceBindingProperty()) + .Invoke(args[0].WithAnnotation(ShouldBeObservableAnnotation.Instance)) + .WithAnnotation(ResultIsObservableAnnotation.Instance) + )); + AddMethodTranslator(() => default(Nullable).ToString(), new GenericMethodCompiler( + args => new JsIdentifierExpression("dotvvm").Member("globalize").Member("bindingTimeOnlyToString") + .WithAnnotation(new GlobalizeResourceBindingProperty()) + .Invoke(args[0].WithAnnotation(ShouldBeObservableAnnotation.Instance)) + .WithAnnotation(ResultIsObservableAnnotation.Instance) + )); + AddMethodTranslator(() => default(TimeSpan).ToString("fmt"), new GenericMethodCompiler( + args => new JsIdentifierExpression("dotvvm").Member("globalize").Member("bindingTimeOnlyToString") + .WithAnnotation(new GlobalizeResourceBindingProperty()) + .Invoke(args[0].WithAnnotation(ShouldBeObservableAnnotation.Instance), args[1]) + .WithAnnotation(ResultIsObservableAnnotation.Instance) + )); foreach (var num in ReflectionUtils.GetNumericTypes().Except(new[] { typeof(char) })) { diff --git a/src/Framework/Framework/Controls/TextBox.cs b/src/Framework/Framework/Controls/TextBox.cs index ccc373d38b..980d030cc1 100644 --- a/src/Framework/Framework/Controls/TextBox.cs +++ b/src/Framework/Framework/Controls/TextBox.cs @@ -131,7 +131,7 @@ public static FormatValueType ResolveValueType(IValueBinding? binding) { return FormatValueType.DateOnly; } - else if (binding != null && (binding.ResultType == typeof(TimeOnly) || binding.ResultType == typeof(TimeOnly?))) + else if (binding != null && (binding.ResultType == typeof(TimeOnly) || binding.ResultType == typeof(TimeOnly?) || binding.ResultType == typeof(TimeSpan) || binding.ResultType == typeof(TimeSpan?))) { return FormatValueType.TimeOnly; } diff --git a/src/Framework/Framework/Resources/Scripts/Globalize/globalize.js b/src/Framework/Framework/Resources/Scripts/Globalize/globalize.js index cc15069eab..b783e76917 100644 --- a/src/Framework/Framework/Resources/Scripts/Globalize/globalize.js +++ b/src/Framework/Framework/Resources/Scripts/Globalize/globalize.js @@ -240,7 +240,8 @@ Globalize.cultures[ "default" ] = { g: "M/d/yyyy h:mm tt", // G is a combination of the short date ("d") and short time ("T") patterns, separated by a space G: "M/d/yyyy h:mm:ss tt" - } + }, + additionalDefaultPatterns: ["H:mm", "H:mm:ss"] // optional fields for each calendar: /* monthsGenitive: @@ -1536,7 +1537,7 @@ Globalize.localize = function( key, cultureSelector ) { Globalize.parseDate = function (value, formats, culture, previousValue) { culture = this.findClosestCulture( culture ); - var date, prop, patterns; + var date, prop, patterns, pattern; if ( formats ) { if ( typeof formats === "string" ) { formats = [ formats ]; @@ -1560,6 +1561,14 @@ Globalize.parseDate = function (value, formats, culture, previousValue) { break; } } + if (!date) { + for ( pattern of culture.calendar.additionalDefaultPatterns) { + date = parseExact(value, pattern, culture, previousValue); + if ( date ) { + break; + } + } + } } return date || null; diff --git a/src/Framework/Framework/Resources/Scripts/Globalize/globalize.min.js b/src/Framework/Framework/Resources/Scripts/Globalize/globalize.min.js index de1daf9058..62b662be31 100644 --- a/src/Framework/Framework/Resources/Scripts/Globalize/globalize.min.js +++ b/src/Framework/Framework/Resources/Scripts/Globalize/globalize.min.js @@ -1 +1,9 @@ -!function(e,t){var r,n,a,s,u,l,i,c,o,f,h,d,p,g,m,y,b,M,v,k,F,x,z,A;r=function(e){return new r.prototype.init(e)},"undefined"!=typeof require&&"undefined"!=typeof exports&&"undefined"!=typeof module?module.exports=r:e.dotvvm_Globalize=r,r.cultures={},r.prototype={constructor:r,init:function(e){return this.cultures=r.cultures,this.cultureSelector=e,this}},r.prototype.init.prototype=r.prototype,r.cultures.default={name:"en",englishName:"English",nativeName:"English",isRTL:!1,language:"en",numberFormat:{pattern:["-n"],decimals:2,",":",",".":".",groupSizes:[3],"+":"+","-":"-",NaN:"NaN",negativeInfinity:"-Infinity",positiveInfinity:"Infinity",percent:{pattern:["-n %","n %"],decimals:2,groupSizes:[3],",":",",".":".",symbol:"%"},currency:{pattern:["($n)","$n"],decimals:2,groupSizes:[3],",":",",".":".",symbol:"$"}},calendars:{standard:{name:"Gregorian_USEnglish","/":"/",":":":",firstDay:0,days:{names:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],namesAbbr:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],namesShort:["Su","Mo","Tu","We","Th","Fr","Sa"]},months:{names:["January","February","March","April","May","June","July","August","September","October","November","December",""],namesAbbr:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""]},AM:["AM","am","AM"],PM:["PM","pm","PM"],eras:[{name:"A.D.",start:null,offset:0}],twoDigitYearMax:2029,patterns:{d:"M/d/yyyy",D:"dddd, MMMM dd, yyyy",t:"h:mm tt",T:"h:mm:ss tt",f:"dddd, MMMM dd, yyyy h:mm tt",F:"dddd, MMMM dd, yyyy h:mm:ss tt",M:"MMMM dd",Y:"yyyy MMMM",S:"yyyy'-'MM'-'dd'T'HH':'mm':'ss",g:"M/d/yyyy h:mm tt",G:"M/d/yyyy h:mm:ss tt"}}},messages:{}},r.cultures.default.calendar=r.cultures.default.calendars.standard,r.cultures.en=r.cultures.default,r.cultureSelector="en",n=/^0x[a-f0-9]+$/i,a=/^[+-]?infinity$/i,s=/^[+-]?\d*\.?\d*(e[+-]?\d+)?$/,u=/^\s+|\s+$/g,l=function(e,t){if(e.indexOf)return e.indexOf(t);for(var r=0,n=e.length;r1&&n.length1?parseInt(f[1],10):0;c=(f=(c=f[0]).split("."))[0],o=f.length>1?f[1]:"";h>0?(c+=(o=m(o,h,!1)).slice(0,h),o=o.substr(h)):h<0&&(o=(c=m(c,(h=-h)+1)).slice(-h,c.length)+o,c=c.slice(0,-h)),o=t>0?r["."]+(o.length>t?o.slice(0,t):m(o,t)):"";for(var d=c.length-1,p=r[","],g="";d>=0;){if(0===s||s>d)return c.slice(0,d+1)+(g.length?p+g+o:o);g=c.slice(d-s+1,d+1)+(g.length?p+g:""),d-=s,u1&&(l=parseInt(r.slice(1),10));var i=r.charAt(0).toUpperCase();if("DGNCP".indexOf(i)>-1){var c,o;switch(i){case"D":a="n",u=g(u),-1!==l&&(u=m(""+u,l,!0)),t<0&&(u="-"+u);break;case"G":o=[0];var f=u.toString();l=f.indexOf(".")>=0?f.length-f.indexOf(".")-1:0;case"N":c=s;case"C":c=c||s.currency;case"P":c=c||s.percent,a=t<0?c.pattern[0]:c.pattern[1]||"n",-1===l&&(l=c.decimals),u=e(u*("P"===i?100:1),l,c,o);break;default:throw"Bad number format specifier: "+i}for(var h=/n|\$|-|%/g,d="";;){var p=h.lastIndex,y=h.exec(a);if(d+=a.slice(p,y?y.index:a.length),!y)break;switch(y[0]){case"n":d+=u;break;case"$":d+=s.currency.symbol;break;case"-":/[1-9]/.test(u)&&(d+=s["-"]);break;case"%":d+=s.percent.symbol}}return d}if(null!=r.match(/^[0#.,]+$/g)&&r.match(/^[0#.,]+$/g).length>0)return formatCustomNumber(t,r,n.numberFormat);throw"Bad number format: "+r}}(),formatCustomNumber=function(e,t,r){if(!t||isNaN(+e))return e;var n=(e="-"==t.charAt(0)?-e:+e)<0?e=-e:0,t=t.split(".");e=+(e=toFixed(e,t.length>1?t[1].length:0))+"";var a=t.length>1?t[1].lastIndexOf("0"):0,s=e.split(".");t.length>1&&(!s[1]||s[1]&&s[1].length<=a)&&(e=(+e).toFixed(a+1));var u=t[0].split(",");t[0]=u.join("");var l=t[0]&&t[0].indexOf("0");if(l>-1)for(;s[0].length1&&e.length>1?r["."]+e[1]:"",(n?"-":"")+e[0]+e[1]},toFixed=function(e,t){return(+(Math.round(+(e+"e"+t))+"e"+-t)).toFixed(t)},k=function(){return/\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g},F=function(e,t){if(!t)return 0;for(var r,n=e.getTime(),a=0,s=t.length;a=r)return a;return 0},x=function(e,t,r,n){var a=e.getFullYear();return!n&&t.eras&&(a-=t.eras[r].offset),a},function(){var e,t,r,n,a,s,u;e=function(e,t){if(t<100){var r=new Date,n=F(r),a=x(r,e,n),s=e.twoDigitYearMax;(t+=a-a%100)>(s="string"==typeof s?(new Date).getFullYear()%100+parseInt(s,10):s)&&(t-=100)}return t},t=function(e,t,r){var n,a=e.days,i=e._upperDays;return i||(e._upperDays=i=[u(a.names),u(a.namesAbbr),u(a.namesShort)]),t=s(t),r?-1===(n=l(i[1],t))&&(n=l(i[2],t)):n=l(i[0],t),n},r=function(e,t,r){var n=e.months,a=e.monthsGenitive||e.months,i=e._upperMonths,c=e._upperMonthsGen;i||(e._upperMonths=i=[u(n.names),u(n.namesAbbr)],e._upperMonthsGen=c=[u(a.names),u(a.namesAbbr)]),t=s(t);var o=l(r?i[1]:i[0],t);return o<0&&(o=l(r?c[1]:c[0],t)),o},n=function(e,t){var r=e._parseRegExp;if(r){var n=r[t];if(n)return n}else e._parseRegExp=r={};for(var a,s=b(e,t).replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g,"\\\\$1"),u=["^"],l=[],i=0,c=0,o=k();null!==(a=o.exec(s));){var f=s.slice(i,a.index);if(i=o.lastIndex,(c+=y(f,u))%2)u.push(a[0]);else{var h,d=a[0],p=d.length;switch(d){case"dddd":case"ddd":case"MMMM":case"MMM":case"gg":case"g":h="(\\D+)";break;case"tt":case"t":h="(\\D*)";break;case"yyyy":case"fff":case"ff":case"f":h="(\\d{"+p+"})";break;case"dd":case"d":case"MM":case"M":case"yy":case"y":case"HH":case"H":case"hh":case"h":case"mm":case"m":case"ss":case"s":h="(\\d\\d?)";break;case"zzz":h="([+-]?\\d\\d?:\\d{2})";break;case"zz":case"z":h="([+-]?\\d\\d?)";break;case"/":h="(\\"+e["/"]+")";break;default:throw"Invalid date format pattern '"+d+"'."}h&&u.push(h),l.push(a[0])}}y(s.slice(i),u),u.push("$");var g={regExp:u.join("").replace(/\s+/g,"\\s+"),groups:l};return r[t]=g},a=function(e,t,r){return er},s=function(e){return e.split(" ").join(" ").toUpperCase()},u=function(e){for(var t=[],r=0,n=e.length;r-1&&(e=(e=e.replace(u.numberFormat.currency.symbol,"")).replace(u.numberFormat.currency["."],u.numberFormat["."])),e=p(e),a.test(e))l=parseFloat(e);else if(!t&&n.test(e))l=parseInt(e,16);else{var c=A(e,i,i.pattern[0]),o=c[0],f=c[1];""===o&&"(n)"!==i.pattern[0]&&(o=(c=A(e,i,"(n)"))[0],f=c[1]),""===o&&"-n"!==i.pattern[0]&&(o=(c=A(e,i,"-n"))[0],f=c[1]),o=o||"+";var h,d,g=f.indexOf("e");g<0&&(g=f.indexOf("E")),g<0?(d=f,h=null):(d=f.substr(0,g),h=f.substr(g+1));var m,y,b=i["."],M=d.indexOf(b);M<0?(m=d,y=null):(m=d.substr(0,M),y=d.substr(M+b.length));var v=i[","];m=m.split(v).join("");var k=v.replace(/\u00A0/g," ");v!==k&&(m=m.split(k).join(""));var F=o+m;if(null!==y&&(F+="."+y),null!==h){var x=A(h,i,"-n");F+="e"+(x[0]||"+")+x[1]}s.test(F)&&(l=parseFloat(F))}return l},r.culture=function(e){return void 0!==e&&(this.cultureSelector=e),this.findClosestCulture(e)||this.culture.default}}(this); \ No newline at end of file +/*! + * Globalize + * + * http://github.com/jquery/globalize + * + * Copyright Software Freedom Conservancy, Inc. + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + */(function(de,I){var h,ne,ae,ie,fe,P,J,K,L,ue,Q,$,S,X,j,q,V,ee,le,te,R,U,W,_;h=function(e){return new h.prototype.init(e)},typeof require!="undefined"&&typeof exports!="undefined"&&typeof module!="undefined"?module.exports=h:de.dotvvm_Globalize=h,h.cultures={},h.prototype={constructor:h,init:function(e){return this.cultures=h.cultures,this.cultureSelector=e,this}},h.prototype.init.prototype=h.prototype,h.cultures.default={name:"en",englishName:"English",nativeName:"English",isRTL:!1,language:"en",numberFormat:{pattern:["-n"],decimals:2,",":",",".":".",groupSizes:[3],"+":"+","-":"-",NaN:"NaN",negativeInfinity:"-Infinity",positiveInfinity:"Infinity",percent:{pattern:["-n %","n %"],decimals:2,groupSizes:[3],",":",",".":".",symbol:"%"},currency:{pattern:["($n)","$n"],decimals:2,groupSizes:[3],",":",",".":".",symbol:"$"}},calendars:{standard:{name:"Gregorian_USEnglish","/":"/",":":":",firstDay:0,days:{names:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],namesAbbr:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],namesShort:["Su","Mo","Tu","We","Th","Fr","Sa"]},months:{names:["January","February","March","April","May","June","July","August","September","October","November","December",""],namesAbbr:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""]},AM:["AM","am","AM"],PM:["PM","pm","PM"],eras:[{name:"A.D.",start:null,offset:0}],twoDigitYearMax:2029,patterns:{d:"M/d/yyyy",D:"dddd, MMMM dd, yyyy",t:"h:mm tt",T:"h:mm:ss tt",f:"dddd, MMMM dd, yyyy h:mm tt",F:"dddd, MMMM dd, yyyy h:mm:ss tt",M:"MMMM dd",Y:"yyyy MMMM",S:"yyyy'-'MM'-'dd'T'HH':'mm':'ss",g:"M/d/yyyy h:mm tt",G:"M/d/yyyy h:mm:ss tt"},additionalDefaultPatterns:["H:mm","H:mm:ss"]}},messages:{}},h.cultures.default.calendar=h.cultures.default.calendars.standard,h.cultures.en=h.cultures.default,h.cultureSelector="en",ne=/^0x[a-f0-9]+$/i,ae=/^[+-]?infinity$/i,ie=/^[+-]?\d*\.?\d*(e[+-]?\d+)?$/,fe=/^\s+|\s+$/g,P=function(e,r){if(e.indexOf)return e.indexOf(r);for(var n=0,s=e.length;n1&&H.length1?parseInt(c[1],10):0;f=c[0],c=f.split("."),f=c[0],l=c.length>1?c[1]:"";var b;p>0?(l=j(l,p,!1),f+=l.slice(0,p),l=l.substr(p)):p<0&&(p=-p,f=j(f,p+1),l=f.slice(-p,f.length)+l,f=f.slice(0,-p)),n>0?l=s["."]+(l.length>n?l.slice(0,n):j(l,n)):l="";for(var k=f.length-1,g=s[","],y="";k>=0;){if(o===0||o>k)return f.slice(0,k+1)+(y.length?g+y+l:l);y=f.slice(k-o+1,k+1)+(y.length?g+y:""),k-=o,a1&&(o=parseInt(n.slice(1),10));var u=n.charAt(0).toUpperCase(),d="DGNCP";if(d.indexOf(u)>-1){var f,l;switch(u){case"D":a="n",t=X(t),o!==-1&&(t=j(""+t,o,!0)),r<0&&(t="-"+t);break;case"G":l=[0];var c=t.toString();c.indexOf(".")>=0?o=c.length-c.indexOf(".")-1:o=0;case"N":f=i;case"C":f=f||i.currency;case"P":f=f||i.percent,a=r<0?f.pattern[0]:f.pattern[1]||"n",o===-1&&(o=f.decimals),t=e(t*(u==="P"?100:1),o,f,l);break;default:throw"Bad number format specifier: "+u}for(var p=/n|\$|-|%/g,b="";;){var k=p.lastIndex,g=p.exec(a);if(b+=a.slice(k,g?g.index:a.length),!g)break;switch(g[0]){case"n":b+=t;break;case"$":b+=i.currency.symbol;break;case"-":/[1-9]/.test(t)&&(b+=i["-"]);break;case"%":b+=i.percent.symbol;break}}return b}else{if(n.match(/^[0#.,]+$/g)!=null&&n.match(/^[0#.,]+$/g).length>0)return formatCustomNumber(r,n,s.numberFormat);throw"Bad number format: "+n}}}(),formatCustomNumber=function(t,a,n){if(!a||isNaN(+t))return t;var s=".",i=",",t=a.charAt(0)=="-"?-t:+t,o=t<0?t=-t:0,a=a.split(s);t=toFixed(t,a.length>1?a[1].length:0),t=+t+"";var u=a.length>1?a[1].lastIndexOf("0"):0,d=t.split(s);a.length>1&&(!d[1]||d[1]&&d[1].length<=u)&&(t=(+t).toFixed(u+1));var f=a[0].split(i);a[0]=f.join("");var l=a[0]&&a[0].indexOf("0");if(l>-1)for(;d[0].length1&&t.length>1?n["."]+t[1]:"",(o?"-":"")+t[0]+t[1]},toFixed=function(e,r){return(+(Math.round(+(e+"e"+r))+"e"+-r)).toFixed(r)},te=function(){return/\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g},R=function(e,r){if(!r)return 0;for(var n,s=e.getTime(),i=0,t=r.length;i=n)return i;return 0},U=function(e,r,n,s){var i=e.getFullYear();return!s&&r.eras&&(i-=r.eras[n].offset),i},function(){var e,r,n,s,i,t,o;e=function(a,u){if(u<100){var d=new Date,f=R(d),l=U(d,a,f),c=a.twoDigitYearMax;c=typeof c=="string"?new Date().getFullYear()%100+parseInt(c,10):c,u+=l-l%100,u>c&&(u-=100)}return u},r=function(a,u,d){var f,l=a.days,c=a._upperDays;return c||(a._upperDays=c=[o(l.names),o(l.namesAbbr),o(l.namesShort)]),u=t(u),d?(f=P(c[1],u),f===-1&&(f=P(c[2],u))):f=P(c[0],u),f},n=function(a,u,d){var f=a.months,l=a.monthsGenitive||a.months,c=a._upperMonths,p=a._upperMonthsGen;c||(a._upperMonths=c=[o(f.names),o(f.namesAbbr)],a._upperMonthsGen=p=[o(l.names),o(l.namesAbbr)]),u=t(u);var b=P(d?c[1]:c[0],u);return b<0&&(b=P(d?p[1]:p[0],u)),b},s=function(a,u){var d=a._parseRegExp;if(!d)a._parseRegExp=d={};else{var f=d[u];if(f)return f}for(var l=V(a,u).replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g,"\\\\$1"),c=["^"],p=[],b=0,k=0,g=te(),y;(y=g.exec(l))!==null;){var M=l.slice(b,y.index);if(b=g.lastIndex,k+=q(M,c),k%2){c.push(y[0]);continue}var A=y[0],z=A.length,F;switch(A){case"dddd":case"ddd":case"MMMM":case"MMM":case"gg":case"g":F="(\\D+)";break;case"tt":case"t":F="(\\D*)";break;case"yyyy":case"fff":case"ff":case"f":F="(\\d{"+z+"})";break;case"dd":case"d":case"MM":case"M":case"yy":case"y":case"HH":case"H":case"hh":case"h":case"mm":case"m":case"ss":case"s":F="(\\d\\d?)";break;case"zzz":F="([+-]?\\d\\d?:\\d{2})";break;case"zz":case"z":F="([+-]?\\d\\d?)";break;case"/":F="(\\"+a["/"]+")";break;default:throw"Invalid date format pattern '"+A+"'."}F&&c.push(F),p.push(y[0])}q(l.slice(b),c),c.push("$");var C=c.join("").replace(/\s+/g,"\\s+"),E={regExp:C,groups:p};return d[u]=E},i=function(a,u,d){return ad},t=function(a){return a.split("\xA0").join(" ").toUpperCase()},o=function(a){for(var u=[],d=0,f=a.length;d-1&&(e=e.replace(s.numberFormat.currency.symbol,""),e=e.replace(s.numberFormat.currency["."],s.numberFormat["."])),e=S(e),ae.test(e))i=parseFloat(e);else if(!r&&ne.test(e))i=parseInt(e,16);else{var o=_(e,t,t.pattern[0]),a=o[0],u=o[1];a===""&&t.pattern[0]!=="(n)"&&(o=_(e,t,"(n)"),a=o[0],u=o[1]),a===""&&t.pattern[0]!=="-n"&&(o=_(e,t,"-n"),a=o[0],u=o[1]),a=a||"+";var d,f,l=u.indexOf("e");l<0&&(l=u.indexOf("E")),l<0?(f=u,d=null):(f=u.substr(0,l),d=u.substr(l+1));var c,p,b=t["."],k=f.indexOf(b);k<0?(c=f,p=null):(c=f.substr(0,k),p=f.substr(k+b.length));var g=t[","];c=c.split(g).join("");var y=g.replace(/\u00A0/g," ");g!==y&&(c=c.split(y).join(""));var M=a+c;if(p!==null&&(M+="."+p),d!==null){var A=_(d,t,"-n");M+="e"+(A[0]||"+")+A[1]}ie.test(M)&&(i=parseFloat(M))}return i},h.culture=function(e){return typeof e!="undefined"&&(this.cultureSelector=e),this.findClosestCulture(e)||this.culture.default}})(this); diff --git a/src/Framework/Framework/Resources/Scripts/metadata/primitiveTypes.ts b/src/Framework/Framework/Resources/Scripts/metadata/primitiveTypes.ts index 88dc7e0577..afc11dee2f 100644 --- a/src/Framework/Framework/Resources/Scripts/metadata/primitiveTypes.ts +++ b/src/Framework/Framework/Resources/Scripts/metadata/primitiveTypes.ts @@ -230,6 +230,10 @@ function validateTimeSpan(value: any) { if (typeof value === "number") { return { value: serializeTimeSpan(value), wasCoerced: true }; } + + if (value instanceof Date) { + return { value: serializeTimeOnly(value, false), wasCoerced: true }; + } } function validateDateTimeOffset(value: any) { diff --git a/src/Framework/Framework/Resources/Scripts/serialization/date.ts b/src/Framework/Framework/Resources/Scripts/serialization/date.ts index bc71cb7625..87a0b5948f 100644 --- a/src/Framework/Framework/Resources/Scripts/serialization/date.ts +++ b/src/Framework/Framework/Resources/Scripts/serialization/date.ts @@ -70,8 +70,13 @@ export function serializeDateOnly(date: Date): string { return padNumber(date.getFullYear(), 4) + "-" + padNumber(date.getMonth() + 1, 2) + "-" + padNumber(date.getDate(), 2) } -export function serializeTimeOnly(date: Date): string { - return padNumber(date.getHours(), 2) + ':' + padNumber(date.getMinutes(), 2) + ':' + padNumber(date.getSeconds(), 2) + '.' + padNumber(date.getMilliseconds(), 3) + '0000'; +export function serializeTimeOnly(date: Date, useMilliseconds: boolean = true): string { + const result = padNumber(date.getHours(), 2) + ':' + padNumber(date.getMinutes(), 2) + ':' + padNumber(date.getSeconds(), 2); + if (useMilliseconds) { + return result + '.' + padNumber(date.getMilliseconds(), 3) + '0000'; + } else{ + return result; + } } export function serializeTimeSpan(ticks: number): string { diff --git a/src/Framework/Framework/package.json b/src/Framework/Framework/package.json index da5367c90c..3ebf0aa004 100644 --- a/src/Framework/Framework/package.json +++ b/src/Framework/Framework/package.json @@ -21,6 +21,7 @@ "build-development": "rollup -c && npm run tsc-types", "build-rollup": "npm run build-production && npm run build-development", "build-production": "rollup -c --environment BUILD:production", + "build-globalize": "esbuild --minify --outfile=./Resources/Scripts/Globalize/globalize.min.js ./Resources/Scripts/Globalize/globalize.js", "test": "jest --silent", "tsc-check": "tsc -p . --noEmit", "tsc-types": "tsc -d -p . --outFile ./obj/typescript-types/dotvvm.d.ts --emitDeclarationOnly --skipLibCheck" diff --git a/src/Samples/Common/ViewModels/ControlSamples/TextBox/TextBoxFormatViewModel.cs b/src/Samples/Common/ViewModels/ControlSamples/TextBox/TextBoxFormatViewModel.cs index 28f32063ca..14efb2ba35 100644 --- a/src/Samples/Common/ViewModels/ControlSamples/TextBox/TextBoxFormatViewModel.cs +++ b/src/Samples/Common/ViewModels/ControlSamples/TextBox/TextBoxFormatViewModel.cs @@ -13,6 +13,7 @@ public TextBoxFormatViewModel() public DateTime DateValue { get; set; } = DateTime.Parse("2015-12-27T00:00:00.0000000"); public DateTime? NullableDateValue { get; set; } = DateTime.Parse("2015-12-27T00:00:00.0000000"); public double NumberValue { get; set; } = 123.123456789; + public TimeSpan TimeSpanValue { get; set; } = new TimeSpan(11, 48, 25); public double? NullableNumberValue { get; set; } = 123.123456789; public double BigNumberValue { get; set; } = 12356789.987654; public string CurrentCulture => Context.GetCurrentUICulture().Name; diff --git a/src/Samples/Common/ViewModels/ControlSamples/TextBox/TextBoxTypesViewModel.cs b/src/Samples/Common/ViewModels/ControlSamples/TextBox/TextBoxTypesViewModel.cs index 13fdcbac48..06efe8753b 100644 --- a/src/Samples/Common/ViewModels/ControlSamples/TextBox/TextBoxTypesViewModel.cs +++ b/src/Samples/Common/ViewModels/ControlSamples/TextBox/TextBoxTypesViewModel.cs @@ -14,6 +14,8 @@ public class TextBoxTypesViewModel : DotvvmViewModelBase public double? NullableNumber { get; set; } = 42.42; public int Int { get; set; } public int? NullableInt { get; set; } + public TimeSpan TimeSpan { get; set; } = new TimeSpan(11, 48, 25); + public TimeSpan? NullableTimeSpan { get; } = new TimeSpan(11, 48, 25); } } diff --git a/src/Samples/Common/Views/ControlSamples/TextBox/TextBox_Format.dothtml b/src/Samples/Common/Views/ControlSamples/TextBox/TextBox_Format.dothtml index 8b6298d15d..180c580d38 100644 --- a/src/Samples/Common/Views/ControlSamples/TextBox/TextBox_Format.dothtml +++ b/src/Samples/Common/Views/ControlSamples/TextBox/TextBox_Format.dothtml @@ -58,6 +58,22 @@ Text="{value: NullableNumberValue}">

+

+ TimeSpan Format:
+ + + +

Custom Numeric Format:

+

+ TimeSpan Format:
+ + + +

Nullable Number:

+ +
+ +

diff --git a/src/Samples/Tests/Tests/Control/TextBoxTests.cs b/src/Samples/Tests/Tests/Control/TextBoxTests.cs index 3ddb4d7663..0ee75c0481 100644 --- a/src/Samples/Tests/Tests/Control/TextBoxTests.cs +++ b/src/Samples/Tests/Tests/Control/TextBoxTests.cs @@ -165,6 +165,12 @@ public void Control_TextBox_StringFormat(string cultureName, string url, string var nullableNumberValueText = browser.First("#nullableNumberValueText"); AssertUI.InnerTextEquals(nullableNumberValueText, 123.123456789.ToString(culture)); + var timespanTextbox = browser.First("#timespanTextbox"); + AssertUI.Attribute(timespanTextbox, "value", new TimeSpan(11, 48, 25).ToString("hh\\:mm")); + + var timespanValueText = browser.First("#timespanValueText"); + AssertUI.InnerTextEquals(timespanValueText, new TimeSpan(11, 48, 25).ToString()); + //write new valid values dateTextBox.Clear().SendKeys(dateResult2); numberTextbox.Clear().SendKeys(2000.ToString("n0", culture)); @@ -180,11 +186,14 @@ public void Control_TextBox_StringFormat(string cultureName, string url, string //write invalid values dateTextBox.Clear().SendKeys("dsasdasd"); numberTextbox.Clear().SendKeys("000//a"); + timespanTextbox.Clear().SendKeys("08:23"); dateTextBox.Click(); //check displayed values (behavior change in 3.0 - previous values should stay there) AssertUI.InnerTextEquals(dateText, new DateTime(2018, 12, 27).ToString("G", culture)); AssertUI.InnerTextEquals(numberValueText, 2000.ToString(culture)); + AssertUI.InnerTextEquals(timespanTextbox, new TimeSpan(8, 23, 0).ToString("hh\\:mm")); + AssertUI.InnerTextEquals(timespanValueText, new TimeSpan(8, 23, 0).ToString()); AssertUI.Attribute(numberTextbox, "value", "000//a"); AssertUI.Attribute(dateTextBox, "value", "dsasdasd"); @@ -192,11 +201,13 @@ public void Control_TextBox_StringFormat(string cultureName, string url, string //write new valid values dateTextBox.Clear().SendKeys(new DateTime(2018, 1, 1).ToString("d", culture)); numberTextbox.Clear().SendKeys(1000.550277.ToString(culture)); + timespanTextbox.Clear().SendKeys(new TimeSpan(11, 48, 25).ToString("hh\\:mm")); dateTextBox.Click(); //check new values AssertUI.InnerTextEquals(dateText, new DateTime(2018, 1, 1).ToString("G", culture)); AssertUI.InnerTextEquals(numberValueText, 1000.550277.ToString(culture)); + AssertUI.InnerTextEquals(timespanValueText, new TimeSpan(11, 48, 0).ToString()); AssertUI.Attribute(numberTextbox, "value", 1000.550277.ToString("n4", culture)); AssertUI.Attribute(dateTextBox, "value", dateResult3); @@ -209,6 +220,10 @@ public void Control_TextBox_StringFormat(string cultureName, string url, string nullableDateTextBox.Clear().SendKeys(new DateTime(2020, 4, 2).ToString("d", culture)).SendKeys(Keys.Tab); AssertUI.Attribute(nullableDateTextBox, "value", new DateTime(2020, 4, 2).ToString("G", culture)); AssertUI.InnerTextEquals(nullableDateText, new DateTime(2020, 4, 2).ToString("G", culture)); + + timespanTextbox.Clear().SendKeys(new TimeSpan(22, 23, 45).ToString("t", culture)).SendKeys(Keys.Tab); + AssertUI.InnerTextEquals(timespanTextbox, new TimeSpan(22, 23, 45).ToString("hh\\:mm")); + AssertUI.InnerTextEquals(timespanValueText, new TimeSpan(22, 23, 45).ToString()); }); } @@ -288,6 +303,9 @@ public void Control_TextBox_TextBox_Types(string localizationId) AssertUI.Value(browser.Single("input[data-ui='datetime-textbox']"), "2017-01-01T08:08"); AssertUI.Value(browser.Single("input[data-ui='nullable-datetime-textbox']"), "2017-01-01T20:10"); + AssertUI.Value(browser.Single("input[data-ui='timespan-textbox']"), "11:48:25"); + AssertUI.Value(browser.Single("input[data-ui='nullable-timespan-textbox']"), "11:48:25"); + var intTextBox = browser.Single("input[data-ui='int-textbox']"); AssertUI.Value(intTextBox, "0"); intTextBox.SetFocus();