Completed
Push — master ( be0721...cced22 )
by Matthew
02:48
created

web/static/js/vendor/jquery.i18n/jquery.i18n.dist.js   F

Complexity

Total Complexity 240
Complexity/F 2.35

Size

Lines of Code 2516
Function Count 102

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 240
dl 0
loc 2516
rs 2.4
c 0
b 0
f 0
cc 0
nc 0
mnd 3
bc 220
fnc 102
bpm 2.1568
cpm 2.3529
noi 34

How to fix   Complexity   

Complexity

Complex classes like web/static/js/vendor/jquery.i18n/jquery.i18n.dist.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*!
2
 * jQuery Internationalization library
3
 *
4
 * Copyright (C) 2012 Santhosh Thottingal
5
 *
6
 * jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
7
 * anything special to choose one license or the other and you don't have to
8
 * notify anyone which license you are using. You are free to use
9
 * UniversalLanguageSelector in commercial projects as long as the copyright
10
 * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
11
 *
12
 * @licence GNU General Public Licence 2.0 or later
13
 * @licence MIT License
14
 */
15
16
( function ( $ ) {
17
	'use strict';
18
19
	var nav, I18N,
20
		slice = Array.prototype.slice;
21
	/**
22
	 * @constructor
23
	 * @param {Object} options
24
	 */
25
	I18N = function ( options ) {
26
		// Load defaults
27
		this.options = $.extend( {}, I18N.defaults, options );
28
29
		this.parser = this.options.parser;
30
		this.locale = this.options.locale;
31
		this.messageStore = this.options.messageStore;
32
		this.languages = {};
33
34
		this.init();
35
	};
36
37
	I18N.prototype = {
38
		/**
39
		 * Initialize by loading locales and setting up
40
		 * String.prototype.toLocaleString and String.locale.
41
		 */
42
		init: function () {
43
			var i18n = this;
44
45
			// Set locale of String environment
46
			String.locale = i18n.locale;
47
48
			// Override String.localeString method
49
			String.prototype.toLocaleString = function () {
50
				var localeParts, localePartIndex, value, locale, fallbackIndex,
51
					tryingLocale, message;
52
53
				value = this.valueOf();
54
				locale = i18n.locale;
55
				fallbackIndex = 0;
56
57
				while ( locale ) {
58
					// Iterate through locales starting at most-specific until
59
					// localization is found. As in fi-Latn-FI, fi-Latn and fi.
60
					localeParts = locale.split( '-' );
61
					localePartIndex = localeParts.length;
62
63
					do {
64
						tryingLocale = localeParts.slice( 0, localePartIndex ).join( '-' );
65
						message = i18n.messageStore.get( tryingLocale, value );
66
67
						if ( message ) {
68
							return message;
69
						}
70
71
						localePartIndex--;
72
					} while ( localePartIndex );
73
74
					if ( locale === 'en' ) {
75
						break;
76
					}
77
78
					locale = ( $.i18n.fallbacks[ i18n.locale ] && $.i18n.fallbacks[ i18n.locale ][ fallbackIndex ] ) ||
79
						i18n.options.fallbackLocale;
80
					$.i18n.log( 'Trying fallback locale for ' + i18n.locale + ': ' + locale );
81
82
					fallbackIndex++;
83
				}
84
85
				// key not found
86
				return '';
87
			};
88
		},
89
90
		/*
91
		 * Destroy the i18n instance.
92
		 */
93
		destroy: function () {
94
			$.removeData( document, 'i18n' );
95
		},
96
97
		/**
98
		 * General message loading API This can take a URL string for
99
		 * the json formatted messages. Example:
100
		 * <code>load('path/to/all_localizations.json');</code>
101
		 *
102
		 * To load a localization file for a locale:
103
		 * <code>
104
		 * load('path/to/de-messages.json', 'de' );
105
		 * </code>
106
		 *
107
		 * To load a localization file from a directory:
108
		 * <code>
109
		 * load('path/to/i18n/directory', 'de' );
110
		 * </code>
111
		 * The above method has the advantage of fallback resolution.
112
		 * ie, it will automatically load the fallback locales for de.
113
		 * For most usecases, this is the recommended method.
114
		 * It is optional to have trailing slash at end.
115
		 *
116
		 * A data object containing message key- message translation mappings
117
		 * can also be passed. Example:
118
		 * <code>
119
		 * load( { 'hello' : 'Hello' }, optionalLocale );
120
		 * </code>
121
		 *
122
		 * A source map containing key-value pair of languagename and locations
123
		 * can also be passed. Example:
124
		 * <code>
125
		 * load( {
126
		 * bn: 'i18n/bn.json',
127
		 * he: 'i18n/he.json',
128
		 * en: 'i18n/en.json'
129
		 * } )
130
		 * </code>
131
		 *
132
		 * If the data argument is null/undefined/false,
133
		 * all cached messages for the i18n instance will get reset.
134
		 *
135
		 * @param {string|Object} source
136
		 * @param {string} locale Language tag
137
		 * @return {jQuery.Promise}
138
		 */
139
		load: function ( source, locale ) {
140
			var fallbackLocales, locIndex, fallbackLocale, sourceMap = {};
141
			if ( !source && !locale ) {
142
				source = 'i18n/' + $.i18n().locale + '.json';
143
				locale = $.i18n().locale;
144
			}
145
			if ( typeof source === 'string' &&
146
				source.split( '.' ).pop() !== 'json'
147
			) {
148
				// Load specified locale then check for fallbacks when directory is specified in load()
149
				sourceMap[ locale ] = source + '/' + locale + '.json';
150
				fallbackLocales = ( $.i18n.fallbacks[ locale ] || [] )
151
					.concat( this.options.fallbackLocale );
152
				for ( locIndex in fallbackLocales ) {
153
					fallbackLocale = fallbackLocales[ locIndex ];
154
					sourceMap[ fallbackLocale ] = source + '/' + fallbackLocale + '.json';
155
				}
156
				return this.load( sourceMap );
157
			} else {
158
				return this.messageStore.load( source, locale );
159
			}
160
161
		},
162
163
		/**
164
		 * Does parameter and magic word substitution.
165
		 *
166
		 * @param {string} key Message key
167
		 * @param {Array} parameters Message parameters
168
		 * @return {string}
169
		 */
170
		parse: function ( key, parameters ) {
171
			var message = key.toLocaleString();
172
			// FIXME: This changes the state of the I18N object,
173
			// should probably not change the 'this.parser' but just
174
			// pass it to the parser.
175
			this.parser.language = $.i18n.languages[ $.i18n().locale ] || $.i18n.languages[ 'default' ];
176
			if ( message === '' ) {
177
				message = key;
178
			}
179
			return this.parser.parse( message, parameters );
180
		}
181
	};
182
183
	/**
184
	 * Process a message from the $.I18N instance
185
	 * for the current document, stored in jQuery.data(document).
186
	 *
187
	 * @param {string} key Key of the message.
188
	 * @param {string} param1 [param...] Variadic list of parameters for {key}.
189
	 * @return {string|$.I18N} Parsed message, or if no key was given
190
	 * the instance of $.I18N is returned.
191
	 */
192
	$.i18n = function ( key, param1 ) {
193
		var parameters,
194
			i18n = $.data( document, 'i18n' ),
195
			options = typeof key === 'object' && key;
196
197
		// If the locale option for this call is different then the setup so far,
198
		// update it automatically. This doesn't just change the context for this
199
		// call but for all future call as well.
200
		// If there is no i18n setup yet, don't do this. It will be taken care of
201
		// by the `new I18N` construction below.
202
		// NOTE: It should only change language for this one call.
203
		// Then cache instances of I18N somewhere.
204
		if ( options && options.locale && i18n && i18n.locale !== options.locale ) {
205
			String.locale = i18n.locale = options.locale;
206
		}
207
208
		if ( !i18n ) {
209
			i18n = new I18N( options );
210
			$.data( document, 'i18n', i18n );
211
		}
212
213
		if ( typeof key === 'string' ) {
214
			if ( param1 !== undefined ) {
215
				parameters = slice.call( arguments, 1 );
216
			} else {
217
				parameters = [];
218
			}
219
220
			return i18n.parse( key, parameters );
221
		} else {
222
			// FIXME: remove this feature/bug.
223
			return i18n;
224
		}
225
	};
226
227
	$.fn.i18n = function () {
228
		var i18n = $.data( document, 'i18n' );
229
230
		if ( !i18n ) {
231
			i18n = new I18N();
232
			$.data( document, 'i18n', i18n );
233
		}
234
		String.locale = i18n.locale;
235
		return this.each( function () {
236
			var $this = $( this ),
237
				messageKey = $this.data( 'i18n' ),
238
				lBracket, rBracket, type, key;
239
240
			if ( messageKey ) {
241
				lBracket = messageKey.indexOf( '[' );
242
				rBracket = messageKey.indexOf( ']' );
243
				if ( lBracket !== -1 && rBracket !== -1 && lBracket < rBracket ) {
244
					type = messageKey.slice( lBracket + 1, rBracket );
245
					key = messageKey.slice( rBracket + 1 );
246
					if ( type === 'html' ) {
247
						$this.html( i18n.parse( key ) );
248
					} else {
249
						$this.attr( type, i18n.parse( key ) );
250
					}
251
				} else {
252
					$this.text( i18n.parse( messageKey ) );
253
				}
254
			} else {
255
				$this.find( '[data-i18n]' ).i18n();
256
			}
257
		} );
258
	};
259
260
	String.locale = String.locale || $( 'html' ).attr( 'lang' );
261
262
	if ( !String.locale ) {
263
		if ( typeof window.navigator !== undefined ) {
264
			nav = window.navigator;
265
			String.locale = nav.language || nav.userLanguage || '';
266
		} else {
267
			String.locale = '';
268
		}
269
	}
270
271
	$.i18n.languages = {};
272
	$.i18n.messageStore = $.i18n.messageStore || {};
273
	$.i18n.parser = {
274
		// The default parser only handles variable substitution
275
		parse: function ( message, parameters ) {
276
			return message.replace( /\$(\d+)/g, function ( str, match ) {
277
				var index = parseInt( match, 10 ) - 1;
278
				return parameters[ index ] !== undefined ? parameters[ index ] : '$' + match;
279
			} );
280
		},
281
		emitter: {}
282
	};
283
	$.i18n.fallbacks = {};
284
	$.i18n.debug = false;
285
	$.i18n.log = function ( /* arguments */ ) {
286
		if ( window.console && $.i18n.debug ) {
287
			window.console.log.apply( window.console, arguments );
288
		}
289
	};
290
	/* Static members */
291
	I18N.defaults = {
292
		locale: String.locale,
293
		fallbackLocale: 'en',
294
		parser: $.i18n.parser,
295
		messageStore: $.i18n.messageStore
296
	};
297
298
	// Expose constructor
299
	$.i18n.constructor = I18N;
300
}( jQuery ) );
301
/*!
302
 * jQuery Internationalization library - Message Store
303
 *
304
 * Copyright (C) 2012 Santhosh Thottingal
305
 *
306
 * jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do anything special to
307
 * choose one license or the other and you don't have to notify anyone which license you are using.
308
 * You are free to use UniversalLanguageSelector in commercial projects as long as the copyright
309
 * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
310
 *
311
 * @licence GNU General Public Licence 2.0 or later
312
 * @licence MIT License
313
 */
314
315
( function ( $, window, undefined ) {
316
	'use strict';
317
318
	var MessageStore = function () {
319
		this.messages = {};
320
		this.sources = {};
321
	};
322
323
	/**
324
	 * See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading
325
	 */
326
	MessageStore.prototype = {
327
328
		/**
329
		 * General message loading API This can take a URL string for
330
		 * the json formatted messages.
331
		 * <code>load('path/to/all_localizations.json');</code>
332
		 *
333
		 * This can also load a localization file for a locale <code>
334
		 * load( 'path/to/de-messages.json', 'de' );
335
		 * </code>
336
		 * A data object containing message key- message translation mappings
337
		 * can also be passed Eg:
338
		 * <code>
339
		 * load( { 'hello' : 'Hello' }, optionalLocale );
340
		 * </code> If the data argument is
341
		 * null/undefined/false,
342
		 * all cached messages for the i18n instance will get reset.
343
		 *
344
		 * @param {string|Object} source
345
		 * @param {string} locale Language tag
346
		 * @return {jQuery.Promise}
347
		 */
348
		load: function ( source, locale ) {
349
			var key = null,
350
				deferred = null,
351
				deferreds = [],
352
				messageStore = this;
353
354
			if ( typeof source === 'string' ) {
355
				// This is a URL to the messages file.
356
				$.i18n.log( 'Loading messages from: ' + source );
357
				deferred = jsonMessageLoader( source )
358
					.done( function ( localization ) {
359
						messageStore.set( locale, localization );
360
					} );
361
362
				return deferred.promise();
363
			}
364
365
			if ( locale ) {
366
				// source is an key-value pair of messages for given locale
367
				messageStore.set( locale, source );
368
369
				return $.Deferred().resolve();
370
			} else {
371
				// source is a key-value pair of locales and their source
372
				for ( key in source ) {
373
					if ( Object.prototype.hasOwnProperty.call( source, key ) ) {
374
						locale = key;
375
						// No {locale} given, assume data is a group of languages,
376
						// call this function again for each language.
377
						deferreds.push( messageStore.load( source[ key ], locale ) );
378
					}
379
				}
380
				return $.when.apply( $, deferreds );
381
			}
382
383
		},
384
385
		/**
386
		 * Set messages to the given locale.
387
		 * If locale exists, add messages to the locale.
388
		 *
389
		 * @param {string} locale
390
		 * @param {Object} messages
391
		 */
392
		set: function ( locale, messages ) {
393
			if ( !this.messages[ locale ] ) {
394
				this.messages[ locale ] = messages;
395
			} else {
396
				this.messages[ locale ] = $.extend( this.messages[ locale ], messages );
397
			}
398
		},
399
400
		/**
401
		 *
402
		 * @param {string} locale
403
		 * @param {string} messageKey
404
		 * @return {boolean}
405
		 */
406
		get: function ( locale, messageKey ) {
407
			return this.messages[ locale ] && this.messages[ locale ][ messageKey ];
408
		}
409
	};
410
411
	function jsonMessageLoader( url ) {
412
		var deferred = $.Deferred();
413
414
		$.getJSON( url )
415
			.done( deferred.resolve )
416
			.fail( function ( jqxhr, settings, exception ) {
417
				$.i18n.log( 'Error in loading messages from ' + url + ' Exception: ' + exception );
418
				// Ignore 404 exception, because we are handling fallabacks explicitly
419
				deferred.resolve();
420
			} );
421
422
		return deferred.promise();
423
	}
424
425
	$.extend( $.i18n.messageStore, new MessageStore() );
426
}( jQuery, window ) );
427
/*!
428
 * jQuery Internationalization library
429
 *
430
 * Copyright (C) 2012 Santhosh Thottingal
431
 *
432
 * jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do anything special to
433
 * choose one license or the other and you don't have to notify anyone which license you are using.
434
 * You are free to use UniversalLanguageSelector in commercial projects as long as the copyright
435
 * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
436
 *
437
 * @licence GNU General Public Licence 2.0 or later
438
 * @licence MIT License
439
 */
440
( function ( $, undefined ) {
441
	'use strict';
442
443
	$.i18n = $.i18n || {};
444
	$.extend( $.i18n.fallbacks, {
445
		ab: [ 'ru' ],
446
		ace: [ 'id' ],
447
		aln: [ 'sq' ],
448
		// Not so standard - als is supposed to be Tosk Albanian,
449
		// but in Wikipedia it's used for a Germanic language.
450
		als: [ 'gsw', 'de' ],
451
		an: [ 'es' ],
452
		anp: [ 'hi' ],
453
		arn: [ 'es' ],
454
		arz: [ 'ar' ],
455
		av: [ 'ru' ],
456
		ay: [ 'es' ],
457
		ba: [ 'ru' ],
458
		bar: [ 'de' ],
459
		'bat-smg': [ 'sgs', 'lt' ],
460
		bcc: [ 'fa' ],
461
		'be-x-old': [ 'be-tarask' ],
462
		bh: [ 'bho' ],
463
		bjn: [ 'id' ],
464
		bm: [ 'fr' ],
465
		bpy: [ 'bn' ],
466
		bqi: [ 'fa' ],
467
		bug: [ 'id' ],
468
		'cbk-zam': [ 'es' ],
469
		ce: [ 'ru' ],
470
		crh: [ 'crh-latn' ],
471
		'crh-cyrl': [ 'ru' ],
472
		csb: [ 'pl' ],
473
		cv: [ 'ru' ],
474
		'de-at': [ 'de' ],
475
		'de-ch': [ 'de' ],
476
		'de-formal': [ 'de' ],
477
		dsb: [ 'de' ],
478
		dtp: [ 'ms' ],
479
		egl: [ 'it' ],
480
		eml: [ 'it' ],
481
		ff: [ 'fr' ],
482
		fit: [ 'fi' ],
483
		'fiu-vro': [ 'vro', 'et' ],
484
		frc: [ 'fr' ],
485
		frp: [ 'fr' ],
486
		frr: [ 'de' ],
487
		fur: [ 'it' ],
488
		gag: [ 'tr' ],
489
		gan: [ 'gan-hant', 'zh-hant', 'zh-hans' ],
490
		'gan-hans': [ 'zh-hans' ],
491
		'gan-hant': [ 'zh-hant', 'zh-hans' ],
492
		gl: [ 'pt' ],
493
		glk: [ 'fa' ],
494
		gn: [ 'es' ],
495
		gsw: [ 'de' ],
496
		hif: [ 'hif-latn' ],
497
		hsb: [ 'de' ],
498
		ht: [ 'fr' ],
499
		ii: [ 'zh-cn', 'zh-hans' ],
500
		inh: [ 'ru' ],
501
		iu: [ 'ike-cans' ],
502
		jut: [ 'da' ],
503
		jv: [ 'id' ],
504
		kaa: [ 'kk-latn', 'kk-cyrl' ],
505
		kbd: [ 'kbd-cyrl' ],
506
		khw: [ 'ur' ],
507
		kiu: [ 'tr' ],
508
		kk: [ 'kk-cyrl' ],
509
		'kk-arab': [ 'kk-cyrl' ],
510
		'kk-latn': [ 'kk-cyrl' ],
511
		'kk-cn': [ 'kk-arab', 'kk-cyrl' ],
512
		'kk-kz': [ 'kk-cyrl' ],
513
		'kk-tr': [ 'kk-latn', 'kk-cyrl' ],
514
		kl: [ 'da' ],
515
		'ko-kp': [ 'ko' ],
516
		koi: [ 'ru' ],
517
		krc: [ 'ru' ],
518
		ks: [ 'ks-arab' ],
519
		ksh: [ 'de' ],
520
		ku: [ 'ku-latn' ],
521
		'ku-arab': [ 'ckb' ],
522
		kv: [ 'ru' ],
523
		lad: [ 'es' ],
524
		lb: [ 'de' ],
525
		lbe: [ 'ru' ],
526
		lez: [ 'ru' ],
527
		li: [ 'nl' ],
528
		lij: [ 'it' ],
529
		liv: [ 'et' ],
530
		lmo: [ 'it' ],
531
		ln: [ 'fr' ],
532
		ltg: [ 'lv' ],
533
		lzz: [ 'tr' ],
534
		mai: [ 'hi' ],
535
		'map-bms': [ 'jv', 'id' ],
536
		mg: [ 'fr' ],
537
		mhr: [ 'ru' ],
538
		min: [ 'id' ],
539
		mo: [ 'ro' ],
540
		mrj: [ 'ru' ],
541
		mwl: [ 'pt' ],
542
		myv: [ 'ru' ],
543
		mzn: [ 'fa' ],
544
		nah: [ 'es' ],
545
		nap: [ 'it' ],
546
		nds: [ 'de' ],
547
		'nds-nl': [ 'nl' ],
548
		'nl-informal': [ 'nl' ],
549
		no: [ 'nb' ],
550
		os: [ 'ru' ],
551
		pcd: [ 'fr' ],
552
		pdc: [ 'de' ],
553
		pdt: [ 'de' ],
554
		pfl: [ 'de' ],
555
		pms: [ 'it' ],
556
		pt: [ 'pt-br' ],
557
		'pt-br': [ 'pt' ],
558
		qu: [ 'es' ],
559
		qug: [ 'qu', 'es' ],
560
		rgn: [ 'it' ],
561
		rmy: [ 'ro' ],
562
		'roa-rup': [ 'rup' ],
563
		rue: [ 'uk', 'ru' ],
564
		ruq: [ 'ruq-latn', 'ro' ],
565
		'ruq-cyrl': [ 'mk' ],
566
		'ruq-latn': [ 'ro' ],
567
		sa: [ 'hi' ],
568
		sah: [ 'ru' ],
569
		scn: [ 'it' ],
570
		sg: [ 'fr' ],
571
		sgs: [ 'lt' ],
572
		sli: [ 'de' ],
573
		sr: [ 'sr-ec' ],
574
		srn: [ 'nl' ],
575
		stq: [ 'de' ],
576
		su: [ 'id' ],
577
		szl: [ 'pl' ],
578
		tcy: [ 'kn' ],
579
		tg: [ 'tg-cyrl' ],
580
		tt: [ 'tt-cyrl', 'ru' ],
581
		'tt-cyrl': [ 'ru' ],
582
		ty: [ 'fr' ],
583
		udm: [ 'ru' ],
584
		ug: [ 'ug-arab' ],
585
		uk: [ 'ru' ],
586
		vec: [ 'it' ],
587
		vep: [ 'et' ],
588
		vls: [ 'nl' ],
589
		vmf: [ 'de' ],
590
		vot: [ 'fi' ],
591
		vro: [ 'et' ],
592
		wa: [ 'fr' ],
593
		wo: [ 'fr' ],
594
		wuu: [ 'zh-hans' ],
595
		xal: [ 'ru' ],
596
		xmf: [ 'ka' ],
597
		yi: [ 'he' ],
598
		za: [ 'zh-hans' ],
599
		zea: [ 'nl' ],
600
		zh: [ 'zh-hans' ],
601
		'zh-classical': [ 'lzh' ],
602
		'zh-cn': [ 'zh-hans' ],
603
		'zh-hant': [ 'zh-hans' ],
604
		'zh-hk': [ 'zh-hant', 'zh-hans' ],
605
		'zh-min-nan': [ 'nan' ],
606
		'zh-mo': [ 'zh-hk', 'zh-hant', 'zh-hans' ],
607
		'zh-my': [ 'zh-sg', 'zh-hans' ],
608
		'zh-sg': [ 'zh-hans' ],
609
		'zh-tw': [ 'zh-hant', 'zh-hans' ],
610
		'zh-yue': [ 'yue' ]
611
	} );
612
}( jQuery ) );
613
/*!
614
 * jQuery Internationalization library
615
 *
616
 * Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar
617
 *
618
 * jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
619
 * anything special to choose one license or the other and you don't have to
620
 * notify anyone which license you are using. You are free to use
621
 * UniversalLanguageSelector in commercial projects as long as the copyright
622
 * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
623
 *
624
 * @licence GNU General Public Licence 2.0 or later
625
 * @licence MIT License
626
 */
627
628
( function ( $ ) {
629
	'use strict';
630
631
	var MessageParser = function ( options ) {
632
		this.options = $.extend( {}, $.i18n.parser.defaults, options );
633
		this.language = $.i18n.languages[ String.locale ] || $.i18n.languages[ 'default' ];
634
		this.emitter = $.i18n.parser.emitter;
635
	};
636
637
	MessageParser.prototype = {
638
639
		constructor: MessageParser,
640
641
		simpleParse: function ( message, parameters ) {
642
			return message.replace( /\$(\d+)/g, function ( str, match ) {
643
				var index = parseInt( match, 10 ) - 1;
644
645
				return parameters[ index ] !== undefined ? parameters[ index ] : '$' + match;
646
			} );
647
		},
648
649
		parse: function ( message, replacements ) {
650
			if ( message.indexOf( '{{' ) < 0 ) {
651
				return this.simpleParse( message, replacements );
652
			}
653
654
			this.emitter.language = $.i18n.languages[ $.i18n().locale ] ||
655
				$.i18n.languages[ 'default' ];
656
657
			return this.emitter.emit( this.ast( message ), replacements );
658
		},
659
660
		ast: function ( message ) {
661
			var pipe, colon, backslash, anyCharacter, dollar, digits, regularLiteral,
662
				regularLiteralWithoutBar, regularLiteralWithoutSpace, escapedOrLiteralWithoutBar,
663
				escapedOrRegularLiteral, templateContents, templateName, openTemplate,
664
				closeTemplate, expression, paramExpression, result,
665
				pos = 0;
666
667
			// Try parsers until one works, if none work return null
668
			function choice( parserSyntax ) {
669
				return function () {
670
					var i, result;
671
672
					for ( i = 0; i < parserSyntax.length; i++ ) {
673
						result = parserSyntax[ i ]();
674
675
						if ( result !== null ) {
676
							return result;
677
						}
678
					}
679
680
					return null;
681
				};
682
			}
683
684
			// Try several parserSyntax-es in a row.
685
			// All must succeed; otherwise, return null.
686
			// This is the only eager one.
687
			function sequence( parserSyntax ) {
688
				var i, res,
689
					originalPos = pos,
690
					result = [];
691
692
				for ( i = 0; i < parserSyntax.length; i++ ) {
693
					res = parserSyntax[ i ]();
694
695
					if ( res === null ) {
696
						pos = originalPos;
697
698
						return null;
699
					}
700
701
					result.push( res );
702
				}
703
704
				return result;
705
			}
706
707
			// Run the same parser over and over until it fails.
708
			// Must succeed a minimum of n times; otherwise, return null.
709
			function nOrMore( n, p ) {
710
				return function () {
711
					var originalPos = pos,
712
						result = [],
713
						parsed = p();
714
715
					while ( parsed !== null ) {
716
						result.push( parsed );
717
						parsed = p();
718
					}
719
720
					if ( result.length < n ) {
721
						pos = originalPos;
722
723
						return null;
724
					}
725
726
					return result;
727
				};
728
			}
729
730
			// Helpers -- just make parserSyntax out of simpler JS builtin types
731
732
			function makeStringParser( s ) {
733
				var len = s.length;
734
735
				return function () {
736
					var result = null;
737
738
					if ( message.slice( pos, pos + len ) === s ) {
739
						result = s;
740
						pos += len;
741
					}
742
743
					return result;
744
				};
745
			}
746
747
			function makeRegexParser( regex ) {
748
				return function () {
749
					var matches = message.slice( pos ).match( regex );
750
751
					if ( matches === null ) {
752
						return null;
753
					}
754
755
					pos += matches[ 0 ].length;
756
757
					return matches[ 0 ];
758
				};
759
			}
760
761
			pipe = makeStringParser( '|' );
762
			colon = makeStringParser( ':' );
763
			backslash = makeStringParser( '\\' );
764
			anyCharacter = makeRegexParser( /^./ );
765
			dollar = makeStringParser( '$' );
766
			digits = makeRegexParser( /^\d+/ );
767
			regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ );
768
			regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ );
769
			regularLiteralWithoutSpace = makeRegexParser( /^[^{}\[\]$\s]/ );
770
771
			// There is a general pattern:
772
			// parse a thing;
773
			// if it worked, apply transform,
774
			// otherwise return null.
775
			// But using this as a combinator seems to cause problems
776
			// when combined with nOrMore().
777
			// May be some scoping issue.
778
			function transform( p, fn ) {
779
				return function () {
780
					var result = p();
781
782
					return result === null ? null : fn( result );
783
				};
784
			}
785
786
			// Used to define "literals" within template parameters. The pipe
787
			// character is the parameter delimeter, so by default
788
			// it is not a literal in the parameter
789
			function literalWithoutBar() {
790
				var result = nOrMore( 1, escapedOrLiteralWithoutBar )();
791
792
				return result === null ? null : result.join( '' );
793
			}
794
795
			function literal() {
796
				var result = nOrMore( 1, escapedOrRegularLiteral )();
797
798
				return result === null ? null : result.join( '' );
799
			}
800
801
			function escapedLiteral() {
802
				var result = sequence( [ backslash, anyCharacter ] );
803
804
				return result === null ? null : result[ 1 ];
805
			}
806
807
			choice( [ escapedLiteral, regularLiteralWithoutSpace ] );
808
			escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] );
809
			escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] );
810
811
			function replacement() {
812
				var result = sequence( [ dollar, digits ] );
813
814
				if ( result === null ) {
815
					return null;
816
				}
817
818
				return [ 'REPLACE', parseInt( result[ 1 ], 10 ) - 1 ];
819
			}
820
821
			templateName = transform(
822
				// see $wgLegalTitleChars
823
				// not allowing : due to the need to catch "PLURAL:$1"
824
				makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ),
825
826
				function ( result ) {
827
					return result.toString();
828
				}
829
			);
830
831
			function templateParam() {
832
				var expr,
833
					result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] );
834
835
				if ( result === null ) {
836
					return null;
837
				}
838
839
				expr = result[ 1 ];
840
841
				// use a "CONCAT" operator if there are multiple nodes,
842
				// otherwise return the first node, raw.
843
				return expr.length > 1 ? [ 'CONCAT' ].concat( expr ) : expr[ 0 ];
844
			}
845
846
			function templateWithReplacement() {
847
				var result = sequence( [ templateName, colon, replacement ] );
848
849
				return result === null ? null : [ result[ 0 ], result[ 2 ] ];
850
			}
851
852
			function templateWithOutReplacement() {
853
				var result = sequence( [ templateName, colon, paramExpression ] );
854
855
				return result === null ? null : [ result[ 0 ], result[ 2 ] ];
856
			}
857
858
			templateContents = choice( [
859
				function () {
860
					var res = sequence( [
861
						// templates can have placeholders for dynamic
862
						// replacement eg: {{PLURAL:$1|one car|$1 cars}}
863
						// or no placeholders eg:
864
						// {{GRAMMAR:genitive|{{SITENAME}}}
865
						choice( [ templateWithReplacement, templateWithOutReplacement ] ),
866
						nOrMore( 0, templateParam )
867
					] );
868
869
					return res === null ? null : res[ 0 ].concat( res[ 1 ] );
870
				},
871
				function () {
872
					var res = sequence( [ templateName, nOrMore( 0, templateParam ) ] );
873
874
					if ( res === null ) {
875
						return null;
876
					}
877
878
					return [ res[ 0 ] ].concat( res[ 1 ] );
879
				}
880
			] );
881
882
			openTemplate = makeStringParser( '{{' );
883
			closeTemplate = makeStringParser( '}}' );
884
885
			function template() {
886
				var result = sequence( [ openTemplate, templateContents, closeTemplate ] );
887
888
				return result === null ? null : result[ 1 ];
889
			}
890
891
			expression = choice( [ template, replacement, literal ] );
892
			paramExpression = choice( [ template, replacement, literalWithoutBar ] );
893
894
			function start() {
895
				var result = nOrMore( 0, expression )();
896
897
				if ( result === null ) {
898
					return null;
899
				}
900
901
				return [ 'CONCAT' ].concat( result );
902
			}
903
904
			result = start();
905
906
			/*
907
			 * For success, the pos must have gotten to the end of the input
908
			 * and returned a non-null.
909
			 * n.b. This is part of language infrastructure, so we do not throw an internationalizable message.
910
			 */
911
			if ( result === null || pos !== message.length ) {
912
				throw new Error( 'Parse error at position ' + pos.toString() + ' in input: ' + message );
913
			}
914
915
			return result;
916
		}
917
918
	};
919
920
	$.extend( $.i18n.parser, new MessageParser() );
921
}( jQuery ) );
922
/*!
923
 * jQuery Internationalization library
924
 *
925
 * Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar
926
 *
927
 * jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do
928
 * anything special to choose one license or the other and you don't have to
929
 * notify anyone which license you are using. You are free to use
930
 * UniversalLanguageSelector in commercial projects as long as the copyright
931
 * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
932
 *
933
 * @licence GNU General Public Licence 2.0 or later
934
 * @licence MIT License
935
 */
936
937
( function ( $ ) {
938
	'use strict';
939
940
	var MessageParserEmitter = function () {
941
		this.language = $.i18n.languages[ String.locale ] || $.i18n.languages[ 'default' ];
942
	};
943
944
	MessageParserEmitter.prototype = {
945
		constructor: MessageParserEmitter,
946
947
		/**
948
		 * (We put this method definition here, and not in prototype, to make
949
		 * sure it's not overwritten by any magic.) Walk entire node structure,
950
		 * applying replacements and template functions when appropriate
951
		 *
952
		 * @param {Mixed} node abstract syntax tree (top node or subnode)
953
		 * @param {Array} replacements for $1, $2, ... $n
954
		 * @return {Mixed} single-string node or array of nodes suitable for
955
		 *  jQuery appending.
956
		 */
957
		emit: function ( node, replacements ) {
958
			var ret, subnodes, operation,
959
				messageParserEmitter = this;
960
961
			switch ( typeof node ) {
962
			case 'string':
963
			case 'number':
964
				ret = node;
965
				break;
966
			case 'object':
967
				// node is an array of nodes
968
				subnodes = $.map( node.slice( 1 ), function ( n ) {
969
					return messageParserEmitter.emit( n, replacements );
970
				} );
971
972
				operation = node[ 0 ].toLowerCase();
973
974
				if ( typeof messageParserEmitter[ operation ] === 'function' ) {
975
					ret = messageParserEmitter[ operation ]( subnodes, replacements );
976
				} else {
977
					throw new Error( 'unknown operation "' + operation + '"' );
978
				}
979
980
				break;
981
			case 'undefined':
982
				// Parsing the empty string (as an entire expression, or as a
983
				// paramExpression in a template) results in undefined
984
				// Perhaps a more clever parser can detect this, and return the
985
				// empty string? Or is that useful information?
986
				// The logical thing is probably to return the empty string here
987
				// when we encounter undefined.
988
				ret = '';
989
				break;
990
			default:
991
				throw new Error( 'unexpected type in AST: ' + typeof node );
992
			}
993
994
			return ret;
995
		},
996
997
		/**
998
		 * Parsing has been applied depth-first we can assume that all nodes
999
		 * here are single nodes Must return a single node to parents -- a
1000
		 * jQuery with synthetic span However, unwrap any other synthetic spans
1001
		 * in our children and pass them upwards
1002
		 *
1003
		 * @param {Array} nodes Mixed, some single nodes, some arrays of nodes.
1004
		 * @return {string}
1005
		 */
1006
		concat: function ( nodes ) {
1007
			var result = '';
1008
1009
			$.each( nodes, function ( i, node ) {
1010
				// strings, integers, anything else
1011
				result += node;
1012
			} );
1013
1014
			return result;
1015
		},
1016
1017
		/**
1018
		 * Return escaped replacement of correct index, or string if
1019
		 * unavailable. Note that we expect the parsed parameter to be
1020
		 * zero-based. i.e. $1 should have become [ 0 ]. if the specified
1021
		 * parameter is not found return the same string (e.g. "$99" ->
1022
		 * parameter 98 -> not found -> return "$99" ) TODO throw error if
1023
		 * nodes.length > 1 ?
1024
		 *
1025
		 * @param {Array} nodes One element, integer, n >= 0
1026
		 * @param {Array} replacements for $1, $2, ... $n
1027
		 * @return {string} replacement
1028
		 */
1029
		replace: function ( nodes, replacements ) {
1030
			var index = parseInt( nodes[ 0 ], 10 );
1031
1032
			if ( index < replacements.length ) {
1033
				// replacement is not a string, don't touch!
1034
				return replacements[ index ];
1035
			} else {
1036
				// index not found, fallback to displaying variable
1037
				return '$' + ( index + 1 );
1038
			}
1039
		},
1040
1041
		/**
1042
		 * Transform parsed structure into pluralization n.b. The first node may
1043
		 * be a non-integer (for instance, a string representing an Arabic
1044
		 * number). So convert it back with the current language's
1045
		 * convertNumber.
1046
		 *
1047
		 * @param {Array} nodes List [ {String|Number}, {String}, {String} ... ]
1048
		 * @return {string} selected pluralized form according to current
1049
		 *  language.
1050
		 */
1051
		plural: function ( nodes ) {
1052
			var count = parseFloat( this.language.convertNumber( nodes[ 0 ], 10 ) ),
1053
				forms = nodes.slice( 1 );
1054
1055
			return forms.length ? this.language.convertPlural( count, forms ) : '';
1056
		},
1057
1058
		/**
1059
		 * Transform parsed structure into gender Usage
1060
		 * {{gender:gender|masculine|feminine|neutral}}.
1061
		 *
1062
		 * @param {Array} nodes List [ {String}, {String}, {String} , {String} ]
1063
		 * @return {string} selected gender form according to current language
1064
		 */
1065
		gender: function ( nodes ) {
1066
			var gender = nodes[ 0 ],
1067
				forms = nodes.slice( 1 );
1068
1069
			return this.language.gender( gender, forms );
1070
		},
1071
1072
		/**
1073
		 * Transform parsed structure into grammar conversion. Invoked by
1074
		 * putting {{grammar:form|word}} in a message
1075
		 *
1076
		 * @param {Array} nodes List [{Grammar case eg: genitive}, {String word}]
1077
		 * @return {string} selected grammatical form according to current
1078
		 *  language.
1079
		 */
1080
		grammar: function ( nodes ) {
1081
			var form = nodes[ 0 ],
1082
				word = nodes[ 1 ];
1083
1084
			return word && form && this.language.convertGrammar( word, form );
1085
		}
1086
	};
1087
1088
	$.extend( $.i18n.parser.emitter, new MessageParserEmitter() );
1089
}( jQuery ) );
1090
/*global pluralRuleParser */
1091
( function ( $ ) {
1092
	'use strict';
1093
1094
	// jscs:disable
1095
	var language = {
1096
		// CLDR plural rules generated using
1097
		// libs/CLDRPluralRuleParser/tools/PluralXML2JSON.html
1098
		'pluralRules': {
1099
			'af': {
1100
				'one': 'n = 1'
1101
			},
1102
			'ak': {
1103
				'one': 'n = 0..1'
1104
			},
1105
			'am': {
1106
				'one': 'i = 0 or n = 1'
1107
			},
1108
			'ar': {
1109
				'zero': 'n = 0',
1110
				'one': 'n = 1',
1111
				'two': 'n = 2',
1112
				'few': 'n % 100 = 3..10',
1113
				'many': 'n % 100 = 11..99'
1114
			},
1115
			'ars': {
1116
				'zero': 'n = 0',
1117
				'one': 'n = 1',
1118
				'two': 'n = 2',
1119
				'few': 'n % 100 = 3..10',
1120
				'many': 'n % 100 = 11..99'
1121
			},
1122
			'as': {
1123
				'one': 'i = 0 or n = 1'
1124
			},
1125
			'asa': {
1126
				'one': 'n = 1'
1127
			},
1128
			'ast': {
1129
				'one': 'i = 1 and v = 0'
1130
			},
1131
			'az': {
1132
				'one': 'n = 1'
1133
			},
1134
			'be': {
1135
				'one': 'n % 10 = 1 and n % 100 != 11',
1136
				'few': 'n % 10 = 2..4 and n % 100 != 12..14',
1137
				'many': 'n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14'
1138
			},
1139
			'bem': {
1140
				'one': 'n = 1'
1141
			},
1142
			'bez': {
1143
				'one': 'n = 1'
1144
			},
1145
			'bg': {
1146
				'one': 'n = 1'
1147
			},
1148
			'bh': {
1149
				'one': 'n = 0..1'
1150
			},
1151
			'bm': {},
1152
			'bn': {
1153
				'one': 'i = 0 or n = 1'
1154
			},
1155
			'bo': {},
1156
			'br': {
1157
				'one': 'n % 10 = 1 and n % 100 != 11,71,91',
1158
				'two': 'n % 10 = 2 and n % 100 != 12,72,92',
1159
				'few': 'n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99',
1160
				'many': 'n != 0 and n % 1000000 = 0'
1161
			},
1162
			'brx': {
1163
				'one': 'n = 1'
1164
			},
1165
			'bs': {
1166
				'one': 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11',
1167
				'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14'
1168
			},
1169
			'ca': {
1170
				'one': 'i = 1 and v = 0'
1171
			},
1172
			'ce': {
1173
				'one': 'n = 1'
1174
			},
1175
			'cgg': {
1176
				'one': 'n = 1'
1177
			},
1178
			'chr': {
1179
				'one': 'n = 1'
1180
			},
1181
			'ckb': {
1182
				'one': 'n = 1'
1183
			},
1184
			'cs': {
1185
				'one': 'i = 1 and v = 0',
1186
				'few': 'i = 2..4 and v = 0',
1187
				'many': 'v != 0'
1188
			},
1189
			'cy': {
1190
				'zero': 'n = 0',
1191
				'one': 'n = 1',
1192
				'two': 'n = 2',
1193
				'few': 'n = 3',
1194
				'many': 'n = 6'
1195
			},
1196
			'da': {
1197
				'one': 'n = 1 or t != 0 and i = 0,1'
1198
			},
1199
			'de': {
1200
				'one': 'i = 1 and v = 0'
1201
			},
1202
			'dsb': {
1203
				'one': 'v = 0 and i % 100 = 1 or f % 100 = 1',
1204
				'two': 'v = 0 and i % 100 = 2 or f % 100 = 2',
1205
				'few': 'v = 0 and i % 100 = 3..4 or f % 100 = 3..4'
1206
			},
1207
			'dv': {
1208
				'one': 'n = 1'
1209
			},
1210
			'dz': {},
1211
			'ee': {
1212
				'one': 'n = 1'
1213
			},
1214
			'el': {
1215
				'one': 'n = 1'
1216
			},
1217
			'en': {
1218
				'one': 'i = 1 and v = 0'
1219
			},
1220
			'eo': {
1221
				'one': 'n = 1'
1222
			},
1223
			'es': {
1224
				'one': 'n = 1'
1225
			},
1226
			'et': {
1227
				'one': 'i = 1 and v = 0'
1228
			},
1229
			'eu': {
1230
				'one': 'n = 1'
1231
			},
1232
			'fa': {
1233
				'one': 'i = 0 or n = 1'
1234
			},
1235
			'ff': {
1236
				'one': 'i = 0,1'
1237
			},
1238
			'fi': {
1239
				'one': 'i = 1 and v = 0'
1240
			},
1241
			'fil': {
1242
				'one': 'v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9'
1243
			},
1244
			'fo': {
1245
				'one': 'n = 1'
1246
			},
1247
			'fr': {
1248
				'one': 'i = 0,1'
1249
			},
1250
			'fur': {
1251
				'one': 'n = 1'
1252
			},
1253
			'fy': {
1254
				'one': 'i = 1 and v = 0'
1255
			},
1256
			'ga': {
1257
				'one': 'n = 1',
1258
				'two': 'n = 2',
1259
				'few': 'n = 3..6',
1260
				'many': 'n = 7..10'
1261
			},
1262
			'gd': {
1263
				'one': 'n = 1,11',
1264
				'two': 'n = 2,12',
1265
				'few': 'n = 3..10,13..19'
1266
			},
1267
			'gl': {
1268
				'one': 'i = 1 and v = 0'
1269
			},
1270
			'gsw': {
1271
				'one': 'n = 1'
1272
			},
1273
			'gu': {
1274
				'one': 'i = 0 or n = 1'
1275
			},
1276
			'guw': {
1277
				'one': 'n = 0..1'
1278
			},
1279
			'gv': {
1280
				'one': 'v = 0 and i % 10 = 1',
1281
				'two': 'v = 0 and i % 10 = 2',
1282
				'few': 'v = 0 and i % 100 = 0,20,40,60,80',
1283
				'many': 'v != 0'
1284
			},
1285
			'ha': {
1286
				'one': 'n = 1'
1287
			},
1288
			'haw': {
1289
				'one': 'n = 1'
1290
			},
1291
			'he': {
1292
				'one': 'i = 1 and v = 0',
1293
				'two': 'i = 2 and v = 0',
1294
				'many': 'v = 0 and n != 0..10 and n % 10 = 0'
1295
			},
1296
			'hi': {
1297
				'one': 'i = 0 or n = 1'
1298
			},
1299
			'hr': {
1300
				'one': 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11',
1301
				'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14'
1302
			},
1303
			'hsb': {
1304
				'one': 'v = 0 and i % 100 = 1 or f % 100 = 1',
1305
				'two': 'v = 0 and i % 100 = 2 or f % 100 = 2',
1306
				'few': 'v = 0 and i % 100 = 3..4 or f % 100 = 3..4'
1307
			},
1308
			'hu': {
1309
				'one': 'n = 1'
1310
			},
1311
			'hy': {
1312
				'one': 'i = 0,1'
1313
			},
1314
			'id': {},
1315
			'ig': {},
1316
			'ii': {},
1317
			'in': {},
1318
			'is': {
1319
				'one': 't = 0 and i % 10 = 1 and i % 100 != 11 or t != 0'
1320
			},
1321
			'it': {
1322
				'one': 'i = 1 and v = 0'
1323
			},
1324
			'iu': {
1325
				'one': 'n = 1',
1326
				'two': 'n = 2'
1327
			},
1328
			'iw': {
1329
				'one': 'i = 1 and v = 0',
1330
				'two': 'i = 2 and v = 0',
1331
				'many': 'v = 0 and n != 0..10 and n % 10 = 0'
1332
			},
1333
			'ja': {},
1334
			'jbo': {},
1335
			'jgo': {
1336
				'one': 'n = 1'
1337
			},
1338
			'ji': {
1339
				'one': 'i = 1 and v = 0'
1340
			},
1341
			'jmc': {
1342
				'one': 'n = 1'
1343
			},
1344
			'jv': {},
1345
			'jw': {},
1346
			'ka': {
1347
				'one': 'n = 1'
1348
			},
1349
			'kab': {
1350
				'one': 'i = 0,1'
1351
			},
1352
			'kaj': {
1353
				'one': 'n = 1'
1354
			},
1355
			'kcg': {
1356
				'one': 'n = 1'
1357
			},
1358
			'kde': {},
1359
			'kea': {},
1360
			'kk': {
1361
				'one': 'n = 1'
1362
			},
1363
			'kkj': {
1364
				'one': 'n = 1'
1365
			},
1366
			'kl': {
1367
				'one': 'n = 1'
1368
			},
1369
			'km': {},
1370
			'kn': {
1371
				'one': 'i = 0 or n = 1'
1372
			},
1373
			'ko': {},
1374
			'ks': {
1375
				'one': 'n = 1'
1376
			},
1377
			'ksb': {
1378
				'one': 'n = 1'
1379
			},
1380
			'ksh': {
1381
				'zero': 'n = 0',
1382
				'one': 'n = 1'
1383
			},
1384
			'ku': {
1385
				'one': 'n = 1'
1386
			},
1387
			'kw': {
1388
				'one': 'n = 1',
1389
				'two': 'n = 2'
1390
			},
1391
			'ky': {
1392
				'one': 'n = 1'
1393
			},
1394
			'lag': {
1395
				'zero': 'n = 0',
1396
				'one': 'i = 0,1 and n != 0'
1397
			},
1398
			'lb': {
1399
				'one': 'n = 1'
1400
			},
1401
			'lg': {
1402
				'one': 'n = 1'
1403
			},
1404
			'lkt': {},
1405
			'ln': {
1406
				'one': 'n = 0..1'
1407
			},
1408
			'lo': {},
1409
			'lt': {
1410
				'one': 'n % 10 = 1 and n % 100 != 11..19',
1411
				'few': 'n % 10 = 2..9 and n % 100 != 11..19',
1412
				'many': 'f != 0'
1413
			},
1414
			'lv': {
1415
				'zero': 'n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19',
1416
				'one': 'n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1'
1417
			},
1418
			'mas': {
1419
				'one': 'n = 1'
1420
			},
1421
			'mg': {
1422
				'one': 'n = 0..1'
1423
			},
1424
			'mgo': {
1425
				'one': 'n = 1'
1426
			},
1427
			'mk': {
1428
				'one': 'v = 0 and i % 10 = 1 or f % 10 = 1'
1429
			},
1430
			'ml': {
1431
				'one': 'n = 1'
1432
			},
1433
			'mn': {
1434
				'one': 'n = 1'
1435
			},
1436
			'mo': {
1437
				'one': 'i = 1 and v = 0',
1438
				'few': 'v != 0 or n = 0 or n != 1 and n % 100 = 1..19'
1439
			},
1440
			'mr': {
1441
				'one': 'i = 0 or n = 1'
1442
			},
1443
			'ms': {},
1444
			'mt': {
1445
				'one': 'n = 1',
1446
				'few': 'n = 0 or n % 100 = 2..10',
1447
				'many': 'n % 100 = 11..19'
1448
			},
1449
			'my': {},
1450
			'nah': {
1451
				'one': 'n = 1'
1452
			},
1453
			'naq': {
1454
				'one': 'n = 1',
1455
				'two': 'n = 2'
1456
			},
1457
			'nb': {
1458
				'one': 'n = 1'
1459
			},
1460
			'nd': {
1461
				'one': 'n = 1'
1462
			},
1463
			'ne': {
1464
				'one': 'n = 1'
1465
			},
1466
			'nl': {
1467
				'one': 'i = 1 and v = 0'
1468
			},
1469
			'nn': {
1470
				'one': 'n = 1'
1471
			},
1472
			'nnh': {
1473
				'one': 'n = 1'
1474
			},
1475
			'no': {
1476
				'one': 'n = 1'
1477
			},
1478
			'nqo': {},
1479
			'nr': {
1480
				'one': 'n = 1'
1481
			},
1482
			'nso': {
1483
				'one': 'n = 0..1'
1484
			},
1485
			'ny': {
1486
				'one': 'n = 1'
1487
			},
1488
			'nyn': {
1489
				'one': 'n = 1'
1490
			},
1491
			'om': {
1492
				'one': 'n = 1'
1493
			},
1494
			'or': {
1495
				'one': 'n = 1'
1496
			},
1497
			'os': {
1498
				'one': 'n = 1'
1499
			},
1500
			'pa': {
1501
				'one': 'n = 0..1'
1502
			},
1503
			'pap': {
1504
				'one': 'n = 1'
1505
			},
1506
			'pl': {
1507
				'one': 'i = 1 and v = 0',
1508
				'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14',
1509
				'many': 'v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14'
1510
			},
1511
			'prg': {
1512
				'zero': 'n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19',
1513
				'one': 'n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1'
1514
			},
1515
			'ps': {
1516
				'one': 'n = 1'
1517
			},
1518
			'pt': {
1519
				'one': 'n = 0..2 and n != 2'
1520
			},
1521
			'pt-PT': {
1522
				'one': 'n = 1 and v = 0'
1523
			},
1524
			'rm': {
1525
				'one': 'n = 1'
1526
			},
1527
			'ro': {
1528
				'one': 'i = 1 and v = 0',
1529
				'few': 'v != 0 or n = 0 or n != 1 and n % 100 = 1..19'
1530
			},
1531
			'rof': {
1532
				'one': 'n = 1'
1533
			},
1534
			'root': {},
1535
			'ru': {
1536
				'one': 'v = 0 and i % 10 = 1 and i % 100 != 11',
1537
				'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14',
1538
				'many': 'v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14'
1539
			},
1540
			'rwk': {
1541
				'one': 'n = 1'
1542
			},
1543
			'sah': {},
1544
			'saq': {
1545
				'one': 'n = 1'
1546
			},
1547
			'sdh': {
1548
				'one': 'n = 1'
1549
			},
1550
			'se': {
1551
				'one': 'n = 1',
1552
				'two': 'n = 2'
1553
			},
1554
			'seh': {
1555
				'one': 'n = 1'
1556
			},
1557
			'ses': {},
1558
			'sg': {},
1559
			'sh': {
1560
				'one': 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11',
1561
				'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14'
1562
			},
1563
			'shi': {
1564
				'one': 'i = 0 or n = 1',
1565
				'few': 'n = 2..10'
1566
			},
1567
			'si': {
1568
				'one': 'n = 0,1 or i = 0 and f = 1'
1569
			},
1570
			'sk': {
1571
				'one': 'i = 1 and v = 0',
1572
				'few': 'i = 2..4 and v = 0',
1573
				'many': 'v != 0'
1574
			},
1575
			'sl': {
1576
				'one': 'v = 0 and i % 100 = 1',
1577
				'two': 'v = 0 and i % 100 = 2',
1578
				'few': 'v = 0 and i % 100 = 3..4 or v != 0'
1579
			},
1580
			'sma': {
1581
				'one': 'n = 1',
1582
				'two': 'n = 2'
1583
			},
1584
			'smi': {
1585
				'one': 'n = 1',
1586
				'two': 'n = 2'
1587
			},
1588
			'smj': {
1589
				'one': 'n = 1',
1590
				'two': 'n = 2'
1591
			},
1592
			'smn': {
1593
				'one': 'n = 1',
1594
				'two': 'n = 2'
1595
			},
1596
			'sms': {
1597
				'one': 'n = 1',
1598
				'two': 'n = 2'
1599
			},
1600
			'sn': {
1601
				'one': 'n = 1'
1602
			},
1603
			'so': {
1604
				'one': 'n = 1'
1605
			},
1606
			'sq': {
1607
				'one': 'n = 1'
1608
			},
1609
			'sr': {
1610
				'one': 'v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11',
1611
				'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14'
1612
			},
1613
			'ss': {
1614
				'one': 'n = 1'
1615
			},
1616
			'ssy': {
1617
				'one': 'n = 1'
1618
			},
1619
			'st': {
1620
				'one': 'n = 1'
1621
			},
1622
			'sv': {
1623
				'one': 'i = 1 and v = 0'
1624
			},
1625
			'sw': {
1626
				'one': 'i = 1 and v = 0'
1627
			},
1628
			'syr': {
1629
				'one': 'n = 1'
1630
			},
1631
			'ta': {
1632
				'one': 'n = 1'
1633
			},
1634
			'te': {
1635
				'one': 'n = 1'
1636
			},
1637
			'teo': {
1638
				'one': 'n = 1'
1639
			},
1640
			'th': {},
1641
			'ti': {
1642
				'one': 'n = 0..1'
1643
			},
1644
			'tig': {
1645
				'one': 'n = 1'
1646
			},
1647
			'tk': {
1648
				'one': 'n = 1'
1649
			},
1650
			'tl': {
1651
				'one': 'v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9'
1652
			},
1653
			'tn': {
1654
				'one': 'n = 1'
1655
			},
1656
			'to': {},
1657
			'tr': {
1658
				'one': 'n = 1'
1659
			},
1660
			'ts': {
1661
				'one': 'n = 1'
1662
			},
1663
			'tzm': {
1664
				'one': 'n = 0..1 or n = 11..99'
1665
			},
1666
			'ug': {
1667
				'one': 'n = 1'
1668
			},
1669
			'uk': {
1670
				'one': 'v = 0 and i % 10 = 1 and i % 100 != 11',
1671
				'few': 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14',
1672
				'many': 'v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14'
1673
			},
1674
			'ur': {
1675
				'one': 'i = 1 and v = 0'
1676
			},
1677
			'uz': {
1678
				'one': 'n = 1'
1679
			},
1680
			've': {
1681
				'one': 'n = 1'
1682
			},
1683
			'vi': {},
1684
			'vo': {
1685
				'one': 'n = 1'
1686
			},
1687
			'vun': {
1688
				'one': 'n = 1'
1689
			},
1690
			'wa': {
1691
				'one': 'n = 0..1'
1692
			},
1693
			'wae': {
1694
				'one': 'n = 1'
1695
			},
1696
			'wo': {},
1697
			'xh': {
1698
				'one': 'n = 1'
1699
			},
1700
			'xog': {
1701
				'one': 'n = 1'
1702
			},
1703
			'yi': {
1704
				'one': 'i = 1 and v = 0'
1705
			},
1706
			'yo': {},
1707
			'yue': {},
1708
			'zh': {},
1709
			'zu': {
1710
				'one': 'i = 0 or n = 1'
1711
			}
1712
		},
1713
		// jscs:enable
1714
1715
		/**
1716
		 * Plural form transformations, needed for some languages.
1717
		 *
1718
		 * @param {integer} count
1719
		 *            Non-localized quantifier
1720
		 * @param {Array} forms
1721
		 *            List of plural forms
1722
		 * @return {string} Correct form for quantifier in this language
1723
		 */
1724
		convertPlural: function ( count, forms ) {
1725
			var pluralRules,
1726
				pluralFormIndex,
1727
				index,
1728
				explicitPluralPattern = new RegExp( '\\d+=', 'i' ),
1729
				formCount,
1730
				form;
1731
1732
			if ( !forms || forms.length === 0 ) {
1733
				return '';
1734
			}
1735
1736
			// Handle for Explicit 0= & 1= values
1737
			for ( index = 0; index < forms.length; index++ ) {
1738
				form = forms[ index ];
1739
				if ( explicitPluralPattern.test( form ) ) {
1740
					formCount = parseInt( form.slice( 0, form.indexOf( '=' ) ), 10 );
1741
					if ( formCount === count ) {
1742
						return ( form.slice( form.indexOf( '=' ) + 1 ) );
1743
					}
1744
					forms[ index ] = undefined;
1745
				}
1746
			}
1747
1748
			forms = $.map( forms, function ( form ) {
1749
				if ( form !== undefined ) {
1750
					return form;
1751
				}
1752
			} );
1753
1754
			pluralRules = this.pluralRules[ $.i18n().locale ];
1755
1756
			if ( !pluralRules ) {
1757
				// default fallback.
1758
				return ( count === 1 ) ? forms[ 0 ] : forms[ 1 ];
1759
			}
1760
1761
			pluralFormIndex = this.getPluralForm( count, pluralRules );
1762
			pluralFormIndex = Math.min( pluralFormIndex, forms.length - 1 );
1763
1764
			return forms[ pluralFormIndex ];
1765
		},
1766
1767
		/**
1768
		 * For the number, get the plural for index
1769
		 *
1770
		 * @param {integer} number
1771
		 * @param {Object} pluralRules
1772
		 * @return {integer} plural form index
1773
		 */
1774
		getPluralForm: function ( number, pluralRules ) {
1775
			var i,
1776
				pluralForms = [ 'zero', 'one', 'two', 'few', 'many', 'other' ],
1777
				pluralFormIndex = 0;
1778
1779
			for ( i = 0; i < pluralForms.length; i++ ) {
1780
				if ( pluralRules[ pluralForms[ i ] ] ) {
1781
					if ( pluralRuleParser( pluralRules[ pluralForms[ i ] ], number ) ) {
1782
						return pluralFormIndex;
1783
					}
1784
1785
					pluralFormIndex++;
1786
				}
1787
			}
1788
1789
			return pluralFormIndex;
1790
		},
1791
1792
		/**
1793
		 * Converts a number using digitTransformTable.
1794
		 *
1795
		 * @param {number} num Value to be converted
1796
		 * @param {boolean} integer Convert the return value to an integer
1797
		 */
1798
		convertNumber: function ( num, integer ) {
1799
			var tmp, item, i,
1800
				transformTable, numberString, convertedNumber;
1801
1802
			// Set the target Transform table:
1803
			transformTable = this.digitTransformTable( $.i18n().locale );
1804
			numberString = String( num );
1805
			convertedNumber = '';
1806
1807
			if ( !transformTable ) {
1808
				return num;
1809
			}
1810
1811
			// Check if the restore to Latin number flag is set:
1812
			if ( integer ) {
1813
				if ( parseFloat( num, 10 ) === num ) {
1814
					return num;
1815
				}
1816
1817
				tmp = [];
1818
1819
				for ( item in transformTable ) {
1820
					tmp[ transformTable[ item ] ] = item;
1821
				}
1822
1823
				transformTable = tmp;
1824
			}
1825
1826
			for ( i = 0; i < numberString.length; i++ ) {
1827
				if ( transformTable[ numberString[ i ] ] ) {
1828
					convertedNumber += transformTable[ numberString[ i ] ];
1829
				} else {
1830
					convertedNumber += numberString[ i ];
1831
				}
1832
			}
1833
1834
			return integer ? parseFloat( convertedNumber, 10 ) : convertedNumber;
1835
		},
1836
1837
		/**
1838
		 * Grammatical transformations, needed for inflected languages.
1839
		 * Invoked by putting {{grammar:form|word}} in a message.
1840
		 * Override this method for languages that need special grammar rules
1841
		 * applied dynamically.
1842
		 *
1843
		 * @param {string} word
1844
		 * @param {string} form
1845
		 * @return {string}
1846
		 */
1847
		convertGrammar: function ( word, form ) { /*jshint unused: false */
1848
			return word;
1849
		},
1850
1851
		/**
1852
		 * Provides an alternative text depending on specified gender. Usage
1853
		 * {{gender:[gender|user object]|masculine|feminine|neutral}}. If second
1854
		 * or third parameter are not specified, masculine is used.
1855
		 *
1856
		 * These details may be overriden per language.
1857
		 *
1858
		 * @param {string} gender
1859
		 *      male, female, or anything else for neutral.
1860
		 * @param {Array} forms
1861
		 *      List of gender forms
1862
		 *
1863
		 * @return {string}
1864
		 */
1865
		gender: function ( gender, forms ) {
1866
			if ( !forms || forms.length === 0 ) {
1867
				return '';
1868
			}
1869
1870
			while ( forms.length < 2 ) {
1871
				forms.push( forms[ forms.length - 1 ] );
1872
			}
1873
1874
			if ( gender === 'male' ) {
1875
				return forms[ 0 ];
1876
			}
1877
1878
			if ( gender === 'female' ) {
1879
				return forms[ 1 ];
1880
			}
1881
1882
			return ( forms.length === 3 ) ? forms[ 2 ] : forms[ 0 ];
1883
		},
1884
1885
		/**
1886
		 * Get the digit transform table for the given language
1887
		 * See http://cldr.unicode.org/translation/numbering-systems
1888
		 *
1889
		 * @param {string} language
1890
		 * @return {Array|boolean} List of digits in the passed language or false
1891
		 * representation, or boolean false if there is no information.
1892
		 */
1893
		digitTransformTable: function ( language ) {
1894
			var tables = {
1895
				ar: '٠١٢٣٤٥٦٧٨٩',
1896
				fa: '۰۱۲۳۴۵۶۷۸۹',
1897
				ml: '൦൧൨൩൪൫൬൭൮൯',
1898
				kn: '೦೧೨೩೪೫೬೭೮೯',
1899
				lo: '໐໑໒໓໔໕໖໗໘໙',
1900
				or: '୦୧୨୩୪୫୬୭୮୯',
1901
				kh: '០១២៣៤៥៦៧៨៩',
1902
				pa: '੦੧੨੩੪੫੬੭੮੯',
1903
				gu: '૦૧૨૩૪૫૬૭૮૯',
1904
				hi: '०१२३४५६७८९',
1905
				my: '၀၁၂၃၄၅၆၇၈၉',
1906
				ta: '௦௧௨௩௪௫௬௭௮௯',
1907
				te: '౦౧౨౩౪౫౬౭౮౯',
1908
				th: '๐๑๒๓๔๕๖๗๘๙', // FIXME use iso 639 codes
1909
				bo: '༠༡༢༣༤༥༦༧༨༩' // FIXME use iso 639 codes
1910
			};
1911
1912
			if ( !tables[ language ] ) {
1913
				return false;
1914
			}
1915
1916
			return tables[ language ].split( '' );
1917
		}
1918
	};
1919
1920
	$.extend( $.i18n.languages, {
1921
		'default': language
1922
	} );
1923
}( jQuery ) );
1924
/**
1925
 * cldrpluralparser.js
1926
 * A parser engine for CLDR plural rules.
1927
 *
1928
 * Copyright 2012-2014 Santhosh Thottingal and other contributors
1929
 * Released under the MIT license
1930
 * http://opensource.org/licenses/MIT
1931
 *
1932
 * @version 0.1.0
1933
 * @source https://github.com/santhoshtr/CLDRPluralRuleParser
1934
 * @author Santhosh Thottingal <[email protected]>
1935
 * @author Timo Tijhof
1936
 * @author Amir Aharoni
1937
 */
1938
1939
/**
1940
 * Evaluates a plural rule in CLDR syntax for a number
1941
 * @param {string} rule
1942
 * @param {integer} number
1943
 * @return {boolean} true if evaluation passed, false if evaluation failed.
1944
 */
1945
1946
// UMD returnExports https://github.com/umdjs/umd/blob/master/returnExports.js
1947
(function(root, factory) {
1948
	if (typeof define === 'function' && define.amd) {
1949
		// AMD. Register as an anonymous module.
1950
		define(factory);
1951
	} else if (typeof exports === 'object') {
1952
		// Node. Does not work with strict CommonJS, but
1953
		// only CommonJS-like environments that support module.exports,
1954
		// like Node.
1955
		module.exports = factory();
1956
	} else {
1957
		// Browser globals (root is window)
1958
		root.pluralRuleParser = factory();
1959
	}
1960
}(this, function() {
1961
1962
function pluralRuleParser(rule, number) {
1963
	'use strict';
1964
1965
	/*
1966
	Syntax: see http://unicode.org/reports/tr35/#Language_Plural_Rules
1967
	-----------------------------------------------------------------
1968
	condition     = and_condition ('or' and_condition)*
1969
		('@integer' samples)?
1970
		('@decimal' samples)?
1971
	and_condition = relation ('and' relation)*
1972
	relation      = is_relation | in_relation | within_relation
1973
	is_relation   = expr 'is' ('not')? value
1974
	in_relation   = expr (('not')? 'in' | '=' | '!=') range_list
1975
	within_relation = expr ('not')? 'within' range_list
1976
	expr          = operand (('mod' | '%') value)?
1977
	operand       = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
1978
	range_list    = (range | value) (',' range_list)*
1979
	value         = digit+
1980
	digit         = 0|1|2|3|4|5|6|7|8|9
1981
	range         = value'..'value
1982
	samples       = sampleRange (',' sampleRange)* (',' ('…'|'...'))?
1983
	sampleRange   = decimalValue '~' decimalValue
1984
	decimalValue  = value ('.' value)?
1985
	*/
1986
1987
	// We don't evaluate the samples section of the rule. Ignore it.
1988
	rule = rule.split('@')[0].replace(/^\s*/, '').replace(/\s*$/, '');
1989
1990
	if (!rule.length) {
1991
		// Empty rule or 'other' rule.
1992
		return true;
1993
	}
1994
1995
	// Indicates the current position in the rule as we parse through it.
1996
	// Shared among all parsing functions below.
1997
	var pos = 0,
1998
		operand,
1999
		expression,
2000
		relation,
2001
		result,
2002
		whitespace = makeRegexParser(/^\s+/),
2003
		value = makeRegexParser(/^\d+/),
2004
		_n_ = makeStringParser('n'),
2005
		_i_ = makeStringParser('i'),
2006
		_f_ = makeStringParser('f'),
2007
		_t_ = makeStringParser('t'),
2008
		_v_ = makeStringParser('v'),
2009
		_w_ = makeStringParser('w'),
2010
		_is_ = makeStringParser('is'),
2011
		_isnot_ = makeStringParser('is not'),
2012
		_isnot_sign_ = makeStringParser('!='),
2013
		_equal_ = makeStringParser('='),
2014
		_mod_ = makeStringParser('mod'),
2015
		_percent_ = makeStringParser('%'),
2016
		_not_ = makeStringParser('not'),
2017
		_in_ = makeStringParser('in'),
2018
		_within_ = makeStringParser('within'),
2019
		_range_ = makeStringParser('..'),
2020
		_comma_ = makeStringParser(','),
2021
		_or_ = makeStringParser('or'),
2022
		_and_ = makeStringParser('and');
2023
2024
	function debug() {
2025
		// console.log.apply(console, arguments);
2026
	}
2027
2028
	debug('pluralRuleParser', rule, number);
2029
2030
	// Try parsers until one works, if none work return null
2031
	function choice(parserSyntax) {
2032
		return function() {
2033
			var i, result;
2034
2035
			for (i = 0; i < parserSyntax.length; i++) {
2036
				result = parserSyntax[i]();
2037
2038
				if (result !== null) {
2039
					return result;
2040
				}
2041
			}
2042
2043
			return null;
2044
		};
2045
	}
2046
2047
	// Try several parserSyntax-es in a row.
2048
	// All must succeed; otherwise, return null.
2049
	// This is the only eager one.
2050
	function sequence(parserSyntax) {
2051
		var i, parserRes,
2052
			originalPos = pos,
2053
			result = [];
2054
2055
		for (i = 0; i < parserSyntax.length; i++) {
2056
			parserRes = parserSyntax[i]();
2057
2058
			if (parserRes === null) {
2059
				pos = originalPos;
2060
2061
				return null;
2062
			}
2063
2064
			result.push(parserRes);
2065
		}
2066
2067
		return result;
2068
	}
2069
2070
	// Run the same parser over and over until it fails.
2071
	// Must succeed a minimum of n times; otherwise, return null.
2072
	function nOrMore(n, p) {
2073
		return function() {
2074
			var originalPos = pos,
2075
				result = [],
2076
				parsed = p();
2077
2078
			while (parsed !== null) {
2079
				result.push(parsed);
2080
				parsed = p();
2081
			}
2082
2083
			if (result.length < n) {
2084
				pos = originalPos;
2085
2086
				return null;
2087
			}
2088
2089
			return result;
2090
		};
2091
	}
2092
2093
	// Helpers - just make parserSyntax out of simpler JS builtin types
2094
	function makeStringParser(s) {
2095
		var len = s.length;
2096
2097
		return function() {
2098
			var result = null;
2099
2100
			if (rule.substr(pos, len) === s) {
2101
				result = s;
2102
				pos += len;
2103
			}
2104
2105
			return result;
2106
		};
2107
	}
2108
2109
	function makeRegexParser(regex) {
2110
		return function() {
2111
			var matches = rule.substr(pos).match(regex);
2112
2113
			if (matches === null) {
2114
				return null;
2115
			}
2116
2117
			pos += matches[0].length;
2118
2119
			return matches[0];
2120
		};
2121
	}
2122
2123
	/**
2124
	 * Integer digits of n.
2125
	 */
2126
	function i() {
2127
		var result = _i_();
2128
2129
		if (result === null) {
2130
			debug(' -- failed i', parseInt(number, 10));
2131
2132
			return result;
2133
		}
2134
2135
		result = parseInt(number, 10);
2136
		debug(' -- passed i ', result);
2137
2138
		return result;
2139
	}
2140
2141
	/**
2142
	 * Absolute value of the source number (integer and decimals).
2143
	 */
2144
	function n() {
2145
		var result = _n_();
2146
2147
		if (result === null) {
2148
			debug(' -- failed n ', number);
2149
2150
			return result;
2151
		}
2152
2153
		result = parseFloat(number, 10);
2154
		debug(' -- passed n ', result);
2155
2156
		return result;
2157
	}
2158
2159
	/**
2160
	 * Visible fractional digits in n, with trailing zeros.
2161
	 */
2162
	function f() {
2163
		var result = _f_();
2164
2165
		if (result === null) {
2166
			debug(' -- failed f ', number);
2167
2168
			return result;
2169
		}
2170
2171
		result = (number + '.').split('.')[1] || 0;
2172
		debug(' -- passed f ', result);
2173
2174
		return result;
2175
	}
2176
2177
	/**
2178
	 * Visible fractional digits in n, without trailing zeros.
2179
	 */
2180
	function t() {
2181
		var result = _t_();
2182
2183
		if (result === null) {
2184
			debug(' -- failed t ', number);
2185
2186
			return result;
2187
		}
2188
2189
		result = (number + '.').split('.')[1].replace(/0$/, '') || 0;
2190
		debug(' -- passed t ', result);
2191
2192
		return result;
2193
	}
2194
2195
	/**
2196
	 * Number of visible fraction digits in n, with trailing zeros.
2197
	 */
2198
	function v() {
2199
		var result = _v_();
2200
2201
		if (result === null) {
2202
			debug(' -- failed v ', number);
2203
2204
			return result;
2205
		}
2206
2207
		result = (number + '.').split('.')[1].length || 0;
2208
		debug(' -- passed v ', result);
2209
2210
		return result;
2211
	}
2212
2213
	/**
2214
	 * Number of visible fraction digits in n, without trailing zeros.
2215
	 */
2216
	function w() {
2217
		var result = _w_();
2218
2219
		if (result === null) {
2220
			debug(' -- failed w ', number);
2221
2222
			return result;
2223
		}
2224
2225
		result = (number + '.').split('.')[1].replace(/0$/, '').length || 0;
2226
		debug(' -- passed w ', result);
2227
2228
		return result;
2229
	}
2230
2231
	// operand       = 'n' | 'i' | 'f' | 't' | 'v' | 'w'
2232
	operand = choice([n, i, f, t, v, w]);
2233
2234
	// expr          = operand (('mod' | '%') value)?
2235
	expression = choice([mod, operand]);
2236
2237
	function mod() {
2238
		var result = sequence(
2239
			[operand, whitespace, choice([_mod_, _percent_]), whitespace, value]
2240
		);
2241
2242
		if (result === null) {
2243
			debug(' -- failed mod');
2244
2245
			return null;
2246
		}
2247
2248
		debug(' -- passed ' + parseInt(result[0], 10) + ' ' + result[2] + ' ' + parseInt(result[4], 10));
2249
2250
		return parseInt(result[0], 10) % parseInt(result[4], 10);
2251
	}
2252
2253
	function not() {
2254
		var result = sequence([whitespace, _not_]);
2255
2256
		if (result === null) {
2257
			debug(' -- failed not');
2258
2259
			return null;
2260
		}
2261
2262
		return result[1];
2263
	}
2264
2265
	// is_relation   = expr 'is' ('not')? value
2266
	function is() {
2267
		var result = sequence([expression, whitespace, choice([_is_]), whitespace, value]);
2268
2269
		if (result !== null) {
2270
			debug(' -- passed is : ' + result[0] + ' == ' + parseInt(result[4], 10));
2271
2272
			return result[0] === parseInt(result[4], 10);
2273
		}
2274
2275
		debug(' -- failed is');
2276
2277
		return null;
2278
	}
2279
2280
	// is_relation   = expr 'is' ('not')? value
2281
	function isnot() {
2282
		var result = sequence(
2283
			[expression, whitespace, choice([_isnot_, _isnot_sign_]), whitespace, value]
2284
		);
2285
2286
		if (result !== null) {
2287
			debug(' -- passed isnot: ' + result[0] + ' != ' + parseInt(result[4], 10));
2288
2289
			return result[0] !== parseInt(result[4], 10);
2290
		}
2291
2292
		debug(' -- failed isnot');
2293
2294
		return null;
2295
	}
2296
2297
	function not_in() {
2298
		var i, range_list,
2299
			result = sequence([expression, whitespace, _isnot_sign_, whitespace, rangeList]);
2300
2301
		if (result !== null) {
2302
			debug(' -- passed not_in: ' + result[0] + ' != ' + result[4]);
2303
			range_list = result[4];
2304
2305
			for (i = 0; i < range_list.length; i++) {
2306
				if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) {
2307
					return false;
2308
				}
2309
			}
2310
2311
			return true;
2312
		}
2313
2314
		debug(' -- failed not_in');
2315
2316
		return null;
2317
	}
2318
2319
	// range_list    = (range | value) (',' range_list)*
2320
	function rangeList() {
2321
		var result = sequence([choice([range, value]), nOrMore(0, rangeTail)]),
2322
			resultList = [];
2323
2324
		if (result !== null) {
2325
			resultList = resultList.concat(result[0]);
2326
2327
			if (result[1][0]) {
2328
				resultList = resultList.concat(result[1][0]);
2329
			}
2330
2331
			return resultList;
2332
		}
2333
2334
		debug(' -- failed rangeList');
2335
2336
		return null;
2337
	}
2338
2339
	function rangeTail() {
2340
		// ',' range_list
2341
		var result = sequence([_comma_, rangeList]);
2342
2343
		if (result !== null) {
2344
			return result[1];
2345
		}
2346
2347
		debug(' -- failed rangeTail');
2348
2349
		return null;
2350
	}
2351
2352
	// range         = value'..'value
2353
	function range() {
2354
		var i, array, left, right,
2355
			result = sequence([value, _range_, value]);
2356
2357
		if (result !== null) {
2358
			debug(' -- passed range');
2359
2360
			array = [];
2361
			left = parseInt(result[0], 10);
2362
			right = parseInt(result[2], 10);
2363
2364
			for (i = left; i <= right; i++) {
2365
				array.push(i);
2366
			}
2367
2368
			return array;
2369
		}
2370
2371
		debug(' -- failed range');
2372
2373
		return null;
2374
	}
2375
2376
	function _in() {
2377
		var result, range_list, i;
2378
2379
		// in_relation   = expr ('not')? 'in' range_list
2380
		result = sequence(
2381
			[expression, nOrMore(0, not), whitespace, choice([_in_, _equal_]), whitespace, rangeList]
2382
		);
2383
2384
		if (result !== null) {
2385
			debug(' -- passed _in:' + result);
2386
2387
			range_list = result[5];
2388
2389
			for (i = 0; i < range_list.length; i++) {
2390
				if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) {
2391
					return (result[1][0] !== 'not');
2392
				}
2393
			}
2394
2395
			return (result[1][0] === 'not');
2396
		}
2397
2398
		debug(' -- failed _in ');
2399
2400
		return null;
2401
	}
2402
2403
	/**
2404
	 * The difference between "in" and "within" is that
2405
	 * "in" only includes integers in the specified range,
2406
	 * while "within" includes all values.
2407
	 */
2408
	function within() {
2409
		var range_list, result;
2410
2411
		// within_relation = expr ('not')? 'within' range_list
2412
		result = sequence(
2413
			[expression, nOrMore(0, not), whitespace, _within_, whitespace, rangeList]
2414
		);
2415
2416
		if (result !== null) {
2417
			debug(' -- passed within');
2418
2419
			range_list = result[5];
2420
2421
			if ((result[0] >= parseInt(range_list[0], 10)) &&
2422
				(result[0] < parseInt(range_list[range_list.length - 1], 10))) {
2423
2424
				return (result[1][0] !== 'not');
2425
			}
2426
2427
			return (result[1][0] === 'not');
2428
		}
2429
2430
		debug(' -- failed within ');
2431
2432
		return null;
2433
	}
2434
2435
	// relation      = is_relation | in_relation | within_relation
2436
	relation = choice([is, not_in, isnot, _in, within]);
2437
2438
	// and_condition = relation ('and' relation)*
2439
	function and() {
2440
		var i,
2441
			result = sequence([relation, nOrMore(0, andTail)]);
2442
2443
		if (result) {
2444
			if (!result[0]) {
2445
				return false;
2446
			}
2447
2448
			for (i = 0; i < result[1].length; i++) {
2449
				if (!result[1][i]) {
2450
					return false;
2451
				}
2452
			}
2453
2454
			return true;
2455
		}
2456
2457
		debug(' -- failed and');
2458
2459
		return null;
2460
	}
2461
2462
	// ('and' relation)*
2463
	function andTail() {
2464
		var result = sequence([whitespace, _and_, whitespace, relation]);
2465
2466
		if (result !== null) {
2467
			debug(' -- passed andTail' + result);
2468
2469
			return result[3];
2470
		}
2471
2472
		debug(' -- failed andTail');
2473
2474
		return null;
2475
2476
	}
2477
	//  ('or' and_condition)*
2478
	function orTail() {
2479
		var result = sequence([whitespace, _or_, whitespace, and]);
2480
2481
		if (result !== null) {
2482
			debug(' -- passed orTail: ' + result[3]);
2483
2484
			return result[3];
2485
		}
2486
2487
		debug(' -- failed orTail');
2488
2489
		return null;
2490
	}
2491
2492
	// condition     = and_condition ('or' and_condition)*
2493
	function condition() {
2494
		var i,
2495
			result = sequence([and, nOrMore(0, orTail)]);
2496
2497
		if (result) {
2498
			for (i = 0; i < result[1].length; i++) {
2499
				if (result[1][i]) {
2500
					return true;
2501
				}
2502
			}
2503
2504
			return result[0];
2505
		}
2506
2507
		return false;
2508
	}
2509
2510
	result = condition();
2511
2512
	/**
2513
	 * For success, the pos must have gotten to the end of the rule
2514
	 * and returned a non-null.
2515
	 * n.b. This is part of language infrastructure,
2516
	 * so we do not throw an internationalizable message.
2517
	 */
2518
	if (result === null) {
2519
		throw new Error('Parse error at position ' + pos.toString() + ' for rule: ' + rule);
2520
	}
2521
2522
	if (pos !== rule.length) {
2523
		debug('Warning: Rule not parsed completely. Parser stopped at ' + rule.substr(0, pos) + ' for rule: ' + rule);
2524
	}
2525
2526
	return result;
2527
}
2528
2529
return pluralRuleParser;
2530
2531
}));
2532