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

web/static/js/vendor/jquery.i18n/jquery.i18n.parser.js   C

Complexity

Total Complexity 54
Complexity/F 1.86

Size

Lines of Code 294
Function Count 29

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
wmc 54
nc 8192
mnd 2
bc 43
fnc 29
dl 0
loc 294
rs 6.8539
bpm 1.4826
cpm 1.862
noi 0
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like web/static/js/vendor/jquery.i18n/jquery.i18n.parser.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) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar
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 MessageParser = function ( options ) {
20
		this.options = $.extend( {}, $.i18n.parser.defaults, options );
21
		this.language = $.i18n.languages[ String.locale ] || $.i18n.languages[ 'default' ];
22
		this.emitter = $.i18n.parser.emitter;
23
	};
24
25
	MessageParser.prototype = {
26
27
		constructor: MessageParser,
28
29
		simpleParse: function ( message, parameters ) {
30
			return message.replace( /\$(\d+)/g, function ( str, match ) {
31
				var index = parseInt( match, 10 ) - 1;
32
33
				return parameters[ index ] !== undefined ? parameters[ index ] : '$' + match;
34
			} );
35
		},
36
37
		parse: function ( message, replacements ) {
38
			if ( message.indexOf( '{{' ) < 0 ) {
39
				return this.simpleParse( message, replacements );
40
			}
41
42
			this.emitter.language = $.i18n.languages[ $.i18n().locale ] ||
43
				$.i18n.languages[ 'default' ];
44
45
			return this.emitter.emit( this.ast( message ), replacements );
46
		},
47
48
		ast: function ( message ) {
49
			var pipe, colon, backslash, anyCharacter, dollar, digits, regularLiteral,
50
				regularLiteralWithoutBar, regularLiteralWithoutSpace, escapedOrLiteralWithoutBar,
51
				escapedOrRegularLiteral, templateContents, templateName, openTemplate,
52
				closeTemplate, expression, paramExpression, result,
53
				pos = 0;
54
55
			// Try parsers until one works, if none work return null
56
			function choice( parserSyntax ) {
57
				return function () {
58
					var i, result;
59
60
					for ( i = 0; i < parserSyntax.length; i++ ) {
61
						result = parserSyntax[ i ]();
62
63
						if ( result !== null ) {
64
							return result;
65
						}
66
					}
67
68
					return null;
69
				};
70
			}
71
72
			// Try several parserSyntax-es in a row.
73
			// All must succeed; otherwise, return null.
74
			// This is the only eager one.
75
			function sequence( parserSyntax ) {
76
				var i, res,
77
					originalPos = pos,
78
					result = [];
79
80
				for ( i = 0; i < parserSyntax.length; i++ ) {
81
					res = parserSyntax[ i ]();
82
83
					if ( res === null ) {
84
						pos = originalPos;
85
86
						return null;
87
					}
88
89
					result.push( res );
90
				}
91
92
				return result;
93
			}
94
95
			// Run the same parser over and over until it fails.
96
			// Must succeed a minimum of n times; otherwise, return null.
97
			function nOrMore( n, p ) {
98
				return function () {
99
					var originalPos = pos,
100
						result = [],
101
						parsed = p();
102
103
					while ( parsed !== null ) {
104
						result.push( parsed );
105
						parsed = p();
106
					}
107
108
					if ( result.length < n ) {
109
						pos = originalPos;
110
111
						return null;
112
					}
113
114
					return result;
115
				};
116
			}
117
118
			// Helpers -- just make parserSyntax out of simpler JS builtin types
119
120
			function makeStringParser( s ) {
121
				var len = s.length;
122
123
				return function () {
124
					var result = null;
125
126
					if ( message.slice( pos, pos + len ) === s ) {
127
						result = s;
128
						pos += len;
129
					}
130
131
					return result;
132
				};
133
			}
134
135
			function makeRegexParser( regex ) {
136
				return function () {
137
					var matches = message.slice( pos ).match( regex );
138
139
					if ( matches === null ) {
140
						return null;
141
					}
142
143
					pos += matches[ 0 ].length;
144
145
					return matches[ 0 ];
146
				};
147
			}
148
149
			pipe = makeStringParser( '|' );
150
			colon = makeStringParser( ':' );
151
			backslash = makeStringParser( '\\' );
152
			anyCharacter = makeRegexParser( /^./ );
153
			dollar = makeStringParser( '$' );
154
			digits = makeRegexParser( /^\d+/ );
155
			regularLiteral = makeRegexParser( /^[^{}\[\]$\\]/ );
156
			regularLiteralWithoutBar = makeRegexParser( /^[^{}\[\]$\\|]/ );
157
			regularLiteralWithoutSpace = makeRegexParser( /^[^{}\[\]$\s]/ );
158
159
			// There is a general pattern:
160
			// parse a thing;
161
			// if it worked, apply transform,
162
			// otherwise return null.
163
			// But using this as a combinator seems to cause problems
164
			// when combined with nOrMore().
165
			// May be some scoping issue.
166
			function transform( p, fn ) {
167
				return function () {
168
					var result = p();
169
170
					return result === null ? null : fn( result );
171
				};
172
			}
173
174
			// Used to define "literals" within template parameters. The pipe
175
			// character is the parameter delimeter, so by default
176
			// it is not a literal in the parameter
177
			function literalWithoutBar() {
178
				var result = nOrMore( 1, escapedOrLiteralWithoutBar )();
179
180
				return result === null ? null : result.join( '' );
181
			}
182
183
			function literal() {
184
				var result = nOrMore( 1, escapedOrRegularLiteral )();
185
186
				return result === null ? null : result.join( '' );
187
			}
188
189
			function escapedLiteral() {
190
				var result = sequence( [ backslash, anyCharacter ] );
191
192
				return result === null ? null : result[ 1 ];
193
			}
194
195
			choice( [ escapedLiteral, regularLiteralWithoutSpace ] );
196
			escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] );
197
			escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] );
198
199
			function replacement() {
200
				var result = sequence( [ dollar, digits ] );
201
202
				if ( result === null ) {
203
					return null;
204
				}
205
206
				return [ 'REPLACE', parseInt( result[ 1 ], 10 ) - 1 ];
207
			}
208
209
			templateName = transform(
210
				// see $wgLegalTitleChars
211
				// not allowing : due to the need to catch "PLURAL:$1"
212
				makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/ ),
213
214
				function ( result ) {
215
					return result.toString();
216
				}
217
			);
218
219
			function templateParam() {
220
				var expr,
221
					result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] );
222
223
				if ( result === null ) {
224
					return null;
225
				}
226
227
				expr = result[ 1 ];
228
229
				// use a "CONCAT" operator if there are multiple nodes,
230
				// otherwise return the first node, raw.
231
				return expr.length > 1 ? [ 'CONCAT' ].concat( expr ) : expr[ 0 ];
232
			}
233
234
			function templateWithReplacement() {
235
				var result = sequence( [ templateName, colon, replacement ] );
236
237
				return result === null ? null : [ result[ 0 ], result[ 2 ] ];
238
			}
239
240
			function templateWithOutReplacement() {
241
				var result = sequence( [ templateName, colon, paramExpression ] );
242
243
				return result === null ? null : [ result[ 0 ], result[ 2 ] ];
244
			}
245
246
			templateContents = choice( [
247
				function () {
248
					var res = sequence( [
249
						// templates can have placeholders for dynamic
250
						// replacement eg: {{PLURAL:$1|one car|$1 cars}}
251
						// or no placeholders eg:
252
						// {{GRAMMAR:genitive|{{SITENAME}}}
253
						choice( [ templateWithReplacement, templateWithOutReplacement ] ),
254
						nOrMore( 0, templateParam )
255
					] );
256
257
					return res === null ? null : res[ 0 ].concat( res[ 1 ] );
258
				},
259
				function () {
260
					var res = sequence( [ templateName, nOrMore( 0, templateParam ) ] );
261
262
					if ( res === null ) {
263
						return null;
264
					}
265
266
					return [ res[ 0 ] ].concat( res[ 1 ] );
267
				}
268
			] );
269
270
			openTemplate = makeStringParser( '{{' );
271
			closeTemplate = makeStringParser( '}}' );
272
273
			function template() {
274
				var result = sequence( [ openTemplate, templateContents, closeTemplate ] );
275
276
				return result === null ? null : result[ 1 ];
277
			}
278
279
			expression = choice( [ template, replacement, literal ] );
280
			paramExpression = choice( [ template, replacement, literalWithoutBar ] );
281
282
			function start() {
283
				var result = nOrMore( 0, expression )();
284
285
				if ( result === null ) {
286
					return null;
287
				}
288
289
				return [ 'CONCAT' ].concat( result );
290
			}
291
292
			result = start();
293
294
			/*
295
			 * For success, the pos must have gotten to the end of the input
296
			 * and returned a non-null.
297
			 * n.b. This is part of language infrastructure, so we do not throw an internationalizable message.
298
			 */
299
			if ( result === null || pos !== message.length ) {
300
				throw new Error( 'Parse error at position ' + pos.toString() + ' in input: ' + message );
301
			}
302
303
			return result;
304
		}
305
306
	};
307
308
	$.extend( $.i18n.parser, new MessageParser() );
309
}( jQuery ) );
310