locale.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. define("dojox/date/hebrew/locale", ["dojo/main", "dojo/date", "dojo/i18n", "dojo/regexp", "dojo/string", "./Date", "./numerals", "dojo/i18n!dojo/cldr/nls/hebrew"],
  2. function(dojo, dd, i18n, regexp, string, hebrewDate, numerals){
  3. dojo.getObject("date.hebrew.locale", true, dojox);
  4. dojo.experimental("dojox.date.hebrew.locale");
  5. //Load the bundles containing localization information for
  6. // names and formats
  7. dojo.requireLocalization("dojo.cldr", "hebrew");
  8. // Format a pattern without literals
  9. function formatPattern(dateObject, bundle, locale, fullYear, pattern){
  10. return pattern.replace(/([a-z])\1*/ig, function(match){
  11. var s, pad;
  12. var c = match.charAt(0);
  13. var l = match.length;
  14. var widthList = ["abbr", "wide", "narrow"];
  15. switch(c){
  16. case 'y':
  17. if(locale.match(/^he(?:-.+)?$/)){
  18. s = numerals.getYearHebrewLetters(dateObject.getFullYear());
  19. }else{
  20. s = String(dateObject.getFullYear());
  21. }
  22. break;
  23. case 'M':
  24. var m = dateObject.getMonth();
  25. if(l<3){
  26. if(!dateObject.isLeapYear(dateObject.getFullYear()) && m>5){m--;}
  27. if(locale.match(/^he(?:-.+)?$/)){
  28. s = numerals.getMonthHebrewLetters(m);
  29. }else{
  30. s = m+1; pad = true;
  31. }
  32. }else{
  33. var monthNames = dojox.date.hebrew.locale.getNames('months',widthList[l-3], 'format', locale, dateObject);
  34. s = monthNames[m];
  35. }
  36. break;
  37. case 'd':
  38. if(locale.match(/^he(?:-.+)?$/)){
  39. s = dateObject.getDateLocalized(locale);
  40. }else{
  41. s = dateObject.getDate(); pad = true;
  42. }
  43. break;
  44. case 'E':
  45. var d = dateObject.getDay();
  46. if(l<3){
  47. s = d+1; pad = true;
  48. }else{
  49. var propD = ["days", "format", widthList[l-3]].join("-");
  50. s = bundle[propD][d];
  51. }
  52. break;
  53. case 'a':
  54. var timePeriod = (dateObject.getHours() < 12) ? 'am' : 'pm';
  55. s = bundle['dayPeriods-format-wide-' + timePeriod];
  56. break;
  57. case 'h':
  58. case 'H':
  59. case 'K':
  60. case 'k':
  61. var h = dateObject.getHours();
  62. // strange choices in the date format make it impossible to write this succinctly
  63. switch (c){
  64. case 'h': // 1-12
  65. s = (h % 12) || 12;
  66. break;
  67. case 'H': // 0-23
  68. s = h;
  69. break;
  70. case 'K': // 0-11
  71. s = (h % 12);
  72. break;
  73. case 'k': // 1-24
  74. s = h || 24;
  75. break;
  76. }
  77. pad = true;
  78. break;
  79. case 'm':
  80. s = dateObject.getMinutes(); pad = true;
  81. break;
  82. case 's':
  83. s = dateObject.getSeconds(); pad = true;
  84. break;
  85. case 'S':
  86. s = Math.round(dateObject.getMilliseconds() * Math.pow(10, l-3)); pad = true;
  87. break;
  88. case 'z':
  89. s = "";
  90. break;
  91. default:
  92. throw new Error("dojox.date.hebrew.locale.formatPattern: invalid pattern char: "+pattern);
  93. }
  94. if(pad){ s = string.pad(s, l); }
  95. return s;
  96. });
  97. }
  98. dojox.date.hebrew.locale.format = function(/*hebrew.Date*/dateObject, /*object?*/options){
  99. // based on and similar to dojo.date.locale.format
  100. //summary:
  101. // Format a Date object as a String, using settings.
  102. //
  103. // description:
  104. // Create a string from a hebrew.Date object using a known pattern.
  105. // By default, this method formats both date and time from dateObject.
  106. // Default formatting lengths is 'short'
  107. //
  108. // dateObject:
  109. // the date and/or time to be formatted. If a time only is formatted,
  110. // the values in the year, month, and day fields are irrelevant. The
  111. // opposite is true when formatting only dates.
  112. options = options || {};
  113. var locale = i18n.normalizeLocale(options.locale);
  114. var formatLength = options.formatLength || 'short';
  115. var bundle = dojox.date.hebrew.locale._getHebrewBundle(locale);
  116. var str = [];
  117. var sauce = dojo.hitch(this, formatPattern, dateObject, bundle, locale, options.fullYear);
  118. if(options.selector == "year"){
  119. var year = dateObject.getFullYear();
  120. return locale.match(/^he(?:-.+)?$/) ?
  121. numerals.getYearHebrewLetters(year) : year;
  122. }
  123. if(options.selector != "time"){
  124. var datePattern = options.datePattern || bundle["dateFormat-"+formatLength];
  125. if(datePattern){str.push(_processPattern(datePattern, sauce));}
  126. }
  127. if(options.selector != "date"){
  128. var timePattern = options.timePattern || bundle["timeFormat-"+formatLength];
  129. if(timePattern){str.push(_processPattern(timePattern, sauce));}
  130. }
  131. var result = str.join(" "); //TODO: use locale-specific pattern to assemble date + time
  132. return result; // String
  133. };
  134. dojox.date.hebrew.locale.regexp = function(/*object?*/options){
  135. // based on and similar to dojo.date.locale.regexp
  136. // summary:
  137. // Builds the regular needed to parse a hebrew.Date
  138. return dojox.date.hebrew.locale._parseInfo(options).regexp; // String
  139. };
  140. dojox.date.hebrew.locale._parseInfo = function(/*oblect?*/options){
  141. /* based on and similar to dojo.date.locale._parseInfo */
  142. options = options || {};
  143. var locale = i18n.normalizeLocale(options.locale);
  144. var bundle = dojox.date.hebrew.locale._getHebrewBundle(locale);
  145. var formatLength = options.formatLength || 'short';
  146. var datePattern = options.datePattern || bundle["dateFormat-" + formatLength];
  147. var timePattern = options.timePattern || bundle["timeFormat-" + formatLength];
  148. var pattern;
  149. if(options.selector == 'date'){
  150. pattern = datePattern;
  151. }else if(options.selector == 'time'){
  152. pattern = timePattern;
  153. }else{
  154. pattern = (timePattern === undefined) ? datePattern : datePattern + ' ' + timePattern; //hebrew resource file does not contain time patterns - a bug?
  155. }
  156. var tokens = [];
  157. var re = _processPattern(pattern, dojo.hitch(this, _buildDateTimeRE, tokens, bundle, options));
  158. return {regexp: re, tokens: tokens, bundle: bundle};
  159. };
  160. dojox.date.hebrew.locale.parse = function(/*String*/value, /*Object?*/options){
  161. // based on and similar to dojo.date.locale.parse
  162. // summary: This function parse string date value according to options
  163. // example:
  164. // | var dateHebrew = dojox.date.hebrew.locale.parse('11/10/5740', {datePattern:'dd/MM/yy', selector:'date'});
  165. // | in Hebrew locale string for parsing contains Hebrew Numerals
  166. // |
  167. // | options = {datePattern:'dd MMMM yy', selector:'date'};
  168. // |
  169. // | y - year
  170. // | M, MM - short month
  171. // | MMM, MMMM - long month
  172. // | d - date
  173. // | a - am, pm
  174. // | E, EE, EEE, EEEE - week day
  175. // |
  176. // | h, H, k, K, m, s, S, - time format
  177. value = value.replace(/[\u200E\u200F\u202A-\u202E]/g, ""); //remove special chars
  178. if(!options){options={};}
  179. var info = dojox.date.hebrew.locale._parseInfo(options);
  180. var tokens = info.tokens, bundle = info.bundle;
  181. var re = new RegExp("^" + info.regexp + "$");
  182. var match = re.exec(value);
  183. var locale = i18n.normalizeLocale(options.locale);
  184. if(!match){ return null; } // null
  185. var date, date1;
  186. //var result = [1970,0,1,0,0,0,0]; //
  187. var result = [5730,3,23,0,0,0,0]; // hebrew date for [1970,0,1,0,0,0,0] used in gregorian locale
  188. var amPm = "";
  189. var mLength = 0;
  190. var widthList = ["abbr", "wide", "narrow"];
  191. var valid = dojo.every(match, function(v, i){
  192. if(!i){return true;}
  193. var token=tokens[i-1];
  194. var l=token.length;
  195. switch(token.charAt(0)){
  196. case 'y':
  197. if(locale.match(/^he(?:-.+)?$/)){
  198. result[0] = numerals.parseYearHebrewLetters(v);
  199. }else{
  200. result[0] = Number(v);
  201. }
  202. break;
  203. case 'M':
  204. //if it is short format, month is one letter or two letter with "geresh"
  205. if(l>2){
  206. //we do not know here if the year is leap or not
  207. var months = dojox.date.hebrew.locale.getNames('months', widthList[l-3], 'format', locale, new hebrewDate(5769, 1, 1)),
  208. leapmonths = dojox.date.hebrew.locale.getNames('months', widthList[l-3], 'format', locale, new hebrewDate(5768, 1, 1));
  209. if(!options.strict){
  210. //Tolerate abbreviating period in month part
  211. //Case-insensitive comparison
  212. v = v.replace(".","").toLowerCase();
  213. months = dojo.map(months, function(s){ return s ? s.replace(".","").toLowerCase() : s; } );
  214. leapmonths = dojo.map(leapmonths, function(s){ return s ? s.replace(".","").toLowerCase() : s; } );
  215. }
  216. var monthName = v;
  217. v = dojo.indexOf(months, monthName);
  218. if(v == -1){
  219. v = dojo.indexOf(leapmonths, monthName);
  220. if(v == -1){
  221. //console.debug("dojox.date.hebrew.locale.parse: Could not parse month name: second " + v +"'.");
  222. return false;
  223. }
  224. }
  225. mLength = l;
  226. }else{
  227. if(locale.match(/^he(?:-.+)?$/)){
  228. v = numerals.parseMonthHebrewLetters(v);
  229. }else{
  230. v--;
  231. }
  232. }
  233. result[1] = Number(v);
  234. break;
  235. case 'D':
  236. result[1] = 0;
  237. // fallthrough...
  238. case 'd':
  239. if(locale.match(/^he(?:-.+)?$/)){
  240. result[2] = numerals.parseDayHebrewLetters(v);
  241. }else{
  242. result[2] = Number(v);
  243. }
  244. break;
  245. case 'a': //am/pm
  246. var am = options.am || bundle['dayPeriods-format-wide-am'],
  247. pm = options.pm || bundle['dayPeriods-format-wide-pm'];
  248. if(!options.strict){
  249. var period = /\./g;
  250. v = v.replace(period,'').toLowerCase();
  251. am = am.replace(period,'').toLowerCase();
  252. pm = pm.replace(period,'').toLowerCase();
  253. }
  254. if(options.strict && v != am && v != pm){
  255. return false;
  256. }
  257. // we might not have seen the hours field yet, so store the state and apply hour change later
  258. amPm = (v == pm) ? 'p' : (v == am) ? 'a' : '';
  259. break;
  260. case 'K': //hour (1-24)
  261. if(v == 24){ v = 0; }
  262. // fallthrough...
  263. case 'h': //hour (1-12)
  264. case 'H': //hour (0-23)
  265. case 'k': //hour (0-11)
  266. //in the 12-hour case, adjusting for am/pm requires the 'a' part
  267. //which could come before or after the hour, so we will adjust later
  268. result[3] = Number(v);
  269. break;
  270. case 'm': //minutes
  271. result[4] = Number(v);
  272. break;
  273. case 's': //seconds
  274. result[5] = Number(v);
  275. break;
  276. case 'S': //milliseconds
  277. result[6] = Number(v);
  278. }
  279. return true;
  280. });
  281. var hours = +result[3];
  282. if(amPm === 'p' && hours < 12){
  283. result[3] = hours + 12; //e.g., 3pm -> 15
  284. }else if(amPm === 'a' && hours == 12){
  285. result[3] = 0; //12am -> 0
  286. }
  287. var dateObject = new hebrewDate(result[0], result[1], result[2], result[3], result[4], result[5], result[6]); // hebrew.Date
  288. //for non leap year, the index of the short month start from adar should be increased by 1
  289. if(mLength < 3 && result[1] >= 5 && !dateObject.isLeapYear(dateObject.getFullYear())){
  290. dateObject.setMonth(result[1]+1);
  291. }
  292. return dateObject; // hebrew.Date
  293. };
  294. function _processPattern(pattern, applyPattern, applyLiteral, applyAll){
  295. //summary: Process a pattern with literals in it
  296. // Break up on single quotes, treat every other one as a literal, except '' which becomes '
  297. var identity = function(x){return x;};
  298. applyPattern = applyPattern || identity;
  299. applyLiteral = applyLiteral || identity;
  300. applyAll = applyAll || identity;
  301. //split on single quotes (which escape literals in date format strings)
  302. //but preserve escaped single quotes (e.g., o''clock)
  303. var chunks = pattern.match(/(''|[^'])+/g);
  304. var literal = pattern.charAt(0) == "'";
  305. dojo.forEach(chunks, function(chunk, i){
  306. if(!chunk){
  307. chunks[i]='';
  308. }else{
  309. chunks[i]=(literal ? applyLiteral : applyPattern)(chunk);
  310. literal = !literal;
  311. }
  312. });
  313. return applyAll(chunks.join(''));
  314. }
  315. function _buildDateTimeRE (tokens, bundle, options, pattern){
  316. // based on and similar to dojo.date.locale._buildDateTimeRE
  317. //
  318. pattern = regexp.escapeString(pattern);
  319. var locale = i18n.normalizeLocale(options.locale);
  320. return pattern.replace(/([a-z])\1*/ig, function(match){
  321. // Build a simple regexp. Avoid captures, which would ruin the tokens list
  322. var s;
  323. var c = match.charAt(0);
  324. var l = match.length;
  325. var p2 = '', p3 = '';
  326. if(options.strict){
  327. if(l > 1){ p2 = '0' + '{'+(l-1)+'}'; }
  328. if(l > 2){ p3 = '0' + '{'+(l-2)+'}'; }
  329. }else{
  330. p2 = '0?'; p3 = '0{0,2}';
  331. }
  332. switch(c){
  333. case 'y':
  334. s = '\\S+';
  335. break;
  336. case 'M':
  337. if(locale.match('^he(?:-.+)?$')){
  338. s = (l>2) ? '\\S+ ?\\S+' : '\\S{1,4}';
  339. }else{
  340. s = (l>2) ? '\\S+ ?\\S+' : p2+'[1-9]|1[0-2]';
  341. }
  342. break;
  343. case 'd':
  344. if(locale.match('^he(?:-.+)?$')){
  345. s = '\\S[\'\"\'\u05F3]{1,2}\\S?';
  346. }else{
  347. s = '[12]\\d|'+p2+'[1-9]|30';
  348. }
  349. break;
  350. case 'E':
  351. if(locale.match('^he(?:-.+)?$')){
  352. s = (l>3) ? '\\S+ ?\\S+' : '\\S';
  353. }else{
  354. s = '\\S+';
  355. }
  356. break;
  357. case 'h': //hour (1-12)
  358. s = p2+'[1-9]|1[0-2]';
  359. break;
  360. case 'k': //hour (0-11)
  361. s = p2+'\\d|1[01]';
  362. break;
  363. case 'H': //hour (0-23)
  364. s = p2+'\\d|1\\d|2[0-3]';
  365. break;
  366. case 'K': //hour (1-24)
  367. s = p2+'[1-9]|1\\d|2[0-4]';
  368. break;
  369. case 'm':
  370. case 's':
  371. s = p2+'\\d|[0-5]\\d';
  372. break;
  373. case 'S':
  374. s = '\\d{'+l+'}';
  375. break;
  376. case 'a':
  377. var am = options.am || bundle['dayPeriods-format-wide-am'],
  378. pm = options.pm || bundle['dayPeriods-format-wide-pm'];
  379. if(options.strict){
  380. s = am + '|' + pm;
  381. }else{
  382. s = am + '|' + pm;
  383. if(am != am.toLowerCase()){ s += '|' + am.toLowerCase(); }
  384. if(pm != pm.toLowerCase()){ s += '|' + pm.toLowerCase(); }
  385. }
  386. break;
  387. default:
  388. s = ".*";
  389. }
  390. if(tokens){ tokens.push(match); }
  391. return "(" + s + ")"; // add capture
  392. }).replace(/[\xa0 ]/g, "[\\s\\xa0]"); // normalize whitespace. Need explicit handling of \xa0 for IE. */
  393. }
  394. var _customFormats = [];
  395. dojox.date.hebrew.locale.addCustomFormats = function(/*String*/packageName, /*String*/bundleName){
  396. // summary:
  397. // Add a reference to a bundle containing localized custom formats to be
  398. // used by date/time formatting and parsing routines.
  399. //
  400. // description:
  401. // The user may add custom localized formats where the bundle has properties following the
  402. // same naming convention used by dojo.cldr: `dateFormat-xxxx` / `timeFormat-xxxx`
  403. // The pattern string should match the format used by the CLDR.
  404. // See dojo.date.locale.format() for details.
  405. // The resources must be loaded by dojo.requireLocalization() prior to use
  406. _customFormats.push({pkg:packageName,name:bundleName});
  407. };
  408. dojox.date.hebrew.locale._getHebrewBundle = function(/*String*/locale){
  409. var hebrew = {};
  410. dojo.forEach(_customFormats, function(desc){
  411. var bundle = i18n.getLocalization(desc.pkg, desc.name, locale);
  412. hebrew = dojo.mixin(hebrew, bundle);
  413. }, this);
  414. return hebrew; /*Object*/
  415. };
  416. dojox.date.hebrew.locale.addCustomFormats("dojo.cldr","hebrew");
  417. dojox.date.hebrew.locale.getNames = function(/*String*/item, /*String*/type, /*String?*/context, /*String?*/locale, /*dojox.date.hebrew.Date?*/date){
  418. // summary:
  419. // Used to get localized strings from dojo.cldr for day or month names.
  420. //
  421. // item:
  422. // 'months' || 'days'
  423. // type:
  424. // 'wide' || 'narrow' || 'abbr' (e.g. "Monday", "Mon", or "M" respectively, in English)
  425. // use:
  426. // 'standAlone' || 'format' (default)
  427. // locale:
  428. // override locale used to find the names
  429. // date:
  430. // required for item=months to determine leap month name
  431. //
  432. // using var monthNames = dojox.date.hebrew.locale.getNames('months', 'wide', 'format', 'he', new hebrewDate(5768, 2, 12));
  433. var label,
  434. lookup = dojox.date.hebrew.locale._getHebrewBundle(locale),
  435. props = [item, context, type];
  436. if(context == 'standAlone'){
  437. var key = props.join('-');
  438. label = lookup[key];
  439. // Fall back to 'format' flavor of name
  440. if(label[0] == 1){ label = undefined; } // kludge, in the absence of real aliasing support in dojo.cldr
  441. }
  442. props[1] = 'format';
  443. // return by copy so changes won't be made accidentally to the in-memory model
  444. var result = (label || lookup[props.join('-')]).concat();
  445. if(item == "months"){
  446. if(date.isLeapYear(date.getFullYear())){
  447. // Adar I (6th position in the array) will be used.
  448. // Substitute the leap month Adar II for the regular Adar (7th position)
  449. props.push("leap");
  450. result[6] = lookup[props.join('-')];
  451. }else{
  452. // Remove Adar I but leave an empty position in the array
  453. delete result[5];
  454. }
  455. }
  456. return result; /*Array*/
  457. };
  458. return dojox.date.hebrew.locale;
  459. });