www-thermferm/jqwidgets/globalization/globalize.js

changeset 301
dbaed96a4a0c
parent 117
7119ea8a5225
child 511
2d1d43c3a2c2
equal deleted inserted replaced
300:4ce46ff3e37d 301:dbaed96a4a0c
5 * 5 *
6 * Copyright Software Freedom Conservancy, Inc. 6 * Copyright Software Freedom Conservancy, Inc.
7 * Dual licensed under the MIT or GPL Version 2 licenses. 7 * Dual licensed under the MIT or GPL Version 2 licenses.
8 * http://jquery.org/license 8 * http://jquery.org/license
9 */ 9 */
10 10 (function(j,g){var r,w,l,x,p,v,u,t,m,a,k,y,q,c,n,s,z,o,i,h,d,f,e,b;r=function(A){return new r.prototype.init(A)};if(typeof require!=="undefined"&&typeof exports!=="undefined"&&typeof module!=="undefined"){module.exports=r}else{j.Globalize=r}r.cultures={};r.prototype={constructor:r,init:function(A){this.cultures=r.cultures;this.cultureSelector=A;return this}};r.prototype.init.prototype=r.prototype;r.cultures["default"]={name:"en",englishName:"English",nativeName:"English",isRTL:false,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\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"}}},messages:{}};r.cultures["default"].calendar=r.cultures["default"].calendars.standard;r.cultures.en=r.cultures["default"];r.cultureSelector="en";w=/^0x[a-f0-9]+$/i;l=/^[+\-]?infinity$/i;x=/^[+\-]?\d*\.?\d*(e[+\-]?\d+)?$/;p=/^\s+|\s+$/g;v=function(D,C){if(D.indexOf){return D.indexOf(C)}for(var A=0,B=D.length;A<B;A++){if(D[A]===C){return A}}return -1};u=function(B,A){return B.substr(B.length-A.length)===A};t=function(){var J,C,A,B,G,H,F=arguments[0]||{},E=1,D=arguments.length,I=false;if(typeof F==="boolean"){I=F;F=arguments[1]||{};E=2}if(typeof F!=="object"&&!a(F)){F={}}for(;E<D;E++){if((J=arguments[E])!=null){for(C in J){A=F[C];B=J[C];if(F===B){continue}if(I&&B&&(k(B)||(G=m(B)))){if(G){G=false;H=A&&m(A)?A:[]}else{H=A&&k(A)?A:{}}F[C]=t(I,H,B)}else{if(B!==g){F[C]=B}}}}}return F};m=Array.isArray||function(A){return Object.prototype.toString.call(A)==="[object Array]"};a=function(A){return Object.prototype.toString.call(A)==="[object Function]"};k=function(A){return Object.prototype.toString.call(A)==="[object Object]"};y=function(B,A){return B.indexOf(A)===0};q=function(A){return(A+"").replace(p,"")};c=function(A){if(isNaN(A)){return NaN}return Math[A<0?"ceil":"floor"](A)};n=function(D,B,C){var A;for(A=D.length;A<B;A+=1){D=(C?("0"+D):(D+"0"))}return D};s=function(D,A){var C=0,F=false;for(var E=0,B=D.length;E<B;E++){var G=D.charAt(E);switch(G){case"'":if(F){A.push("'")}else{C++}F=false;break;case"\\":if(F){A.push("\\")}F=!F;break;default:A.push(G);F=false;break}}return C};z=function(E,D){D=D||"F";var C,B=E.patterns,A=D.length;if(A===1){C=B[D];if(!C){throw"Invalid date format string '"+D+"'."}D=C}else{if(A===2&&D.charAt(0)==="%"){D=D.charAt(1)}}return D};o=function(U,Y,Z){var M=Z.calendar,I=M.convert,ab;if(!Y||!Y.length||Y==="i"){if(Z&&Z.name.length){if(I){ab=o(U,M.patterns.F,Z)}else{var J=new Date(U.getTime()),Q=d(U,M.eras);J.setFullYear(f(U,M,Q));ab=J.toLocaleString()}}else{ab=U.toString()}return ab}var V=M.eras,B=Y==="s";Y=z(M,Y);ab=[];var F,W=["0","00","000"],K,L,A=/([^d]|^)(d|dd)([^d]|$)/g,aa=0,R=h(),C;function H(ac,af){var ae,ad=ac+"";if(af>1&&ad.length<af){ae=(W[af-2]+ad);return ae.substr(ae.length-af,af)}else{ae=ad}return ae}function X(){if(K||L){return K}K=A.test(Y);L=true;return K}function D(ad,ac){if(C){return C[ac]}switch(ac){case 0:return ad.getFullYear();case 1:return ad.getMonth();case 2:return ad.getDate();default:throw"Invalid part value "+ac}}if(!B&&I){C=I.fromGregorian(U)}for(;;){var G=R.lastIndex,P=R.exec(Y);var N=Y.slice(G,P?P.index:Y.length);aa+=s(N,ab);if(!P){break}if(aa%2){ab.push(P[0]);continue}var S=P[0],E=S.length;switch(S){case"ddd":case"dddd":var O=(E===3)?M.days.namesAbbr:M.days.names;ab.push(O[U.getDay()]);break;case"d":case"dd":K=true;ab.push(H(D(U,2),E));break;case"MMM":case"MMMM":var T=D(U,1);ab.push((M.monthsGenitive&&X())?(M.monthsGenitive[E===3?"namesAbbr":"names"][T]):(M.months[E===3?"namesAbbr":"names"][T]));break;case"M":case"MM":ab.push(H(D(U,1)+1,E));break;case"y":case"yy":case"yyyy":T=C?C[0]:f(U,M,d(U,V),B);if(E<4){T=T%100}ab.push(H(T,E));break;case"h":case"hh":F=U.getHours()%12;if(F===0){F=12}ab.push(H(F,E));break;case"H":case"HH":ab.push(H(U.getHours(),E));break;case"m":case"mm":ab.push(H(U.getMinutes(),E));break;case"s":case"ss":ab.push(H(U.getSeconds(),E));break;case"t":case"tt":T=U.getHours()<12?(M.AM?M.AM[0]:" "):(M.PM?M.PM[0]:" ");ab.push(E===1?T.charAt(0):T);break;case"f":case"ff":case"fff":ab.push(H(U.getMilliseconds(),3).substr(0,E));break;case"z":case"zz":F=U.getTimezoneOffset()/60;ab.push((F<=0?"+":"-")+H(Math.floor(Math.abs(F)),E));break;case"zzz":F=U.getTimezoneOffset()/60;ab.push((F<=0?"+":"-")+H(Math.floor(Math.abs(F)),2)+":"+H(Math.abs(U.getTimezoneOffset()%60),2));break;case"g":case"gg":if(M.eras){ab.push(M.eras[d(U,V)].name)}break;case"/":ab.push(M["/"]);break;default:throw"Invalid date format pattern '"+S+"'."}}return ab.join("")};(function(){var A;A=function(H,I,P){var F=P.groupSizes,B=F[0],C=1,M=Math.pow(10,I),D=Math.round(H*M)/M;if(!isFinite(D)){D=H}H=D;var G=H+"",O="",L=G.split(/e/i),N=L.length>1?parseInt(L[1],10):0;G=L[0];L=G.split(".");G=L[0];O=L.length>1?L[1]:"";var E;if(N>0){O=n(O,N,false);G+=O.slice(0,N);O=O.substr(N)}else{if(N<0){N=-N;G=n(G,N+1,true);O=G.slice(-N,G.length)+O;G=G.slice(0,-N)}}if(I>0){O=P["."]+((O.length>I)?O.slice(0,I):n(O,I))}else{O=""}var K=G.length-1,Q=P[","],J="";while(K>=0){if(B===0||B>K){return G.slice(0,K+1)+(J.length?(Q+J+O):O)}J=G.slice(K-B+1,K+1)+(J.length?(Q+J):"");K-=B;if(C<F.length){B=F[C];C++}}return G.slice(0,K+1)+Q+J+O};i=function(M,L,F){if(!isFinite(M)){if(M===Infinity){return F.numberFormat.positiveInfinity}if(M===-Infinity){return F.numberFormat.negativeInfinity}return F.numberFormat.NaN}if(!L||L==="i"){return F.name.length?M.toLocaleString():M.toString()}L=L||"D";var D=F.numberFormat,E=Math.abs(M),G=-1,K;if(L.length>1){G=parseInt(L.slice(1),10)}var J=L.charAt(0).toUpperCase(),N;switch(J){case"D":K="n";E=c(E);if(G!==-1){E=n(""+E,G,true)}if(M<0){E="-"+E}break;case"N":N=D;case"C":N=N||D.currency;case"P":N=N||D.percent;K=M<0?N.pattern[0]:(N.pattern[1]||"n");if(G===-1){G=N.decimals}E=A(E*(J==="P"?100:1),G,N);break;default:throw"Bad number format specifier: "+J}var B=/n|\$|-|%/g,I="";for(;;){var H=B.lastIndex,C=B.exec(K);I+=K.slice(H,C?C.index:K.length);if(!C){break}switch(C[0]){case"n":I+=E;break;case"$":I+=D.currency.symbol;break;case"-":if(/[1-9]/.test(E)){I+=D["-"]}break;case"%":I+=D.percent.symbol;break}}return I}}());h=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)};d=function(C,B){if(!B){return 0}var F,E=C.getTime();for(var D=0,A=B.length;D<A;D++){F=B[D].start;if(F===null||E>=F){return D}}return 0};f=function(B,D,A,E){var C=B.getFullYear();if(!E&&D.eras){C-=D.eras[A].offset}return C};(function(){var B,A,D,C,G,F,E;B=function(M,K){if(K<100){var I=new Date(),H=d(I),L=f(I,M,H),J=M.twoDigitYearMax;J=typeof J==="string"?new Date().getFullYear()%100+parseInt(J,10):J;K+=L-(L%100);if(K>J){K-=100}}return K};A=function(L,K,I){var H,M=L.days,J=L._upperDays;if(!J){L._upperDays=J=[E(M.names),E(M.namesAbbr),E(M.namesShort)]}K=F(K);if(I){H=v(J[1],K);if(H===-1){H=v(J[2],K)}}else{H=v(J[0],K)}return H};D=function(O,N,J){var H=O.months,I=O.monthsGenitive||O.months,L=O._upperMonths,M=O._upperMonthsGen;if(!L){O._upperMonths=L=[E(H.names),E(H.namesAbbr)];O._upperMonthsGen=M=[E(I.names),E(I.namesAbbr)]}N=F(N);var K=v(J?L[1]:L[0],N);if(K<0){K=v(J?M[1]:M[0],N)}return K};C=function(H,S){var U=H._parseRegExp;if(!U){H._parseRegExp=U={}}else{var L=U[S];if(L){return L}}var R=z(H,S).replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g,"\\\\$1"),P=["^"],I=[],O=0,K=0,X=h(),M;while((M=X.exec(R))!==null){var W=R.slice(O,M.index);O=X.lastIndex;K+=s(W,P);if(K%2){P.push(M[0]);continue}var J=M[0],N=J.length,T;switch(J){case"dddd":case"ddd":case"MMMM":case"MMM":case"gg":case"g":T="(\\D+)";break;case"tt":case"t":T="(\\D*)";break;case"yyyy":case"fff":case"ff":case"f":T="(\\d{"+N+"})";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":T="(\\d\\d?)";break;case"zzz":T="([+-]?\\d\\d?:\\d{2})";break;case"zz":case"z":T="([+-]?\\d\\d?)";break;case"/":T="(\\/)";break;default:throw"Invalid date format pattern '"+J+"'."}if(T){P.push(T)}I.push(M[0])}s(R.slice(O),P);P.push("$");var V=P.join("").replace(/\s+/g,"\\s+"),Q={regExp:V,groups:I};return U[S]=Q};G=function(J,H,I){return J<H||J>I};F=function(H){return H.split("\u00A0").join(" ").toUpperCase()};E=function(H){var K=[];for(var J=0,I=H.length;J<I;J++){K[J]=F(H[J])}return K};e=function(ab,ai,aj){ab=q(ab);var U=aj.calendar,ao=C(U,ai),O=new RegExp(ao.regExp).exec(ab);if(O===null){return null}var ak=ao.groups,Z=null,S=null,an=null,am=null,T=null,M=0,ae,ad=0,al=0,H=0,J=null,V=false;for(var af=0,ah=ak.length;af<ah;af++){var I=O[af+1];if(I){var aa=ak[af],L=aa.length,N=parseInt(I,10);switch(aa){case"dd":case"d":am=N;if(G(am,1,31)){return null}break;case"MMM":case"MMMM":an=D(U,I,L===3);if(G(an,0,11)){return null}break;case"M":case"MM":an=N-1;if(G(an,0,11)){return null}break;case"y":case"yy":case"yyyy":S=L<4?B(U,N):N;if(G(S,0,9999)){return null}break;case"h":case"hh":M=N;if(M===12){M=0}if(G(M,0,11)){return null}break;case"H":case"HH":M=N;if(G(M,0,23)){return null}break;case"m":case"mm":ad=N;if(G(ad,0,59)){return null}break;case"s":case"ss":al=N;if(G(al,0,59)){return null}break;case"tt":case"t":V=U.PM&&(I===U.PM[0]||I===U.PM[1]||I===U.PM[2]);if(!V&&(!U.AM||(I!==U.AM[0]&&I!==U.AM[1]&&I!==U.AM[2]))){return null}break;case"f":case"ff":case"fff":H=N*Math.pow(10,3-L);if(G(H,0,999)){return null}break;case"ddd":case"dddd":T=A(U,I,L===3);if(G(T,0,6)){return null}break;case"zzz":var K=I.split(/:/);if(K.length!==2){return null}ae=parseInt(K[0],10);if(G(ae,-12,13)){return null}var Q=parseInt(K[1],10);if(G(Q,0,59)){return null}J=(ae*60)+(y(I,"-")?-Q:Q);break;case"z":case"zz":ae=N;if(G(ae,-12,13)){return null}J=ae*60;break;case"g":case"gg":var W=I;if(!W||!U.eras){return null}W=q(W.toLowerCase());for(var ag=0,ac=U.eras.length;ag<ac;ag++){if(W===U.eras[ag].name.toLowerCase()){Z=ag;break}}if(Z===null){return null}break}}}var R=new Date(),Y,P=U.convert;Y=P?P.fromGregorian(R)[0]:R.getFullYear();if(S===null){S=Y}else{if(U.eras){S+=U.eras[(Z||0)].offset}}if(an===null){an=0}if(am===null){am=1}if(P){R=P.toGregorian(S,an,am);if(R===null){return null}}else{R.setFullYear(S,an,am);if(R.getDate()!==am){return null}if(T!==null&&R.getDay()!==T){return null}}if(V&&M<12){M+=12}R.setHours(M,ad,al,H);if(J!==null){var X=R.getMinutes()-(J+R.getTimezoneOffset());R.setHours(R.getHours()+parseInt(X/60,10),X%60)}return R}}());b=function(D,C,B){var F=C["-"],E=C["+"],A;switch(B){case"n -":F=" "+F;E=" "+E;case"n-":if(u(D,F)){A=["-",D.substr(0,D.length-F.length)]}else{if(u(D,E)){A=["+",D.substr(0,D.length-E.length)]}}break;case"- n":F+=" ";E+=" ";case"-n":if(y(D,F)){A=["-",D.substr(F.length)]}else{if(y(D,E)){A=["+",D.substr(E.length)]}}break;case"(n)":if(y(D,"(")&&u(D,")")){A=["-",D.substr(1,D.length-2)]}break}return A||["",D]};r.prototype.findClosestCulture=function(A){return r.findClosestCulture.call(this,A)};r.prototype.format=function(A,B,C){return r.format.call(this,A,B,C)};r.prototype.localize=function(A,B){return r.localize.call(this,A,B)};r.prototype.parseInt=function(B,A,C){return r.parseInt.call(this,B,A,C)};r.prototype.parseFloat=function(B,A,C){return r.parseFloat.call(this,B,A,C)};r.prototype.culture=function(A){return r.culture.call(this,A)};r.addCultureInfo=function(E,B,D){var C={},A=false;if(typeof E!=="string"){D=E;E=this.culture().name;C=this.cultures[E]}else{if(typeof B!=="string"){D=B;A=(this.cultures[E]==null);C=this.cultures[E]||this.cultures["default"]}else{A=true;C=this.cultures[B]}}this.cultures[E]=t(true,{},C,D);if(A){this.cultures[E].calendar=this.cultures[E].calendars.standard}};r.findClosestCulture=function(A){var I;if(!A){return this.findClosestCulture(this.cultureSelector)||this.cultures["default"]}if(typeof A==="string"){A=A.split(",")}if(m(A)){var C,M=this.cultures,K=A,H,D=K.length,L=[];for(H=0;H<D;H++){A=q(K[H]);var B,G=A.split(";");C=q(G[0]);if(G.length===1){B=1}else{A=q(G[1]);if(A.indexOf("q=")===0){A=A.substr(2);B=parseFloat(A);B=isNaN(B)?0:B}else{B=1}}L.push({lang:C,pri:B})}L.sort(function(O,N){if(O.pri<N.pri){return 1}else{if(O.pri>N.pri){return -1}}return 0});for(H=0;H<D;H++){C=L[H].lang;I=M[C];if(I){return I}}for(H=0;H<D;H++){C=L[H].lang;do{var J=C.lastIndexOf("-");if(J===-1){break}C=C.substr(0,J);I=M[C];if(I){return I}}while(1)}for(H=0;H<D;H++){C=L[H].lang;for(var F in M){var E=M[F];if(E.language==C){return E}}}}else{if(typeof A==="object"){return A}}return I||null};r.format=function(B,C,D){var A=this.findClosestCulture(D);if(B instanceof Date){B=o(B,C,A)}else{if(typeof B==="number"){B=i(B,C,A)}}return B};r.localize=function(A,B){return this.findClosestCulture(B).messages[A]||this.cultures["default"].messages[A]};r.parseDate=function(I,G,E){E=this.findClosestCulture(E);var C,A,B;if(G){if(typeof G==="string"){G=[G]}if(G.length){for(var F=0,D=G.length;F<D;F++){var H=G[F];if(H){C=e(I,H,E);if(C){break}}}}}else{B=E.calendar.patterns;for(A in B){C=e(I,B[A],E);if(C){break}}}return C||null};r.parseInt=function(B,A,C){return c(r.parseFloat(B,A,C))};r.parseFloat=function(O,H,J){if(typeof H!=="number"){J=H;H=10}var Q=this.findClosestCulture(J);var T=NaN,F=Q.numberFormat;if(O.indexOf(Q.numberFormat.currency.symbol)>-1){O=O.replace(Q.numberFormat.currency.symbol,"");O=O.replace(Q.numberFormat.currency["."],Q.numberFormat["."])}if(O.indexOf(Q.numberFormat.percent.symbol)>-1){O=O.replace(Q.numberFormat.percent.symbol,"")}O=O.replace(/ /g,"");if(l.test(O)){T=parseFloat(O)}else{if(!H&&w.test(O)){T=parseInt(O,16)}else{var C=b(O,F,F.pattern[0]),S=C[0],I=C[1];if(S===""&&F.pattern[0]!=="(n)"){C=b(O,F,"(n)");S=C[0];I=C[1]}if(S===""&&F.pattern[0]!=="-n"){C=b(O,F,"-n");S=C[0];I=C[1]}S=S||"+";var N,K,R=I.indexOf("e");if(R<0){R=I.indexOf("E")}if(R<0){K=I;N=null}else{K=I.substr(0,R);N=I.substr(R+1)}var P,G,D=F["."],A=K.indexOf(D);if(A<0){P=K;G=null}else{P=K.substr(0,A);G=K.substr(A+D.length)}var L=F[","];P=P.split(L).join("");var E=L.replace(/\u00A0/g," ");if(L!==E){P=P.split(E).join("")}var M=S+P;if(G!==null){M+="."+G}if(N!==null){var B=b(N,F,"-n");M+="e"+(B[0]||"+")+B[1]}if(x.test(M)){T=parseFloat(M)}}}return T};r.culture=function(A){if(typeof A!=="undefined"){this.cultureSelector=A}return this.findClosestCulture(A)||this.cultures["default"]}}(this));
11 (function( window, undefined ) {
12
13 var Globalize,
14 // private variables
15 regexHex,
16 regexInfinity,
17 regexParseFloat,
18 regexTrim,
19 // private JavaScript utility functions
20 arrayIndexOf,
21 endsWith,
22 extend,
23 isArray,
24 isFunction,
25 isObject,
26 startsWith,
27 trim,
28 truncate,
29 zeroPad,
30 // private Globalization utility functions
31 appendPreOrPostMatch,
32 expandFormat,
33 formatDate,
34 formatNumber,
35 getTokenRegExp,
36 getEra,
37 getEraYear,
38 parseExact,
39 parseNegativePattern;
40
41 // Global variable (Globalize) or CommonJS module (globalize)
42 Globalize = function( cultureSelector ) {
43 return new Globalize.prototype.init( cultureSelector );
44 };
45
46 if ( typeof require !== "undefined" &&
47 typeof exports !== "undefined" &&
48 typeof module !== "undefined" ) {
49 // Assume CommonJS
50 module.exports = Globalize;
51 } else {
52 // Export as global variable
53 window.Globalize = Globalize;
54 }
55
56 Globalize.cultures = {};
57
58 Globalize.prototype = {
59 constructor: Globalize,
60 init: function( cultureSelector ) {
61 this.cultures = Globalize.cultures;
62 this.cultureSelector = cultureSelector;
63
64 return this;
65 }
66 };
67 Globalize.prototype.init.prototype = Globalize.prototype;
68
69 // 1. When defining a culture, all fields are required except the ones stated as optional.
70 // 2. Each culture should have a ".calendars" object with at least one calendar named "standard"
71 // which serves as the default calendar in use by that culture.
72 // 3. Each culture should have a ".calendar" object which is the current calendar being used,
73 // it may be dynamically changed at any time to one of the calendars in ".calendars".
74 Globalize.cultures[ "default" ] = {
75 // A unique name for the culture in the form <language code>-<country/region code>
76 name: "en",
77 // the name of the culture in the english language
78 englishName: "English",
79 // the name of the culture in its own language
80 nativeName: "English",
81 // whether the culture uses right-to-left text
82 isRTL: false,
83 // "language" is used for so-called "specific" cultures.
84 // For example, the culture "es-CL" means "Spanish, in Chili".
85 // It represents the Spanish-speaking culture as it is in Chili,
86 // which might have different formatting rules or even translations
87 // than Spanish in Spain. A "neutral" culture is one that is not
88 // specific to a region. For example, the culture "es" is the generic
89 // Spanish culture, which may be a more generalized version of the language
90 // that may or may not be what a specific culture expects.
91 // For a specific culture like "es-CL", the "language" field refers to the
92 // neutral, generic culture information for the language it is using.
93 // This is not always a simple matter of the string before the dash.
94 // For example, the "zh-Hans" culture is netural (Simplified Chinese).
95 // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage
96 // field is "zh-CHS", not "zh".
97 // This field should be used to navigate from a specific culture to it's
98 // more general, neutral culture. If a culture is already as general as it
99 // can get, the language may refer to itself.
100 language: "en",
101 // numberFormat defines general number formatting rules, like the digits in
102 // each grouping, the group separator, and how negative numbers are displayed.
103 numberFormat: {
104 // [negativePattern]
105 // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency,
106 // but is still defined as an array for consistency with them.
107 // negativePattern: one of "(n)|-n|- n|n-|n -"
108 pattern: [ "-n" ],
109 // number of decimal places normally shown
110 decimals: 2,
111 // string that separates number groups, as in 1,000,000
112 ",": ",",
113 // string that separates a number from the fractional portion, as in 1.99
114 ".": ".",
115 // array of numbers indicating the size of each number group.
116 // TODO: more detailed description and example
117 groupSizes: [ 3 ],
118 // symbol used for positive numbers
119 "+": "+",
120 // symbol used for negative numbers
121 "-": "-",
122 // symbol used for NaN (Not-A-Number)
123 "NaN": "NaN",
124 // symbol used for Negative Infinity
125 negativeInfinity: "-Infinity",
126 // symbol used for Positive Infinity
127 positiveInfinity: "Infinity",
128 percent: {
129 // [negativePattern, positivePattern]
130 // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %"
131 // positivePattern: one of "n %|n%|%n|% n"
132 pattern: [ "-n %", "n %" ],
133 // number of decimal places normally shown
134 decimals: 2,
135 // array of numbers indicating the size of each number group.
136 // TODO: more detailed description and example
137 groupSizes: [ 3 ],
138 // string that separates number groups, as in 1,000,000
139 ",": ",",
140 // string that separates a number from the fractional portion, as in 1.99
141 ".": ".",
142 // symbol used to represent a percentage
143 symbol: "%"
144 },
145 currency: {
146 // [negativePattern, positivePattern]
147 // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)"
148 // positivePattern: one of "$n|n$|$ n|n $"
149 pattern: [ "($n)", "$n" ],
150 // number of decimal places normally shown
151 decimals: 2,
152 // array of numbers indicating the size of each number group.
153 // TODO: more detailed description and example
154 groupSizes: [ 3 ],
155 // string that separates number groups, as in 1,000,000
156 ",": ",",
157 // string that separates a number from the fractional portion, as in 1.99
158 ".": ".",
159 // symbol used to represent currency
160 symbol: "$"
161 }
162 },
163 // calendars defines all the possible calendars used by this culture.
164 // There should be at least one defined with name "standard", and is the default
165 // calendar used by the culture.
166 // A calendar contains information about how dates are formatted, information about
167 // the calendar's eras, a standard set of the date formats,
168 // translations for day and month names, and if the calendar is not based on the Gregorian
169 // calendar, conversion functions to and from the Gregorian calendar.
170 calendars: {
171 standard: {
172 // name that identifies the type of calendar this is
173 name: "Gregorian_USEnglish",
174 // separator of parts of a date (e.g. "/" in 11/05/1955)
175 "/": "/",
176 // separator of parts of a time (e.g. ":" in 05:44 PM)
177 ":": ":",
178 // the first day of the week (0 = Sunday, 1 = Monday, etc)
179 firstDay: 0,
180 days: {
181 // full day names
182 names: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
183 // abbreviated day names
184 namesAbbr: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
185 // shortest day names
186 namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ]
187 },
188 months: {
189 // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar)
190 names: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" ],
191 // abbreviated month names
192 namesAbbr: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" ]
193 },
194 // AM and PM designators in one of these forms:
195 // The usual view, and the upper and lower case versions
196 // [ standard, lowercase, uppercase ]
197 // The culture does not use AM or PM (likely all standard date formats use 24 hour time)
198 // null
199 AM: [ "AM", "am", "AM" ],
200 PM: [ "PM", "pm", "PM" ],
201 eras: [
202 // eras in reverse chronological order.
203 // name: the name of the era in this culture (e.g. A.D., C.E.)
204 // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era.
205 // offset: offset in years from gregorian calendar
206 {
207 "name": "A.D.",
208 "start": null,
209 "offset": 0
210 }
211 ],
212 // when a two digit year is given, it will never be parsed as a four digit
213 // year greater than this year (in the appropriate era for the culture)
214 // Set it as a full year (e.g. 2029) or use an offset format starting from
215 // the current year: "+19" would correspond to 2029 if the current year 2010.
216 twoDigitYearMax: 2029,
217 // set of predefined date and time patterns used by the culture
218 // these represent the format someone in this culture would expect
219 // to see given the portions of the date that are shown.
220 patterns: {
221 // short date pattern
222 d: "M/d/yyyy",
223 // long date pattern
224 D: "dddd, MMMM dd, yyyy",
225 // short time pattern
226 t: "h:mm tt",
227 // long time pattern
228 T: "h:mm:ss tt",
229 // long date, short time pattern
230 f: "dddd, MMMM dd, yyyy h:mm tt",
231 // long date, long time pattern
232 F: "dddd, MMMM dd, yyyy h:mm:ss tt",
233 // month/day pattern
234 M: "MMMM dd",
235 // month/year pattern
236 Y: "yyyy MMMM",
237 // S is a sortable format that does not vary by culture
238 S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"
239 }
240 // optional fields for each calendar:
241 /*
242 monthsGenitive:
243 Same as months but used when the day preceeds the month.
244 Omit if the culture has no genitive distinction in month names.
245 For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx
246 convert:
247 Allows for the support of non-gregorian based calendars. This convert object is used to
248 to convert a date to and from a gregorian calendar date to handle parsing and formatting.
249 The two functions:
250 fromGregorian( date )
251 Given the date as a parameter, return an array with parts [ year, month, day ]
252 corresponding to the non-gregorian based year, month, and day for the calendar.
253 toGregorian( year, month, day )
254 Given the non-gregorian year, month, and day, return a new Date() object
255 set to the corresponding date in the gregorian calendar.
256 */
257 }
258 },
259 // For localized strings
260 messages: {}
261 };
262
263 Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard;
264
265 Globalize.cultures.en = Globalize.cultures[ "default" ];
266
267 Globalize.cultureSelector = "en";
268
269 //
270 // private variables
271 //
272
273 regexHex = /^0x[a-f0-9]+$/i;
274 regexInfinity = /^[+\-]?infinity$/i;
275 regexParseFloat = /^[+\-]?\d*\.?\d*(e[+\-]?\d+)?$/;
276 regexTrim = /^\s+|\s+$/g;
277
278 //
279 // private JavaScript utility functions
280 //
281
282 arrayIndexOf = function( array, item ) {
283 if ( array.indexOf ) {
284 return array.indexOf( item );
285 }
286 for ( var i = 0, length = array.length; i < length; i++ ) {
287 if ( array[i] === item ) {
288 return i;
289 }
290 }
291 return -1;
292 };
293
294 endsWith = function( value, pattern ) {
295 return value.substr( value.length - pattern.length ) === pattern;
296 };
297
298 extend = function() {
299 var options, name, src, copy, copyIsArray, clone,
300 target = arguments[0] || {},
301 i = 1,
302 length = arguments.length,
303 deep = false;
304
305 // Handle a deep copy situation
306 if ( typeof target === "boolean" ) {
307 deep = target;
308 target = arguments[1] || {};
309 // skip the boolean and the target
310 i = 2;
311 }
312
313 // Handle case when target is a string or something (possible in deep copy)
314 if ( typeof target !== "object" && !isFunction(target) ) {
315 target = {};
316 }
317
318 for ( ; i < length; i++ ) {
319 // Only deal with non-null/undefined values
320 if ( (options = arguments[ i ]) != null ) {
321 // Extend the base object
322 for ( name in options ) {
323 src = target[ name ];
324 copy = options[ name ];
325
326 // Prevent never-ending loop
327 if ( target === copy ) {
328 continue;
329 }
330
331 // Recurse if we're merging plain objects or arrays
332 if ( deep && copy && ( isObject(copy) || (copyIsArray = isArray(copy)) ) ) {
333 if ( copyIsArray ) {
334 copyIsArray = false;
335 clone = src && isArray(src) ? src : [];
336
337 } else {
338 clone = src && isObject(src) ? src : {};
339 }
340
341 // Never move original objects, clone them
342 target[ name ] = extend( deep, clone, copy );
343
344 // Don't bring in undefined values
345 } else if ( copy !== undefined ) {
346 target[ name ] = copy;
347 }
348 }
349 }
350 }
351
352 // Return the modified object
353 return target;
354 };
355
356 isArray = Array.isArray || function( obj ) {
357 return Object.prototype.toString.call( obj ) === "[object Array]";
358 };
359
360 isFunction = function( obj ) {
361 return Object.prototype.toString.call( obj ) === "[object Function]";
362 };
363
364 isObject = function( obj ) {
365 return Object.prototype.toString.call( obj ) === "[object Object]";
366 };
367
368 startsWith = function( value, pattern ) {
369 return value.indexOf( pattern ) === 0;
370 };
371
372 trim = function( value ) {
373 return ( value + "" ).replace( regexTrim, "" );
374 };
375
376 truncate = function( value ) {
377 if ( isNaN( value ) ) {
378 return NaN;
379 }
380 return Math[ value < 0 ? "ceil" : "floor" ]( value );
381 };
382
383 zeroPad = function( str, count, left ) {
384 var l;
385 for ( l = str.length; l < count; l += 1 ) {
386 str = ( left ? ("0" + str) : (str + "0") );
387 }
388 return str;
389 };
390
391 //
392 // private Globalization utility functions
393 //
394
395 appendPreOrPostMatch = function( preMatch, strings ) {
396 // appends pre- and post- token match strings while removing escaped characters.
397 // Returns a single quote count which is used to determine if the token occurs
398 // in a string literal.
399 var quoteCount = 0,
400 escaped = false;
401 for ( var i = 0, il = preMatch.length; i < il; i++ ) {
402 var c = preMatch.charAt( i );
403 switch ( c ) {
404 case "\'":
405 if ( escaped ) {
406 strings.push( "\'" );
407 }
408 else {
409 quoteCount++;
410 }
411 escaped = false;
412 break;
413 case "\\":
414 if ( escaped ) {
415 strings.push( "\\" );
416 }
417 escaped = !escaped;
418 break;
419 default:
420 strings.push( c );
421 escaped = false;
422 break;
423 }
424 }
425 return quoteCount;
426 };
427
428 expandFormat = function( cal, format ) {
429 // expands unspecified or single character date formats into the full pattern.
430 format = format || "F";
431 var pattern,
432 patterns = cal.patterns,
433 len = format.length;
434 if ( len === 1 ) {
435 pattern = patterns[ format ];
436 if ( !pattern ) {
437 throw "Invalid date format string \'" + format + "\'.";
438 }
439 format = pattern;
440 }
441 else if ( len === 2 && format.charAt(0) === "%" ) {
442 // %X escape format -- intended as a custom format string that is only one character, not a built-in format.
443 format = format.charAt( 1 );
444 }
445 return format;
446 };
447
448 formatDate = function( value, format, culture ) {
449 var cal = culture.calendar,
450 convert = cal.convert,
451 ret;
452
453 if ( !format || !format.length || format === "i" ) {
454 if ( culture && culture.name.length ) {
455 if ( convert ) {
456 // non-gregorian calendar, so we cannot use built-in toLocaleString()
457 ret = formatDate( value, cal.patterns.F, culture );
458 }
459 else {
460 var eraDate = new Date( value.getTime() ),
461 era = getEra( value, cal.eras );
462 eraDate.setFullYear( getEraYear(value, cal, era) );
463 ret = eraDate.toLocaleString();
464 }
465 }
466 else {
467 ret = value.toString();
468 }
469 return ret;
470 }
471
472 var eras = cal.eras,
473 sortable = format === "s";
474 format = expandFormat( cal, format );
475
476 // Start with an empty string
477 ret = [];
478 var hour,
479 zeros = [ "0", "00", "000" ],
480 foundDay,
481 checkedDay,
482 dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g,
483 quoteCount = 0,
484 tokenRegExp = getTokenRegExp(),
485 converted;
486
487 function padZeros( num, c ) {
488 var r, s = num + "";
489 if ( c > 1 && s.length < c ) {
490 r = ( zeros[c - 2] + s);
491 return r.substr( r.length - c, c );
492 }
493 else {
494 r = s;
495 }
496 return r;
497 }
498
499 function hasDay() {
500 if ( foundDay || checkedDay ) {
501 return foundDay;
502 }
503 foundDay = dayPartRegExp.test( format );
504 checkedDay = true;
505 return foundDay;
506 }
507
508 function getPart( date, part ) {
509 if ( converted ) {
510 return converted[ part ];
511 }
512 switch ( part ) {
513 case 0:
514 return date.getFullYear();
515 case 1:
516 return date.getMonth();
517 case 2:
518 return date.getDate();
519 default:
520 throw "Invalid part value " + part;
521 }
522 }
523
524 if ( !sortable && convert ) {
525 converted = convert.fromGregorian( value );
526 }
527
528 for ( ; ; ) {
529 // Save the current index
530 var index = tokenRegExp.lastIndex,
531 // Look for the next pattern
532 ar = tokenRegExp.exec( format );
533
534 // Append the text before the pattern (or the end of the string if not found)
535 var preMatch = format.slice( index, ar ? ar.index : format.length );
536 quoteCount += appendPreOrPostMatch( preMatch, ret );
537
538 if ( !ar ) {
539 break;
540 }
541
542 // do not replace any matches that occur inside a string literal.
543 if ( quoteCount % 2 ) {
544 ret.push( ar[0] );
545 continue;
546 }
547
548 var current = ar[ 0 ],
549 clength = current.length;
550
551 switch ( current ) {
552 case "ddd":
553 //Day of the week, as a three-letter abbreviation
554 case "dddd":
555 // Day of the week, using the full name
556 var names = ( clength === 3 ) ? cal.days.namesAbbr : cal.days.names;
557 ret.push( names[value.getDay()] );
558 break;
559 case "d":
560 // Day of month, without leading zero for single-digit days
561 case "dd":
562 // Day of month, with leading zero for single-digit days
563 foundDay = true;
564 ret.push(
565 padZeros( getPart(value, 2), clength )
566 );
567 break;
568 case "MMM":
569 // Month, as a three-letter abbreviation
570 case "MMMM":
571 // Month, using the full name
572 var part = getPart( value, 1 );
573 ret.push(
574 ( cal.monthsGenitive && hasDay() ) ?
575 ( cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] ) :
576 ( cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] )
577 );
578 break;
579 case "M":
580 // Month, as digits, with no leading zero for single-digit months
581 case "MM":
582 // Month, as digits, with leading zero for single-digit months
583 ret.push(
584 padZeros( getPart(value, 1) + 1, clength )
585 );
586 break;
587 case "y":
588 // Year, as two digits, but with no leading zero for years less than 10
589 case "yy":
590 // Year, as two digits, with leading zero for years less than 10
591 case "yyyy":
592 // Year represented by four full digits
593 part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra(value, eras), sortable );
594 if ( clength < 4 ) {
595 part = part % 100;
596 }
597 ret.push(
598 padZeros( part, clength )
599 );
600 break;
601 case "h":
602 // Hours with no leading zero for single-digit hours, using 12-hour clock
603 case "hh":
604 // Hours with leading zero for single-digit hours, using 12-hour clock
605 hour = value.getHours() % 12;
606 if ( hour === 0 ) hour = 12;
607 ret.push(
608 padZeros( hour, clength )
609 );
610 break;
611 case "H":
612 // Hours with no leading zero for single-digit hours, using 24-hour clock
613 case "HH":
614 // Hours with leading zero for single-digit hours, using 24-hour clock
615 ret.push(
616 padZeros( value.getHours(), clength )
617 );
618 break;
619 case "m":
620 // Minutes with no leading zero for single-digit minutes
621 case "mm":
622 // Minutes with leading zero for single-digit minutes
623 ret.push(
624 padZeros( value.getMinutes(), clength )
625 );
626 break;
627 case "s":
628 // Seconds with no leading zero for single-digit seconds
629 case "ss":
630 // Seconds with leading zero for single-digit seconds
631 ret.push(
632 padZeros( value.getSeconds(), clength )
633 );
634 break;
635 case "t":
636 // One character am/pm indicator ("a" or "p")
637 case "tt":
638 // Multicharacter am/pm indicator
639 part = value.getHours() < 12 ? ( cal.AM ? cal.AM[0] : " " ) : ( cal.PM ? cal.PM[0] : " " );
640 ret.push( clength === 1 ? part.charAt(0) : part );
641 break;
642 case "f":
643 // Deciseconds
644 case "ff":
645 // Centiseconds
646 case "fff":
647 // Milliseconds
648 ret.push(
649 padZeros( value.getMilliseconds(), 3 ).substr( 0, clength )
650 );
651 break;
652 case "z":
653 // Time zone offset, no leading zero
654 case "zz":
655 // Time zone offset with leading zero
656 hour = value.getTimezoneOffset() / 60;
657 ret.push(
658 ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), clength )
659 );
660 break;
661 case "zzz":
662 // Time zone offset with leading zero
663 hour = value.getTimezoneOffset() / 60;
664 ret.push(
665 ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), 2 ) +
666 // Hard coded ":" separator, rather than using cal.TimeSeparator
667 // Repeated here for consistency, plus ":" was already assumed in date parsing.
668 ":" + padZeros( Math.abs(value.getTimezoneOffset() % 60), 2 )
669 );
670 break;
671 case "g":
672 case "gg":
673 if ( cal.eras ) {
674 ret.push(
675 cal.eras[ getEra(value, eras) ].name
676 );
677 }
678 break;
679 case "/":
680 ret.push( cal["/"] );
681 break;
682 default:
683 throw "Invalid date format pattern \'" + current + "\'.";
684 }
685 }
686 return ret.join( "" );
687 };
688
689 // formatNumber
690 (function() {
691 var expandNumber;
692
693 expandNumber = function( number, precision, formatInfo ) {
694 var groupSizes = formatInfo.groupSizes,
695 curSize = groupSizes[ 0 ],
696 curGroupIndex = 1,
697 factor = Math.pow( 10, precision ),
698 rounded = Math.round( number * factor ) / factor;
699
700 if ( !isFinite(rounded) ) {
701 rounded = number;
702 }
703 number = rounded;
704
705 var numberString = number+"",
706 right = "",
707 split = numberString.split( /e/i ),
708 exponent = split.length > 1 ? parseInt( split[1], 10 ) : 0;
709 numberString = split[ 0 ];
710 split = numberString.split( "." );
711 numberString = split[ 0 ];
712 right = split.length > 1 ? split[ 1 ] : "";
713
714 var l;
715 if ( exponent > 0 ) {
716 right = zeroPad( right, exponent, false );
717 numberString += right.slice( 0, exponent );
718 right = right.substr( exponent );
719 }
720 else if ( exponent < 0 ) {
721 exponent = -exponent;
722 numberString = zeroPad( numberString, exponent + 1, true );
723 right = numberString.slice( -exponent, numberString.length ) + right;
724 numberString = numberString.slice( 0, -exponent );
725 }
726
727 if ( precision > 0 ) {
728 right = formatInfo[ "." ] +
729 ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) );
730 }
731 else {
732 right = "";
733 }
734
735 var stringIndex = numberString.length - 1,
736 sep = formatInfo[ "," ],
737 ret = "";
738
739 while ( stringIndex >= 0 ) {
740 if ( curSize === 0 || curSize > stringIndex ) {
741 return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? (sep + ret + right) : right );
742 }
743 ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? (sep + ret) : "" );
744
745 stringIndex -= curSize;
746
747 if ( curGroupIndex < groupSizes.length ) {
748 curSize = groupSizes[ curGroupIndex ];
749 curGroupIndex++;
750 }
751 }
752
753 return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right;
754 };
755
756 formatNumber = function( value, format, culture ) {
757 if ( !isFinite(value) ) {
758 if ( value === Infinity ) {
759 return culture.numberFormat.positiveInfinity;
760 }
761 if ( value === -Infinity ) {
762 return culture.numberFormat.negativeInfinity;
763 }
764 return culture.numberFormat.NaN;
765 }
766 if ( !format || format === "i" ) {
767 return culture.name.length ? value.toLocaleString() : value.toString();
768 }
769 format = format || "D";
770
771 var nf = culture.numberFormat,
772 number = Math.abs( value ),
773 precision = -1,
774 pattern;
775 if ( format.length > 1 ) precision = parseInt( format.slice(1), 10 );
776
777 var current = format.charAt( 0 ).toUpperCase(),
778 formatInfo;
779
780 switch ( current ) {
781 case "D":
782 pattern = "n";
783 number = truncate( number );
784 if ( precision !== -1 ) {
785 number = zeroPad( "" + number, precision, true );
786 }
787 if ( value < 0 ) number = "-" + number;
788 break;
789 case "N":
790 formatInfo = nf;
791 /* falls through */
792 case "C":
793 formatInfo = formatInfo || nf.currency;
794 /* falls through */
795 case "P":
796 formatInfo = formatInfo || nf.percent;
797 pattern = value < 0 ? formatInfo.pattern[ 0 ] : ( formatInfo.pattern[1] || "n" );
798 if ( precision === -1 ) precision = formatInfo.decimals;
799 number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo );
800 break;
801 default:
802 throw "Bad number format specifier: " + current;
803 }
804
805 var patternParts = /n|\$|-|%/g,
806 ret = "";
807 for ( ; ; ) {
808 var index = patternParts.lastIndex,
809 ar = patternParts.exec( pattern );
810
811 ret += pattern.slice( index, ar ? ar.index : pattern.length );
812
813 if ( !ar ) {
814 break;
815 }
816
817 switch ( ar[0] ) {
818 case "n":
819 ret += number;
820 break;
821 case "$":
822 ret += nf.currency.symbol;
823 break;
824 case "-":
825 // don't make 0 negative
826 if ( /[1-9]/.test(number) ) {
827 ret += nf[ "-" ];
828 }
829 break;
830 case "%":
831 ret += nf.percent.symbol;
832 break;
833 }
834 }
835
836 return ret;
837 };
838
839 }());
840
841 getTokenRegExp = function() {
842 // regular expression for matching date and time tokens in format strings.
843 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);
844 };
845
846 getEra = function( date, eras ) {
847 if ( !eras ) return 0;
848 var start, ticks = date.getTime();
849 for ( var i = 0, l = eras.length; i < l; i++ ) {
850 start = eras[ i ].start;
851 if ( start === null || ticks >= start ) {
852 return i;
853 }
854 }
855 return 0;
856 };
857
858 getEraYear = function( date, cal, era, sortable ) {
859 var year = date.getFullYear();
860 if ( !sortable && cal.eras ) {
861 // convert normal gregorian year to era-shifted gregorian
862 // year by subtracting the era offset
863 year -= cal.eras[ era ].offset;
864 }
865 return year;
866 };
867
868 // parseExact
869 (function() {
870 var expandYear,
871 getDayIndex,
872 getMonthIndex,
873 getParseRegExp,
874 outOfRange,
875 toUpper,
876 toUpperArray;
877
878 expandYear = function( cal, year ) {
879 // expands 2-digit year into 4 digits.
880 if ( year < 100 ) {
881 var now = new Date(),
882 era = getEra( now ),
883 curr = getEraYear( now, cal, era ),
884 twoDigitYearMax = cal.twoDigitYearMax;
885 twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax;
886 year += curr - ( curr % 100 );
887 if ( year > twoDigitYearMax ) {
888 year -= 100;
889 }
890 }
891 return year;
892 };
893
894 getDayIndex = function ( cal, value, abbr ) {
895 var ret,
896 days = cal.days,
897 upperDays = cal._upperDays;
898 if ( !upperDays ) {
899 cal._upperDays = upperDays = [
900 toUpperArray( days.names ),
901 toUpperArray( days.namesAbbr ),
902 toUpperArray( days.namesShort )
903 ];
904 }
905 value = toUpper( value );
906 if ( abbr ) {
907 ret = arrayIndexOf( upperDays[1], value );
908 if ( ret === -1 ) {
909 ret = arrayIndexOf( upperDays[2], value );
910 }
911 }
912 else {
913 ret = arrayIndexOf( upperDays[0], value );
914 }
915 return ret;
916 };
917
918 getMonthIndex = function( cal, value, abbr ) {
919 var months = cal.months,
920 monthsGen = cal.monthsGenitive || cal.months,
921 upperMonths = cal._upperMonths,
922 upperMonthsGen = cal._upperMonthsGen;
923 if ( !upperMonths ) {
924 cal._upperMonths = upperMonths = [
925 toUpperArray( months.names ),
926 toUpperArray( months.namesAbbr )
927 ];
928 cal._upperMonthsGen = upperMonthsGen = [
929 toUpperArray( monthsGen.names ),
930 toUpperArray( monthsGen.namesAbbr )
931 ];
932 }
933 value = toUpper( value );
934 var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value );
935 if ( i < 0 ) {
936 i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value );
937 }
938 return i;
939 };
940
941 getParseRegExp = function( cal, format ) {
942 // converts a format string into a regular expression with groups that
943 // can be used to extract date fields from a date string.
944 // check for a cached parse regex.
945 var re = cal._parseRegExp;
946 if ( !re ) {
947 cal._parseRegExp = re = {};
948 }
949 else {
950 var reFormat = re[ format ];
951 if ( reFormat ) {
952 return reFormat;
953 }
954 }
955
956 // expand single digit formats, then escape regular expression characters.
957 var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ),
958 regexp = [ "^" ],
959 groups = [],
960 index = 0,
961 quoteCount = 0,
962 tokenRegExp = getTokenRegExp(),
963 match;
964
965 // iterate through each date token found.
966 while ( (match = tokenRegExp.exec(expFormat)) !== null ) {
967 var preMatch = expFormat.slice( index, match.index );
968 index = tokenRegExp.lastIndex;
969
970 // don't replace any matches that occur inside a string literal.
971 quoteCount += appendPreOrPostMatch( preMatch, regexp );
972 if ( quoteCount % 2 ) {
973 regexp.push( match[0] );
974 continue;
975 }
976
977 // add a regex group for the token.
978 var m = match[ 0 ],
979 len = m.length,
980 add;
981 switch ( m ) {
982 case "dddd": case "ddd":
983 case "MMMM": case "MMM":
984 case "gg": case "g":
985 add = "(\\D+)";
986 break;
987 case "tt": case "t":
988 add = "(\\D*)";
989 break;
990 case "yyyy":
991 case "fff":
992 case "ff":
993 case "f":
994 add = "(\\d{" + len + "})";
995 break;
996 case "dd": case "d":
997 case "MM": case "M":
998 case "yy": case "y":
999 case "HH": case "H":
1000 case "hh": case "h":
1001 case "mm": case "m":
1002 case "ss": case "s":
1003 add = "(\\d\\d?)";
1004 break;
1005 case "zzz":
1006 add = "([+-]?\\d\\d?:\\d{2})";
1007 break;
1008 case "zz": case "z":
1009 add = "([+-]?\\d\\d?)";
1010 break;
1011 case "/":
1012 add = "(\\/)";
1013 break;
1014 default:
1015 throw "Invalid date format pattern \'" + m + "\'.";
1016 }
1017 if ( add ) {
1018 regexp.push( add );
1019 }
1020 groups.push( match[0] );
1021 }
1022 appendPreOrPostMatch( expFormat.slice(index), regexp );
1023 regexp.push( "$" );
1024
1025 // allow whitespace to differ when matching formats.
1026 var regexpStr = regexp.join( "" ).replace( /\s+/g, "\\s+" ),
1027 parseRegExp = { "regExp": regexpStr, "groups": groups };
1028
1029 // cache the regex for this format.
1030 return re[ format ] = parseRegExp;
1031 };
1032
1033 outOfRange = function( value, low, high ) {
1034 return value < low || value > high;
1035 };
1036
1037 toUpper = function( value ) {
1038 // "he-IL" has non-breaking space in weekday names.
1039 return value.split( "\u00A0" ).join( " " ).toUpperCase();
1040 };
1041
1042 toUpperArray = function( arr ) {
1043 var results = [];
1044 for ( var i = 0, l = arr.length; i < l; i++ ) {
1045 results[ i ] = toUpper( arr[i] );
1046 }
1047 return results;
1048 };
1049
1050 parseExact = function( value, format, culture ) {
1051 // try to parse the date string by matching against the format string
1052 // while using the specified culture for date field names.
1053 value = trim( value );
1054 var cal = culture.calendar,
1055 // convert date formats into regular expressions with groupings.
1056 // use the regexp to determine the input format and extract the date fields.
1057 parseInfo = getParseRegExp( cal, format ),
1058 match = new RegExp( parseInfo.regExp ).exec( value );
1059 if ( match === null ) {
1060 return null;
1061 }
1062 // found a date format that matches the input.
1063 var groups = parseInfo.groups,
1064 era = null, year = null, month = null, date = null, weekDay = null,
1065 hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null,
1066 pmHour = false;
1067 // iterate the format groups to extract and set the date fields.
1068 for ( var j = 0, jl = groups.length; j < jl; j++ ) {
1069 var matchGroup = match[ j + 1 ];
1070 if ( matchGroup ) {
1071 var current = groups[ j ],
1072 clength = current.length,
1073 matchInt = parseInt( matchGroup, 10 );
1074 switch ( current ) {
1075 case "dd": case "d":
1076 // Day of month.
1077 date = matchInt;
1078 // check that date is generally in valid range, also checking overflow below.
1079 if ( outOfRange(date, 1, 31) ) return null;
1080 break;
1081 case "MMM": case "MMMM":
1082 month = getMonthIndex( cal, matchGroup, clength === 3 );
1083 if ( outOfRange(month, 0, 11) ) return null;
1084 break;
1085 case "M": case "MM":
1086 // Month.
1087 month = matchInt - 1;
1088 if ( outOfRange(month, 0, 11) ) return null;
1089 break;
1090 case "y": case "yy":
1091 case "yyyy":
1092 year = clength < 4 ? expandYear( cal, matchInt ) : matchInt;
1093 if ( outOfRange(year, 0, 9999) ) return null;
1094 break;
1095 case "h": case "hh":
1096 // Hours (12-hour clock).
1097 hour = matchInt;
1098 if ( hour === 12 ) hour = 0;
1099 if ( outOfRange(hour, 0, 11) ) return null;
1100 break;
1101 case "H": case "HH":
1102 // Hours (24-hour clock).
1103 hour = matchInt;
1104 if ( outOfRange(hour, 0, 23) ) return null;
1105 break;
1106 case "m": case "mm":
1107 // Minutes.
1108 min = matchInt;
1109 if ( outOfRange(min, 0, 59) ) return null;
1110 break;
1111 case "s": case "ss":
1112 // Seconds.
1113 sec = matchInt;
1114 if ( outOfRange(sec, 0, 59) ) return null;
1115 break;
1116 case "tt": case "t":
1117 // AM/PM designator.
1118 // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of
1119 // the AM tokens. If not, fail the parse for this format.
1120 pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] );
1121 if (
1122 !pmHour && (
1123 !cal.AM || ( matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2] )
1124 )
1125 ) return null;
1126 break;
1127 case "f":
1128 // Deciseconds.
1129 case "ff":
1130 // Centiseconds.
1131 case "fff":
1132 // Milliseconds.
1133 msec = matchInt * Math.pow( 10, 3 - clength );
1134 if ( outOfRange(msec, 0, 999) ) return null;
1135 break;
1136 case "ddd":
1137 // Day of week.
1138 case "dddd":
1139 // Day of week.
1140 weekDay = getDayIndex( cal, matchGroup, clength === 3 );
1141 if ( outOfRange(weekDay, 0, 6) ) return null;
1142 break;
1143 case "zzz":
1144 // Time zone offset in +/- hours:min.
1145 var offsets = matchGroup.split( /:/ );
1146 if ( offsets.length !== 2 ) return null;
1147 hourOffset = parseInt( offsets[0], 10 );
1148 if ( outOfRange(hourOffset, -12, 13) ) return null;
1149 var minOffset = parseInt( offsets[1], 10 );
1150 if ( outOfRange(minOffset, 0, 59) ) return null;
1151 tzMinOffset = ( hourOffset * 60 ) + ( startsWith(matchGroup, "-") ? -minOffset : minOffset );
1152 break;
1153 case "z": case "zz":
1154 // Time zone offset in +/- hours.
1155 hourOffset = matchInt;
1156 if ( outOfRange(hourOffset, -12, 13) ) return null;
1157 tzMinOffset = hourOffset * 60;
1158 break;
1159 case "g": case "gg":
1160 var eraName = matchGroup;
1161 if ( !eraName || !cal.eras ) return null;
1162 eraName = trim( eraName.toLowerCase() );
1163 for ( var i = 0, l = cal.eras.length; i < l; i++ ) {
1164 if ( eraName === cal.eras[i].name.toLowerCase() ) {
1165 era = i;
1166 break;
1167 }
1168 }
1169 // could not find an era with that name
1170 if ( era === null ) return null;
1171 break;
1172 }
1173 }
1174 }
1175 var result = new Date(), defaultYear, convert = cal.convert;
1176 defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear();
1177 if ( year === null ) {
1178 year = defaultYear;
1179 }
1180 else if ( cal.eras ) {
1181 // year must be shifted to normal gregorian year
1182 // but not if year was not specified, its already normal gregorian
1183 // per the main if clause above.
1184 year += cal.eras[( era || 0 )].offset;
1185 }
1186 // set default day and month to 1 and January, so if unspecified, these are the defaults
1187 // instead of the current day/month.
1188 if ( month === null ) {
1189 month = 0;
1190 }
1191 if ( date === null ) {
1192 date = 1;
1193 }
1194 // now have year, month, and date, but in the culture's calendar.
1195 // convert to gregorian if necessary
1196 if ( convert ) {
1197 result = convert.toGregorian( year, month, date );
1198 // conversion failed, must be an invalid match
1199 if ( result === null ) return null;
1200 }
1201 else {
1202 // have to set year, month and date together to avoid overflow based on current date.
1203 result.setFullYear( year, month, date );
1204 // check to see if date overflowed for specified month (only checked 1-31 above).
1205 if ( result.getDate() !== date ) return null;
1206 // invalid day of week.
1207 if ( weekDay !== null && result.getDay() !== weekDay ) {
1208 return null;
1209 }
1210 }
1211 // if pm designator token was found make sure the hours fit the 24-hour clock.
1212 if ( pmHour && hour < 12 ) {
1213 hour += 12;
1214 }
1215 result.setHours( hour, min, sec, msec );
1216 if ( tzMinOffset !== null ) {
1217 // adjust timezone to utc before applying local offset.
1218 var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() );
1219 // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours
1220 // to ensure both these fields will not exceed this range. adjustedMin will range
1221 // somewhere between -1440 and 1500, so we only need to split this into hours.
1222 result.setHours( result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60 );
1223 }
1224 return result;
1225 };
1226 }());
1227
1228 parseNegativePattern = function( value, nf, negativePattern ) {
1229 var neg = nf[ "-" ],
1230 pos = nf[ "+" ],
1231 ret;
1232 switch ( negativePattern ) {
1233 case "n -":
1234 neg = " " + neg;
1235 pos = " " + pos;
1236 /* falls through */
1237 case "n-":
1238 if ( endsWith(value, neg) ) {
1239 ret = [ "-", value.substr(0, value.length - neg.length) ];
1240 }
1241 else if ( endsWith(value, pos) ) {
1242 ret = [ "+", value.substr(0, value.length - pos.length) ];
1243 }
1244 break;
1245 case "- n":
1246 neg += " ";
1247 pos += " ";
1248 /* falls through */
1249 case "-n":
1250 if ( startsWith(value, neg) ) {
1251 ret = [ "-", value.substr(neg.length) ];
1252 }
1253 else if ( startsWith(value, pos) ) {
1254 ret = [ "+", value.substr(pos.length) ];
1255 }
1256 break;
1257 case "(n)":
1258 if ( startsWith(value, "(") && endsWith(value, ")") ) {
1259 ret = [ "-", value.substr(1, value.length - 2) ];
1260 }
1261 break;
1262 }
1263 return ret || [ "", value ];
1264 };
1265
1266 //
1267 // public instance functions
1268 //
1269
1270 Globalize.prototype.findClosestCulture = function( cultureSelector ) {
1271 return Globalize.findClosestCulture.call( this, cultureSelector );
1272 };
1273
1274 Globalize.prototype.format = function( value, format, cultureSelector ) {
1275 return Globalize.format.call( this, value, format, cultureSelector );
1276 };
1277
1278 Globalize.prototype.localize = function( key, cultureSelector ) {
1279 return Globalize.localize.call( this, key, cultureSelector );
1280 };
1281
1282 Globalize.prototype.parseInt = function( value, radix, cultureSelector ) {
1283 return Globalize.parseInt.call( this, value, radix, cultureSelector );
1284 };
1285
1286 Globalize.prototype.parseFloat = function( value, radix, cultureSelector ) {
1287 return Globalize.parseFloat.call( this, value, radix, cultureSelector );
1288 };
1289
1290 Globalize.prototype.culture = function( cultureSelector ) {
1291 return Globalize.culture.call( this, cultureSelector );
1292 };
1293
1294 //
1295 // public singleton functions
1296 //
1297
1298 Globalize.addCultureInfo = function( cultureName, baseCultureName, info ) {
1299
1300 var base = {},
1301 isNew = false;
1302
1303 if ( typeof cultureName !== "string" ) {
1304 // cultureName argument is optional string. If not specified, assume info is first
1305 // and only argument. Specified info deep-extends current culture.
1306 info = cultureName;
1307 cultureName = this.culture().name;
1308 base = this.cultures[ cultureName ];
1309 } else if ( typeof baseCultureName !== "string" ) {
1310 // baseCultureName argument is optional string. If not specified, assume info is second
1311 // argument. Specified info deep-extends specified culture.
1312 // If specified culture does not exist, create by deep-extending default
1313 info = baseCultureName;
1314 isNew = ( this.cultures[ cultureName ] == null );
1315 base = this.cultures[ cultureName ] || this.cultures[ "default" ];
1316 } else {
1317 // cultureName and baseCultureName specified. Assume a new culture is being created
1318 // by deep-extending an specified base culture
1319 isNew = true;
1320 base = this.cultures[ baseCultureName ];
1321 }
1322
1323 this.cultures[ cultureName ] = extend(true, {},
1324 base,
1325 info
1326 );
1327 // Make the standard calendar the current culture if it's a new culture
1328 if ( isNew ) {
1329 this.cultures[ cultureName ].calendar = this.cultures[ cultureName ].calendars.standard;
1330 }
1331 };
1332
1333 Globalize.findClosestCulture = function( name ) {
1334 var match;
1335 if ( !name ) {
1336 return this.findClosestCulture( this.cultureSelector ) || this.cultures[ "default" ];
1337 }
1338 if ( typeof name === "string" ) {
1339 name = name.split( "," );
1340 }
1341 if ( isArray(name) ) {
1342 var lang,
1343 cultures = this.cultures,
1344 list = name,
1345 i, l = list.length,
1346 prioritized = [];
1347 for ( i = 0; i < l; i++ ) {
1348 name = trim( list[i] );
1349 var pri, parts = name.split( ";" );
1350 lang = trim( parts[0] );
1351 if ( parts.length === 1 ) {
1352 pri = 1;
1353 }
1354 else {
1355 name = trim( parts[1] );
1356 if ( name.indexOf("q=") === 0 ) {
1357 name = name.substr( 2 );
1358 pri = parseFloat( name );
1359 pri = isNaN( pri ) ? 0 : pri;
1360 }
1361 else {
1362 pri = 1;
1363 }
1364 }
1365 prioritized.push({ lang: lang, pri: pri });
1366 }
1367 prioritized.sort(function( a, b ) {
1368 if ( a.pri < b.pri ) {
1369 return 1;
1370 } else if ( a.pri > b.pri ) {
1371 return -1;
1372 }
1373 return 0;
1374 });
1375 // exact match
1376 for ( i = 0; i < l; i++ ) {
1377 lang = prioritized[ i ].lang;
1378 match = cultures[ lang ];
1379 if ( match ) {
1380 return match;
1381 }
1382 }
1383
1384 // neutral language match
1385 for ( i = 0; i < l; i++ ) {
1386 lang = prioritized[ i ].lang;
1387 do {
1388 var index = lang.lastIndexOf( "-" );
1389 if ( index === -1 ) {
1390 break;
1391 }
1392 // strip off the last part. e.g. en-US => en
1393 lang = lang.substr( 0, index );
1394 match = cultures[ lang ];
1395 if ( match ) {
1396 return match;
1397 }
1398 }
1399 while ( 1 );
1400 }
1401
1402 // last resort: match first culture using that language
1403 for ( i = 0; i < l; i++ ) {
1404 lang = prioritized[ i ].lang;
1405 for ( var cultureKey in cultures ) {
1406 var culture = cultures[ cultureKey ];
1407 if ( culture.language == lang ) {
1408 return culture;
1409 }
1410 }
1411 }
1412 }
1413 else if ( typeof name === "object" ) {
1414 return name;
1415 }
1416 return match || null;
1417 };
1418
1419 Globalize.format = function( value, format, cultureSelector ) {
1420 var culture = this.findClosestCulture( cultureSelector );
1421 if ( value instanceof Date ) {
1422 value = formatDate( value, format, culture );
1423 }
1424 else if ( typeof value === "number" ) {
1425 value = formatNumber( value, format, culture );
1426 }
1427 return value;
1428 };
1429
1430 Globalize.localize = function( key, cultureSelector ) {
1431 return this.findClosestCulture( cultureSelector ).messages[ key ] ||
1432 this.cultures[ "default" ].messages[ key ];
1433 };
1434
1435 Globalize.parseDate = function( value, formats, culture ) {
1436 culture = this.findClosestCulture( culture );
1437
1438 var date, prop, patterns;
1439 if ( formats ) {
1440 if ( typeof formats === "string" ) {
1441 formats = [ formats ];
1442 }
1443 if ( formats.length ) {
1444 for ( var i = 0, l = formats.length; i < l; i++ ) {
1445 var format = formats[ i ];
1446 if ( format ) {
1447 date = parseExact( value, format, culture );
1448 if ( date ) {
1449 break;
1450 }
1451 }
1452 }
1453 }
1454 } else {
1455 patterns = culture.calendar.patterns;
1456 for ( prop in patterns ) {
1457 date = parseExact( value, patterns[prop], culture );
1458 if ( date ) {
1459 break;
1460 }
1461 }
1462 }
1463
1464 return date || null;
1465 };
1466
1467 Globalize.parseInt = function( value, radix, cultureSelector ) {
1468 return truncate( Globalize.parseFloat(value, radix, cultureSelector) );
1469 };
1470
1471 Globalize.parseFloat = function( value, radix, cultureSelector ) {
1472 // radix argument is optional
1473 if ( typeof radix !== "number" ) {
1474 cultureSelector = radix;
1475 radix = 10;
1476 }
1477
1478 var culture = this.findClosestCulture( cultureSelector );
1479 var ret = NaN,
1480 nf = culture.numberFormat;
1481
1482 if ( value.indexOf(culture.numberFormat.currency.symbol) > -1 ) {
1483 // remove currency symbol
1484 value = value.replace( culture.numberFormat.currency.symbol, "" );
1485 // replace decimal seperator
1486 value = value.replace( culture.numberFormat.currency["."], culture.numberFormat["."] );
1487 }
1488
1489 //Remove percentage character from number string before parsing
1490 if ( value.indexOf(culture.numberFormat.percent.symbol) > -1){
1491 value = value.replace( culture.numberFormat.percent.symbol, "" );
1492 }
1493
1494 // remove spaces: leading, trailing and between - and number. Used for negative currency pt-BR
1495 value = value.replace( / /g, "" );
1496
1497 // allow infinity or hexidecimal
1498 if ( regexInfinity.test(value) ) {
1499 ret = parseFloat( value );
1500 }
1501 else if ( !radix && regexHex.test(value) ) {
1502 ret = parseInt( value, 16 );
1503 }
1504 else {
1505
1506 // determine sign and number
1507 var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ),
1508 sign = signInfo[ 0 ],
1509 num = signInfo[ 1 ];
1510
1511 // #44 - try parsing as "(n)"
1512 if ( sign === "" && nf.pattern[0] !== "(n)" ) {
1513 signInfo = parseNegativePattern( value, nf, "(n)" );
1514 sign = signInfo[ 0 ];
1515 num = signInfo[ 1 ];
1516 }
1517
1518 // try parsing as "-n"
1519 if ( sign === "" && nf.pattern[0] !== "-n" ) {
1520 signInfo = parseNegativePattern( value, nf, "-n" );
1521 sign = signInfo[ 0 ];
1522 num = signInfo[ 1 ];
1523 }
1524
1525 sign = sign || "+";
1526
1527 // determine exponent and number
1528 var exponent,
1529 intAndFraction,
1530 exponentPos = num.indexOf( "e" );
1531 if ( exponentPos < 0 ) exponentPos = num.indexOf( "E" );
1532 if ( exponentPos < 0 ) {
1533 intAndFraction = num;
1534 exponent = null;
1535 }
1536 else {
1537 intAndFraction = num.substr( 0, exponentPos );
1538 exponent = num.substr( exponentPos + 1 );
1539 }
1540 // determine decimal position
1541 var integer,
1542 fraction,
1543 decSep = nf[ "." ],
1544 decimalPos = intAndFraction.indexOf( decSep );
1545 if ( decimalPos < 0 ) {
1546 integer = intAndFraction;
1547 fraction = null;
1548 }
1549 else {
1550 integer = intAndFraction.substr( 0, decimalPos );
1551 fraction = intAndFraction.substr( decimalPos + decSep.length );
1552 }
1553 // handle groups (e.g. 1,000,000)
1554 var groupSep = nf[ "," ];
1555 integer = integer.split( groupSep ).join( "" );
1556 var altGroupSep = groupSep.replace( /\u00A0/g, " " );
1557 if ( groupSep !== altGroupSep ) {
1558 integer = integer.split( altGroupSep ).join( "" );
1559 }
1560 // build a natively parsable number string
1561 var p = sign + integer;
1562 if ( fraction !== null ) {
1563 p += "." + fraction;
1564 }
1565 if ( exponent !== null ) {
1566 // exponent itself may have a number patternd
1567 var expSignInfo = parseNegativePattern( exponent, nf, "-n" );
1568 p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ];
1569 }
1570 if ( regexParseFloat.test(p) ) {
1571 ret = parseFloat( p );
1572 }
1573 }
1574 return ret;
1575 };
1576
1577 Globalize.culture = function( cultureSelector ) {
1578 // setter
1579 if ( typeof cultureSelector !== "undefined" ) {
1580 this.cultureSelector = cultureSelector;
1581 }
1582 // getter
1583 return this.findClosestCulture( cultureSelector ) || this.cultures[ "default" ];
1584 };
1585
1586 }( this ));

mercurial