Adding float-no-zero branch hosted build

This commit is contained in:
ahocevar
2014-03-07 10:55:12 +01:00
parent 84cad42f6d
commit bd9092199b
1664 changed files with 731463 additions and 0 deletions
@@ -0,0 +1,806 @@
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utility functions for supporting Bidi issues.
*/
/**
* Namespace for bidi supporting functions.
*/
goog.provide('goog.i18n.bidi');
/**
* @define {boolean} FORCE_RTL forces the {@link goog.i18n.bidi.IS_RTL} constant
* to say that the current locale is a RTL locale. This should only be used
* if you want to override the default behavior for deciding whether the
* current locale is RTL or not.
*
* {@see goog.i18n.bidi.IS_RTL}
*/
goog.define('goog.i18n.bidi.FORCE_RTL', false);
/**
* Constant that defines whether or not the current locale is a RTL locale.
* If {@link goog.i18n.bidi.FORCE_RTL} is not true, this constant will default
* to check that {@link goog.LOCALE} is one of a few major RTL locales.
*
* <p>This is designed to be a maximally efficient compile-time constant. For
* example, for the default goog.LOCALE, compiling
* "if (goog.i18n.bidi.IS_RTL) alert('rtl') else {}" should produce no code. It
* is this design consideration that limits the implementation to only
* supporting a few major RTL locales, as opposed to the broader repertoire of
* something like goog.i18n.bidi.isRtlLanguage.
*
* <p>Since this constant refers to the directionality of the locale, it is up
* to the caller to determine if this constant should also be used for the
* direction of the UI.
*
* {@see goog.LOCALE}
*
* @type {boolean}
*
* TODO(user): write a test that checks that this is a compile-time constant.
*/
goog.i18n.bidi.IS_RTL = goog.i18n.bidi.FORCE_RTL ||
(goog.LOCALE.substring(0, 2).toLowerCase() == 'ar' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'fa' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'he' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'iw' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'ur' ||
goog.LOCALE.substring(0, 2).toLowerCase() == 'yi') &&
(goog.LOCALE.length == 2 ||
goog.LOCALE.substring(2, 3) == '-' ||
goog.LOCALE.substring(2, 3) == '_');
/**
* Unicode formatting characters and directionality string constants.
* @enum {string}
*/
goog.i18n.bidi.Format = {
/** Unicode "Left-To-Right Embedding" (LRE) character. */
LRE: '\u202A',
/** Unicode "Right-To-Left Embedding" (RLE) character. */
RLE: '\u202B',
/** Unicode "Pop Directional Formatting" (PDF) character. */
PDF: '\u202C',
/** Unicode "Left-To-Right Mark" (LRM) character. */
LRM: '\u200E',
/** Unicode "Right-To-Left Mark" (RLM) character. */
RLM: '\u200F'
};
/**
* Directionality enum.
* @enum {number}
*/
goog.i18n.bidi.Dir = {
RTL: -1,
UNKNOWN: 0,
LTR: 1
};
/**
* 'right' string constant.
* @type {string}
*/
goog.i18n.bidi.RIGHT = 'right';
/**
* 'left' string constant.
* @type {string}
*/
goog.i18n.bidi.LEFT = 'left';
/**
* 'left' if locale is RTL, 'right' if not.
* @type {string}
*/
goog.i18n.bidi.I18N_RIGHT = goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.LEFT :
goog.i18n.bidi.RIGHT;
/**
* 'right' if locale is RTL, 'left' if not.
* @type {string}
*/
goog.i18n.bidi.I18N_LEFT = goog.i18n.bidi.IS_RTL ? goog.i18n.bidi.RIGHT :
goog.i18n.bidi.LEFT;
/**
* Convert a directionality given in various formats to a goog.i18n.bidi.Dir
* constant. Useful for interaction with different standards of directionality
* representation.
*
* @param {goog.i18n.bidi.Dir|number|boolean} givenDir Directionality given in
* one of the following formats:
* 1. A goog.i18n.bidi.Dir constant.
* 2. A number (positive = LRT, negative = RTL, 0 = unknown).
* 3. A boolean (true = RTL, false = LTR).
* @return {goog.i18n.bidi.Dir} A goog.i18n.bidi.Dir constant matching the given
* directionality.
*/
goog.i18n.bidi.toDir = function(givenDir) {
if (typeof givenDir == 'number') {
return givenDir > 0 ? goog.i18n.bidi.Dir.LTR :
givenDir < 0 ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.UNKNOWN;
} else {
return givenDir ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR;
}
};
/**
* A practical pattern to identify strong LTR characters. This pattern is not
* theoretically correct according to the Unicode standard. It is simplified for
* performance and small code size.
* @type {string}
* @private
*/
goog.i18n.bidi.ltrChars_ =
'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF' +
'\u200E\u2C00-\uFB1C\uFE00-\uFE6F\uFEFD-\uFFFF';
/**
* A practical pattern to identify strong RTL character. This pattern is not
* theoretically correct according to the Unicode standard. It is simplified
* for performance and small code size.
* @type {string}
* @private
*/
goog.i18n.bidi.rtlChars_ = '\u0591-\u07FF\u200F\uFB1D-\uFDFF\uFE70-\uFEFC';
/**
* Simplified regular expression for an HTML tag (opening or closing) or an HTML
* escape. We might want to skip over such expressions when estimating the text
* directionality.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.htmlSkipReg_ = /<[^>]*>|&[^;]+;/g;
/**
* Returns the input text with spaces instead of HTML tags or HTML escapes, if
* opt_isStripNeeded is true. Else returns the input as is.
* Useful for text directionality estimation.
* Note: the function should not be used in other contexts; it is not 100%
* correct, but rather a good-enough implementation for directionality
* estimation purposes.
* @param {string} str The given string.
* @param {boolean=} opt_isStripNeeded Whether to perform the stripping.
* Default: false (to retain consistency with calling functions).
* @return {string} The given string cleaned of HTML tags / escapes.
* @private
*/
goog.i18n.bidi.stripHtmlIfNeeded_ = function(str, opt_isStripNeeded) {
return opt_isStripNeeded ? str.replace(goog.i18n.bidi.htmlSkipReg_, ' ') :
str;
};
/**
* Regular expression to check for RTL characters.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rtlCharReg_ = new RegExp('[' + goog.i18n.bidi.rtlChars_ + ']');
/**
* Regular expression to check for LTR characters.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.ltrCharReg_ = new RegExp('[' + goog.i18n.bidi.ltrChars_ + ']');
/**
* Test whether the given string has any RTL characters in it.
* @param {string} str The given string that need to be tested.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether the string contains RTL characters.
*/
goog.i18n.bidi.hasAnyRtl = function(str, opt_isHtml) {
return goog.i18n.bidi.rtlCharReg_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
str, opt_isHtml));
};
/**
* Test whether the given string has any RTL characters in it.
* @param {string} str The given string that need to be tested.
* @return {boolean} Whether the string contains RTL characters.
* @deprecated Use hasAnyRtl.
*/
goog.i18n.bidi.hasRtlChar = goog.i18n.bidi.hasAnyRtl;
/**
* Test whether the given string has any LTR characters in it.
* @param {string} str The given string that need to be tested.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether the string contains LTR characters.
*/
goog.i18n.bidi.hasAnyLtr = function(str, opt_isHtml) {
return goog.i18n.bidi.ltrCharReg_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
str, opt_isHtml));
};
/**
* Regular expression pattern to check if the first character in the string
* is LTR.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.ltrRe_ = new RegExp('^[' + goog.i18n.bidi.ltrChars_ + ']');
/**
* Regular expression pattern to check if the first character in the string
* is RTL.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rtlRe_ = new RegExp('^[' + goog.i18n.bidi.rtlChars_ + ']');
/**
* Check if the first character in the string is RTL or not.
* @param {string} str The given string that need to be tested.
* @return {boolean} Whether the first character in str is an RTL char.
*/
goog.i18n.bidi.isRtlChar = function(str) {
return goog.i18n.bidi.rtlRe_.test(str);
};
/**
* Check if the first character in the string is LTR or not.
* @param {string} str The given string that need to be tested.
* @return {boolean} Whether the first character in str is an LTR char.
*/
goog.i18n.bidi.isLtrChar = function(str) {
return goog.i18n.bidi.ltrRe_.test(str);
};
/**
* Check if the first character in the string is neutral or not.
* @param {string} str The given string that need to be tested.
* @return {boolean} Whether the first character in str is a neutral char.
*/
goog.i18n.bidi.isNeutralChar = function(str) {
return !goog.i18n.bidi.isLtrChar(str) && !goog.i18n.bidi.isRtlChar(str);
};
/**
* Regular expressions to check if a piece of text is of LTR directionality
* on first character with strong directionality.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.ltrDirCheckRe_ = new RegExp(
'^[^' + goog.i18n.bidi.rtlChars_ + ']*[' + goog.i18n.bidi.ltrChars_ + ']');
/**
* Regular expressions to check if a piece of text is of RTL directionality
* on first character with strong directionality.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rtlDirCheckRe_ = new RegExp(
'^[^' + goog.i18n.bidi.ltrChars_ + ']*[' + goog.i18n.bidi.rtlChars_ + ']');
/**
* Check whether the first strongly directional character (if any) is RTL.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether RTL directionality is detected using the first
* strongly-directional character method.
*/
goog.i18n.bidi.startsWithRtl = function(str, opt_isHtml) {
return goog.i18n.bidi.rtlDirCheckRe_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
str, opt_isHtml));
};
/**
* Check whether the first strongly directional character (if any) is RTL.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether RTL directionality is detected using the first
* strongly-directional character method.
* @deprecated Use startsWithRtl.
*/
goog.i18n.bidi.isRtlText = goog.i18n.bidi.startsWithRtl;
/**
* Check whether the first strongly directional character (if any) is LTR.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether LTR directionality is detected using the first
* strongly-directional character method.
*/
goog.i18n.bidi.startsWithLtr = function(str, opt_isHtml) {
return goog.i18n.bidi.ltrDirCheckRe_.test(goog.i18n.bidi.stripHtmlIfNeeded_(
str, opt_isHtml));
};
/**
* Check whether the first strongly directional character (if any) is LTR.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether LTR directionality is detected using the first
* strongly-directional character method.
* @deprecated Use startsWithLtr.
*/
goog.i18n.bidi.isLtrText = goog.i18n.bidi.startsWithLtr;
/**
* Regular expression to check if a string looks like something that must
* always be LTR even in RTL text, e.g. a URL. When estimating the
* directionality of text containing these, we treat these as weakly LTR,
* like numbers.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.isRequiredLtrRe_ = /^http:\/\/.*/;
/**
* Check whether the input string either contains no strongly directional
* characters or looks like a url.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether neutral directionality is detected.
*/
goog.i18n.bidi.isNeutralText = function(str, opt_isHtml) {
str = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml);
return goog.i18n.bidi.isRequiredLtrRe_.test(str) ||
!goog.i18n.bidi.hasAnyLtr(str) && !goog.i18n.bidi.hasAnyRtl(str);
};
/**
* Regular expressions to check if the last strongly-directional character in a
* piece of text is LTR.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.ltrExitDirCheckRe_ = new RegExp(
'[' + goog.i18n.bidi.ltrChars_ + '][^' + goog.i18n.bidi.rtlChars_ + ']*$');
/**
* Regular expressions to check if the last strongly-directional character in a
* piece of text is RTL.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rtlExitDirCheckRe_ = new RegExp(
'[' + goog.i18n.bidi.rtlChars_ + '][^' + goog.i18n.bidi.ltrChars_ + ']*$');
/**
* Check if the exit directionality a piece of text is LTR, i.e. if the last
* strongly-directional character in the string is LTR.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether LTR exit directionality was detected.
*/
goog.i18n.bidi.endsWithLtr = function(str, opt_isHtml) {
return goog.i18n.bidi.ltrExitDirCheckRe_.test(
goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};
/**
* Check if the exit directionality a piece of text is LTR, i.e. if the last
* strongly-directional character in the string is LTR.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether LTR exit directionality was detected.
* @deprecated Use endsWithLtr.
*/
goog.i18n.bidi.isLtrExitText = goog.i18n.bidi.endsWithLtr;
/**
* Check if the exit directionality a piece of text is RTL, i.e. if the last
* strongly-directional character in the string is RTL.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether RTL exit directionality was detected.
*/
goog.i18n.bidi.endsWithRtl = function(str, opt_isHtml) {
return goog.i18n.bidi.rtlExitDirCheckRe_.test(
goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml));
};
/**
* Check if the exit directionality a piece of text is RTL, i.e. if the last
* strongly-directional character in the string is RTL.
* @param {string} str String being checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether RTL exit directionality was detected.
* @deprecated Use endsWithRtl.
*/
goog.i18n.bidi.isRtlExitText = goog.i18n.bidi.endsWithRtl;
/**
* A regular expression for matching right-to-left language codes.
* See {@link #isRtlLanguage} for the design.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rtlLocalesRe_ = new RegExp(
'^(ar|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_](Arab|Hebr|Thaa|Nkoo|Tfng))' +
'(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)',
'i');
/**
* Check if a BCP 47 / III language code indicates an RTL language, i.e. either:
* - a language code explicitly specifying one of the right-to-left scripts,
* e.g. "az-Arab", or<p>
* - a language code specifying one of the languages normally written in a
* right-to-left script, e.g. "fa" (Farsi), except ones explicitly specifying
* Latin or Cyrillic script (which are the usual LTR alternatives).<p>
* The list of right-to-left scripts appears in the 100-199 range in
* http://www.unicode.org/iso15924/iso15924-num.html, of which Arabic and
* Hebrew are by far the most widely used. We also recognize Thaana, N'Ko, and
* Tifinagh, which also have significant modern usage. The rest (Syriac,
* Samaritan, Mandaic, etc.) seem to have extremely limited or no modern usage
* and are not recognized to save on code size.
* The languages usually written in a right-to-left script are taken as those
* with Suppress-Script: Hebr|Arab|Thaa|Nkoo|Tfng in
* http://www.iana.org/assignments/language-subtag-registry,
* as well as Sindhi (sd) and Uyghur (ug).
* Other subtags of the language code, e.g. regions like EG (Egypt), are
* ignored.
* @param {string} lang BCP 47 (a.k.a III) language code.
* @return {boolean} Whether the language code is an RTL language.
*/
goog.i18n.bidi.isRtlLanguage = function(lang) {
return goog.i18n.bidi.rtlLocalesRe_.test(lang);
};
/**
* Regular expression for bracket guard replacement in html.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.bracketGuardHtmlRe_ =
/(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(&lt;.*?(&gt;)+)/g;
/**
* Regular expression for bracket guard replacement in text.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.bracketGuardTextRe_ =
/(\(.*?\)+)|(\[.*?\]+)|(\{.*?\}+)|(<.*?>+)/g;
/**
* Apply bracket guard using html span tag. This is to address the problem of
* messy bracket display frequently happens in RTL layout.
* @param {string} s The string that need to be processed.
* @param {boolean=} opt_isRtlContext specifies default direction (usually
* direction of the UI).
* @return {string} The processed string, with all bracket guarded.
*/
goog.i18n.bidi.guardBracketInHtml = function(s, opt_isRtlContext) {
var useRtl = opt_isRtlContext === undefined ?
goog.i18n.bidi.hasAnyRtl(s) : opt_isRtlContext;
if (useRtl) {
return s.replace(goog.i18n.bidi.bracketGuardHtmlRe_,
'<span dir=rtl>$&</span>');
}
return s.replace(goog.i18n.bidi.bracketGuardHtmlRe_,
'<span dir=ltr>$&</span>');
};
/**
* Apply bracket guard using LRM and RLM. This is to address the problem of
* messy bracket display frequently happens in RTL layout.
* This version works for both plain text and html. But it does not work as
* good as guardBracketInHtml in some cases.
* @param {string} s The string that need to be processed.
* @param {boolean=} opt_isRtlContext specifies default direction (usually
* direction of the UI).
* @return {string} The processed string, with all bracket guarded.
*/
goog.i18n.bidi.guardBracketInText = function(s, opt_isRtlContext) {
var useRtl = opt_isRtlContext === undefined ?
goog.i18n.bidi.hasAnyRtl(s) : opt_isRtlContext;
var mark = useRtl ? goog.i18n.bidi.Format.RLM : goog.i18n.bidi.Format.LRM;
return s.replace(goog.i18n.bidi.bracketGuardTextRe_, mark + '$&' + mark);
};
/**
* Enforce the html snippet in RTL directionality regardless overall context.
* If the html piece was enclosed by tag, dir will be applied to existing
* tag, otherwise a span tag will be added as wrapper. For this reason, if
* html snippet start with with tag, this tag must enclose the whole piece. If
* the tag already has a dir specified, this new one will override existing
* one in behavior (tested on FF and IE).
* @param {string} html The string that need to be processed.
* @return {string} The processed string, with directionality enforced to RTL.
*/
goog.i18n.bidi.enforceRtlInHtml = function(html) {
if (html.charAt(0) == '<') {
return html.replace(/<\w+/, '$& dir=rtl');
}
// '\n' is important for FF so that it won't incorrectly merge span groups
return '\n<span dir=rtl>' + html + '</span>';
};
/**
* Enforce RTL on both end of the given text piece using unicode BiDi formatting
* characters RLE and PDF.
* @param {string} text The piece of text that need to be wrapped.
* @return {string} The wrapped string after process.
*/
goog.i18n.bidi.enforceRtlInText = function(text) {
return goog.i18n.bidi.Format.RLE + text + goog.i18n.bidi.Format.PDF;
};
/**
* Enforce the html snippet in RTL directionality regardless overall context.
* If the html piece was enclosed by tag, dir will be applied to existing
* tag, otherwise a span tag will be added as wrapper. For this reason, if
* html snippet start with with tag, this tag must enclose the whole piece. If
* the tag already has a dir specified, this new one will override existing
* one in behavior (tested on FF and IE).
* @param {string} html The string that need to be processed.
* @return {string} The processed string, with directionality enforced to RTL.
*/
goog.i18n.bidi.enforceLtrInHtml = function(html) {
if (html.charAt(0) == '<') {
return html.replace(/<\w+/, '$& dir=ltr');
}
// '\n' is important for FF so that it won't incorrectly merge span groups
return '\n<span dir=ltr>' + html + '</span>';
};
/**
* Enforce LTR on both end of the given text piece using unicode BiDi formatting
* characters LRE and PDF.
* @param {string} text The piece of text that need to be wrapped.
* @return {string} The wrapped string after process.
*/
goog.i18n.bidi.enforceLtrInText = function(text) {
return goog.i18n.bidi.Format.LRE + text + goog.i18n.bidi.Format.PDF;
};
/**
* Regular expression to find dimensions such as "padding: .3 0.4ex 5px 6;"
* @type {RegExp}
* @private
*/
goog.i18n.bidi.dimensionsRe_ =
/:\s*([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)\s+([.\d][.\w]*)/g;
/**
* Regular expression for left.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.leftRe_ = /left/gi;
/**
* Regular expression for right.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.rightRe_ = /right/gi;
/**
* Placeholder regular expression for swapping.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.tempRe_ = /%%%%/g;
/**
* Swap location parameters and 'left'/'right' in CSS specification. The
* processed string will be suited for RTL layout. Though this function can
* cover most cases, there are always exceptions. It is suggested to put
* those exceptions in separate group of CSS string.
* @param {string} cssStr CSS spefication string.
* @return {string} Processed CSS specification string.
*/
goog.i18n.bidi.mirrorCSS = function(cssStr) {
return cssStr.
// reverse dimensions
replace(goog.i18n.bidi.dimensionsRe_, ':$1 $4 $3 $2').
replace(goog.i18n.bidi.leftRe_, '%%%%'). // swap left and right
replace(goog.i18n.bidi.rightRe_, goog.i18n.bidi.LEFT).
replace(goog.i18n.bidi.tempRe_, goog.i18n.bidi.RIGHT);
};
/**
* Regular expression for hebrew double quote substitution, finding quote
* directly after hebrew characters.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.doubleQuoteSubstituteRe_ = /([\u0591-\u05f2])"/g;
/**
* Regular expression for hebrew single quote substitution, finding quote
* directly after hebrew characters.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.singleQuoteSubstituteRe_ = /([\u0591-\u05f2])'/g;
/**
* Replace the double and single quote directly after a Hebrew character with
* GERESH and GERSHAYIM. In such case, most likely that's user intention.
* @param {string} str String that need to be processed.
* @return {string} Processed string with double/single quote replaced.
*/
goog.i18n.bidi.normalizeHebrewQuote = function(str) {
return str.
replace(goog.i18n.bidi.doubleQuoteSubstituteRe_, '$1\u05f4').
replace(goog.i18n.bidi.singleQuoteSubstituteRe_, '$1\u05f3');
};
/**
* Regular expression to split a string into "words" for directionality
* estimation based on relative word counts.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.wordSeparatorRe_ = /\s+/;
/**
* Regular expression to check if a string contains any numerals. Used to
* differentiate between completely neutral strings and those containing
* numbers, which are weakly LTR.
* @type {RegExp}
* @private
*/
goog.i18n.bidi.hasNumeralsRe_ = /\d/;
/**
* This constant controls threshold of RTL directionality.
* @type {number}
* @private
*/
goog.i18n.bidi.rtlDetectionThreshold_ = 0.40;
/**
* Estimates the directionality of a string based on relative word counts.
* If the number of RTL words is above a certain percentage of the total number
* of strongly directional words, returns RTL.
* Otherwise, if any words are strongly or weakly LTR, returns LTR.
* Otherwise, returns UNKNOWN, which is used to mean "neutral".
* Numbers are counted as weakly LTR.
* @param {string} str The string to be checked.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {goog.i18n.bidi.Dir} Estimated overall directionality of {@code str}.
*/
goog.i18n.bidi.estimateDirection = function(str, opt_isHtml) {
var rtlCount = 0;
var totalCount = 0;
var hasWeaklyLtr = false;
var tokens = goog.i18n.bidi.stripHtmlIfNeeded_(str, opt_isHtml).
split(goog.i18n.bidi.wordSeparatorRe_);
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if (goog.i18n.bidi.startsWithRtl(token)) {
rtlCount++;
totalCount++;
} else if (goog.i18n.bidi.isRequiredLtrRe_.test(token)) {
hasWeaklyLtr = true;
} else if (goog.i18n.bidi.hasAnyLtr(token)) {
totalCount++;
} else if (goog.i18n.bidi.hasNumeralsRe_.test(token)) {
hasWeaklyLtr = true;
}
}
return totalCount == 0 ?
(hasWeaklyLtr ? goog.i18n.bidi.Dir.LTR : goog.i18n.bidi.Dir.UNKNOWN) :
(rtlCount / totalCount > goog.i18n.bidi.rtlDetectionThreshold_ ?
goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR);
};
/**
* Check the directionality of a piece of text, return true if the piece of
* text should be laid out in RTL direction.
* @param {string} str The piece of text that need to be detected.
* @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped.
* Default: false.
* @return {boolean} Whether this piece of text should be laid out in RTL.
*/
goog.i18n.bidi.detectRtlDirectionality = function(str, opt_isHtml) {
return goog.i18n.bidi.estimateDirection(str, opt_isHtml) ==
goog.i18n.bidi.Dir.RTL;
};
/**
* Sets text input element's directionality and text alignment based on a
* given directionality.
* @param {Element} element Input field element to set directionality to.
* @param {goog.i18n.bidi.Dir|number|boolean} dir Desired directionality, given
* in one of the following formats:
* 1. A goog.i18n.bidi.Dir constant.
* 2. A number (positive = LRT, negative = RTL, 0 = unknown).
* 3. A boolean (true = RTL, false = LTR).
*/
goog.i18n.bidi.setElementDirAndAlign = function(element, dir) {
if (element &&
(dir = goog.i18n.bidi.toDir(dir)) != goog.i18n.bidi.Dir.UNKNOWN) {
element.style.textAlign = dir == goog.i18n.bidi.Dir.RTL ? 'right' : 'left';
element.dir = dir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
}
};
@@ -0,0 +1,490 @@
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Utility for formatting text for display in a potentially
* opposite-directionality context without garbling.
* Mostly a port of http://go/formatter.cc.
*/
goog.provide('goog.i18n.BidiFormatter');
goog.require('goog.i18n.bidi');
goog.require('goog.string');
/**
* Utility class for formatting text for display in a potentially
* opposite-directionality context without garbling. Provides the following
* functionality:
*
* 1. BiDi Wrapping
* When text in one language is mixed into a document in another, opposite-
* directionality language, e.g. when an English business name is embedded in a
* Hebrew web page, both the inserted string and the text following it may be
* displayed incorrectly unless the inserted string is explicitly separated
* from the surrounding text in a "wrapper" that declares its directionality at
* the start and then resets it back at the end. This wrapping can be done in
* HTML mark-up (e.g. a 'span dir="rtl"' tag) or - only in contexts where
* mark-up can not be used - in Unicode BiDi formatting codes (LRE|RLE and PDF).
* Providing such wrapping services is the basic purpose of the BiDi formatter.
*
* 2. Directionality estimation
* How does one know whether a string about to be inserted into surrounding
* text has the same directionality? Well, in many cases, one knows that this
* must be the case when writing the code doing the insertion, e.g. when a
* localized message is inserted into a localized page. In such cases there is
* no need to involve the BiDi formatter at all. In the remaining cases, e.g.
* when the string is user-entered or comes from a database, the language of
* the string (and thus its directionality) is not known a priori, and must be
* estimated at run-time. The BiDi formatter does this automatically.
*
* 3. Escaping
* When wrapping plain text - i.e. text that is not already HTML or HTML-
* escaped - in HTML mark-up, the text must first be HTML-escaped to prevent XSS
* attacks and other nasty business. This of course is always true, but the
* escaping can not be done after the string has already been wrapped in
* mark-up, so the BiDi formatter also serves as a last chance and includes
* escaping services.
*
* Thus, in a single call, the formatter will escape the input string as
* specified, determine its directionality, and wrap it as necessary. It is
* then up to the caller to insert the return value in the output.
*
* See http://wiki/Main/TemplatesAndBiDi for more information.
*
* @param {goog.i18n.bidi.Dir|number|boolean} contextDir The context
* directionality. May be supplied either as a goog.i18n.bidi.Dir constant,
* as a number (positive = LRT, negative = RTL, 0 = unknown) or as a boolean
* (true = RTL, false = LTR).
* @param {boolean=} opt_alwaysSpan Whether {@link #spanWrap} should always
* use a 'span' tag, even when the input directionality is neutral or
* matches the context, so that the DOM structure of the output does not
* depend on the combination of directionalities. Default: false.
* @constructor
*/
goog.i18n.BidiFormatter = function(contextDir, opt_alwaysSpan) {
/**
* The overall directionality of the context in which the formatter is being
* used.
* @type {goog.i18n.bidi.Dir}
* @private
*/
this.contextDir_ = goog.i18n.bidi.toDir(contextDir);
/**
* Whether {@link #spanWrap} and similar methods should always use the same
* span structure, regardless of the combination of directionalities, for a
* stable DOM structure.
* @type {boolean}
* @private
*/
this.alwaysSpan_ = !!opt_alwaysSpan;
};
/**
* @return {goog.i18n.bidi.Dir} The context directionality.
*/
goog.i18n.BidiFormatter.prototype.getContextDir = function() {
return this.contextDir_;
};
/**
* @return {boolean} Whether alwaysSpan is set.
*/
goog.i18n.BidiFormatter.prototype.getAlwaysSpan = function() {
return this.alwaysSpan_;
};
/**
* @param {goog.i18n.bidi.Dir|number|boolean} contextDir The context
* directionality. May be supplied either as a goog.i18n.bidi.Dir constant,
* as a number (positive = LRT, negative = RTL, 0 = unknown) or as a boolean
* (true = RTL, false = LTR).
*/
goog.i18n.BidiFormatter.prototype.setContextDir = function(contextDir) {
this.contextDir_ = goog.i18n.bidi.toDir(contextDir);
};
/**
* @param {boolean} alwaysSpan Whether {@link #spanWrap} should always use a
* 'span' tag, even when the input directionality is neutral or matches the
* context, so that the DOM structure of the output does not depend on the
* combination of directionalities.
*/
goog.i18n.BidiFormatter.prototype.setAlwaysSpan = function(alwaysSpan) {
this.alwaysSpan_ = alwaysSpan;
};
/**
* Returns the directionality of input argument {@code str}.
* Identical to {@link goog.i18n.bidi.estimateDirection}.
*
* @param {string} str The input text.
* @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
* Default: false.
* @return {goog.i18n.bidi.Dir} Estimated overall directionality of {@code str}.
*/
goog.i18n.BidiFormatter.prototype.estimateDirection =
goog.i18n.bidi.estimateDirection;
/**
* Returns true if two given directionalities are opposite.
* Note: the implementation is based on the numeric values of the Dir enum.
*
* @param {goog.i18n.bidi.Dir} dir1 1st directionality.
* @param {goog.i18n.bidi.Dir} dir2 2nd directionality.
* @return {boolean} Whether the directionalities are opposite.
* @private
*/
goog.i18n.BidiFormatter.prototype.areDirectionalitiesOpposite_ = function(dir1,
dir2) {
return dir1 * dir2 < 0;
};
/**
* Returns a unicode BiDi mark matching the context directionality (LRM or
* RLM) if {@code opt_dirReset}, and if either the directionality or the exit
* directionality of {@code str} is opposite to the context directionality.
* Otherwise returns the empty string.
*
* @param {string} str The input text.
* @param {goog.i18n.bidi.Dir} dir {@code str}'s overall directionality.
* @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
* Default: false.
* @param {boolean=} opt_dirReset Whether to perform the reset. Default: false.
* @return {string} A unicode BiDi mark or the empty string.
* @private
*/
goog.i18n.BidiFormatter.prototype.dirResetIfNeeded_ = function(str, dir,
opt_isHtml, opt_dirReset) {
// endsWithRtl and endsWithLtr are called only if needed (short-circuit).
if (opt_dirReset &&
(this.areDirectionalitiesOpposite_(dir, this.contextDir_) ||
(this.contextDir_ == goog.i18n.bidi.Dir.LTR &&
goog.i18n.bidi.endsWithRtl(str, opt_isHtml)) ||
(this.contextDir_ == goog.i18n.bidi.Dir.RTL &&
goog.i18n.bidi.endsWithLtr(str, opt_isHtml)))) {
return this.contextDir_ == goog.i18n.bidi.Dir.LTR ?
goog.i18n.bidi.Format.LRM : goog.i18n.bidi.Format.RLM;
} else {
return '';
}
};
/**
* Returns "rtl" if {@code str}'s estimated directionality is RTL, and "ltr" if
* it is LTR. In case it's UNKNOWN, returns "rtl" if the context directionality
* is RTL, and "ltr" otherwise.
* Needed for GXP, which can't handle dirAttr.
* Example use case:
* &lt;td expr:dir='bidiFormatter.dirAttrValue(foo)'&gt;
* &lt;gxp:eval expr='foo'&gt;
* &lt;/td&gt;
*
* @param {string} str Text whose directionality is to be estimated.
* @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
* Default: false.
* @return {string} "rtl" or "ltr", according to the logic described above.
*/
goog.i18n.BidiFormatter.prototype.dirAttrValue = function(str, opt_isHtml) {
return this.knownDirAttrValue(this.estimateDirection(str, opt_isHtml));
};
/**
* Returns "rtl" if the given directionality is RTL, and "ltr" if it is LTR. In
* case it's UNKNOWN, returns "rtl" if the context directionality is RTL, and
* "ltr" otherwise.
*
* @param {goog.i18n.bidi.Dir} dir A directionality.
* @return {string} "rtl" or "ltr", according to the logic described above.
*/
goog.i18n.BidiFormatter.prototype.knownDirAttrValue = function(dir) {
if (dir == goog.i18n.bidi.Dir.UNKNOWN) {
dir = this.contextDir_;
}
return dir == goog.i18n.bidi.Dir.RTL ? 'rtl' : 'ltr';
};
/**
* Returns 'dir="ltr"' or 'dir="rtl"', depending on {@code str}'s estimated
* directionality, if it is not the same as the context directionality.
* Otherwise, returns the empty string.
*
* @param {string} str Text whose directionality is to be estimated.
* @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
* Default: false.
* @return {string} 'dir="rtl"' for RTL text in non-RTL context; 'dir="ltr"' for
* LTR text in non-LTR context; else, the empty string.
*/
goog.i18n.BidiFormatter.prototype.dirAttr = function(str, opt_isHtml) {
return this.knownDirAttr(this.estimateDirection(str, opt_isHtml));
};
/**
* Returns 'dir="ltr"' or 'dir="rtl"', depending on the given directionality, if
* it is not the same as the context directionality. Otherwise, returns the
* empty string.
*
* @param {goog.i18n.bidi.Dir} dir A directionality.
* @return {string} 'dir="rtl"' for RTL text in non-RTL context; 'dir="ltr"' for
* LTR text in non-LTR context; else, the empty string.
*/
goog.i18n.BidiFormatter.prototype.knownDirAttr = function(dir) {
if (dir != this.contextDir_) {
return dir == goog.i18n.bidi.Dir.RTL ? 'dir="rtl"' :
dir == goog.i18n.bidi.Dir.LTR ? 'dir="ltr"' : '';
}
return '';
};
/**
* Formats a string of unknown directionality for use in HTML output of the
* context directionality, so an opposite-directionality string is neither
* garbled nor garbles what follows it.
* The algorithm: estimates the directionality of input argument {@code str}. In
* case its directionality doesn't match the context directionality, wraps it
* with a 'span' tag and adds a "dir" attribute (either 'dir="rtl"' or
* 'dir="ltr"'). If setAlwaysSpan(true) was used, the input is always wrapped
* with 'span', skipping just the dir attribute when it's not needed.
*
* If {@code opt_dirReset}, and if the overall directionality or the exit
* directionality of {@code str} are opposite to the context directionality, a
* trailing unicode BiDi mark matching the context directionality is appened
* (LRM or RLM).
*
* If !{@code opt_isHtml}, HTML-escapes {@code str} regardless of wrapping.
*
* @param {string} str The input text.
* @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
* Default: false.
* @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
* matching the context directionality, when needed, to prevent the possible
* garbling of whatever may follow {@code str}. Default: true.
* @return {string} Input text after applying the above processing.
*/
goog.i18n.BidiFormatter.prototype.spanWrap = function(str, opt_isHtml,
opt_dirReset) {
var dir = this.estimateDirection(str, opt_isHtml);
return this.spanWrapWithKnownDir(dir, str, opt_isHtml, opt_dirReset);
};
/**
* Formats a string of given directionality for use in HTML output of the
* context directionality, so an opposite-directionality string is neither
* garbled nor garbles what follows it.
* The algorithm: If {@code dir} doesn't match the context directionality, wraps
* {@code str} with a 'span' tag and adds a "dir" attribute (either 'dir="rtl"'
* or 'dir="ltr"'). If setAlwaysSpan(true) was used, the input is always wrapped
* with 'span', skipping just the dir attribute when it's not needed.
*
* If {@code opt_dirReset}, and if {@code dir} or the exit directionality of
* {@code str} are opposite to the context directionality, a trailing unicode
* BiDi mark matching the context directionality is appened (LRM or RLM).
*
* If !{@code opt_isHtml}, HTML-escapes {@code str} regardless of wrapping.
*
* @param {goog.i18n.bidi.Dir} dir {@code str}'s overall directionality.
* @param {string} str The input text.
* @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
* Default: false.
* @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
* matching the context directionality, when needed, to prevent the possible
* garbling of whatever may follow {@code str}. Default: true.
* @return {string} Input text after applying the above processing.
*/
goog.i18n.BidiFormatter.prototype.spanWrapWithKnownDir = function(dir, str,
opt_isHtml, opt_dirReset) {
opt_dirReset = opt_dirReset || (opt_dirReset == undefined);
// Whether to add the "dir" attribute.
var dirCondition = dir != goog.i18n.bidi.Dir.UNKNOWN && dir !=
this.contextDir_;
if (!opt_isHtml) {
str = goog.string.htmlEscape(str);
}
var result = [];
if (this.alwaysSpan_ || dirCondition) { // Wrap is needed
result.push('<span');
if (dirCondition) {
result.push(dir == goog.i18n.bidi.Dir.RTL ? ' dir="rtl"' : ' dir="ltr"');
}
result.push('>' + str + '</span>');
} else {
result.push(str);
}
result.push(this.dirResetIfNeeded_(str, dir, true, opt_dirReset));
return result.join('');
};
/**
* Formats a string of unknown directionality for use in plain-text output of
* the context directionality, so an opposite-directionality string is neither
* garbled nor garbles what follows it.
* As opposed to {@link #spanWrap}, this makes use of unicode BiDi formatting
* characters. In HTML, its *only* valid use is inside of elements that do not
* allow mark-up, e.g. an 'option' tag.
* The algorithm: estimates the directionality of input argument {@code str}.
* In case it doesn't match the context directionality, wraps it with Unicode
* BiDi formatting characters: RLE{@code str}PDF for RTL text, and
* LRE{@code str}PDF for LTR text.
*
* If {@code opt_dirReset}, and if the overall directionality or the exit
* directionality of {@code str} are opposite to the context directionality, a
* trailing unicode BiDi mark matching the context directionality is appended
* (LRM or RLM).
*
* Does *not* do HTML-escaping regardless of the value of {@code opt_isHtml}.
* The return value can be HTML-escaped as necessary.
*
* @param {string} str The input text.
* @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
* Default: false.
* @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
* matching the context directionality, when needed, to prevent the possible
* garbling of whatever may follow {@code str}. Default: true.
* @return {string} Input text after applying the above processing.
*/
goog.i18n.BidiFormatter.prototype.unicodeWrap = function(str, opt_isHtml,
opt_dirReset) {
var dir = this.estimateDirection(str, opt_isHtml);
return this.unicodeWrapWithKnownDir(dir, str, opt_isHtml, opt_dirReset);
};
/**
* Formats a string of given directionality for use in plain-text output of the
* context directionality, so an opposite-directionality string is neither
* garbled nor garbles what follows it.
* As opposed to {@link #spanWrapWithKnownDir}, makes use of unicode BiDi
* formatting characters. In HTML, its *only* valid use is inside of elements
* that do not allow mark-up, e.g. an 'option' tag.
* The algorithm: If {@code dir} doesn't match the context directionality, wraps
* {@code str} with Unicode BiDi formatting characters: RLE{@code str}PDF for
* RTL text, and LRE{@code str}PDF for LTR text.
*
* If {@code opt_dirReset}, and if the overall directionality or the exit
* directionality of {@code str} are opposite to the context directionality, a
* trailing unicode BiDi mark matching the context directionality is appended
* (LRM or RLM).
*
* Does *not* do HTML-escaping regardless of the value of {@code opt_isHtml}.
* The return value can be HTML-escaped as necessary.
*
* @param {goog.i18n.bidi.Dir} dir {@code str}'s overall directionality.
* @param {string} str The input text.
* @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
* Default: false.
* @param {boolean=} opt_dirReset Whether to append a trailing unicode bidi mark
* matching the context directionality, when needed, to prevent the possible
* garbling of whatever may follow {@code str}. Default: true.
* @return {string} Input text after applying the above processing.
*/
goog.i18n.BidiFormatter.prototype.unicodeWrapWithKnownDir = function(dir, str,
opt_isHtml, opt_dirReset) {
opt_dirReset = opt_dirReset || (opt_dirReset == undefined);
var result = [];
if (dir != goog.i18n.bidi.Dir.UNKNOWN && dir != this.contextDir_) {
result.push(dir == goog.i18n.bidi.Dir.RTL ? goog.i18n.bidi.Format.RLE :
goog.i18n.bidi.Format.LRE);
result.push(str);
result.push(goog.i18n.bidi.Format.PDF);
} else {
result.push(str);
}
result.push(this.dirResetIfNeeded_(str, dir, opt_isHtml, opt_dirReset));
return result.join('');
};
/**
* Returns a Unicode BiDi mark matching the context directionality (LRM or RLM)
* if the directionality or the exit directionality of {@code str} are opposite
* to the context directionality. Otherwise returns the empty string.
*
* @param {string} str The input text.
* @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped.
* Default: false.
* @return {string} A Unicode bidi mark matching the global directionality or
* the empty string.
*/
goog.i18n.BidiFormatter.prototype.markAfter = function(str, opt_isHtml) {
return this.dirResetIfNeeded_(str,
this.estimateDirection(str, opt_isHtml), opt_isHtml, true);
};
/**
* Returns the Unicode BiDi mark matching the context directionality (LRM for
* LTR context directionality, RLM for RTL context directionality), or the
* empty string for neutral / unknown context directionality.
*
* @return {string} LRM for LTR context directionality and RLM for RTL context
* directionality.
*/
goog.i18n.BidiFormatter.prototype.mark = function() {
switch (this.contextDir_) {
case (goog.i18n.bidi.Dir.LTR):
return goog.i18n.bidi.Format.LRM;
case (goog.i18n.bidi.Dir.RTL):
return goog.i18n.bidi.Format.RLM;
default:
return '';
}
};
/**
* Returns 'right' for RTL context directionality. Otherwise (LTR or neutral /
* unknown context directionality) returns 'left'.
*
* @return {string} 'right' for RTL context directionality and 'left' for other
* context directionality.
*/
goog.i18n.BidiFormatter.prototype.startEdge = function() {
return this.contextDir_ == goog.i18n.bidi.Dir.RTL ?
goog.i18n.bidi.RIGHT : goog.i18n.bidi.LEFT;
};
/**
* Returns 'left' for RTL context directionality. Otherwise (LTR or neutral /
* unknown context directionality) returns 'right'.
*
* @return {string} 'left' for RTL context directionality and 'right' for other
* context directionality.
*/
goog.i18n.BidiFormatter.prototype.endEdge = function() {
return this.contextDir_ == goog.i18n.bidi.Dir.RTL ?
goog.i18n.bidi.LEFT : goog.i18n.bidi.RIGHT;
};
@@ -0,0 +1,157 @@
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview The decompressor for Base88 compressed character lists.
*
* The compression is by base 88 encoding the delta between two adjacent
* characters in ths list. The deltas can be positive or negative. Also, there
* would be character ranges. These three types of values
* are given enum values 0, 1 and 2 respectively. Initial 3 bits are used for
* encoding the type and total length of the encoded value. Length enums 0, 1
* and 2 represents lengths 1, 2 and 4. So (value * 8 + type * 3 + length enum)
* is encoded in base 88 by following characters for numbers from 0 to 87:
* 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ (continued in next line)
* abcdefghijklmnopqrstuvwxyz!#$%()*+,-.:;<=>?@[]^_`{|}~
*
* Value uses 0 based counting. That is value for the range [a, b] is 0 and
* that of [a, c] is 1. Simillarly, the delta of "ab" is 0.
*
* Following python script can be used to compress character lists taken
* standard input: http://go/charlistcompressor.py
*
*/
goog.provide('goog.i18n.CharListDecompressor');
goog.require('goog.array');
goog.require('goog.i18n.uChar');
/**
* Class to decompress base88 compressed character list.
* @constructor
*/
goog.i18n.CharListDecompressor = function() {
this.buildCharMap_('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr' +
'stuvwxyz!#$%()*+,-.:;<=>?@[]^_`{|}~');
};
/**
* 1-1 mapping from ascii characters used in encoding to an integer in the
* range 0 to 87.
* @type {Object}
* @private
*/
goog.i18n.CharListDecompressor.prototype.charMap_ = null;
/**
* Builds the map from ascii characters used for the base88 scheme to number
* each character represents.
* @param {string} str The string of characters used in base88 scheme.
* @private
*/
goog.i18n.CharListDecompressor.prototype.buildCharMap_ = function(str) {
if (!this.charMap_) {
this.charMap_ = {};
for (var i = 0; i < str.length; i++) {
this.charMap_[str.charAt(i)] = i;
}
}
};
/**
* Gets the number encoded in base88 scheme by a substring of given length
* and placed at the a given position of the string.
* @param {string} str String containing sequence of characters encoding a
* number in base 88 scheme.
* @param {number} start Starting position of substring encoding the number.
* @param {number} leng Length of the substring encoding the number.
* @return {number} The encoded number.
* @private
*/
goog.i18n.CharListDecompressor.prototype.getCodeAt_ = function(str, start,
leng) {
var result = 0;
for (var i = 0; i < leng; i++) {
var c = this.charMap_[str.charAt(start + i)];
result += c * Math.pow(88, i);
}
return result;
};
/**
* Add character(s) specified by the value and type to given list and return
* the next character in the sequence.
* @param {Array.<string>} list The list of characters to which the specified
* characters are appended.
* @param {number} lastcode The last codepoint that was added to the list.
* @param {number} value The value component that representing the delta or
* range.
* @param {number} type The type component that representing whether the value
* is a positive or negative delta or range.
* @return {number} Last codepoint that is added to the list.
* @private
*/
goog.i18n.CharListDecompressor.prototype.addChars_ = function(list, lastcode,
value, type) {
if (type == 0) {
lastcode += value + 1;
goog.array.extend(list, goog.i18n.uChar.fromCharCode(lastcode));
} else if (type == 1) {
lastcode -= value + 1;
goog.array.extend(list, goog.i18n.uChar.fromCharCode(lastcode));
} else if (type == 2) {
for (var i = 0; i <= value; i++) {
lastcode++;
goog.array.extend(list, goog.i18n.uChar.fromCharCode(lastcode));
}
}
return lastcode;
};
/**
* Gets the list of characters specified in the given string by base 88 scheme.
* @param {string} str The string encoding character list.
* @return {Array.<string>} The list of characters specified by the given string
* in base 88 scheme.
*/
goog.i18n.CharListDecompressor.prototype.toCharList = function(str) {
var metasize = 8;
var result = [];
var lastcode = 0;
var i = 0;
while (i < str.length) {
var c = this.charMap_[str.charAt(i)];
var meta = c % metasize;
var type = Math.floor(meta / 3);
var leng = (meta % 3) + 1;
if (leng == 3) {
leng++;
}
var code = this.getCodeAt_(str, i, leng);
var value = Math.floor(code / metasize);
lastcode = this.addChars_(result, lastcode, value, type);
i += leng;
}
return result;
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,58 @@
// Copyright 2013 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Contains helper functions for performing locale-sensitive
* collation.
*/
goog.provide('goog.i18n.collation');
/**
* Returns the comparator for a locale. If a locale is not explicitly specified,
* a comparator for the user's locale will be returned. Note that if the browser
* does not support locale-sensitive string comparisons, the comparator returned
* will be a simple codepoint comparator.
*
* @param {string=} opt_locale the locale that the comparator is used for.
* @return {function(string, string): number} The locale-specific comparator.
*/
goog.i18n.collation.createComparator = function(opt_locale) {
// See http://code.google.com/p/v8-i18n.
if (goog.i18n.collation.hasNativeComparator()) {
var intl = goog.global.Intl;
return new intl.Collator([opt_locale || goog.LOCALE]).compare;
} else {
return function(arg1, arg2) {
return arg1.localeCompare(arg2);
};
}
};
/**
* Returns true if a locale-sensitive comparator is available for a locale. If
* a locale is not explicitly specified, the user's locale is used instead.
*
* @param {string=} opt_locale The locale to be checked.
* @return {boolean} Whether there is a locale-sensitive comparator available
* for the locale.
*/
goog.i18n.collation.hasNativeComparator = function(opt_locale) {
var intl = goog.global.Intl;
return !!(intl && intl.Collator);
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,424 @@
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview A utility to get better currency format pattern.
*
* This module implements a new currency format representation model. It
* provides 3 currency representation forms: global, portable and local. Local
* format is the most popular format people use to represent currency in its
* circulating country without worrying about how it should be distinguished
* from other currencies. Global format is a formal representation in context
* of multiple currencies in same page, it is ISO 4217 currency code. Portable
* format is a compromise between global and local. It looks similar to how
* people would like to see how their currency is being represented in other
* media. While at the same time, it should be distinguishable to world's
* popular currencies (like USD, EUR) and currencies somewhat relevant in the
* area (like CNY in HK, though native currency is HKD). There is no guarantee
* of uniqueness.
*
*/
goog.provide('goog.i18n.currency');
goog.provide('goog.i18n.currency.CurrencyInfo');
goog.provide('goog.i18n.currency.CurrencyInfoTier2');
/**
* The mask of precision field.
* @private
*/
goog.i18n.currency.PRECISION_MASK_ = 0x07;
/**
* Whether the currency sign should be positioned after the number.
* @private
*/
goog.i18n.currency.POSITION_FLAG_ = 0x08;
/**
* Whether a space should be inserted between the number and currency sign.
* @private
*/
goog.i18n.currency.SPACE_FLAG_ = 0x20;
/**
* This function will add tier2 currency support. Be default, only tier1
* (most popular currencies) are supported. If an application really needs
* to support some of the rarely used currencies, it should call this function
* before any other functions in this namespace.
*/
goog.i18n.currency.addTier2Support = function() {
for (var key in goog.i18n.currency.CurrencyInfoTier2) {
goog.i18n.currency.CurrencyInfo[key] =
goog.i18n.currency.CurrencyInfoTier2[key];
}
};
/**
* Global currency pattern always uses ISO-4217 currency code as prefix. Local
* currency sign is added if it is different from currency code. Each currency
* is unique in this form. The negative side is that ISO code looks weird in
* some countries as people normally do not use it. Local currency sign
* alleviates the problem, but also makes it a little verbose.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Global currency pattern string for given currency.
*/
goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
var patternNum = info[0];
if (currencyCode == info[1]) {
return goog.i18n.currency.getCurrencyPattern_(patternNum, info[1]);
}
return currencyCode + ' ' +
goog.i18n.currency.getCurrencyPattern_(patternNum, info[1]);
};
/**
* Return global currency sign string for those applications
* that want to handle currency sign themselves.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Global currency sign for given currency.
*/
goog.i18n.currency.getGlobalCurrencySign = function(currencyCode) {
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
return (currencyCode == info[1]) ? currencyCode :
currencyCode + ' ' + info[1];
};
/**
* Local currency pattern is the most frequently used pattern in currency's
* native region. It does not care about how it is distinguished from other
* currencies.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Local currency pattern string for given currency.
*/
goog.i18n.currency.getLocalCurrencyPattern = function(currencyCode) {
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
return goog.i18n.currency.getCurrencyPattern_(info[0], info[1]);
};
/**
* Returns local currency sign string for those applications that need to
* handle currency sign separately.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Local currency sign for given currency.
*/
goog.i18n.currency.getLocalCurrencySign = function(currencyCode) {
return goog.i18n.currency.CurrencyInfo[currencyCode][1];
};
/**
* Portable currency pattern is a compromise between local and global. It is
* not a mere blend or mid-way between the two. Currency sign is chosen so that
* it looks familiar to native users. It also has enough information to
* distinguish itself from other popular currencies in its native region.
* In this pattern, currency sign symbols that has availability problem in
* popular fonts are also avoided.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Portable currency pattern string for given currency.
*/
goog.i18n.currency.getPortableCurrencyPattern = function(currencyCode) {
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
return goog.i18n.currency.getCurrencyPattern_(info[0], info[2]);
};
/**
* Return portable currency sign string for those applications that need to
* handle currency sign themselves.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Portable currency sign for given currency.
*/
goog.i18n.currency.getPortableCurrencySign = function(currencyCode) {
return goog.i18n.currency.CurrencyInfo[currencyCode][2];
};
/**
* This function returns the default currency sign position. Some applications
* may want to handle currency sign and currency amount separately. This
* function can be used in such situations to correctly position the currency
* sign relative to the amount.
*
* To match the behavior of ICU, position is not determined by display locale.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {boolean} true if currency should be positioned before amount field.
*/
goog.i18n.currency.isPrefixSignPosition = function(currencyCode) {
return (goog.i18n.currency.CurrencyInfo[currencyCode][0] &
goog.i18n.currency.POSITION_FLAG_) == 0;
};
/**
* This function constructs the currency pattern. Currency sign is provided. The
* pattern information is encoded in patternNum.
*
* @param {number} patternNum Encoded pattern number that has
* currency pattern information.
* @param {string} sign The currency sign that will be used in pattern.
* @return {string} currency pattern string.
* @private
*/
goog.i18n.currency.getCurrencyPattern_ = function(patternNum, sign) {
var strParts = ['#,##0'];
var precision = patternNum & goog.i18n.currency.PRECISION_MASK_;
if (precision > 0) {
strParts.push('.');
for (var i = 0; i < precision; i++) {
strParts.push('0');
}
}
if ((patternNum & goog.i18n.currency.POSITION_FLAG_) == 0) {
strParts.unshift((patternNum & goog.i18n.currency.SPACE_FLAG_) ?
"' " : "'");
strParts.unshift(sign);
strParts.unshift("'");
} else {
strParts.push((patternNum & goog.i18n.currency.SPACE_FLAG_) ? " '" : "'",
sign, "'");
}
return strParts.join('');
};
/**
* Modify currency pattern string by adjusting precision for given currency.
* Standard currency pattern will have 2 digit after decimal point.
* Examples:
* $#,##0.00 -> $#,##0 (precision == 0)
* $#,##0.00 -> $#,##0.0 (precision == 1)
* $#,##0.00 -> $#,##0.000 (precision == 3)
*
* @param {string} pattern currency pattern string.
* @param {string} currencyCode 3-letter currency code.
* @return {string} modified currency pattern string.
*/
goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
var strParts = ['0'];
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
var precision = info[0] & goog.i18n.currency.PRECISION_MASK_;
if (precision > 0) {
strParts.push('.');
for (var i = 0; i < precision; i++) {
strParts.push('0');
}
}
return pattern.replace(/0.00/g, strParts.join(''));
};
/**
* Tier 1 currency information.
*
* The first number in the array is a combination of the precision mask and
* other flags. The precision mask indicates how many decimal places to show for
* the currency. Valid values are [0..7]. The position flag indicates whether
* the currency sign should be positioned after the number. Valid values are 0
* (before the number) or 16 (after the number). The space flag indicates
* whether a space should be inserted between the currency sign and number.
* Valid values are 0 (no space) and 24 (space).
*
* The number in the array is calculated by adding together the mask and flag
* values. For example:
*
* 0: no precision (0), currency sign first (0), no space (0)
* 2: two decimals precision (2), currency sign first (0), no space (0)
* 18: two decimals precision (2), currency sign last (16), no space (0)
* 42: two decimals precision (2), currency sign last (16), space (24)
*
* @type {!Object.<!Array>}
*/
goog.i18n.currency.CurrencyInfo = {
'AED': [2, 'dh', '\u062f.\u0625.', 'DH'],
'AUD': [2, '$', 'AU$'],
'BDT': [2, '\u09F3', 'Tk'],
'BRL': [2, 'R$', 'R$'],
'CAD': [2, '$', 'C$'],
'CHF': [2, 'CHF', 'CHF'],
'CLP': [0, '$', 'CL$'],
'CNY': [2, '¥', 'RMB¥'],
'COP': [0, '$', 'COL$'],
'CRC': [0, '\u20a1', 'CR\u20a1'],
'CZK': [2, 'K\u010d', 'K\u010d'],
'DKK': [18, 'kr', 'kr'],
'DOP': [2, '$', 'RD$'],
'EGP': [2, '£', 'LE'],
'EUR': [18, '€', '€'],
'GBP': [2, '£', 'GB£'],
'HKD': [2, '$', 'HK$'],
'ILS': [2, '\u20AA', 'IL\u20AA'],
'INR': [2, '\u20B9', 'Rs'],
'ISK': [0, 'kr', 'kr'],
'JMD': [2, '$', 'JA$'],
'JPY': [0, '¥', 'JP¥'],
'KRW': [0, '\u20A9', 'KR₩'],
'LKR': [2, 'Rs', 'SLRs'],
'MNT': [0, '\u20AE', 'MN₮'],
'MXN': [2, '$', 'Mex$'],
'MYR': [2, 'RM', 'RM'],
'NOK': [18, 'kr', 'NOkr'],
'PAB': [2, 'B/.', 'B/.'],
'PEN': [2, 'S/.', 'S/.'],
'PHP': [2, '\u20B1', 'Php'],
'PKR': [0, 'Rs', 'PKRs.'],
'RUB': [42, 'руб.', 'руб.'],
'SAR': [2, 'Rial', 'Rial'],
'SEK': [2, 'kr', 'kr'],
'SGD': [2, '$', 'S$'],
'THB': [2, '\u0e3f', 'THB'],
'TRY': [2, 'TL', 'YTL'],
'TWD': [2, 'NT$', 'NT$'],
'USD': [2, '$', 'US$'],
'UYU': [2, '$', 'UY$'],
'VND': [0, '\u20AB', 'VN\u20AB'],
'YER': [0, 'Rial', 'Rial'],
'ZAR': [2, 'R', 'ZAR']
};
/**
* Tier 2 currency information.
* @type {!Object.<!Array>}
*/
goog.i18n.currency.CurrencyInfoTier2 = {
'AFN': [16, 'Af.', 'AFN'],
'ALL': [0, 'Lek', 'Lek'],
'AMD': [0, 'Dram', 'dram'],
'AOA': [2, 'Kz', 'Kz'],
'ARS': [2, '$', 'AR$'],
'AWG': [2, 'Afl.', 'Afl.'],
'AZN': [2, 'man.', 'man.'],
'BAM': [18, 'KM', 'KM'],
'BBD': [2, '$', 'Bds$'],
'BGN': [2, 'lev', 'lev'],
'BHD': [3, 'din', 'din'],
'BIF': [0, 'FBu', 'FBu'],
'BMD': [2, '$', 'BD$'],
'BND': [2, '$', 'B$'],
'BOB': [2, 'Bs', 'Bs'],
'BSD': [2, '$', 'BS$'],
'BTN': [2, 'Nu.', 'Nu.'],
'BWP': [2, 'P', 'pula'],
'BYR': [0, 'BYR', 'BYR'],
'BZD': [2, '$', 'BZ$'],
'CDF': [2, 'FrCD', 'CDF'],
'CUC': [1, '$', 'CUC$'],
'CUP': [2, '$', 'CU$'],
'CVE': [2, 'CVE', 'Esc'],
'DJF': [0, 'Fdj', 'Fdj'],
'DZD': [2, 'din', 'din'],
'ERN': [2, 'Nfk', 'Nfk'],
'ETB': [2, 'Birr', 'Birr'],
'FJD': [2, '$', 'FJ$'],
'FKP': [2, '£', 'FK£'],
'GEL': [2, 'GEL', 'GEL'],
'GHS': [2, 'GHS', 'GHS'],
'GIP': [2, '£', 'GI£'],
'GMD': [2, 'GMD', 'GMD'],
'GNF': [0, 'FG', 'FG'],
'GTQ': [2, 'Q', 'GTQ'],
'GYD': [0, '$', 'GY$'],
'HNL': [2, 'L', 'HNL'],
'HRK': [2, 'kn', 'kn'],
'HTG': [2, 'HTG', 'HTG'],
'HUF': [0, 'Ft', 'Ft'],
'IDR': [0, 'Rp', 'Rp'],
'IQD': [0, 'din', 'IQD'],
'IRR': [0, 'Rial', 'IRR'],
'JOD': [3, 'din', 'JOD'],
'KES': [2, 'Ksh', 'Ksh'],
'KGS': [2, 'KGS', 'KGS'],
'KHR': [2, 'Riel', 'KHR'],
'KMF': [0, 'CF', 'KMF'],
'KPW': [0, '\u20A9KP', 'KPW'],
'KWD': [3, 'din', 'KWD'],
'KYD': [2, '$', 'KY$'],
'KZT': [2, '\u20B8', 'KZT'],
'LAK': [0, '\u20AD', '\u20AD'],
'LBP': [0, 'L£', 'LBP'],
'LRD': [2, '$', 'L$'],
'LSL': [2, 'LSL', 'LSL'],
'LTL': [2, 'Lt', 'Lt'],
'LVL': [2, 'Ls', 'Ls'],
'LYD': [3, 'din', 'LD'],
'MAD': [2, 'dh', 'MAD'],
'MDL': [2, 'MDL', 'MDL'],
'MGA': [0, 'Ar', 'MGA'],
'MKD': [2, 'din', 'MKD'],
'MMK': [0, 'K', 'MMK'],
'MOP': [2, 'MOP', 'MOP$'],
'MRO': [0, 'MRO', 'MRO'],
'MUR': [0, 'MURs', 'MURs'],
'MWK': [2, 'MWK', 'MWK'],
'MZN': [2, 'MTn', 'MTn'],
'NAD': [2, '$', 'N$'],
'NGN': [2, '\u20A6', 'NG\u20A6'],
'NIO': [2, 'C$', 'C$'],
'NPR': [2, 'Rs', 'NPRs'],
'NZD': [2, '$', 'NZ$'],
'OMR': [3, 'Rial', 'OMR'],
'PGK': [2, 'PGK', 'PGK'],
'PLN': [2, 'z\u0142', 'z\u0142'],
'PYG': [0, 'Gs', 'PYG'],
'QAR': [2, 'Rial', 'QR'],
'RON': [2, 'RON', 'RON'],
'RSD': [0, 'din', 'RSD'],
'RWF': [0, 'RF', 'RF'],
'SBD': [2, '$', 'SI$'],
'SCR': [2, 'SCR', 'SCR'],
'SDG': [2, 'SDG', 'SDG'],
'SHP': [2, '£', 'SH£'],
'SLL': [0, 'SLL', 'SLL'],
'SOS': [0, 'SOS', 'SOS'],
'SRD': [2, '$', 'SR$'],
'STD': [0, 'Db', 'Db'],
'SYP': [16, '£', 'SY£'],
'SZL': [2, 'SZL', 'SZL'],
'TJS': [2, 'Som', 'TJS'],
'TND': [3, 'din', 'DT'],
'TOP': [2, 'T$', 'T$'],
'TTD': [2, '$', 'TT$'],
'TZS': [0, 'TSh', 'TSh'],
'UAH': [2, '\u20B4', 'UAH'],
'UGX': [0, 'UGX', 'UGX'],
'UYU': [1, '$', '$U'],
'UZS': [0, 'so\u02bcm', 'UZS'],
'VEF': [2, 'Bs', 'Bs'],
'VUV': [0, 'VUV', 'VUV'],
'WST': [2, 'WST', 'WST'],
'XAF': [0, 'FCFA', 'FCFA'],
'XCD': [2, '$', 'EC$'],
'XOF': [0, 'CFA', 'CFA'],
'XPF': [0, 'FCFP', 'FCFP'],
'ZMK': [0, 'ZMK', 'ZMK']
};
@@ -0,0 +1,210 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Currency code map.
*/
/**
* Namespace for locale number format functions
*/
goog.provide('goog.i18n.currencyCodeMap');
goog.provide('goog.i18n.currencyCodeMapTier2');
/**
* The mapping of currency symbol through intl currency code.
* The source of information is mostly from wikipedia and CLDR. Since there is
* no authoritive source, items are judged by personal perception.
* If an application need currency support that available in tier2, it
* should extend currencyCodeMap to include tier2 data by doing this:
* goog.object.extend(goog.i18n.currencyCodeMap,
* goog.i18n.currencyCodeMapTier2);
*
* @type {Object}
* @const
*/
goog.i18n.currencyCodeMap = {
'AED': '\u062F\u002e\u0625',
'ARS': '$',
'AUD': '$',
'BDT': '\u09F3',
'BRL': 'R$',
'CAD': '$',
'CHF': 'Fr.',
'CLP': '$',
'CNY': '\u00a5',
'COP': '$',
'CRC': '\u20a1',
'CUP': '$',
'CZK': 'K\u010d',
'DKK': 'kr',
'DOP': '$',
'EGP': '\u00a3',
'EUR': '\u20ac',
'GBP': '\u00a3',
'HKD': '$',
'HRK': 'kn',
'HUF': 'Ft',
'IDR': 'Rp',
'ILS': '\u20AA',
'INR': 'Rs',
'IQD': '\u0639\u062F',
'ISK': 'kr',
'JMD': '$',
'JPY': '\u00a5',
'KRW': '\u20A9',
'KWD': '\u062F\u002e\u0643',
'LKR': 'Rs',
'LVL': 'Ls',
'MNT': '\u20AE',
'MXN': '$',
'MYR': 'RM',
'NOK': 'kr',
'NZD': '$',
'PAB': 'B/.',
'PEN': 'S/.',
'PHP': 'P',
'PKR': 'Rs.',
'PLN': 'z\u0142',
'RON': 'L',
'RUB': '\u0440\u0443\u0431',
'SAR': '\u0633\u002E\u0631',
'SEK': 'kr',
'SGD': '$',
'SKK': 'Sk',
'SYP': 'SYP',
'THB': '\u0e3f',
'TRY': 'TL',
'TWD': 'NT$',
'USD': '$',
'UYU': '$',
'VEF': 'Bs.F',
'VND': '\u20AB',
'XAF': 'FCFA',
'XCD': '$',
'YER': 'YER',
'ZAR': 'R'
};
/**
* This group of currency data is unlikely to be used. In case they are,
* program need to merge it into goog.locale.CurrencyCodeMap.
*
* @type {Object}
* @const
*/
goog.i18n.currencyCodeMapTier2 = {
'AFN': '\u060b',
'ALL': 'Lek',
'AMD': '\u0564\u0580\u002e',
'ANG': '\u0083',
'AOA': 'Kz',
'AWG': '\u0192',
'AZN': 'm',
'BAM': '\u041a\u041c',
'BBD': '$',
'BGN': '\u043b\u0432',
'BHD': '\u0628\u002e\u062f\u002e',
'BIF': 'FBu',
'BMD': '$',
'BND': '$',
'BOB': 'B$',
'BSD': '$',
'BTN': 'Nu.',
'BWP': 'P',
'BYR': 'Br',
'BZD': '$',
'CDF': 'F',
'CVE': '$',
'DJF': 'Fdj',
'DZD': '\u062f\u062C',
'EEK': 'EEK',
'ERN': 'Nfk',
'ETB': 'Br',
'FJD': '$',
'FKP': '\u00a3',
'GEL': 'GEL',
'GHS': '\u20B5',
'GIP': '\u00a3',
'GMD': 'D',
'GNF': 'FG',
'GTQ': 'Q',
'GYD': '$',
'HNL': 'L',
'HTG': 'G',
'IRR': '\ufdfc',
'JOD': 'JOD',
'KES': 'KSh',
'KGS': 'som',
'KHR': '\u17DB',
'KMF': 'KMF',
'KPW': '\u20A9',
'KYD': '$',
'KZT': 'KZT',
'LAK': '\u20AD',
'LBP': '\u0644\u002e\u0644',
'LRD': '$',
'LSL': 'L',
'LTL': 'Lt',
'LYD': '\u0644\u002e\u062F',
'MAD': '\u0645\u002E\u062F\u002E',
'MDL': 'MDL',
'MGA': 'MGA',
'MKD': 'MKD',
'MMK': 'K',
'MOP': 'MOP$',
'MRO': 'UM',
'MUR': 'Rs',
'MVR': 'Rf',
'MWK': 'MK',
'MZN': 'MTn',
'NAD': '$',
'NGN': '\u20A6',
'NIO': 'C$',
'NPR': 'Rs',
'OMR': '\u0639\u002E\u062F\u002E',
'PGK': 'K',
'PYG': '\u20b2',
'QAR': '\u0642\u002E\u0631',
'RSD': '\u0420\u0421\u0414',
'RWF': 'RF',
'SBD': '$',
'SCR': 'SR',
'SDG': 'SDG',
'SHP': '\u00a3',
'SLL': 'Le',
'SOS': 'So. Sh.',
'SRD': '$',
'STD': 'Db',
'SZL': 'L',
'TJS': 'TJS',
'TMM': 'm',
'TND': '\u062F\u002e\u062A ',
'TOP': 'T$',
'TTD': '$',
'TZS': 'TZS',
'UAH': 'UAH',
'UGX': 'USh',
'UZS': 'UZS',
'VUV': 'Vt',
'WST': 'WS$',
'XOF': 'CFA',
'XPF': 'F',
'ZMK': 'ZK',
'ZWD': '$'
};
@@ -0,0 +1,673 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Functions for dealing with date/time formatting.
*/
/**
* Namespace for i18n date/time formatting functions
*/
goog.provide('goog.i18n.DateTimeFormat');
goog.provide('goog.i18n.DateTimeFormat.Format');
goog.require('goog.asserts');
goog.require('goog.i18n.DateTimeSymbols');
goog.require('goog.i18n.TimeZone');
goog.require('goog.string');
/**
* Datetime formatting functions following the pattern specification as defined
* in JDK, ICU and CLDR, with minor modification for typical usage in JS.
* Pattern specification: (Refer to JDK/ICU/CLDR)
* <pre>
* Symbol Meaning Presentation Example
* ------ ------- ------------ -------
* G era designator (Text) AD
* y# year (Number) 1996
* Y* year (week of year) (Number) 1997
* u* extended year (Number) 4601
* M month in year (Text & Number) July & 07
* d day in month (Number) 10
* h hour in am/pm (1~12) (Number) 12
* H hour in day (0~23) (Number) 0
* m minute in hour (Number) 30
* s second in minute (Number) 55
* S fractional second (Number) 978
* E day of week (Text) Tuesday
* e* day of week (local 1~7) (Number) 2
* D* day in year (Number) 189
* F* day of week in month (Number) 2 (2nd Wed in July)
* w* week in year (Number) 27
* W* week in month (Number) 2
* a am/pm marker (Text) PM
* k hour in day (1~24) (Number) 24
* K hour in am/pm (0~11) (Number) 0
* z time zone (Text) Pacific Standard Time
* Z time zone (RFC 822) (Number) -0800
* v time zone (generic) (Text) Pacific Time
* g* Julian day (Number) 2451334
* A* milliseconds in day (Number) 69540000
* ' escape for text (Delimiter) 'Date='
* '' single quote (Literal) 'o''clock'
*
* Item marked with '*' are not supported yet.
* Item marked with '#' works different than java
*
* The count of pattern letters determine the format.
* (Text): 4 or more, use full form, <4, use short or abbreviated form if it
* exists. (e.g., "EEEE" produces "Monday", "EEE" produces "Mon")
*
* (Number): the minimum number of digits. Shorter numbers are zero-padded to
* this amount (e.g. if "m" produces "6", "mm" produces "06"). Year is handled
* specially; that is, if the count of 'y' is 2, the Year will be truncated to
* 2 digits. (e.g., if "yyyy" produces "1997", "yy" produces "97".) Unlike other
* fields, fractional seconds are padded on the right with zero.
*
* (Text & Number): 3 or over, use text, otherwise use number. (e.g., "M"
* produces "1", "MM" produces "01", "MMM" produces "Jan", and "MMMM" produces
* "January".)
*
* Any characters in the pattern that are not in the ranges of ['a'..'z'] and
* ['A'..'Z'] will be treated as quoted text. For instance, characters like ':',
* '.', ' ', '#' and '@' will appear in the resulting time text even they are
* not embraced within single quotes.
* </pre>
*/
/**
* Construct a DateTimeFormat object based on current locale.
* @constructor
* @param {string|number} pattern pattern specification or pattern type.
*/
goog.i18n.DateTimeFormat = function(pattern) {
goog.asserts.assert(goog.isDef(pattern), 'Pattern must be defined');
this.patternParts_ = [];
if (typeof pattern == 'number') {
this.applyStandardPattern_(pattern);
} else {
this.applyPattern_(pattern);
}
};
/**
* Enum to identify predefined Date/Time format pattern.
* @enum {number}
*/
goog.i18n.DateTimeFormat.Format = {
FULL_DATE: 0,
LONG_DATE: 1,
MEDIUM_DATE: 2,
SHORT_DATE: 3,
FULL_TIME: 4,
LONG_TIME: 5,
MEDIUM_TIME: 6,
SHORT_TIME: 7,
FULL_DATETIME: 8,
LONG_DATETIME: 9,
MEDIUM_DATETIME: 10,
SHORT_DATETIME: 11
};
/**
* regular expression pattern for parsing pattern string
* @type {Array.<RegExp>}
* @private
*/
goog.i18n.DateTimeFormat.TOKENS_ = [
//quote string
/^\'(?:[^\']|\'\')*\'/,
// pattern chars
/^(?:G+|y+|M+|k+|S+|E+|a+|h+|K+|H+|c+|L+|Q+|d+|m+|s+|v+|z+|Z+)/,
// and all the other chars
/^[^\'GyMkSEahKHcLQdmsvzZ]+/ // and all the other chars
];
/**
* These are token types, corresponding to above token definitions.
* @enum {number}
* @private
*/
goog.i18n.DateTimeFormat.PartTypes_ = {
QUOTED_STRING: 0,
FIELD: 1,
LITERAL: 2
};
/**
* Apply specified pattern to this formatter object.
* @param {string} pattern String specifying how the date should be formatted.
* @private
*/
goog.i18n.DateTimeFormat.prototype.applyPattern_ = function(pattern) {
// lex the pattern, once for all uses
while (pattern) {
for (var i = 0; i < goog.i18n.DateTimeFormat.TOKENS_.length; ++i) {
var m = pattern.match(goog.i18n.DateTimeFormat.TOKENS_[i]);
if (m) {
var part = m[0];
pattern = pattern.substring(part.length);
if (i == goog.i18n.DateTimeFormat.PartTypes_.QUOTED_STRING) {
if (part == "''") {
part = "'"; // '' -> '
} else {
part = part.substring(1, part.length - 1); // strip quotes
part = part.replace(/\'\'/, "'");
}
}
this.patternParts_.push({ text: part, type: i });
break;
}
}
}
};
/**
* Format the given date object according to preset pattern and current lcoale.
* @param {goog.date.DateLike} date The Date object that is being formatted.
* @param {goog.i18n.TimeZone=} opt_timeZone optional, if specified, time
* related fields will be formatted based on its setting. When this field
* is not specified, "undefined" will be pass around and those function
* that really need time zone service will create a default one.
* @return {string} Formatted string for the given date.
*/
goog.i18n.DateTimeFormat.prototype.format = function(date, opt_timeZone) {
// We don't want to write code to calculate each date field because we
// want to maximize performance and minimize code size.
// JavaScript only provide API to render local time.
// Suppose target date is: 16:00 GMT-0400
// OS local time is: 12:00 GMT-0800
// We want to create a Local Date Object : 16:00 GMT-0800, and fix the
// time zone display ourselves.
// Thing get a little bit tricky when daylight time transition happens. For
// example, suppose OS timeZone is America/Los_Angeles, it is impossible to
// represent "2006/4/2 02:30" even for those timeZone that has no transition
// at this time. Because 2:00 to 3:00 on that day does not exising in
// America/Los_Angeles time zone. To avoid calculating date field through
// our own code, we uses 3 Date object instead, one for "Year, month, day",
// one for time within that day, and one for timeZone object since it need
// the real time to figure out actual time zone offset.
var diff = opt_timeZone ?
(date.getTimezoneOffset() - opt_timeZone.getOffset(date)) * 60000 : 0;
var dateForDate = diff ? new Date(date.getTime() + diff) : date;
var dateForTime = dateForDate;
// in daylight time switch on/off hour, diff adjustment could alter time
// because of timeZone offset change, move 1 day forward or backward.
if (opt_timeZone &&
dateForDate.getTimezoneOffset() != date.getTimezoneOffset()) {
diff += diff > 0 ? -24 * 60 * 60000 : 24 * 60 * 60000;
dateForTime = new Date(date.getTime() + diff);
}
var out = [];
for (var i = 0; i < this.patternParts_.length; ++i) {
var text = this.patternParts_[i].text;
if (goog.i18n.DateTimeFormat.PartTypes_.FIELD ==
this.patternParts_[i].type) {
out.push(this.formatField_(text, date, dateForDate, dateForTime,
opt_timeZone));
} else {
out.push(text);
}
}
return out.join('');
};
/**
* Apply a predefined pattern as identified by formatType, which is stored in
* locale specific repository.
* @param {number} formatType A number that identified the predefined pattern.
* @private
*/
goog.i18n.DateTimeFormat.prototype.applyStandardPattern_ =
function(formatType) {
var pattern;
if (formatType < 4) {
pattern = goog.i18n.DateTimeSymbols.DATEFORMATS[formatType];
} else if (formatType < 8) {
pattern = goog.i18n.DateTimeSymbols.TIMEFORMATS[formatType - 4];
} else if (formatType < 12) {
pattern = goog.i18n.DateTimeSymbols.DATETIMEFORMATS[formatType - 8];
pattern = pattern.replace('{1}',
goog.i18n.DateTimeSymbols.DATEFORMATS[formatType - 8]);
pattern = pattern.replace('{0}',
goog.i18n.DateTimeSymbols.TIMEFORMATS[formatType - 8]);
} else {
this.applyStandardPattern_(goog.i18n.DateTimeFormat.Format.MEDIUM_DATETIME);
return;
}
this.applyPattern_(pattern);
};
/**
* Localizes a string potentially containing numbers, replacing ASCII digits
* with native digits if specified so by the locale. Leaves other characters.
*
* Although this is not private anymore, is should not be used.
* We needed to make it public so that we can use it in goog.date.relative.
* But when CLDR gets better support for relative dates, this will be
* refactored and will become private again.
*
* @param {string} input the string to be localized, using ASCII digits.
* @return {string} localized string, potentially using native digits.
*/
goog.i18n.DateTimeFormat.prototype.localizeNumbers = function(input) {
if (goog.i18n.DateTimeSymbols.ZERODIGIT === undefined) {
return input;
}
var parts = [];
for (var i = 0; i < input.length; i++) {
var c = input.charCodeAt(i);
parts.push((0x30 <= c && c <= 0x39) ? // '0' <= c <= '9'
String.fromCharCode(goog.i18n.DateTimeSymbols.ZERODIGIT + c - 0x30) :
input.charAt(i));
}
return parts.join('');
};
/**
* Formats Era field according to pattern specified.
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatEra_ = function(count, date) {
var value = date.getFullYear() > 0 ? 1 : 0;
return count >= 4 ? goog.i18n.DateTimeSymbols.ERANAMES[value] :
goog.i18n.DateTimeSymbols.ERAS[value];
};
/**
* Formats Year field according to pattern specified
* Javascript Date object seems incapable handling 1BC and
* year before. It can show you year 0 which does not exists.
* following we just keep consistent with javascript's
* toString method. But keep in mind those things should be
* unsupported.
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatYear_ = function(count, date) {
var value = date.getFullYear();
if (value < 0) {
value = -value;
}
return this.localizeNumbers(count == 2 ?
goog.string.padNumber(value % 100, 2) :
String(value));
};
/**
* Formats Month field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatMonth_ = function(count, date) {
var value = date.getMonth();
switch (count) {
case 5: return goog.i18n.DateTimeSymbols.NARROWMONTHS[value];
case 4: return goog.i18n.DateTimeSymbols.MONTHS[value];
case 3: return goog.i18n.DateTimeSymbols.SHORTMONTHS[value];
default:
return this.localizeNumbers(goog.string.padNumber(value + 1, count));
}
};
/**
* Formats (1..24) Hours field according to pattern specified
*
* @param {number} count Number of time pattern char repeats. This controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.format24Hours_ =
function(count, date) {
return this.localizeNumbers(
goog.string.padNumber(date.getHours() || 24, count));
};
/**
* Formats Fractional seconds field according to pattern
* specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
*
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatFractionalSeconds_ =
function(count, date) {
// Fractional seconds left-justify, append 0 for precision beyond 3
var value = date.getTime() % 1000 / 1000;
return this.localizeNumbers(
value.toFixed(Math.min(3, count)).substr(2) +
(count > 3 ? goog.string.padNumber(0, count - 3) : ''));
};
/**
* Formats Day of week field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatDayOfWeek_ =
function(count, date) {
var value = date.getDay();
return count >= 4 ? goog.i18n.DateTimeSymbols.WEEKDAYS[value] :
goog.i18n.DateTimeSymbols.SHORTWEEKDAYS[value];
};
/**
* Formats Am/Pm field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatAmPm_ = function(count, date) {
var hours = date.getHours();
return goog.i18n.DateTimeSymbols.AMPMS[hours >= 12 && hours < 24 ? 1 : 0];
};
/**
* Formats (1..12) Hours field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.format1To12Hours_ =
function(count, date) {
return this.localizeNumbers(
goog.string.padNumber(date.getHours() % 12 || 12, count));
};
/**
* Formats (0..11) Hours field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.format0To11Hours_ =
function(count, date) {
return this.localizeNumbers(
goog.string.padNumber(date.getHours() % 12, count));
};
/**
* Formats (0..23) Hours field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.format0To23Hours_ =
function(count, date) {
return this.localizeNumbers(goog.string.padNumber(date.getHours(), count));
};
/**
* Formats Standalone weekday field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatStandaloneDay_ =
function(count, date) {
var value = date.getDay();
switch (count) {
case 5:
return goog.i18n.DateTimeSymbols.STANDALONENARROWWEEKDAYS[value];
case 4:
return goog.i18n.DateTimeSymbols.STANDALONEWEEKDAYS[value];
case 3:
return goog.i18n.DateTimeSymbols.STANDALONESHORTWEEKDAYS[value];
default:
return this.localizeNumbers(goog.string.padNumber(value, 1));
}
};
/**
* Formats Standalone Month field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatStandaloneMonth_ =
function(count, date) {
var value = date.getMonth();
switch (count) {
case 5:
return goog.i18n.DateTimeSymbols.STANDALONENARROWMONTHS[value];
case 4:
return goog.i18n.DateTimeSymbols.STANDALONEMONTHS[value];
case 3:
return goog.i18n.DateTimeSymbols.STANDALONESHORTMONTHS[value];
default:
return this.localizeNumbers(goog.string.padNumber(value + 1, count));
}
};
/**
* Formats Quarter field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatQuarter_ =
function(count, date) {
var value = Math.floor(date.getMonth() / 3);
return count < 4 ? goog.i18n.DateTimeSymbols.SHORTQUARTERS[value] :
goog.i18n.DateTimeSymbols.QUARTERS[value];
};
/**
* Formats Date field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatDate_ = function(count, date) {
return this.localizeNumbers(goog.string.padNumber(date.getDate(), count));
};
/**
* Formats Minutes field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatMinutes_ =
function(count, date) {
return this.localizeNumbers(goog.string.padNumber(date.getMinutes(), count));
};
/**
* Formats Seconds field according to pattern specified
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatSeconds_ =
function(count, date) {
return this.localizeNumbers(goog.string.padNumber(date.getSeconds(), count));
};
/**
* Formats TimeZone field following RFC
*
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date It holds the date object to be formatted.
* @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
* @return {string} Formatted string that represent this field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatTimeZoneRFC_ =
function(count, date, opt_timeZone) {
opt_timeZone = opt_timeZone ||
goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
// RFC 822 formats should be kept in ASCII, but localized GMT formats may need
// to use native digits.
return count < 4 ? opt_timeZone.getRFCTimeZoneString(date) :
this.localizeNumbers(opt_timeZone.getGMTString(date));
};
/**
* Generate GMT timeZone string for given date
* @param {number} count Number of time pattern char repeats, it controls
* how a field should be formatted.
* @param {goog.date.DateLike} date Whose value being evaluated.
* @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
* @return {string} GMT timeZone string.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatTimeZone_ =
function(count, date, opt_timeZone) {
opt_timeZone = opt_timeZone ||
goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
return count < 4 ? opt_timeZone.getShortName(date) :
opt_timeZone.getLongName(date);
};
/**
* Generate GMT timeZone string for given date
* @param {goog.date.DateLike} date Whose value being evaluated.
* @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
* @return {string} GMT timeZone string.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatTimeZoneId_ =
function(date, opt_timeZone) {
opt_timeZone = opt_timeZone ||
goog.i18n.TimeZone.createTimeZone(date.getTimezoneOffset());
return opt_timeZone.getTimeZoneId();
};
/**
* Formatting one date field.
* @param {string} patternStr The pattern string for the field being formatted.
* @param {goog.date.DateLike} date represents the real date to be formatted.
* @param {goog.date.DateLike} dateForDate used to resolve date fields
* for formatting.
* @param {goog.date.DateLike} dateForTime used to resolve time fields
* for formatting.
* @param {goog.i18n.TimeZone=} opt_timeZone This holds current time zone info.
* @return {string} string representation for the given field.
* @private
*/
goog.i18n.DateTimeFormat.prototype.formatField_ =
function(patternStr, date, dateForDate, dateForTime, opt_timeZone) {
var count = patternStr.length;
switch (patternStr.charAt(0)) {
case 'G': return this.formatEra_(count, dateForDate);
case 'y': return this.formatYear_(count, dateForDate);
case 'M': return this.formatMonth_(count, dateForDate);
case 'k': return this.format24Hours_(count, dateForTime);
case 'S': return this.formatFractionalSeconds_(count, dateForTime);
case 'E': return this.formatDayOfWeek_(count, dateForDate);
case 'a': return this.formatAmPm_(count, dateForTime);
case 'h': return this.format1To12Hours_(count, dateForTime);
case 'K': return this.format0To11Hours_(count, dateForTime);
case 'H': return this.format0To23Hours_(count, dateForTime);
case 'c': return this.formatStandaloneDay_(count, dateForDate);
case 'L': return this.formatStandaloneMonth_(count, dateForDate);
case 'Q': return this.formatQuarter_(count, dateForDate);
case 'd': return this.formatDate_(count, dateForDate);
case 'm': return this.formatMinutes_(count, dateForTime);
case 's': return this.formatSeconds_(count, dateForTime);
case 'v': return this.formatTimeZoneId_(date, opt_timeZone);
case 'z': return this.formatTimeZone_(count, date, opt_timeZone);
case 'Z': return this.formatTimeZoneRFC_(count, date, opt_timeZone);
default: return '';
}
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,214 @@
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Detect Grapheme Cluster Break in a pair of codepoints. Follows
* Unicode 5.1 UAX#29. Tailoring for Virama × Indic Consonants is used.
*
*/
goog.provide('goog.i18n.GraphemeBreak');
goog.require('goog.structs.InversionMap');
/**
* Enum for all Grapheme Cluster Break properties.
* These enums directly corresponds to Grapheme_Cluster_Break property values
* mentioned in http://unicode.org/reports/tr29 table 2. VIRAMA and
* INDIC_CONSONANT are for the Virama × Base tailoring mentioned in the notes.
*
* CR and LF are moved to the bottom of the list because they occur only once
* and so good candidates to take 2 decimal digit values.
* @enum {number}
* @protected
*/
goog.i18n.GraphemeBreak.property = {
ANY: 0,
CONTROL: 1,
EXTEND: 2,
PREPEND: 3,
SPACING_MARK: 4,
INDIC_CONSONANT: 5,
VIRAMA: 6,
L: 7,
V: 8,
T: 9,
LV: 10,
LVT: 11,
CR: 12,
LF: 13,
REGIONAL_INDICATOR: 14
};
/**
* Grapheme Cluster Break property values for all codepoints as inversion map.
* Constructed lazily.
*
* @type {goog.structs.InversionMap}
* @private
*/
goog.i18n.GraphemeBreak.inversions_ = null;
/**
* There are two kinds of grapheme clusters: 1) Legacy 2)Extended. This method
* is to check for legacy rules.
*
* @param {number} prop_a The property enum value of the first character.
* @param {number} prop_b The property enum value of the second character.
* @return {boolean} True if a & b do not form a cluster; False otherwise.
* @private
*/
goog.i18n.GraphemeBreak.applyLegacyBreakRules_ = function(prop_a, prop_b) {
var prop = goog.i18n.GraphemeBreak.property;
if (prop_a == prop.CR && prop_b == prop.LF) {
return false;
}
if (prop_a == prop.CONTROL || prop_a == prop.CR || prop_a == prop.LF) {
return true;
}
if (prop_b == prop.CONTROL || prop_b == prop.CR || prop_b == prop.LF) {
return true;
}
if ((prop_a == prop.L) &&
(prop_b == prop.L || prop_b == prop.V ||
prop_b == prop.LV || prop_b == prop.LVT)) {
return false;
}
if ((prop_a == prop.LV || prop_a == prop.V) &&
(prop_b == prop.V || prop_b == prop.T)) {
return false;
}
if ((prop_a == prop.LVT || prop_a == prop.T) && (prop_b == prop.T)) {
return false;
}
if (prop_b == prop.EXTEND || prop_b == prop.VIRAMA) {
return false;
}
if (prop_a == prop.VIRAMA && prop_b == prop.INDIC_CONSONANT) {
return false;
}
return true;
};
/**
* Method to return property enum value of the codepoint. If it is Hangul LV or
* LVT, then it is computed; for the rest it is picked from the inversion map.
* @param {number} acode The code point value of the character.
* @return {number} Property enum value of codepoint.
* @private
*/
goog.i18n.GraphemeBreak.getBreakProp_ = function(acode) {
if (0xAC00 <= acode && acode <= 0xD7A3) {
var prop = goog.i18n.GraphemeBreak.property;
if (acode % 0x1C == 0x10) {
return prop.LV;
}
return prop.LVT;
} else {
if (!goog.i18n.GraphemeBreak.inversions_) {
goog.i18n.GraphemeBreak.inversions_ = new goog.structs.InversionMap(
[0, 10, 1, 2, 1, 18, 95, 33, 13, 1, 594, 112, 275, 7, 263, 45, 1, 1,
1, 2, 1, 2, 1, 1, 56, 5, 11, 11, 48, 21, 16, 1, 101, 7, 1, 1, 6, 2,
2, 1, 4, 33, 1, 1, 1, 30, 27, 91, 11, 58, 9, 34, 4, 1, 9, 1, 3, 1,
5, 43, 3, 136, 31, 1, 17, 37, 1, 1, 1, 1, 3, 8, 4, 1, 2, 1, 7, 8, 2,
2, 21, 8, 1, 2, 17, 39, 1, 1, 1, 2, 6, 6, 1, 9, 5, 4, 2, 2, 12, 2,
15, 2, 1, 17, 39, 2, 3, 12, 4, 8, 6, 17, 2, 3, 14, 1, 17, 39, 1, 1,
3, 8, 4, 1, 20, 2, 29, 1, 2, 17, 39, 1, 1, 2, 1, 6, 6, 9, 6, 4, 2,
2, 13, 1, 16, 1, 18, 41, 1, 1, 1, 12, 1, 9, 1, 41, 3, 17, 37, 4, 3,
5, 7, 8, 3, 2, 8, 2, 30, 2, 17, 39, 1, 1, 1, 1, 2, 1, 3, 1, 5, 1, 8,
9, 1, 3, 2, 30, 2, 17, 38, 3, 1, 2, 5, 7, 1, 9, 1, 10, 2, 30, 2, 22,
48, 5, 1, 2, 6, 7, 19, 2, 13, 46, 2, 1, 1, 1, 6, 1, 12, 8, 50, 46,
2, 1, 1, 1, 9, 11, 6, 14, 2, 58, 2, 27, 1, 1, 1, 1, 1, 4, 2, 49, 14,
1, 4, 1, 1, 2, 5, 48, 9, 1, 57, 33, 12, 4, 1, 6, 1, 2, 2, 2, 1, 16,
2, 4, 2, 2, 4, 3, 1, 3, 2, 7, 3, 4, 13, 1, 1, 1, 2, 6, 1, 1, 14, 1,
98, 96, 72, 88, 349, 3, 931, 15, 2, 1, 14, 15, 2, 1, 14, 15, 2, 15,
15, 14, 35, 17, 2, 1, 7, 8, 1, 2, 9, 1, 1, 9, 1, 45, 3, 155, 1, 87,
31, 3, 4, 2, 9, 1, 6, 3, 20, 19, 29, 44, 9, 3, 2, 1, 69, 23, 2, 3,
4, 45, 6, 2, 1, 1, 1, 8, 1, 1, 1, 2, 8, 6, 13, 128, 4, 1, 14, 33, 1,
1, 5, 1, 1, 5, 1, 1, 1, 7, 31, 9, 12, 2, 1, 7, 23, 1, 4, 2, 2, 2, 2,
2, 11, 3, 2, 36, 2, 1, 1, 2, 3, 1, 1, 3, 2, 12, 36, 8, 8, 2, 2, 21,
3, 128, 3, 1, 13, 1, 7, 4, 1, 4, 2, 1, 203, 64, 523, 1, 2, 2, 24, 7,
49, 16, 96, 33, 3070, 3, 141, 1, 96, 32, 554, 6, 105, 2, 30164, 4,
1, 10, 33, 1, 80, 2, 272, 1, 3, 1, 4, 1, 23, 2, 2, 1, 24, 30, 4, 4,
3, 8, 1, 1, 13, 2, 16, 34, 16, 1, 27, 18, 24, 24, 4, 8, 2, 23, 11,
1, 1, 12, 32, 3, 1, 5, 3, 3, 36, 1, 2, 4, 2, 1, 3, 1, 69, 35, 6, 2,
2, 2, 2, 12, 1, 8, 1, 1, 18, 16, 1, 3, 6, 1, 5, 48, 1, 1, 3, 2, 2,
5, 2, 1, 1, 32, 9, 1, 2, 2, 5, 1, 1, 201, 14, 2, 1, 1, 9, 8, 2, 1,
2, 1, 2, 1, 1, 1, 18, 11184, 27, 49, 1028, 1024, 6942, 1, 737, 16,
16, 7, 216, 1, 158, 2, 89, 3, 513, 1, 2051, 15, 40, 7, 1, 1472, 1,
1, 1, 53, 14, 1, 57, 2, 1, 45, 3, 4, 2, 1, 1, 2, 1, 66, 3, 36, 5, 1,
6, 2, 75, 2, 1, 48, 3, 9, 1, 1, 1258, 1, 1, 1, 2, 6, 1, 1, 22681,
62, 4, 25042, 1, 1, 3, 3, 1, 5, 8, 8, 2, 7, 30, 4, 148, 3, 8097, 26,
790017, 255],
[1, 13, 1, 12, 1, 0, 1, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
2, 0, 1, 0, 2, 0, 2, 0, 2, 0, 2, 1, 0, 2, 0, 2, 0, 2, 0, 1, 0, 2, 0,
2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 4, 0, 5, 2, 4, 2,
0, 4, 2, 4, 6, 4, 0, 2, 5, 0, 2, 0, 5, 2, 4, 0, 5, 2, 0, 2, 4, 2, 4,
6, 0, 2, 5, 0, 2, 0, 5, 0, 2, 4, 0, 5, 2, 4, 2, 6, 2, 5, 0, 2, 0, 2,
4, 0, 5, 2, 0, 4, 2, 4, 6, 0, 2, 0, 2, 4, 0, 5, 2, 0, 2, 4, 2, 4, 6,
2, 5, 0, 2, 0, 5, 0, 2, 0, 5, 2, 4, 2, 4, 6, 0, 2, 0, 4, 0, 5, 0, 2,
4, 2, 6, 2, 5, 0, 2, 0, 4, 0, 5, 2, 0, 4, 2, 4, 2, 4, 2, 4, 2, 6, 2,
5, 0, 2, 0, 4, 0, 5, 0, 2, 4, 2, 4, 6, 0, 2, 0, 2, 0, 4, 0, 5, 6, 2,
4, 2, 4, 2, 4, 0, 5, 0, 2, 0, 4, 2, 6, 0, 2, 0, 5, 0, 2, 0, 4, 2, 0,
2, 0, 5, 0, 2, 0, 2, 0, 2, 0, 2, 0, 4, 5, 2, 4, 2, 6, 0, 2, 0, 2, 0,
2, 0, 5, 0, 2, 4, 2, 0, 6, 4, 2, 5, 0, 5, 0, 4, 2, 5, 2, 5, 0, 5, 0,
5, 2, 5, 2, 0, 4, 2, 0, 2, 5, 0, 2, 0, 7, 8, 9, 0, 2, 0, 5, 2, 6, 0,
5, 2, 6, 0, 5, 2, 0, 5, 2, 5, 0, 2, 4, 2, 4, 2, 4, 2, 6, 2, 0, 2, 0,
2, 0, 2, 0, 5, 2, 4, 2, 4, 2, 4, 2, 0, 5, 0, 5, 0, 4, 0, 4, 0, 5, 2,
4, 0, 5, 0, 5, 4, 2, 4, 2, 6, 0, 2, 0, 2, 4, 2, 0, 2, 4, 0, 5, 2, 4,
2, 4, 2, 4, 2, 4, 6, 5, 0, 2, 0, 2, 4, 0, 5, 4, 2, 4, 2, 6, 4, 5, 0,
5, 0, 5, 0, 2, 4, 2, 4, 2, 4, 2, 6, 0, 5, 4, 2, 4, 2, 0, 5, 0, 2, 0,
2, 4, 2, 0, 2, 0, 4, 2, 0, 2, 0, 1, 2, 1, 0, 1, 0, 1, 0, 2, 0, 2, 0,
6, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 6, 5, 2, 5, 4,
2, 4, 0, 5, 0, 5, 0, 5, 0, 5, 0, 4, 0, 5, 4, 6, 0, 2, 0, 5, 0, 2, 0,
5, 2, 4, 6, 0, 7, 2, 4, 0, 5, 0, 5, 2, 4, 2, 4, 2, 4, 6, 0, 5, 2, 4,
2, 4, 2, 0, 2, 0, 2, 4, 0, 5, 0, 5, 0, 5, 0, 5, 2, 0, 2, 0, 2, 0, 2,
0, 2, 0, 5, 4, 2, 4, 0, 4, 6, 0, 5, 0, 5, 0, 5, 0, 4, 2, 4, 2, 4, 0,
4, 6, 0, 11, 8, 9, 0, 2, 0, 2, 0, 2, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2,
0, 2, 0, 2, 6, 0, 4, 2, 4, 0, 2, 6, 0, 2, 4, 0, 4, 2, 4, 6, 2, 0, 1,
0, 2, 0, 2, 4, 2, 6, 0, 2, 4, 0, 4, 2, 4, 6, 0, 2, 4, 2, 4, 2, 6, 2,
0, 4, 2, 0, 2, 4, 2, 0, 4, 2, 1, 2, 0, 2, 0, 2, 0, 2, 0, 14, 0, 1,
2],
true);
}
return /** @type {number} */ (
goog.i18n.GraphemeBreak.inversions_.at(acode));
}
};
/**
* There are two kinds of grapheme clusters: 1) Legacy 2)Extended. This method
* is to check for both using a boolean flag to switch between them.
* @param {number} a The code point value of the first character.
* @param {number} b The code point value of the second character.
* @param {boolean=} opt_extended If true, indicates extended grapheme cluster;
* If false, indicates legacy cluster.
* @return {boolean} True if a & b do not form a cluster; False otherwise.
*/
goog.i18n.GraphemeBreak.hasGraphemeBreak = function(a, b, opt_extended) {
var prop_a = goog.i18n.GraphemeBreak.getBreakProp_(a);
var prop_b = goog.i18n.GraphemeBreak.getBreakProp_(b);
var prop = goog.i18n.GraphemeBreak.property;
return goog.i18n.GraphemeBreak.applyLegacyBreakRules_(prop_a, prop_b) &&
!(opt_extended &&
(prop_a == prop.PREPEND || prop_b == prop.SPACING_MARK));
};
@@ -0,0 +1,771 @@
// Copyright 2010 The Closure Library Authors. All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Message/plural format library with locale support.
*
* Message format grammar:
*
* messageFormatPattern := string ( "{" messageFormatElement "}" string )*
* messageFormatElement := argumentIndex [ "," elementFormat ]
* elementFormat := "plural" "," pluralStyle
* | "selectordinal" "," ordinalStyle
* | "select" "," selectStyle
* pluralStyle := pluralFormatPattern
* ordinalStyle := selectFormatPattern
* selectStyle := selectFormatPattern
* pluralFormatPattern := [ "offset" ":" offsetIndex ] pluralForms*
* selectFormatPattern := pluralForms*
* pluralForms := stringKey "{" ( "{" messageFormatElement "}"|string )* "}"
*
* This is a subset of the ICU MessageFormatSyntax:
* http://userguide.icu-project.org/formatparse/messages
* See also http://go/plurals and http://go/ordinals for internal details.
*
*
* Message example:
*
* I see {NUM_PEOPLE, plural, offset:1
* =0 {no one at all}
* =1 {{WHO}}
* one {{WHO} and one other person}
* other {{WHO} and # other people}}
* in {PLACE}.
*
* Calling format({'NUM_PEOPLE': 2, 'WHO': 'Mark', 'PLACE': 'Athens'}) would
* produce "I see Mark and one other person in Athens." as output.
*
* OR:
*
* {NUM_FLOOR, selectordinal,
* one {Take the elevator to the #st floor.}
* two {Take the elevator to the #nd floor.}
* few {Take the elevator to the #rd floor.}
* other {Take the elevator to the #th floor.}}
*
* Calling format({'NUM_FLOOR': 22}) would produce
* "Take the elevator to the 22nd floor".
*
* See messageformat_test.html for more examples.
*/
goog.provide('goog.i18n.MessageFormat');
goog.require('goog.asserts');
goog.require('goog.i18n.NumberFormat');
goog.require('goog.i18n.ordinalRules');
goog.require('goog.i18n.pluralRules');
/**
* Constructor of MessageFormat.
* @param {string} pattern The pattern we parse and apply positional parameters
* to.
* @constructor
*/
goog.i18n.MessageFormat = function(pattern) {
/**
* All encountered literals during parse stage. Indices tell us the order of
* replacement.
* @type {!Array.<string>}
* @private
*/
this.literals_ = [];
/**
* Input pattern gets parsed into objects for faster formatting.
* @type {!Array.<!Object>}
* @private
*/
this.parsedPattern_ = [];
/**
* Locale aware number formatter.
* @type {goog.i18n.NumberFormat}
* @private
*/
this.numberFormatter_ = new goog.i18n.NumberFormat(
goog.i18n.NumberFormat.Format.DECIMAL);
this.parsePattern_(pattern);
};
/**
* Literal strings, including '', are replaced with \uFDDF_x_ for
* parsing purposes, and recovered during format phase.
* \uFDDF is a Unicode nonprinting character, not expected to be found in the
* typical message.
* @type {string}
* @private
*/
goog.i18n.MessageFormat.LITERAL_PLACEHOLDER_ = '\uFDDF_';
/**
* Marks a string and block during parsing.
* @enum {number}
* @private
*/
goog.i18n.MessageFormat.Element_ = {
STRING: 0,
BLOCK: 1
};
/**
* Block type.
* @enum {number}
* @private
*/
goog.i18n.MessageFormat.BlockType_ = {
PLURAL: 0,
ORDINAL: 1,
SELECT: 2,
SIMPLE: 3,
STRING: 4,
UNKNOWN: 5
};
/**
* Mandatory option in both select and plural form.
* @type {string}
* @private
*/
goog.i18n.MessageFormat.OTHER_ = 'other';
/**
* Regular expression for looking for string literals.
* @type {RegExp}
* @private
*/
goog.i18n.MessageFormat.REGEX_LITERAL_ = new RegExp("'([{}#].*?)'", 'g');
/**
* Regular expression for looking for '' in the message.
* @type {RegExp}
* @private
*/
goog.i18n.MessageFormat.REGEX_DOUBLE_APOSTROPHE_ = new RegExp("''", 'g');
/**
* Formats a message, treating '#' with special meaning representing
* the number (plural_variable - offset).
* @param {!Object} namedParameters Parameters that either
* influence the formatting or are used as actual data.
* I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}),
* object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters.
* 1st parameter could mean 5 people, which could influence plural format,
* and 2nd parameter is just a data to be printed out in proper position.
* @return {string} Formatted message.
*/
goog.i18n.MessageFormat.prototype.format = function(namedParameters) {
return this.format_(namedParameters, false);
};
/**
* Formats a message, treating '#' as literary character.
* @param {!Object} namedParameters Parameters that either
* influence the formatting or are used as actual data.
* I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}),
* object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters.
* 1st parameter could mean 5 people, which could influence plural format,
* and 2nd parameter is just a data to be printed out in proper position.
* @return {string} Formatted message.
*/
goog.i18n.MessageFormat.prototype.formatIgnoringPound =
function(namedParameters) {
return this.format_(namedParameters, true);
};
/**
* Formats a message.
* @param {!Object} namedParameters Parameters that either
* influence the formatting or are used as actual data.
* I.e. in call to fmt.format({'NUM_PEOPLE': 5, 'NAME': 'Angela'}),
* object {'NUM_PEOPLE': 5, 'NAME': 'Angela'} holds positional parameters.
* 1st parameter could mean 5 people, which could influence plural format,
* and 2nd parameter is just a data to be printed out in proper position.
* @param {boolean} ignorePound If true, treat '#' in plural messages as a
* literary character, else treat it as an ICU syntax character, resolving
* to the number (plural_variable - offset).
* @return {string} Formatted message.
* @private
*/
goog.i18n.MessageFormat.prototype.format_ =
function(namedParameters, ignorePound) {
if (this.parsedPattern_.length == 0) {
return '';
}
var result = [];
this.formatBlock_(this.parsedPattern_, namedParameters, ignorePound, result);
var message = result.join('');
if (!ignorePound) {
goog.asserts.assert(message.search('#') == -1, 'Not all # were replaced.');
}
while (this.literals_.length > 0) {
message = message.replace(this.buildPlaceholder_(this.literals_),
this.literals_.pop());
}
return message;
};
/**
* Parses generic block and returns a formatted string.
* @param {!Array.<!Object>} parsedPattern Holds parsed tree.
* @param {!Object} namedParameters Parameters that either influence
* the formatting or are used as actual data.
* @param {boolean} ignorePound If true, treat '#' in plural messages as a
* literary character, else treat it as an ICU syntax character, resolving
* to the number (plural_variable - offset).
* @param {!Array.<!string>} result Each formatting stage appends its product
* to the result.
* @private
*/
goog.i18n.MessageFormat.prototype.formatBlock_ = function(
parsedPattern, namedParameters, ignorePound, result) {
for (var i = 0; i < parsedPattern.length; i++) {
switch (parsedPattern[i].type) {
case goog.i18n.MessageFormat.BlockType_.STRING:
result.push(parsedPattern[i].value);
break;
case goog.i18n.MessageFormat.BlockType_.SIMPLE:
var pattern = parsedPattern[i].value;
this.formatSimplePlaceholder_(pattern, namedParameters, result);
break;
case goog.i18n.MessageFormat.BlockType_.SELECT:
var pattern = parsedPattern[i].value;
this.formatSelectBlock_(pattern, namedParameters, ignorePound, result);
break;
case goog.i18n.MessageFormat.BlockType_.PLURAL:
var pattern = parsedPattern[i].value;
this.formatPluralOrdinalBlock_(pattern,
namedParameters,
goog.i18n.pluralRules.select,
ignorePound,
result);
break;
case goog.i18n.MessageFormat.BlockType_.ORDINAL:
var pattern = parsedPattern[i].value;
this.formatPluralOrdinalBlock_(pattern,
namedParameters,
goog.i18n.ordinalRules.select,
ignorePound,
result);
break;
default:
goog.asserts.fail('Unrecognized block type.');
}
}
};
/**
* Formats simple placeholder.
* @param {!Object} parsedPattern JSON object containing placeholder info.
* @param {!Object} namedParameters Parameters that are used as actual data.
* @param {!Array.<!string>} result Each formatting stage appends its product
* to the result.
* @private
*/
goog.i18n.MessageFormat.prototype.formatSimplePlaceholder_ = function(
parsedPattern, namedParameters, result) {
var value = namedParameters[parsedPattern];
if (!goog.isDef(value)) {
result.push('Undefined parameter - ' + parsedPattern);
return;
}
// Don't push the value yet, it may contain any of # { } in it which
// will break formatter. Insert a placeholder and replace at the end.
this.literals_.push(value);
result.push(this.buildPlaceholder_(this.literals_));
};
/**
* Formats select block. Only one option is selected.
* @param {!Object} parsedPattern JSON object containing select block info.
* @param {!Object} namedParameters Parameters that either influence
* the formatting or are used as actual data.
* @param {boolean} ignorePound If true, treat '#' in plural messages as a
* literary character, else treat it as an ICU syntax character, resolving
* to the number (plural_variable - offset).
* @param {!Array.<!string>} result Each formatting stage appends its product
* to the result.
* @private
*/
goog.i18n.MessageFormat.prototype.formatSelectBlock_ = function(
parsedPattern, namedParameters, ignorePound, result) {
var argumentIndex = parsedPattern.argumentIndex;
if (!goog.isDef(namedParameters[argumentIndex])) {
result.push('Undefined parameter - ' + argumentIndex);
return;
}
var option = parsedPattern[namedParameters[argumentIndex]];
if (!goog.isDef(option)) {
option = parsedPattern[goog.i18n.MessageFormat.OTHER_];
goog.asserts.assertArray(
option, 'Invalid option or missing other option for select block.');
}
this.formatBlock_(option, namedParameters, ignorePound, result);
};
/**
* Formats plural or selectordinal block. Only one option is selected and all #
* are replaced.
* @param {!Object} parsedPattern JSON object containing plural block info.
* @param {!Object} namedParameters Parameters that either influence
* the formatting or are used as actual data.
* @param {!function(number):string} pluralSelector A select function from
* goog.i18n.pluralRules or goog.i18n.ordinalRules which determines which
* plural/ordinal form to use based on the input number's cardinality.
* @param {boolean} ignorePound If true, treat '#' in plural messages as a
* literary character, else treat it as an ICU syntax character, resolving
* to the number (plural_variable - offset).
* @param {!Array.<!string>} result Each formatting stage appends its product
* to the result.
* @private
*/
goog.i18n.MessageFormat.prototype.formatPluralOrdinalBlock_ = function(
parsedPattern, namedParameters, pluralSelector, ignorePound, result) {
var argumentIndex = parsedPattern.argumentIndex;
var argumentOffset = parsedPattern.argumentOffset;
var pluralValue = +namedParameters[argumentIndex];
if (isNaN(pluralValue)) {
// TODO(user): Distinguish between undefined and invalid parameters.
result.push('Undefined or invalid parameter - ' + argumentIndex);
return;
}
var diff = pluralValue - argumentOffset;
// Check if there is an exact match.
var option = parsedPattern[namedParameters[argumentIndex]];
if (!goog.isDef(option)) {
goog.asserts.assert(diff >= 0, 'Argument index smaller than offset.');
var item = pluralSelector(diff);
goog.asserts.assertString(item, 'Invalid plural key.');
option = parsedPattern[item];
// If option is not provided fall back to "other".
if (!goog.isDef(option)) {
option = parsedPattern[goog.i18n.MessageFormat.OTHER_];
}
goog.asserts.assertArray(
option, 'Invalid option or missing other option for plural block.');
}
var pluralResult = [];
this.formatBlock_(option, namedParameters, ignorePound, pluralResult);
var plural = pluralResult.join('');
goog.asserts.assertString(plural, 'Empty block in plural.');
if (ignorePound) {
result.push(plural);
} else {
var localeAwareDiff = this.numberFormatter_.format(diff);
result.push(plural.replace(/#/g, localeAwareDiff));
}
};
/**
* Parses input pattern into an array, for faster reformatting with
* different input parameters.
* Parsing is locale independent.
* @param {string} pattern MessageFormat pattern to parse.
* @private
*/
goog.i18n.MessageFormat.prototype.parsePattern_ = function(pattern) {
if (pattern) {
pattern = this.insertPlaceholders_(pattern);
this.parsedPattern_ = this.parseBlock_(pattern);
}
};
/**
* Replaces string literals with literal placeholders.
* Literals are string of the form '}...', '{...' and '#...' where ... is
* set of characters not containing '
* Builds a dictionary so we can recover literals during format phase.
* @param {string} pattern Pattern to clean up.
* @return {string} Pattern with literals replaced with placeholders.
* @private
*/
goog.i18n.MessageFormat.prototype.insertPlaceholders_ = function(pattern) {
var literals = this.literals_;
var buildPlaceholder = goog.bind(this.buildPlaceholder_, this);
// First replace '' with single quote placeholder since they can be found
// inside other literals.
pattern = pattern.replace(
goog.i18n.MessageFormat.REGEX_DOUBLE_APOSTROPHE_,
function() {
literals.push("'");
return buildPlaceholder(literals);
});
pattern = pattern.replace(
goog.i18n.MessageFormat.REGEX_LITERAL_,
function(match, text) {
literals.push(text);
return buildPlaceholder(literals);
});
return pattern;
};
/**
* Breaks pattern into strings and top level {...} blocks.
* @param {string} pattern (sub)Pattern to be broken.
* @return {Array.<Object>} Each item is {type, value}.
* @private
*/
goog.i18n.MessageFormat.prototype.extractParts_ = function(pattern) {
var prevPos = 0;
var inBlock = false;
var braceStack = [];
var results = [];
var braces = /[{}]/g;
braces.lastIndex = 0; // lastIndex doesn't get set to 0 so we have to.
var match;
while (match = braces.exec(pattern)) {
var pos = match.index;
if (match[0] == '}') {
var brace = braceStack.pop();
goog.asserts.assert(goog.isDef(brace) && brace == '{',
'No matching { for }.');
if (braceStack.length == 0) {
// End of the block.
var part = {};
part.type = goog.i18n.MessageFormat.Element_.BLOCK;
part.value = pattern.substring(prevPos, pos);
results.push(part);
prevPos = pos + 1;
inBlock = false;
}
} else {
if (braceStack.length == 0) {
inBlock = true;
var substring = pattern.substring(prevPos, pos);
if (substring != '') {
results.push({
type: goog.i18n.MessageFormat.Element_.STRING,
value: substring
});
}
prevPos = pos + 1;
}
braceStack.push('{');
}
}
// Take care of the final string, and check if the braceStack is empty.
goog.asserts.assert(braceStack.length == 0,
'There are mismatched { or } in the pattern.');
var substring = pattern.substring(prevPos);
if (substring != '') {
results.push({
type: goog.i18n.MessageFormat.Element_.STRING,
value: substring
});
}
return results;
};
/**
* A regular expression to parse the plural block, extracting the argument
* index and offset (if any).
* @type {RegExp}
* @private
*/
goog.i18n.MessageFormat.PLURAL_BLOCK_RE_ =
/^\s*(\w+)\s*,\s*plural\s*,(?:\s*offset:(\d+))?/;
/**
* A regular expression to parse the ordinal block, extracting the argument
* index.
* @type {RegExp}
* @private
*/
goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_ = /^\s*(\w+)\s*,\s*selectordinal\s*,/;
/**
* A regular expression to parse the select block, extracting the argument
* index.
* @type {RegExp}
* @private
*/
goog.i18n.MessageFormat.SELECT_BLOCK_RE_ = /^\s*(\w+)\s*,\s*select\s*,/;
/**
* Detects which type of a block is the pattern.
* @param {string} pattern Content of the block.
* @return {goog.i18n.MessageFormat.BlockType_} One of the block types.
* @private
*/
goog.i18n.MessageFormat.prototype.parseBlockType_ = function(pattern) {
if (goog.i18n.MessageFormat.PLURAL_BLOCK_RE_.test(pattern)) {
return goog.i18n.MessageFormat.BlockType_.PLURAL;
}
if (goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_.test(pattern)) {
return goog.i18n.MessageFormat.BlockType_.ORDINAL;
}
if (goog.i18n.MessageFormat.SELECT_BLOCK_RE_.test(pattern)) {
return goog.i18n.MessageFormat.BlockType_.SELECT;
}
if (/^\s*\w+\s*/.test(pattern)) {
return goog.i18n.MessageFormat.BlockType_.SIMPLE;
}
return goog.i18n.MessageFormat.BlockType_.UNKNOWN;
};
/**
* Parses generic block.
* @param {string} pattern Content of the block to parse.
* @return {!Array.<!Object>} Subblocks marked as strings, select...
* @private
*/
goog.i18n.MessageFormat.prototype.parseBlock_ = function(pattern) {
var result = [];
var parts = this.extractParts_(pattern);
for (var i = 0; i < parts.length; i++) {
var block = {};
if (goog.i18n.MessageFormat.Element_.STRING == parts[i].type) {
block.type = goog.i18n.MessageFormat.BlockType_.STRING;
block.value = parts[i].value;
} else if (goog.i18n.MessageFormat.Element_.BLOCK == parts[i].type) {
var blockType = this.parseBlockType_(parts[i].value);
switch (blockType) {
case goog.i18n.MessageFormat.BlockType_.SELECT:
block.type = goog.i18n.MessageFormat.BlockType_.SELECT;
block.value = this.parseSelectBlock_(parts[i].value);
break;
case goog.i18n.MessageFormat.BlockType_.PLURAL:
block.type = goog.i18n.MessageFormat.BlockType_.PLURAL;
block.value = this.parsePluralBlock_(parts[i].value);
break;
case goog.i18n.MessageFormat.BlockType_.ORDINAL:
block.type = goog.i18n.MessageFormat.BlockType_.ORDINAL;
block.value = this.parseOrdinalBlock_(parts[i].value);
break;
case goog.i18n.MessageFormat.BlockType_.SIMPLE:
block.type = goog.i18n.MessageFormat.BlockType_.SIMPLE;
block.value = parts[i].value;
break;
default:
goog.asserts.fail('Unknown block type.');
}
} else {
goog.asserts.fail('Unknown part of the pattern.');
}
result.push(block);
}
return result;
};
/**
* Parses a select type of a block and produces JSON object for it.
* @param {string} pattern Subpattern that needs to be parsed as select pattern.
* @return {Object} Object with select block info.
* @private
*/
goog.i18n.MessageFormat.prototype.parseSelectBlock_ = function(pattern) {
var argumentIndex = '';
var replaceRegex = goog.i18n.MessageFormat.SELECT_BLOCK_RE_;
pattern = pattern.replace(replaceRegex, function(string, name) {
argumentIndex = name;
return '';
});
var result = {};
result.argumentIndex = argumentIndex;
var parts = this.extractParts_(pattern);
// Looking for (key block)+ sequence. One of the keys has to be "other".
var pos = 0;
while (pos < parts.length) {
var key = parts[pos].value;
goog.asserts.assertString(key, 'Missing select key element.');
pos++;
goog.asserts.assert(pos < parts.length,
'Missing or invalid select value element.');
if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) {
var value = this.parseBlock_(parts[pos].value);
} else {
goog.asserts.fail('Expected block type.');
}
result[key.replace(/\s/g, '')] = value;
pos++;
}
goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_],
'Missing other key in select statement.');
return result;
};
/**
* Parses a plural type of a block and produces JSON object for it.
* @param {string} pattern Subpattern that needs to be parsed as plural pattern.
* @return {Object} Object with select block info.
* @private
*/
goog.i18n.MessageFormat.prototype.parsePluralBlock_ = function(pattern) {
var argumentIndex = '';
var argumentOffset = 0;
var replaceRegex = goog.i18n.MessageFormat.PLURAL_BLOCK_RE_;
pattern = pattern.replace(replaceRegex, function(string, name, offset) {
argumentIndex = name;
if (offset) {
argumentOffset = parseInt(offset, 10);
}
return '';
});
var result = {};
result.argumentIndex = argumentIndex;
result.argumentOffset = argumentOffset;
var parts = this.extractParts_(pattern);
// Looking for (key block)+ sequence.
var pos = 0;
while (pos < parts.length) {
var key = parts[pos].value;
goog.asserts.assertString(key, 'Missing plural key element.');
pos++;
goog.asserts.assert(pos < parts.length,
'Missing or invalid plural value element.');
if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) {
var value = this.parseBlock_(parts[pos].value);
} else {
goog.asserts.fail('Expected block type.');
}
result[key.replace(/\s*(?:=)?(\w+)\s*/, '$1')] = value;
pos++;
}
goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_],
'Missing other key in plural statement.');
return result;
};
/**
* Parses an ordinal type of a block and produces JSON object for it.
* For example the input string:
* '{FOO, selectordinal, one {Message A}other {Message B}}'
* Should result in the output object:
* {
* argumentIndex: 'FOO',
* argumentOffest: 0,
* one: [ { type: 4, value: 'Message A' } ],
* other: [ { type: 4, value: 'Message B' } ]
* }
* @param {string} pattern Subpattern that needs to be parsed as plural pattern.
* @return {Object} Object with select block info.
* @private
*/
goog.i18n.MessageFormat.prototype.parseOrdinalBlock_ = function(pattern) {
var argumentIndex = '';
var replaceRegex = goog.i18n.MessageFormat.ORDINAL_BLOCK_RE_;
pattern = pattern.replace(replaceRegex, function(string, name) {
argumentIndex = name;
return '';
});
var result = {};
result.argumentIndex = argumentIndex;
result.argumentOffset = 0;
var parts = this.extractParts_(pattern);
// Looking for (key block)+ sequence.
var pos = 0;
while (pos < parts.length) {
var key = parts[pos].value;
goog.asserts.assertString(key, 'Missing ordinal key element.');
pos++;
goog.asserts.assert(pos < parts.length,
'Missing or invalid ordinal value element.');
if (goog.i18n.MessageFormat.Element_.BLOCK == parts[pos].type) {
var value = this.parseBlock_(parts[pos].value);
} else {
goog.asserts.fail('Expected block type.');
}
result[key.replace(/\s*(?:=)?(\w+)\s*/, '$1')] = value;
pos++;
}
goog.asserts.assertArray(result[goog.i18n.MessageFormat.OTHER_],
'Missing other key in selectordinal statement.');
return result;
};
/**
* Builds a placeholder from the last index of the array.
* @param {!Array} literals All literals encountered during parse.
* @return {string} \uFDDF_ + last index + _.
* @private
*/
goog.i18n.MessageFormat.prototype.buildPlaceholder_ = function(literals) {
goog.asserts.assert(literals.length > 0, 'Literal array is empty.');
var index = (literals.length - 1).toString(10);
return goog.i18n.MessageFormat.LITERAL_PLACEHOLDER_ + index + '_';
};
@@ -0,0 +1,111 @@
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Functions for encoding strings according to MIME
* standards, especially RFC 1522.
*/
goog.provide('goog.i18n.mime');
goog.provide('goog.i18n.mime.encode');
goog.require('goog.array');
/**
* Regular expression for matching those characters that are outside the
* range that can be used in the quoted-printable encoding of RFC 1522:
* anything outside the 7-bit ASCII encoding, plus ?, =, _ or space.
* @type {RegExp}
* @private
*/
goog.i18n.mime.NONASCII_ = /[^!-<>@-^`-~]/g;
/**
* Like goog.i18n.NONASCII_ but also omits double-quotes.
* @type {RegExp}
* @private
*/
goog.i18n.mime.NONASCII_NOQUOTE_ = /[^!#-<>@-^`-~]/g;
/**
* Encodes a string for inclusion in a MIME header. The string is encoded
* in UTF-8 according to RFC 1522, using quoted-printable form.
* @param {string} str The string to encode.
* @param {boolean=} opt_noquote Whether double-quote characters should also
* be escaped (should be true if the result will be placed inside a
* quoted string for a parameter value in a MIME header).
* @return {string} The encoded string.
*/
goog.i18n.mime.encode = function(str, opt_noquote) {
var nonascii = opt_noquote ?
goog.i18n.mime.NONASCII_NOQUOTE_ : goog.i18n.mime.NONASCII_;
if (str.search(nonascii) >= 0) {
str = '=?UTF-8?Q?' + str.replace(nonascii,
/**
* @param {string} c The matched char.
* @return {string} The quoted-printable form of utf-8 encoding.
*/
function(c) {
var i = c.charCodeAt(0);
if (i == 32) {
// Special case for space, which can be encoded as _ not =20
return '_';
}
var a = goog.array.concat('', goog.i18n.mime.getHexCharArray(c));
return a.join('=');
}) + '?=';
}
return str;
};
/**
* Get an array of UTF-8 hex codes for a given character.
* @param {string} c The matched character.
* @return {!Array.<string>} A hex array representing the character.
*/
goog.i18n.mime.getHexCharArray = function(c) {
var i = c.charCodeAt(0);
var a = [];
// First convert the UCS-2 character into its UTF-8 bytes
if (i < 128) {
a.push(i);
} else if (i <= 0x7ff) {
a.push(
0xc0 + ((i >> 6) & 0x3f),
0x80 + (i & 0x3f));
} else if (i <= 0xffff) {
a.push(
0xe0 + ((i >> 12) & 0x3f),
0x80 + ((i >> 6) & 0x3f),
0x80 + (i & 0x3f));
} else {
// (This is defensive programming, since ecmascript isn't supposed
// to handle code points that take more than 16 bits.)
a.push(
0xf0 + ((i >> 18) & 0x3f),
0x80 + ((i >> 12) & 0x3f),
0x80 + ((i >> 6) & 0x3f),
0x80 + (i & 0x3f));
}
// Now convert those bytes into hex strings (don't do anything with
// a[0] as that's got the empty string that lets us use join())
for (i = a.length - 1; i >= 0; --i) {
a[i] = a[i].toString(16);
}
return a;
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,589 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
/**
* @fileoverview Ordinal rules.
*
* This file is autogenerated by script:
* http://go/generate_pluralrules.py
* File generated from CLDR ver. 23
*
* Before check in, this file could have been manually edited. This is to
* incorporate changes before we could fix CLDR. All manual modification must be
* documented in this section, and should be removed after those changes land to
* CLDR.
*/
goog.provide('goog.i18n.ordinalRules');
/**
* Ordinal pattern keyword
* @enum {string}
*/
goog.i18n.ordinalRules.Keyword = {
ZERO: 'zero',
ONE: 'one',
TWO: 'two',
FEW: 'few',
MANY: 'many',
OTHER: 'other'
};
/**
* Default ordinal select rule.
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Default value.
* @private
*/
goog.i18n.ordinalRules.defaultSelect_ = function(n) {
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Ordinal select rules for fr locale
*
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
* @private
*/
goog.i18n.ordinalRules.frSelect_ = function(n) {
if (n == 1) {
return goog.i18n.ordinalRules.Keyword.ONE;
}
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Ordinal select rules for hu locale
*
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
* @private
*/
goog.i18n.ordinalRules.huSelect_ = function(n) {
if (n == 1 || n == 5) {
return goog.i18n.ordinalRules.Keyword.ONE;
}
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Ordinal select rules for sv locale
*
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
* @private
*/
goog.i18n.ordinalRules.svSelect_ = function(n) {
if ((n % 10 == 1 || n % 10 == 2) && n % 100 != 11 && n % 100 != 12) {
return goog.i18n.ordinalRules.Keyword.ONE;
}
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Ordinal select rules for en locale
*
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
* @private
*/
goog.i18n.ordinalRules.enSelect_ = function(n) {
if (n % 10 == 1 && n % 100 != 11) {
return goog.i18n.ordinalRules.Keyword.ONE;
}
if (n % 10 == 2 && n % 100 != 12) {
return goog.i18n.ordinalRules.Keyword.TWO;
}
if (n % 10 == 3 && n % 100 != 13) {
return goog.i18n.ordinalRules.Keyword.FEW;
}
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Ordinal select rules for it locale
*
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
* @private
*/
goog.i18n.ordinalRules.itSelect_ = function(n) {
if (n == 11 || n == 8 || n == 80 || n == 800) {
return goog.i18n.ordinalRules.Keyword.MANY;
}
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Ordinal select rules for ca locale
*
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
* @private
*/
goog.i18n.ordinalRules.caSelect_ = function(n) {
if (n == 1 || n == 3) {
return goog.i18n.ordinalRules.Keyword.ONE;
}
if (n == 2) {
return goog.i18n.ordinalRules.Keyword.TWO;
}
if (n == 4) {
return goog.i18n.ordinalRules.Keyword.FEW;
}
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Ordinal select rules for mr locale
*
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
* @private
*/
goog.i18n.ordinalRules.mrSelect_ = function(n) {
if (n == 1) {
return goog.i18n.ordinalRules.Keyword.ONE;
}
if (n == 2 || n == 3) {
return goog.i18n.ordinalRules.Keyword.TWO;
}
if (n == 4) {
return goog.i18n.ordinalRules.Keyword.FEW;
}
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Ordinal select rules for gu locale
*
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
* @private
*/
goog.i18n.ordinalRules.guSelect_ = function(n) {
if (n == 1) {
return goog.i18n.ordinalRules.Keyword.ONE;
}
if (n == 2 || n == 3) {
return goog.i18n.ordinalRules.Keyword.TWO;
}
if (n == 4) {
return goog.i18n.ordinalRules.Keyword.FEW;
}
if (n == 6) {
return goog.i18n.ordinalRules.Keyword.MANY;
}
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Ordinal select rules for bn locale
*
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
* @private
*/
goog.i18n.ordinalRules.bnSelect_ = function(n) {
if (n == 1 || n == 5 || n == 7 || n == 8 || n == 9 || n == 10) {
return goog.i18n.ordinalRules.Keyword.ONE;
}
if (n == 2 || n == 3) {
return goog.i18n.ordinalRules.Keyword.TWO;
}
if (n == 4) {
return goog.i18n.ordinalRules.Keyword.FEW;
}
if (n == 6) {
return goog.i18n.ordinalRules.Keyword.MANY;
}
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Ordinal select rules for zu locale
*
* @param {number} n The count of items.
* @return {goog.i18n.ordinalRules.Keyword} Locale-specific ordinal value.
* @private
*/
goog.i18n.ordinalRules.zuSelect_ = function(n) {
if (n == 1) {
return goog.i18n.ordinalRules.Keyword.ONE;
}
if (n == (n | 0) && n >= 2 && n <= 9) {
return goog.i18n.ordinalRules.Keyword.FEW;
}
if (n == (n | 0) && (n >= 10 && n <= 19 || n >= 100 && n <= 199 || n >= 1000 && n <= 1999)) {
return goog.i18n.ordinalRules.Keyword.MANY;
}
return goog.i18n.ordinalRules.Keyword.OTHER;
};
/**
* Selected ordinal rules by locale.
*/
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
if (goog.LOCALE == 'af') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'am') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'ar') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'bg') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'bn') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.bnSelect_;
}
if (goog.LOCALE == 'br') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'ca') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.caSelect_;
}
if (goog.LOCALE == 'chr') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'cs') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'cy') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'da') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'de') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'de_AT' || goog.LOCALE == 'de-AT') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'de_CH' || goog.LOCALE == 'de-CH') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'el') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'en') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
}
if (goog.LOCALE == 'en_AU' || goog.LOCALE == 'en-AU') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
}
if (goog.LOCALE == 'en_GB' || goog.LOCALE == 'en-GB') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
}
if (goog.LOCALE == 'en_IE' || goog.LOCALE == 'en-IE') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
}
if (goog.LOCALE == 'en_IN' || goog.LOCALE == 'en-IN') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
}
if (goog.LOCALE == 'en_SG' || goog.LOCALE == 'en-SG') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
}
if (goog.LOCALE == 'en_US' || goog.LOCALE == 'en-US') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
}
if (goog.LOCALE == 'en_ZA' || goog.LOCALE == 'en-ZA') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.enSelect_;
}
if (goog.LOCALE == 'es') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'es_419' || goog.LOCALE == 'es-419') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'es_ES' || goog.LOCALE == 'es-ES') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'et') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'eu') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'fa') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'fi') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'fil') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
}
if (goog.LOCALE == 'fr') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
}
if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
}
if (goog.LOCALE == 'gl') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'gsw') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'gu') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.guSelect_;
}
if (goog.LOCALE == 'haw') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'he') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'hi') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.guSelect_;
}
if (goog.LOCALE == 'hr') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'hu') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.huSelect_;
}
if (goog.LOCALE == 'id') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'in') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'is') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'it') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.itSelect_;
}
if (goog.LOCALE == 'iw') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'ja') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'kn') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'ko') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'ln') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'lt') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'lv') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'ml') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'mr') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.mrSelect_;
}
if (goog.LOCALE == 'ms') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
}
if (goog.LOCALE == 'mt') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'nb') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'nl') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'no') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'or') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'pl') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'pt') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'ro') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
}
if (goog.LOCALE == 'ru') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'sk') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'sl') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'sq') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'sr') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'sv') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.svSelect_;
}
if (goog.LOCALE == 'sw') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'ta') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'te') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'th') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'tl') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'tr') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'uk') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'ur') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'vi') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.frSelect_;
}
if (goog.LOCALE == 'zh') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'zh_CN' || goog.LOCALE == 'zh-CN') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'zh_HK' || goog.LOCALE == 'zh-HK') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'zh_TW' || goog.LOCALE == 'zh-TW') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.defaultSelect_;
}
if (goog.LOCALE == 'zu') {
goog.i18n.ordinalRules.select = goog.i18n.ordinalRules.zuSelect_;
}
@@ -0,0 +1,859 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
/**
* @fileoverview Plural rules.
*
* This file is autogenerated by script:
* http://go/generate_pluralrules.py
* File generated from CLDR ver. 23
*
* Before check in, this file could have been manually edited. This is to
* incorporate changes before we could fix CLDR. All manual modification must be
* documented in this section, and should be removed after those changes land to
* CLDR.
*/
goog.provide('goog.i18n.pluralRules');
/**
* Plural pattern keyword
* @enum {string}
*/
goog.i18n.pluralRules.Keyword = {
ZERO: 'zero',
ONE: 'one',
TWO: 'two',
FEW: 'few',
MANY: 'many',
OTHER: 'other'
};
/**
* Default plural select rule.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Default value.
* @private
*/
goog.i18n.pluralRules.defaultSelect_ = function(n) {
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for ar locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.arSelect_ = function(n) {
if (n == 0) {
return goog.i18n.pluralRules.Keyword.ZERO;
}
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n == (n | 0) && n % 100 >= 3 && n % 100 <= 10) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 99) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for he locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.heSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n != 0 && n % 10 == 0) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for en locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.enSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for fil locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.filSelect_ = function(n) {
if (n == 0 || n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for fr locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.frSelect_ = function(n) {
if (n >= 0 && n <= 2 && n != 2) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for lv locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.lvSelect_ = function(n) {
if (n == 0) {
return goog.i18n.pluralRules.Keyword.ZERO;
}
if (n % 10 == 1 && n % 100 != 11) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for iu locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.iuSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for ga locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.gaSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n == (n | 0) && n >= 3 && n <= 6) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n == (n | 0) && n >= 7 && n <= 10) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for ro locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.roSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 0 || n != 1 && n == (n | 0) && n % 100 >= 1 && n % 100 <= 19) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for lt locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.ltSelect_ = function(n) {
if (n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for be locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.beSelect_ = function(n) {
if (n % 10 == 1 && n % 100 != 11) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n % 10 == 0 || n == (n | 0) && n % 10 >= 5 && n % 10 <= 9 || n == (n | 0) && n % 100 >= 11 && n % 100 <= 14) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for cs locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.csSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == (n | 0) && n >= 2 && n <= 4) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for pl locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.plSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n != 1 && (n % 10 == 0 || n % 10 == 1) || n == (n | 0) && n % 10 >= 5 && n % 10 <= 9 || n == (n | 0) && n % 100 >= 12 && n % 100 <= 14) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for sl locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.slSelect_ = function(n) {
if (n % 100 == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n % 100 == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n % 100 == 3 || n % 100 == 4) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for mt locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.mtSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 0 || n == (n | 0) && n % 100 >= 2 && n % 100 <= 10) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 19) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for mk locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.mkSelect_ = function(n) {
if (n % 10 == 1 && n != 11) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for cy locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.cySelect_ = function(n) {
if (n == 0) {
return goog.i18n.pluralRules.Keyword.ZERO;
}
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n == 3) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n == 6) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for lag locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.lagSelect_ = function(n) {
if (n == 0) {
return goog.i18n.pluralRules.Keyword.ZERO;
}
if (n >= 0 && n <= 2 && n != 0 && n != 2) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for shi locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.shiSelect_ = function(n) {
if (n >= 0 && n <= 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == (n | 0) && n >= 2 && n <= 10) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for br locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.brSelect_ = function(n) {
if (n % 10 == 1 && n % 100 != 11 && n % 100 != 71 && n % 100 != 91) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n % 10 == 2 && n % 100 != 12 && n % 100 != 72 && n % 100 != 92) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if ((n % 10 == 3 || n % 10 == 4 || n % 10 == 9) && ((n % 100 < 10 || n % 100 > 19) && (n % 100 < 70 || n % 100 > 79) && (n % 100 < 90 || n % 100 > 99))) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n != 0 && n % 1000000 == 0) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for ksh locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.kshSelect_ = function(n) {
if (n == 0) {
return goog.i18n.pluralRules.Keyword.ZERO;
}
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for tzm locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.tzmSelect_ = function(n) {
if (n == 0 || n == 1 || n == (n | 0) && n >= 11 && n <= 99) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for gv locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.gvSelect_ = function(n) {
if (n % 10 == 1 || n % 10 == 2 || n % 20 == 0) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for gd locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.gdSelect_ = function(n) {
if (n == 1 || n == 11) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2 || n == 12) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n == (n | 0) && (n >= 3 && n <= 10 || n >= 13 && n <= 19)) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Selected plural rules by locale.
*/
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
if (goog.LOCALE == 'af') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'am') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
}
if (goog.LOCALE == 'ar') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.arSelect_;
}
if (goog.LOCALE == 'bg') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'bn') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'br') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.brSelect_;
}
if (goog.LOCALE == 'ca') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'chr') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'cs') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.csSelect_;
}
if (goog.LOCALE == 'cy') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.cySelect_;
}
if (goog.LOCALE == 'da') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'de') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'de_AT' || goog.LOCALE == 'de-AT') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'de_CH' || goog.LOCALE == 'de-CH') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'el') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'en') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'en_AU' || goog.LOCALE == 'en-AU') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'en_GB' || goog.LOCALE == 'en-GB') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'en_IE' || goog.LOCALE == 'en-IE') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'en_IN' || goog.LOCALE == 'en-IN') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'en_SG' || goog.LOCALE == 'en-SG') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'en_US' || goog.LOCALE == 'en-US') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'en_ZA' || goog.LOCALE == 'en-ZA') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'es') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'es_419' || goog.LOCALE == 'es-419') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'es_ES' || goog.LOCALE == 'es-ES') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'et') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'eu') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'fa') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'fi') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'fil') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
}
if (goog.LOCALE == 'fr') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;
}
if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;
}
if (goog.LOCALE == 'gl') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'gsw') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'gu') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'haw') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'he') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.heSelect_;
}
if (goog.LOCALE == 'hi') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
}
if (goog.LOCALE == 'hr') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
}
if (goog.LOCALE == 'hu') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'id') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'in') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'is') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'it') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'iw') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.heSelect_;
}
if (goog.LOCALE == 'ja') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'kn') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'ko') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'ln') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
}
if (goog.LOCALE == 'lt') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.ltSelect_;
}
if (goog.LOCALE == 'lv') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.lvSelect_;
}
if (goog.LOCALE == 'ml') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'mr') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'ms') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'mt') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.mtSelect_;
}
if (goog.LOCALE == 'nb') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'nl') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'no') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'or') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'pl') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.plSelect_;
}
if (goog.LOCALE == 'pt') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'pt_BR' || goog.LOCALE == 'pt-BR') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'pt_PT' || goog.LOCALE == 'pt-PT') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'ro') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.roSelect_;
}
if (goog.LOCALE == 'ru') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
}
if (goog.LOCALE == 'sk') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.csSelect_;
}
if (goog.LOCALE == 'sl') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.slSelect_;
}
if (goog.LOCALE == 'sq') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'sr') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
}
if (goog.LOCALE == 'sv') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'sw') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'ta') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'te') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'th') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'tl') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
}
if (goog.LOCALE == 'tr') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'uk') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
}
if (goog.LOCALE == 'ur') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'vi') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'zh') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'zh_CN' || goog.LOCALE == 'zh-CN') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'zh_HK' || goog.LOCALE == 'zh-HK') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'zh_TW' || goog.LOCALE == 'zh-TW') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
}
if (goog.LOCALE == 'zu') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
@@ -0,0 +1,340 @@
// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Functions to provide timezone information for use with
* date/time format.
*/
goog.provide('goog.i18n.TimeZone');
goog.require('goog.array');
goog.require('goog.date.DateLike');
goog.require('goog.string');
/**
* TimeZone class implemented a time zone resolution and name information
* source for client applications. The time zone object is initiated from
* a time zone information object. Application can initiate a time zone
* statically, or it may choose to initiate from a data obtained from server.
* Each time zone information array is small, but the whole set of data
* is too much for client application to download. If end user is allowed to
* change time zone setting, dynamic retrieval should be the method to use.
* In case only time zone offset is known, there is a decent fallback
* that only use the time zone offset to create a TimeZone object.
* A whole set of time zone information array was available under
* http://go/js_locale_data. It is generated based on CLDR and
* Olson time zone data base (through pytz), and will be updated timely.
*
* @constructor
*/
goog.i18n.TimeZone = function() {
/**
* The standard time zone id.
* @type {string}
* @private
*/
this.timeZoneId_;
/**
* The standard, non-daylight time zone offset, in minutes WEST of UTC.
* @type {number}
* @private
*/
this.standardOffset_;
/**
* An array of strings that can have 2 or 4 elements. The first two elements
* are the long and short names for standard time in this time zone, and the
* last two elements (if present) are the long and short names for daylight
* time in this time zone.
* @type {Array.<string>}
* @private
*/
this.tzNames_;
/**
* This array specifies the Daylight Saving Time transitions for this time
* zone. This is a flat array of numbers which are interpreted in pairs:
* [time1, adjustment1, time2, adjustment2, ...] where each time is a DST
* transition point given as a number of hours since 00:00 UTC, January 1,
* 1970, and each adjustment is the adjustment to apply for times after the
* DST transition, given as minutes EAST of UTC.
* @type {Array.<number>}
* @private
*/
this.transitions_;
};
/**
* The number of milliseconds in an hour.
* @type {number}
* @private
*/
goog.i18n.TimeZone.MILLISECONDS_PER_HOUR_ = 3600 * 1000;
/**
* Indices into the array of time zone names.
* @enum {number}
*/
goog.i18n.TimeZone.NameType = {
STD_SHORT_NAME: 0,
STD_LONG_NAME: 1,
DLT_SHORT_NAME: 2,
DLT_LONG_NAME: 3
};
/**
* This factory method creates a time zone instance. It takes either an object
* containing complete time zone information, or a single number representing a
* constant time zone offset. If the latter form is used, DST functionality is
* not available.
*
* @param {number|Object} timeZoneData If this parameter is a number, it should
* indicate minutes WEST of UTC to be used as a constant time zone offset.
* Otherwise, it should be an object with these four fields:
* <ul>
* <li>id: A string ID for the time zone.
* <li>std_offset: The standard time zone offset in minutes EAST of UTC.
* <li>names: An array of four names (standard short name, standard long
* name, daylight short name, daylight long, name)
* <li>transitions: An array of numbers which are interpreted in pairs:
* [time1, adjustment1, time2, adjustment2, ...] where each time is
* a DST transition point given as a number of hours since 00:00 UTC,
* January 1, 1970, and each adjustment is the adjustment to apply
* for times after the DST transition, given as minutes EAST of UTC.
* </ul>
* @return {!goog.i18n.TimeZone} A goog.i18n.TimeZone object for the given
* time zone data.
*/
goog.i18n.TimeZone.createTimeZone = function(timeZoneData) {
if (typeof timeZoneData == 'number') {
return goog.i18n.TimeZone.createSimpleTimeZone_(timeZoneData);
}
var tz = new goog.i18n.TimeZone();
tz.timeZoneId_ = timeZoneData['id'];
tz.standardOffset_ = -timeZoneData['std_offset'];
tz.tzNames_ = timeZoneData['names'];
tz.transitions_ = timeZoneData['transitions'];
return tz;
};
/**
* This factory method creates a time zone object with a constant offset.
* @param {number} timeZoneOffsetInMinutes Offset in minutes WEST of UTC.
* @return {!goog.i18n.TimeZone} A time zone object with the given constant
* offset. Note that the time zone ID of this object will use the POSIX
* convention, which has a reversed sign ("Etc/GMT+8" means UTC-8 or PST).
* @private
*/
goog.i18n.TimeZone.createSimpleTimeZone_ = function(timeZoneOffsetInMinutes) {
var tz = new goog.i18n.TimeZone();
tz.standardOffset_ = timeZoneOffsetInMinutes;
tz.timeZoneId_ =
goog.i18n.TimeZone.composePosixTimeZoneID_(timeZoneOffsetInMinutes);
var str = goog.i18n.TimeZone.composeUTCString_(timeZoneOffsetInMinutes);
tz.tzNames_ = [str, str];
tz.transitions_ = [];
return tz;
};
/**
* Generate a GMT-relative string for a constant time zone offset.
* @param {number} offset The time zone offset in minutes WEST of UTC.
* @return {string} The GMT string for this offset, which will indicate
* hours EAST of UTC.
* @private
*/
goog.i18n.TimeZone.composeGMTString_ = function(offset) {
var parts = ['GMT'];
parts.push(offset <= 0 ? '+' : '-');
offset = Math.abs(offset);
parts.push(goog.string.padNumber(Math.floor(offset / 60) % 100, 2),
':', goog.string.padNumber(offset % 60, 2));
return parts.join('');
};
/**
* Generate a POSIX time zone ID for a constant time zone offset.
* @param {number} offset The time zone offset in minutes WEST of UTC.
* @return {string} The POSIX time zone ID for this offset, which will indicate
* hours WEST of UTC.
* @private
*/
goog.i18n.TimeZone.composePosixTimeZoneID_ = function(offset) {
if (offset == 0) {
return 'Etc/GMT';
}
var parts = ['Etc/GMT', offset < 0 ? '-' : '+'];
offset = Math.abs(offset);
parts.push(Math.floor(offset / 60) % 100);
offset = offset % 60;
if (offset != 0) {
parts.push(':', goog.string.padNumber(offset, 2));
}
return parts.join('');
};
/**
* Generate a UTC-relative string for a constant time zone offset.
* @param {number} offset The time zone offset in minutes WEST of UTC.
* @return {string} The UTC string for this offset, which will indicate
* hours EAST of UTC.
* @private
*/
goog.i18n.TimeZone.composeUTCString_ = function(offset) {
if (offset == 0) {
return 'UTC';
}
var parts = ['UTC', offset < 0 ? '+' : '-'];
offset = Math.abs(offset);
parts.push(Math.floor(offset / 60) % 100);
offset = offset % 60;
if (offset != 0) {
parts.push(':', offset);
}
return parts.join('');
};
/**
* Convert the contents of time zone object to a timeZoneData object, suitable
* for passing to goog.i18n.TimeZone.createTimeZone.
* @return {!Object} A timeZoneData object (see the documentation for
* goog.i18n.TimeZone.createTimeZone).
*/
goog.i18n.TimeZone.prototype.getTimeZoneData = function() {
return {
'id': this.timeZoneId_,
'std_offset': -this.standardOffset_, // note createTimeZone flips the sign
'names': goog.array.clone(this.tzNames_), // avoid aliasing the array
'transitions': goog.array.clone(this.transitions_) // avoid aliasing
};
};
/**
* Return the DST adjustment to the time zone offset for a given time.
* While Daylight Saving Time is in effect, this number is positive.
* Otherwise, it is zero.
* @param {goog.date.DateLike} date The time to check.
* @return {number} The DST adjustment in minutes EAST of UTC.
*/
goog.i18n.TimeZone.prototype.getDaylightAdjustment = function(date) {
var timeInMs = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(),
date.getUTCDate(), date.getUTCHours(),
date.getUTCMinutes());
var timeInHours = timeInMs / goog.i18n.TimeZone.MILLISECONDS_PER_HOUR_;
var index = 0;
while (index < this.transitions_.length &&
timeInHours >= this.transitions_[index]) {
index += 2;
}
return (index == 0) ? 0 : this.transitions_[index - 1];
};
/**
* Return the GMT representation of this time zone object.
* @param {goog.date.DateLike} date The date for which time to retrieve
* GMT string.
* @return {string} GMT representation string.
*/
goog.i18n.TimeZone.prototype.getGMTString = function(date) {
return goog.i18n.TimeZone.composeGMTString_(this.getOffset(date));
};
/**
* Get the long time zone name for a given date/time.
* @param {goog.date.DateLike} date The time for which to retrieve
* the long time zone name.
* @return {string} The long time zone name.
*/
goog.i18n.TimeZone.prototype.getLongName = function(date) {
return this.tzNames_[this.isDaylightTime(date) ?
goog.i18n.TimeZone.NameType.DLT_LONG_NAME :
goog.i18n.TimeZone.NameType.STD_LONG_NAME];
};
/**
* Get the time zone offset in minutes WEST of UTC for a given date/time.
* @param {goog.date.DateLike} date The time for which to retrieve
* the time zone offset.
* @return {number} The time zone offset in minutes WEST of UTC.
*/
goog.i18n.TimeZone.prototype.getOffset = function(date) {
return this.standardOffset_ - this.getDaylightAdjustment(date);
};
/**
* Get the RFC representation of the time zone for a given date/time.
* @param {goog.date.DateLike} date The time for which to retrieve the
* RFC time zone string.
* @return {string} The RFC time zone string.
*/
goog.i18n.TimeZone.prototype.getRFCTimeZoneString = function(date) {
var offset = -this.getOffset(date);
var parts = [offset < 0 ? '-' : '+'];
offset = Math.abs(offset);
parts.push(goog.string.padNumber(Math.floor(offset / 60) % 100, 2),
goog.string.padNumber(offset % 60, 2));
return parts.join('');
};
/**
* Get the short time zone name for given date/time.
* @param {goog.date.DateLike} date The time for which to retrieve
* the short time zone name.
* @return {string} The short time zone name.
*/
goog.i18n.TimeZone.prototype.getShortName = function(date) {
return this.tzNames_[this.isDaylightTime(date) ?
goog.i18n.TimeZone.NameType.DLT_SHORT_NAME :
goog.i18n.TimeZone.NameType.STD_SHORT_NAME];
};
/**
* Return the time zone ID for this time zone.
* @return {string} The time zone ID.
*/
goog.i18n.TimeZone.prototype.getTimeZoneId = function() {
return this.timeZoneId_;
};
/**
* Check if Daylight Saving Time is in effect at a given time in this time zone.
* @param {goog.date.DateLike} date The time to check.
* @return {boolean} True if Daylight Saving Time is in effect.
*/
goog.i18n.TimeZone.prototype.isDaylightTime = function(date) {
return this.getDaylightAdjustment(date) > 0;
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Object which fetches Unicode codepoint names that are locally
* stored in a bundled database. Currently, only invisible characters are
* covered by this database. See the goog.i18n.uChar.RemoteNameFetcher class for
* a remote database option.
*/
goog.provide('goog.i18n.uChar.LocalNameFetcher');
goog.require('goog.i18n.uChar');
goog.require('goog.i18n.uChar.NameFetcher');
goog.require('goog.log');
/**
* Builds the NameFetcherLocal object. This is a simple object which retrieves
* character names from a local bundled database. This database only covers
* invisible characters. See the goog.i18n.uChar class for more details.
*
* @constructor
* @implements {goog.i18n.uChar.NameFetcher}
*/
goog.i18n.uChar.LocalNameFetcher = function() {
};
/**
* A reference to the LocalNameFetcher logger.
*
* @type {goog.log.Logger}
* @private
*/
goog.i18n.uChar.LocalNameFetcher.logger_ =
goog.log.getLogger('goog.i18n.uChar.LocalNameFetcher');
/** @override */
goog.i18n.uChar.LocalNameFetcher.prototype.prefetch = function(character) {
};
/** @override */
goog.i18n.uChar.LocalNameFetcher.prototype.getName = function(character,
callback) {
var localName = goog.i18n.uChar.toName(character);
if (!localName) {
goog.i18n.uChar.LocalNameFetcher.logger_.
warning('No local name defined for character ' + character);
}
callback(localName);
};
/** @override */
goog.i18n.uChar.LocalNameFetcher.prototype.isNameAvailable = function(
character) {
return !!goog.i18n.uChar.toName(character);
};
@@ -0,0 +1,70 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Definition of the goog.i18n.CharNameFetcher interface. This
* interface is used to retrieve individual character names.
*/
goog.provide('goog.i18n.uChar.NameFetcher');
/**
* NameFetcher interface. Implementations of this interface are used to retrieve
* Unicode character names.
*
* @interface
*/
goog.i18n.uChar.NameFetcher = function() {
};
/**
* Retrieves the names of a given set of characters and stores them in a cache
* for fast retrieval. Offline implementations can simply provide an empty
* implementation.
*
* @param {string} characters The list of characters in base 88 to fetch. These
* lists are stored by category and subcategory in the
* goog.i18n.charpickerdata class.
*/
goog.i18n.uChar.NameFetcher.prototype.prefetch = function(characters) {
};
/**
* Retrieves the name of a particular character.
*
* @param {string} character The character to retrieve.
* @param {function(?string)} callback The callback function called when the
* name retrieval is complete, contains a single string parameter with the
* codepoint name, this parameter will be null if the character name is not
* defined.
*/
goog.i18n.uChar.NameFetcher.prototype.getName = function(character, callback) {
};
/**
* Tests whether the name of a given character is available to be retrieved by
* the getName() function.
*
* @param {string} character The character to test.
* @return {boolean} True if the fetcher can retrieve or has a name available
* for the given character.
*/
goog.i18n.uChar.NameFetcher.prototype.isNameAvailable = function(character) {
};
@@ -0,0 +1,281 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Object which fetches Unicode codepoint names from a remote data
* source. This data source should accept two parameters:
* <ol>
* <li>c - the list of codepoints in hexadecimal format
* <li>p - the name property
* </ol>
* and return a JSON object representation of the result.
* For example, calling this data source with the following URL:
* http://datasource?c=50,ff,102bd&p=name
* Should return a JSON object which looks like this:
* <pre>
* {"50":{"name":"LATIN CAPITAL LETTER P"},
* "ff":{"name":"LATIN SMALL LETTER Y WITH DIAERESIS"},
* "102bd":{"name":"CARIAN LETTER K2"}}
* </pre>.
*/
goog.provide('goog.i18n.uChar.RemoteNameFetcher');
goog.require('goog.Disposable');
goog.require('goog.Uri');
goog.require('goog.i18n.uChar');
goog.require('goog.i18n.uChar.NameFetcher');
goog.require('goog.log');
goog.require('goog.net.XhrIo');
goog.require('goog.structs.Map');
/**
* Builds the RemoteNameFetcher object. This object retrieves codepoint names
* from a remote data source.
*
* @param {string} dataSourceUri URI to the data source.
* @constructor
* @implements {goog.i18n.uChar.NameFetcher}
* @extends {goog.Disposable}
*/
goog.i18n.uChar.RemoteNameFetcher = function(dataSourceUri) {
goog.base(this);
/**
* XHRIo object for prefetch() asynchronous calls.
*
* @type {!goog.net.XhrIo}
* @private
*/
this.prefetchXhrIo_ = new goog.net.XhrIo();
/**
* XHRIo object for getName() asynchronous calls.
*
* @type {!goog.net.XhrIo}
* @private
*/
this.getNameXhrIo_ = new goog.net.XhrIo();
/**
* URI to the data.
*
* @type {string}
* @private
*/
this.dataSourceUri_ = dataSourceUri;
/**
* A cache of all the collected names from the server.
*
* @type {!goog.structs.Map}
* @private
*/
this.charNames_ = new goog.structs.Map();
};
goog.inherits(goog.i18n.uChar.RemoteNameFetcher, goog.Disposable);
/**
* Key to the listener on XHR for prefetch(). Used to clear previous listeners.
*
* @type {goog.events.Key}
* @private
*/
goog.i18n.uChar.RemoteNameFetcher.prototype.prefetchLastListenerKey_;
/**
* Key to the listener on XHR for getName(). Used to clear previous listeners.
*
* @type {goog.events.Key}
* @private
*/
goog.i18n.uChar.RemoteNameFetcher.prototype.getNameLastListenerKey_;
/**
* A reference to the RemoteNameFetcher logger.
*
* @type {goog.log.Logger}
* @private
*/
goog.i18n.uChar.RemoteNameFetcher.logger_ =
goog.log.getLogger('goog.i18n.uChar.RemoteNameFetcher');
/** @override */
goog.i18n.uChar.RemoteNameFetcher.prototype.disposeInternal = function() {
goog.base(this, 'disposeInternal');
this.prefetchXhrIo_.dispose();
this.getNameXhrIo_.dispose();
};
/** @override */
goog.i18n.uChar.RemoteNameFetcher.prototype.prefetch = function(characters) {
// Abort the current request if there is one
if (this.prefetchXhrIo_.isActive()) {
goog.i18n.uChar.RemoteNameFetcher.logger_.
info('Aborted previous prefetch() call for new incoming request');
this.prefetchXhrIo_.abort();
}
if (this.prefetchLastListenerKey_) {
goog.events.unlistenByKey(this.prefetchLastListenerKey_);
}
// Set up new listener
var preFetchCallback = goog.bind(this.prefetchCallback_, this);
this.prefetchLastListenerKey_ = goog.events.listenOnce(this.prefetchXhrIo_,
goog.net.EventType.COMPLETE, preFetchCallback);
this.fetch_(goog.i18n.uChar.RemoteNameFetcher.RequestType_.BASE_88,
characters, this.prefetchXhrIo_);
};
/**
* Callback on completion of the prefetch operation.
*
* @private
*/
goog.i18n.uChar.RemoteNameFetcher.prototype.prefetchCallback_ = function() {
this.processResponse_(this.prefetchXhrIo_);
};
/** @override */
goog.i18n.uChar.RemoteNameFetcher.prototype.getName = function(character,
callback) {
var codepoint = goog.i18n.uChar.toCharCode(character).toString(16);
if (this.charNames_.containsKey(codepoint)) {
var name = /** @type {string} */ (this.charNames_.get(codepoint));
callback(name);
return;
}
// Abort the current request if there is one
if (this.getNameXhrIo_.isActive()) {
goog.i18n.uChar.RemoteNameFetcher.logger_.
info('Aborted previous getName() call for new incoming request');
this.getNameXhrIo_.abort();
}
if (this.getNameLastListenerKey_) {
goog.events.unlistenByKey(this.getNameLastListenerKey_);
}
// Set up new listener
var getNameCallback = goog.bind(this.getNameCallback_, this, codepoint,
callback);
this.getNameLastListenerKey_ = goog.events.listenOnce(this.getNameXhrIo_,
goog.net.EventType.COMPLETE, getNameCallback);
this.fetch_(goog.i18n.uChar.RemoteNameFetcher.RequestType_.CODEPOINT,
codepoint, this.getNameXhrIo_);
};
/**
* Callback on completion of the getName operation.
*
* @param {string} codepoint The codepoint in hexadecimal format.
* @param {function(?string)} callback The callback function called when the
* name retrieval is complete, contains a single string parameter with the
* codepoint name, this parameter will be null if the character name is not
* defined.
* @private
*/
goog.i18n.uChar.RemoteNameFetcher.prototype.getNameCallback_ = function(
codepoint, callback) {
this.processResponse_(this.getNameXhrIo_);
var name = /** @type {?string} */ (this.charNames_.get(codepoint, null));
callback(name);
};
/**
* Process the response received from the server and store results in the cache.
*
* @param {!goog.net.XhrIo} xhrIo The XhrIo object used to make the request.
* @private
*/
goog.i18n.uChar.RemoteNameFetcher.prototype.processResponse_ = function(xhrIo) {
if (!xhrIo.isSuccess()) {
goog.log.error(goog.i18n.uChar.RemoteNameFetcher.logger_,
'Problem with data source: ' + xhrIo.getLastError());
return;
}
var result = xhrIo.getResponseJson();
for (var codepoint in result) {
if (result[codepoint].hasOwnProperty('name')) {
this.charNames_.set(codepoint, result[codepoint]['name']);
}
}
};
/**
* Enum for the different request types.
*
* @enum {string}
* @private
*/
goog.i18n.uChar.RemoteNameFetcher.RequestType_ = {
/**
* Request type that uses a base 88 string containing a set of codepoints to
* be fetched from the server (see goog.i18n.charpickerdata for more
* information on b88).
*/
BASE_88: 'b88',
/**
* Request type that uses a a string of comma separated codepoint values.
*/
CODEPOINT: 'c'
};
/**
* Fetches a set of codepoint names from the data source.
*
* @param {!goog.i18n.uChar.RemoteNameFetcher.RequestType_} requestType The
* request type of the operation. This parameter specifies how the server is
* called to fetch a particular set of codepoints.
* @param {string} requestInput The input to the request, this is the value that
* is passed onto the server to complete the request.
* @param {!goog.net.XhrIo} xhrIo The XHRIo object to execute the server call.
* @private
*/
goog.i18n.uChar.RemoteNameFetcher.prototype.fetch_ = function(requestType,
requestInput, xhrIo) {
var url = new goog.Uri(this.dataSourceUri_);
url.setParameterValue(requestType, requestInput);
url.setParameterValue('p', 'name');
goog.log.info(goog.i18n.uChar.RemoteNameFetcher.logger_, 'Request: ' +
url.toString());
xhrIo.send(url);
};
/** @override */
goog.i18n.uChar.RemoteNameFetcher.prototype.isNameAvailable = function(
character) {
return true;
};