CLIENT GUI Framework
[vnfsdk/refrepo.git] / portal-common / src / main / webapp / common / thirdparty / jquery.i18n / jquery.i18n.properties-1.0.9.js
1 /******************************************************************************\r
2  * jquery.i18n.properties\r
3  * \r
4  * Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and \r
5  * MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses.\r
6  * \r
7  * @version     1.0.x\r
8  * @author      Nuno Fernandes\r
9  * @url         www.codingwithcoffee.com\r
10  * @inspiration Localisation assistance for jQuery (http://keith-wood.name/localisation.html)\r
11  *              by Keith Wood (kbwood{at}iinet.com.au) June 2007\r
12  * \r
13  *****************************************************************************/\r
14 \r
15 (function($) {\r
16 $.i18n = {};\r
17 \r
18 /** Map holding bundle keys (if mode: 'map') */\r
19 $.i18n.map = {};\r
20     \r
21 /**\r
22  * Load and parse message bundle files (.properties),\r
23  * making bundles keys available as javascript variables.\r
24  * \r
25  * i18n files are named <name>.js, or <name>_<language>.js or <name>_<language>_<country>.js\r
26  * Where:\r
27  *      The <language> argument is a valid ISO Language Code. These codes are the lower-case, \r
28  *      two-letter codes as defined by ISO-639. You can find a full list of these codes at a \r
29  *      number of sites, such as: http://www.loc.gov/standards/iso639-2/englangn.html\r
30  *      The <country> argument is a valid ISO Country Code. These codes are the upper-case,\r
31  *      two-letter codes as defined by ISO-3166. You can find a full list of these codes at a\r
32  *      number of sites, such as: http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html\r
33  * \r
34  * Sample usage for a bundles/Messages.properties bundle:\r
35  * $.i18n.properties({\r
36  *      name:      'Messages', \r
37  *      language:  'en_US',\r
38  *      path:      'bundles'\r
39  * });\r
40  * @param  name                 (string/string[], optional) names of file to load (eg, 'Messages' or ['Msg1','Msg2']). Defaults to "Messages"\r
41  * @param  language             (string, optional) language/country code (eg, 'en', 'en_US', 'pt_PT'). if not specified, language reported by the browser will be used instead.\r
42  * @param  path                 (string, optional) path of directory that contains file to load\r
43  * @param  mode                 (string, optional) whether bundles keys are available as JavaScript variables/functions or as a map (eg, 'vars' or 'map')\r
44  * @param  cache        (boolean, optional) whether bundles should be cached by the browser, or forcibly reloaded on each page load. Defaults to false (i.e. forcibly reloaded)\r
45  * @param  encoding     (string, optional) the encoding to request for bundles. Property file resource bundles are specified to be in ISO-8859-1 format. Defaults to UTF-8 for backward compatibility.\r
46  * @param  callback     (function, optional) callback function to be called after script is terminated\r
47  */\r
48 $.i18n.properties = function(settings) {\r
49         // set up settings\r
50     var defaults = {\r
51         name:           'Messages',\r
52         language:       '',\r
53         path:           '',  \r
54         mode:           'vars',\r
55         cache:                  false,\r
56         encoding:       'UTF-8',\r
57         callback:       null\r
58     };\r
59     settings = $.extend(defaults, settings);    \r
60     if(settings.language === null || settings.language == '') {\r
61            settings.language = $.i18n.browserLang();\r
62         }\r
63         if(settings.language === null) {settings.language='';}\r
64         \r
65         // load and parse bundle files\r
66         var files = getFiles(settings.name);\r
67         for(i=0; i<files.length; i++) {\r
68                 // 1. load base (eg, Messages.properties)\r
69                 //loadAndParseFile(settings.path + files[i] + '.properties', settings);\r
70         // 2. with language code (eg, Messages_pt.properties)\r
71                 //if(settings.language.length >= 2) {\r
72         //    loadAndParseFile(settings.path + files[i] + '-' + settings.language.substring(0, 2) +'.properties', settings);\r
73                 //}\r
74                 // 3. with language code and country code (eg, Messages_pt_PT.properties)\r
75                 // 将寻找资源文件的顺序倒置\r
76         if(settings.language.length >= 5) {\r
77             loadAndParseFile(settings.path + files[i] + '-' + settings.language.substring(0, 5) +'.properties', settings);\r
78         } else if(settings.language.length >= 2) {\r
79             loadAndParseFile(settings.path + files[i] + '-' + settings.language.substring(0, 2) +'.properties', settings);\r
80                 } else {\r
81                         loadAndParseFile(settings.path + files[i] + '.properties', settings);\r
82                 }\r
83         }\r
84         \r
85         // call callback\r
86         if(settings.callback){ settings.callback(); }\r
87 };\r
88 \r
89 \r
90 /**\r
91  * When configured with mode: 'map', allows access to bundle values by specifying its key.\r
92  * Eg, jQuery.i18n.prop('com.company.bundles.menu_add')\r
93  */\r
94 $.i18n.prop = function(key /* Add parameters as function arguments as necessary  */) {\r
95         var value = $.i18n.map[key];\r
96         if (value == null)\r
97                 return '[' + key + ']';\r
98         \r
99 //      if(arguments.length < 2) // No arguments.\r
100 //    //if(key == 'spv.lbl.modified') {alert(value);}\r
101 //              return value;\r
102         \r
103 //      if (!$.isArray(placeHolderValues)) {\r
104 //              // If placeHolderValues is not an array, make it into one.\r
105 //              placeHolderValues = [placeHolderValues];\r
106 //              for (var i=2; i<arguments.length; i++)\r
107 //                      placeHolderValues.push(arguments[i]);\r
108 //      }\r
109 \r
110         // Place holder replacement\r
111         /**\r
112          * Tested with:\r
113          *   test.t1=asdf ''{0}''\r
114          *   test.t2=asdf '{0}' '{1}'{1}'zxcv\r
115          *   test.t3=This is \"a quote" 'a''{0}''s'd{fgh{ij'\r
116          *   test.t4="'''{'0}''" {0}{a}\r
117          *   test.t5="'''{0}'''" {1}\r
118          *   test.t6=a {1} b {0} c\r
119          *   test.t7=a 'quoted \\ s\ttringy' \t\t x\r
120          *\r
121          * Produces:\r
122          *   test.t1, p1 ==> asdf 'p1'\r
123          *   test.t2, p1 ==> asdf {0} {1}{1}zxcv\r
124          *   test.t3, p1 ==> This is "a quote" a'{0}'sd{fgh{ij\r
125          *   test.t4, p1 ==> "'{0}'" p1{a}\r
126          *   test.t5, p1 ==> "'{0}'" {1}\r
127          *   test.t6, p1 ==> a {1} b p1 c\r
128          *   test.t6, p1, p2 ==> a p2 b p1 c\r
129          *   test.t6, p1, p2, p3 ==> a p2 b p1 c\r
130          *   test.t7 ==> a quoted \ s   tringy           x\r
131          */\r
132         \r
133         var i;\r
134         if (typeof(value) == 'string') {\r
135         // Handle escape characters. Done separately from the tokenizing loop below because escape characters are \r
136                 // active in quoted strings.\r
137         i = 0;\r
138         while ((i = value.indexOf('\\', i)) != -1) {\r
139                    if (value[i+1] == 't')\r
140                            value = value.substring(0, i) + '\t' + value.substring((i++) + 2); // tab\r
141                    else if (value[i+1] == 'r')\r
142                            value = value.substring(0, i) + '\r' + value.substring((i++) + 2); // return\r
143                    else if (value[i+1] == 'n')\r
144                            value = value.substring(0, i) + '\n' + value.substring((i++) + 2); // line feed\r
145                    else if (value[i+1] == 'f')\r
146                            value = value.substring(0, i) + '\f' + value.substring((i++) + 2); // form feed\r
147                    else if (value[i+1] == '\\')\r
148                            value = value.substring(0, i) + '\\' + value.substring((i++) + 2); // \\r
149                    else\r
150                            value = value.substring(0, i) + value.substring(i+1); // Quietly drop the character\r
151         }\r
152                 \r
153                 // Lazily convert the string to a list of tokens.\r
154                 var arr = [], j, index;\r
155                 i = 0;\r
156                 while (i < value.length) {\r
157                         if (value[i] == '\'') {\r
158                                 // Handle quotes\r
159                                 if (i == value.length-1)\r
160                                         value = value.substring(0, i); // Silently drop the trailing quote\r
161                                 else if (value[i+1] == '\'')\r
162                                         value = value.substring(0, i) + value.substring(++i); // Escaped quote\r
163                                 else {\r
164                                         // Quoted string\r
165                                         j = i + 2;\r
166                                         while ((j = value.indexOf('\'', j)) != -1) {\r
167                                                 if (j == value.length-1 || value[j+1] != '\'') {\r
168                                                         // Found start and end quotes. Remove them\r
169                                                         value = value.substring(0,i) + value.substring(i+1, j) + value.substring(j+1);\r
170                                                         i = j - 1;\r
171                                                         break;\r
172                                                 }\r
173                                                 else {\r
174                                                         // Found a double quote, reduce to a single quote.\r
175                                                         value = value.substring(0,j) + value.substring(++j);\r
176                                                 }\r
177                                         }\r
178                                         \r
179                                         if (j == -1) {\r
180                                                 // There is no end quote. Drop the start quote\r
181                                                 value = value.substring(0,i) + value.substring(i+1);\r
182                                         }\r
183                                 }\r
184                         }\r
185                         else if (value[i] == '{') {\r
186                                 // Beginning of an unquoted place holder.\r
187                                 j = value.indexOf('}', i+1);\r
188                                 if (j == -1)\r
189                                         i++; // No end. Process the rest of the line. Java would throw an exception\r
190                                 else {\r
191                                         // Add 1 to the index so that it aligns with the function arguments.\r
192                                         index = parseInt(value.substring(i+1, j));\r
193                                         if (!isNaN(index) && index >= 0) {\r
194                                                 // Put the line thus far (if it isn't empty) into the array\r
195                                                 var s = value.substring(0, i);\r
196                                                 if (s != "")\r
197                                                         arr.push(s);\r
198                                                 // Put the parameter reference into the array\r
199                                                 arr.push(index);\r
200                                                 // Start the processing over again starting from the rest of the line.\r
201                                                 i = 0;\r
202                                                 value = value.substring(j+1);\r
203                                         }\r
204                                         else\r
205                                                 i = j + 1; // Invalid parameter. Leave as is.\r
206                                 }\r
207                         }\r
208                         else\r
209                                 i++;\r
210                 }\r
211                 \r
212                 // Put the remainder of the no-empty line into the array.\r
213                 if (value != "")\r
214                         arr.push(value);\r
215                 value = arr;\r
216                 \r
217                 // Make the array the value for the entry.\r
218                 $.i18n.map[key] = arr;\r
219         }\r
220         \r
221         if (value.length == 0)\r
222                 return "";\r
223         if (value.lengh == 1 && typeof(value[0]) == "string")\r
224                 return value[0];\r
225         \r
226         var s = "";\r
227         for (i=0; i<value.length; i++) {\r
228                 if (typeof(value[i]) == "string")\r
229                         s += value[i];\r
230                 // Must be a number\r
231                 else if (value[i] + 1 < arguments.length)\r
232                         s += arguments[value[i] + 1];\r
233                 else\r
234                         s += "{"+ value[i] +"}";\r
235         }\r
236         \r
237         return s;\r
238 };\r
239 \r
240 /** Language reported by browser, normalized code */\r
241 $.i18n.browserLang = function() {\r
242         return normaliseLanguageCode(navigator.language /* Mozilla */ || navigator.userLanguage /* IE */);\r
243 }\r
244 \r
245 \r
246 /** Load and parse .properties files */\r
247 function loadAndParseFile(filename, settings) {\r
248         $.ajax({\r
249         url:        filename,\r
250         async:      false,\r
251         cache:          settings.cache,\r
252         contentType:'text/plain;charset='+ settings.encoding,\r
253         dataType:   'text',\r
254         success:    function(data, status) {\r
255                                         parseData(data, settings.mode); \r
256                                         }\r
257     });\r
258 }\r
259 \r
260 /** Parse .properties files */\r
261 function parseData(data, mode) {\r
262    var parsed = '';\r
263    var parameters = data.split( /\n/ );\r
264    var regPlaceHolder = /(\{\d+\})/g;\r
265    var regRepPlaceHolder = /\{(\d+)\}/g;\r
266    var unicodeRE = /(\\u.{4})/ig;\r
267    for(var i=0; i<parameters.length; i++ ) {\r
268        parameters[i] = parameters[i].replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); // trim\r
269        if(parameters[i].length > 0 && parameters[i].match("^#")!="#") { // skip comments\r
270            var pair = parameters[i].split('=');\r
271            if(pair.length > 0) {\r
272                /** Process key & value */\r
273                var name = unescape(pair[0]).replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); // trim\r
274                var value = pair.length == 1 ? "" : pair[1];\r
275                // process multi-line values\r
276                while(value.match(/\\$/)=="\\") {\r
277                         value = value.substring(0, value.length - 1);\r
278                         value += parameters[++i].replace( /\s\s*$/, '' ); // right trim\r
279                }               \r
280                // Put values with embedded '='s back together\r
281                for(var s=2;s<pair.length;s++){ value +='=' + pair[s]; }\r
282                value = value.replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' ); // trim\r
283                \r
284                /** Mode: bundle keys in a map */\r
285                if(mode == 'map' || mode == 'both') {\r
286                    // handle unicode chars possibly left out\r
287                    var unicodeMatches = value.match(unicodeRE);\r
288                    if(unicodeMatches) {\r
289                      for(var u=0; u<unicodeMatches.length; u++) {\r
290                         value = value.replace( unicodeMatches[u], unescapeUnicode(unicodeMatches[u]));\r
291                      }\r
292                    }\r
293                    // add to map\r
294                    $.i18n.map[name] = value;\r
295                }\r
296                \r
297                /** Mode: bundle keys as vars/functions */\r
298                if(mode == 'vars' || mode == 'both') {\r
299                    value = value.replace( /"/g, '\\"' ); // escape quotation mark (")\r
300                    \r
301                    // make sure namespaced key exists (eg, 'some.key') \r
302                    checkKeyNamespace(name);\r
303                    \r
304                    // value with variable substitutions\r
305                    if(regPlaceHolder.test(value)) {\r
306                        var parts = value.split(regPlaceHolder);\r
307                        // process function args\r
308                        var first = true;\r
309                        var fnArgs = '';\r
310                        var usedArgs = [];\r
311                        for(var p=0; p<parts.length; p++) {\r
312                            if(regPlaceHolder.test(parts[p]) && (usedArgs.length == 0 || usedArgs.indexOf(parts[p]) == -1)) {\r
313                                if(!first) {fnArgs += ',';}\r
314                                fnArgs += parts[p].replace(regRepPlaceHolder, 'v$1');\r
315                                usedArgs.push(parts[p]);\r
316                                first = false;\r
317                            }\r
318                        }\r
319                        parsed += name + '=function(' + fnArgs + '){';\r
320                        // process function body\r
321                        var fnExpr = '"' + value.replace(regRepPlaceHolder, '"+v$1+"') + '"';\r
322                        parsed += 'return ' + fnExpr + ';' + '};';\r
323                        \r
324                    // simple value\r
325                    }else{\r
326                        parsed += name+'="'+value+'";';\r
327                    }\r
328                } // END: Mode: bundle keys as vars/functions\r
329            } // END: if(pair.length > 0)\r
330        } // END: skip comments\r
331    }\r
332    eval(parsed);\r
333 }\r
334 \r
335 /** Make sure namespace exists (for keys with dots in name) */\r
336 // TODO key parts that start with numbers quietly fail. i.e. month.short.1=Jan\r
337 function checkKeyNamespace(key) {\r
338         var regDot = /\./;\r
339         if(regDot.test(key)) {\r
340                 var fullname = '';\r
341                 var names = key.split( /\./ );\r
342                 for(var i=0; i<names.length; i++) {\r
343                         if(i>0) {fullname += '.';}\r
344                         fullname += names[i];\r
345                         if(eval('typeof '+fullname+' == "undefined"')) {\r
346                                 eval(fullname + '={};');\r
347                         }\r
348                 }\r
349         }\r
350 }\r
351 \r
352 /** Make sure filename is an array */\r
353 function getFiles(names) {\r
354         return (names && names.constructor == Array) ? names : [names];\r
355 }\r
356 \r
357 /** Ensure language code is in the format aa_AA. */\r
358 function normaliseLanguageCode(lang) {\r
359     lang = lang.toLowerCase();\r
360     if(lang.length > 3) {\r
361         lang = lang.substring(0, 3) + lang.substring(3).toUpperCase();\r
362     }\r
363     return lang;\r
364 }\r
365 \r
366 /** Unescape unicode chars ('\u00e3') */\r
367 function unescapeUnicode(str) {\r
368   // unescape unicode codes\r
369   var codes = [];\r
370   var code = parseInt(str.substr(2), 16);\r
371   if (code >= 0 && code < Math.pow(2, 16)) {\r
372      codes.push(code);\r
373   }\r
374   // convert codes to text\r
375   var unescaped = '';\r
376   for (var i = 0; i < codes.length; ++i) {\r
377     unescaped += String.fromCharCode(codes[i]);\r
378   }\r
379   return unescaped;\r
380 }\r
381 \r
382 /* Cross-Browser Split 1.0.1\r
383 (c) Steven Levithan <stevenlevithan.com>; MIT License\r
384 An ECMA-compliant, uniform cross-browser split method */\r
385 var cbSplit;\r
386 // avoid running twice, which would break `cbSplit._nativeSplit`'s reference to the native `split`\r
387 if (!cbSplit) {    \r
388   cbSplit = function(str, separator, limit) {\r
389       // if `separator` is not a regex, use the native `split`\r
390       if (Object.prototype.toString.call(separator) !== "[object RegExp]") {\r
391         if(typeof cbSplit._nativeSplit == "undefined")\r
392           return str.split(separator, limit);\r
393         else\r
394           return cbSplit._nativeSplit.call(str, separator, limit);\r
395       }\r
396   \r
397       var output = [],\r
398           lastLastIndex = 0,\r
399           flags = (separator.ignoreCase ? "i" : "") +\r
400                   (separator.multiline  ? "m" : "") +\r
401                   (separator.sticky     ? "y" : ""),\r
402           separator = RegExp(separator.source, flags + "g"), // make `global` and avoid `lastIndex` issues by working with a copy\r
403           separator2, match, lastIndex, lastLength;\r
404   \r
405       str = str + ""; // type conversion\r
406       if (!cbSplit._compliantExecNpcg) {\r
407           separator2 = RegExp("^" + separator.source + "$(?!\\s)", flags); // doesn't need /g or /y, but they don't hurt\r
408       }\r
409   \r
410       /* behavior for `limit`: if it's...\r
411       - `undefined`: no limit.\r
412       - `NaN` or zero: return an empty array.\r
413       - a positive number: use `Math.floor(limit)`.\r
414       - a negative number: no limit.\r
415       - other: type-convert, then use the above rules. */\r
416       if (limit === undefined || +limit < 0) {\r
417           limit = Infinity;\r
418       } else {\r
419           limit = Math.floor(+limit);\r
420           if (!limit) {\r
421               return [];\r
422           }\r
423       }\r
424   \r
425       while (match = separator.exec(str)) {\r
426           lastIndex = match.index + match[0].length; // `separator.lastIndex` is not reliable cross-browser\r
427   \r
428           if (lastIndex > lastLastIndex) {\r
429               output.push(str.slice(lastLastIndex, match.index));\r
430   \r
431               // fix browsers whose `exec` methods don't consistently return `undefined` for nonparticipating capturing groups\r
432               if (!cbSplit._compliantExecNpcg && match.length > 1) {\r
433                   match[0].replace(separator2, function () {\r
434                       for (var i = 1; i < arguments.length - 2; i++) {\r
435                           if (arguments[i] === undefined) {\r
436                               match[i] = undefined;\r
437                           }\r
438                       }\r
439                   });\r
440               }\r
441   \r
442               if (match.length > 1 && match.index < str.length) {\r
443                   Array.prototype.push.apply(output, match.slice(1));\r
444               }\r
445   \r
446               lastLength = match[0].length;\r
447               lastLastIndex = lastIndex;\r
448   \r
449               if (output.length >= limit) {\r
450                   break;\r
451               }\r
452           }\r
453   \r
454           if (separator.lastIndex === match.index) {\r
455               separator.lastIndex++; // avoid an infinite loop\r
456           }\r
457       }\r
458   \r
459       if (lastLastIndex === str.length) {\r
460           if (lastLength || !separator.test("")) {\r
461               output.push("");\r
462           }\r
463       } else {\r
464           output.push(str.slice(lastLastIndex));\r
465       }\r
466   \r
467       return output.length > limit ? output.slice(0, limit) : output;\r
468   };\r
469   \r
470   cbSplit._compliantExecNpcg = /()??/.exec("")[1] === undefined; // NPCG: nonparticipating capturing group\r
471   cbSplit._nativeSplit = String.prototype.split;\r
472 \r
473 } // end `if (!cbSplit)`\r
474 String.prototype.split = function (separator, limit) {\r
475     return cbSplit(this, separator, limit);\r
476 };\r
477 \r
478 })(jQuery);\r
479