r105973 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r105972‎ | r105973 | r105974 >
Date:02:46, 13 December 2011
Author:neilk
Status:ok
Tags:
Comment:
moved language library to core mediawiki.jqueryMsg
Modified paths:
  • /trunk/extensions/UploadWizard/UploadWizardHooks.php (modified) (history)
  • /trunk/extensions/UploadWizard/UploadWizardPage.js (modified) (history)
  • /trunk/extensions/UploadWizard/resources/mediawiki.language.parser.js (deleted) (history)
  • /trunk/extensions/UploadWizard/resources/mediawiki.language.parser.peg (deleted) (history)
  • /trunk/extensions/UploadWizard/test/jasmine/SpecRunner.html (modified) (history)
  • /trunk/extensions/UploadWizard/test/jasmine/makeLanguageSpec.php (deleted) (history)
  • /trunk/extensions/UploadWizard/test/jasmine/spec/mediawiki.language.parser.spec.data.js (deleted) (history)
  • /trunk/extensions/UploadWizard/test/jasmine/spec/mediawiki.language.parser.spec.js (deleted) (history)

Diff [purge]

Index: trunk/extensions/UploadWizard/test/jasmine/makeLanguageSpec.php
@@ -1,114 +0,0 @@
2 -<?php
3 -
4 -/**
5 - * This PHP script defines the spec that the Javascript message parser should conform to.
6 - *
7 - * It does this by looking up the results of various string kinds of string parsing, with various languages,
8 - * in the current installation of MediaWiki. It then outputs a static specification, mapping expected inputs to outputs,
9 - * which can be used with the JasmineBDD framework. This specification can then be used by simply including it into
10 - * the SpecRunner.html file.
11 - *
12 - * This is similar to Michael Dale (mdale@mediawiki.org)'s parser tests, except that it doesn't look up the
13 - * API results while doing the test, so the Jasmine run is much faster(at the cost of being out of date in rare
14 - * circumstances. But mostly the parsing that we are doing in Javascript doesn't change much.)
15 - *
16 - */
17 -
18 -$maintenanceDir = dirname( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) ) . '/maintenance';
19 -
20 -require( "$maintenanceDir/Maintenance.php" );
21 -
22 -class MakeLanguageSpec extends Maintenance {
23 -
24 - static $keyToTestArgs = array(
25 - 'undelete_short' => array(
26 - array( 0 ),
27 - array( 1 ),
28 - array( 2 ),
29 - array( 5 ),
30 - array( 21 ),
31 - array( 101 )
32 - ),
33 - 'category-subcat-count' => array(
34 - array( 0, 10 ),
35 - array( 1, 1 ),
36 - array( 1, 2 ),
37 - array( 3, 30 )
38 - )
39 - );
40 -
41 - public function __construct() {
42 - parent::__construct();
43 - $this->mDescription = "Create a JasmineBDD-compatible specification for message parsing";
44 - // add any other options here
45 - }
46 -
47 - public function execute() {
48 - list( $messages, $tests ) = $this->getMessagesAndTests();
49 - $this->writeJavascriptFile( $messages, $tests, "spec/mediawiki.language.parser.spec.data.js" );
50 - }
51 -
52 - private function getMessagesAndTests() {
53 - $messages = array();
54 - $tests = array();
55 - $wfMsgExtOptions = array( 'parsemag' );
56 - foreach ( array( 'en', 'fr', 'ar', 'jp', 'zh' ) as $languageCode ) {
57 - $wfMsgExtOptions['language'] = $languageCode;
58 - foreach ( self::$keyToTestArgs as $key => $testArgs ) {
59 - foreach ($testArgs as $args) {
60 - // get the raw template, without any transformations
61 - $template = wfMsgGetKey( $key, /* useDb */ true, $languageCode, /* transform */ false );
62 -
63 - // get the magic-parsed version with args
64 - $wfMsgExtArgs = array_merge( array( $key, $wfMsgExtOptions ), $args );
65 - $result = call_user_func_array( 'wfMsgExt', $wfMsgExtArgs );
66 -
67 - // record the template, args, language, and expected result
68 - // fake multiple languages by flattening them together
69 - $langKey = $languageCode . '_' . $key;
70 - $messages[ $langKey ] = $template;
71 - $tests[] = array(
72 - 'name' => $languageCode . " " . $key . " " . join( ",", $args ),
73 - 'key' => $langKey,
74 - 'args' => $args,
75 - 'result' => $result,
76 - 'lang' => $languageCode
77 - );
78 - }
79 - }
80 - }
81 - return array( $messages, $tests );
82 - }
83 -
84 - private function writeJavascriptFile( $messages, $tests, $dataSpecFile ) {
85 - global $argv;
86 - $arguments = count($argv) ? $argv : $_SERVER[ 'argv' ];
87 -
88 - $json = new Services_JSON;
89 - $json->pretty = true;
90 - $javascriptPrologue = "// This file stores the results from the PHP parser for certain messages and arguments,\n"
91 - . "// so we can test the equivalent Javascript libraries.\n"
92 - . '// Last generated with ' . join(' ', $arguments) . ' at ' . gmdate('c') . "\n\n";
93 - $javascriptMessages = "mediaWiki.messages.set( " . $json->encode( $messages, true ) . " );\n";
94 - $javascriptTests = 'var jasmineMsgSpec = ' . $json->encode( $tests, true ) . ";\n";
95 -
96 - $fp = fopen( $dataSpecFile, 'w' );
97 - if ( !$fp ) {
98 - die( "couldn't open $dataSpecFile for writing" );
99 - }
100 - $success = fwrite( $fp, $javascriptPrologue . $javascriptMessages . $javascriptTests );
101 - if ( !$success ) {
102 - die( "couldn't write to $dataSpecFile" );
103 - }
104 - $success = fclose( $fp );
105 - if ( !$success ) {
106 - die( "couldn't close $dataSpecFile" );
107 - }
108 - }
109 -}
110 -
111 -$maintClass = "MakeLanguageSpec";
112 -require_once( "$maintenanceDir/doMaintenance.php" );
113 -
114 -
115 -
Index: trunk/extensions/UploadWizard/test/jasmine/SpecRunner.html
@@ -12,10 +12,6 @@
1313
1414 <script type="text/javascript" src="../../../../resources/mediawiki/mediawiki.js"></script>
1515 <script type="text/javascript" src="../../../../resources/mediawiki.language/mediawiki.language.js"></script>
16 - <script type="text/javascript" src="../../../../resources/mediawiki/mediawiki.Uri.js"></script>
17 - <script type="text/javascript" src="../../resources/mediawiki.language.parser.js"></script>
18 - <script type="text/javascript" src="../../../../resources/mediawiki/mediawiki.api.js"></script>
19 - <script type="text/javascript" src="../../../../resources/mediawiki/mediawiki.api.edit.js"></script>
2016 <script type="text/javascript" src="../../../../resources/mediawiki/mediawiki.Title.js"></script>
2117 <script type="text/javascript" src="../../resources/mw.units.js"></script>
2218 <script type="text/javascript" src="../../resources/jquery/jquery.pubsub.js"></script>
@@ -24,11 +20,7 @@
2521
2622
2723 <!-- include spec files here... -->
28 - <script type="text/javascript" src="spec/mw.Api.spec.js"></script>
29 - <script type="text/javascript" src="spec/mw.Api.edit.spec.js"></script>
3024
31 - <script type="text/javascript" src="spec/mediawiki.language.parser.spec.js"></script>
32 -
3325 <script type="text/javascript" src="spec/mw.Title.spec.js"></script>
3426 <script type="text/javascript" src="spec/mw.units.spec.js"></script>
3527 <script type="text/javascript" src="spec/mw.pubsub.spec.js"></script>
Index: trunk/extensions/UploadWizard/test/jasmine/spec/mediawiki.language.parser.spec.js
@@ -1,350 +0,0 @@
2 -/* spec for language & message behaviour in MediaWiki */
3 -
4 -
5 -mediaWiki.messages.set( {
6 - "en_empty": "",
7 - "en_simple": "Simple message",
8 - "en_replace": "Simple $1 replacement",
9 - "en_replace2": "Simple $1 $2 replacements",
10 - "en_link": "Simple [http://example.com link to example].",
11 - "en_link_replace": "Complex [$1 $2] behaviour.",
12 - "en_simple_magic": "Simple {{ALOHOMORA}} message",
13 - "en_undelete_short": "Undelete {{PLURAL:$1|one edit|$1 edits}}",
14 - "en_undelete_empty_param": "Undelete{{PLURAL:$1|| multiple edits}}",
15 - "en_category-subcat-count": "{{PLURAL:$2|This category has only the following subcategory.|This category has the following {{PLURAL:$1|subcategory|$1 subcategories}}, out of $2 total.}}",
16 - "en_escape0": "Escape \\to fantasy island",
17 - "en_escape1": "I had \\$2.50 in my pocket",
18 - "en_escape2": "I had {{PLURAL:$1|the absolute \\|$1\\| which came out to \\$3.00 in my C:\\\\drive| some stuff}}",
19 - "en_fail": "This should fail to {{parse",
20 - "en_fail_magic": "There is no such magic word as {{SIETNAME}}"
21 -} );
22 -
23 -/**
24 - * Tests
25 - */
26 -( function( $j ) {
27 - describe( "mediaWiki.language.parser", function() {
28 -
29 - describe( "basic message functionality", function() {
30 -
31 - it( "should return identity for empty string", function() {
32 - var parser = new mediaWiki.language.parser();
33 - expect( parser.parse( 'en_empty' ).html() ).toEqual( '' );
34 - } );
35 -
36 -
37 - it( "should return identity for simple string", function() {
38 - var parser = new mediaWiki.language.parser();
39 - expect( parser.parse( 'en_simple' ).html() ).toEqual( 'Simple message' );
40 - } );
41 -
42 - } );
43 -
44 - describe( "escaping", function() {
45 -
46 - it ( "should handle simple escaping", function() {
47 - var parser = new mediaWiki.language.parser();
48 - expect( parser.parse( 'en_escape0' ).html() ).toEqual( 'Escape to fantasy island' );
49 - } );
50 -
51 - it ( "should escape dollar signs found in ordinary text when backslashed", function() {
52 - var parser = new mediaWiki.language.parser();
53 - expect( parser.parse( 'en_escape1' ).html() ).toEqual( 'I had $2.50 in my pocket' );
54 - } );
55 -
56 - it ( "should handle a complicated escaping case, including escaped pipe chars in template args", function() {
57 - var parser = new mediaWiki.language.parser();
58 - expect( parser.parse( 'en_escape2', [ 1 ] ).html() ).toEqual( 'I had the absolute |1| which came out to $3.00 in my C:\\drive' );
59 - } );
60 -
61 - } );
62 -
63 - describe( "replacing", function() {
64 -
65 - it ( "should handle simple replacing", function() {
66 - var parser = new mediaWiki.language.parser();
67 - expect( parser.parse( 'en_replace', [ 'foo' ] ).html() ).toEqual( 'Simple foo replacement' );
68 - } );
69 -
70 - it ( "should return $n if replacement not there", function() {
71 - var parser = new mediaWiki.language.parser();
72 - expect( parser.parse( 'en_replace', [] ).html() ).toEqual( 'Simple $1 replacement' );
73 - expect( parser.parse( 'en_replace2', [ 'bar' ] ).html() ).toEqual( 'Simple bar $2 replacements' );
74 - } );
75 -
76 - } );
77 -
78 - describe( "linking", function() {
79 -
80 - it ( "should handle a simple link", function() {
81 - var parser = new mediaWiki.language.parser();
82 - var parsed = parser.parse( 'en_link' );
83 - var contents = parsed.contents();
84 - expect( contents.length ).toEqual( 3 );
85 - expect( contents[0].nodeName ).toEqual( '#text' );
86 - expect( contents[0].nodeValue ).toEqual( 'Simple ' );
87 - expect( contents[1].nodeName ).toEqual( 'A' );
88 - expect( contents[1].getAttribute( 'href' ) ).toEqual( 'http://example.com' );
89 - expect( contents[1].childNodes[0].nodeValue ).toEqual( 'link to example' );
90 - expect( contents[2].nodeName ).toEqual( '#text' );
91 - expect( contents[2].nodeValue ).toEqual( '.' );
92 - } );
93 -
94 - it ( "should replace a URL into a link", function() {
95 - var parser = new mediaWiki.language.parser();
96 - var parsed = parser.parse( 'en_link_replace', [ 'http://example.com/foo', 'linking' ] );
97 - var contents = parsed.contents();
98 - expect( contents.length ).toEqual( 3 );
99 - expect( contents[0].nodeName ).toEqual( '#text' );
100 - expect( contents[0].nodeValue ).toEqual( 'Complex ' );
101 - expect( contents[1].nodeName ).toEqual( 'A' );
102 - expect( contents[1].getAttribute( 'href' ) ).toEqual( 'http://example.com/foo' );
103 - expect( contents[1].childNodes[0].nodeValue ).toEqual( 'linking' );
104 - expect( contents[2].nodeName ).toEqual( '#text' );
105 - expect( contents[2].nodeValue ).toEqual( ' behaviour.' );
106 - } );
107 -
108 - it ( "should bind a click handler into a link", function() {
109 - var parser = new mediaWiki.language.parser();
110 - var clicked = false;
111 - var click = function() { clicked = true; };
112 - var parsed = parser.parse( 'en_link_replace', [ click, 'linking' ] );
113 - var contents = parsed.contents();
114 - expect( contents.length ).toEqual( 3 );
115 - expect( contents[0].nodeName ).toEqual( '#text' );
116 - expect( contents[0].nodeValue ).toEqual( 'Complex ' );
117 - expect( contents[1].nodeName ).toEqual( 'A' );
118 - expect( contents[1].getAttribute( 'href' ) ).toEqual( '#' );
119 - expect( contents[1].childNodes[0].nodeValue ).toEqual( 'linking' );
120 - expect( contents[2].nodeName ).toEqual( '#text' );
121 - expect( contents[2].nodeValue ).toEqual( ' behaviour.' );
122 - // determining bindings is hard in IE
123 - var anchor = parsed.find( 'a' );
124 - if ( ( $j.browser.mozilla || $j.browser.webkit ) && anchor.click ) {
125 - expect( clicked ).toEqual( false );
126 - anchor.click();
127 - expect( clicked ).toEqual( true );
128 - }
129 - } );
130 -
131 - it ( "should wrap a jquery arg around link contents -- even another element", function() {
132 - var parser = new mediaWiki.language.parser();
133 - var clicked = false;
134 - var click = function() { clicked = true; };
135 - var button = $j( '<button>' ).click( click );
136 - var parsed = parser.parse( 'en_link_replace', [ button, 'buttoning' ] );
137 - var contents = parsed.contents();
138 - expect( contents.length ).toEqual( 3 );
139 - expect( contents[0].nodeName ).toEqual( '#text' );
140 - expect( contents[0].nodeValue ).toEqual( 'Complex ' );
141 - expect( contents[1].nodeName ).toEqual( 'BUTTON' );
142 - expect( contents[1].childNodes[0].nodeValue ).toEqual( 'buttoning' );
143 - expect( contents[2].nodeName ).toEqual( '#text' );
144 - expect( contents[2].nodeValue ).toEqual( ' behaviour.' );
145 - // determining bindings is hard in IE
146 - if ( ( $j.browser.mozilla || $j.browser.webkit ) && button.click ) {
147 - expect( clicked ).toEqual( false );
148 - parsed.find( 'button' ).click();
149 - expect( clicked ).toEqual( true );
150 - }
151 - } );
152 -
153 -
154 - } );
155 -
156 -
157 - describe( "magic keywords", function() {
158 - it( "should substitute magic keywords", function() {
159 - var options = {
160 - magic: {
161 - 'alohomora' : 'open'
162 - }
163 - };
164 - var parser = new mediaWiki.language.parser( options );
165 - expect( parser.parse( 'en_simple_magic' ).html() ).toEqual( 'Simple open message' );
166 - } );
167 - } );
168 -
169 - describe( "error conditions", function() {
170 - it( "should return non-existent key in square brackets", function() {
171 - var parser = new mediaWiki.language.parser();
172 - expect( parser.parse( 'en_does_not_exist' ).html() ).toEqual( '[en_does_not_exist]' );
173 - } );
174 -
175 -
176 - it( "should fail to parse", function() {
177 - var parser = new mediaWiki.language.parser();
178 - expect( function() { parser.parse( 'en_fail' ); } ).toThrow(
179 - 'Parse error at position 20 in input: This should fail to {{parse'
180 - );
181 - } );
182 - } );
183 -
184 - describe( "empty parameters", function() {
185 - it( "should deal with empty parameters", function() {
186 - var parser = new mediaWiki.language.parser();
187 - var ast = parser.getAst( 'en_undelete_empty_param' );
188 - expect( parser.parse( 'en_undelete_empty_param', [ 1 ] ).html() ).toEqual( 'Undelete' );
189 - expect( parser.parse( 'en_undelete_empty_param', [ 3 ] ).html() ).toEqual( 'Undelete multiple edits' );
190 -
191 - } );
192 - } );
193 -
194 - describe( "easy message interface functions", function() {
195 - it( "should allow a global that returns strings", function() {
196 - var gM = mediaWiki.language.getMessageFunction();
197 - // passing this through jQuery and back to string, because browsers may have subtle differences, like the case of tag names.
198 - // a surrounding <SPAN> is needed for html() to work right
199 - var expectedHtml = $j( '<span>Complex <a href="http://example.com/foo">linking</a> behaviour.</span>' ).html();
200 - var result = gM( 'en_link_replace', 'http://example.com/foo', 'linking' );
201 - expect( typeof result ).toEqual( 'string' );
202 - expect( result ).toEqual( expectedHtml );
203 - } );
204 -
205 - it( "should allow a jQuery plugin that appends to nodes", function() {
206 - $j.fn.msg = mediaWiki.language.getJqueryMessagePlugin();
207 - var $div = $j( '<div>' ).append( $j( '<p>' ).addClass( 'foo' ) );
208 - var clicked = false;
209 - var $button = $j( '<button>' ).click( function() { clicked = true; } );
210 - $div.find( '.foo' ).msg( 'en_link_replace', $button, 'buttoning' );
211 - // passing this through jQuery and back to string, because browsers may have subtle differences, like the case of tag names.
212 - // a surrounding <SPAN> is needed for html() to work right
213 - var expectedHtml = $j( '<span>Complex <button>buttoning</button> behaviour.</span>' ).html();
214 - var createdHtml = $div.find( '.foo' ).html();
215 - // it is hard to test for clicks with IE; also it inserts or removes spaces around nodes when creating HTML tags, depending on their type.
216 - // so need to check the strings stripped of spaces.
217 - if ( ( $j.browser.mozilla || $j.browser.webkit ) && $button.click ) {
218 - expect( createdHtml ).toEqual( expectedHtml );
219 - $div.find( 'button ').click();
220 - expect( clicked ).toEqual( true );
221 - } else if ( $j.browser.ie ) {
222 - expect( createdHtml.replace( /\s/, '' ) ).toEqual( expectedHtml.replace( /\s/, '' ) );
223 - }
224 - delete $j.fn.msg;
225 - } );
226 -
227 - } );
228 -
229 - // The parser functions can throw errors, but let's not actually blow up for the user -- instead dump the error into the interface so we have
230 - // a chance at fixing this
231 - describe( "easy message interface functions with graceful failures", function() {
232 - it( "should allow a global that returns strings, with graceful failure", function() {
233 - var gM = mediaWiki.language.getMessageFunction();
234 - // passing this through jQuery and back to string, because browsers may have subtle differences, like the case of tag names.
235 - // a surrounding <SPAN> is needed for html() to work right
236 - var expectedHtml = $j( '<span>en_fail: Parse error at position 20 in input: This should fail to {{parse</span>' ).html();
237 - var result = gM( 'en_fail' );
238 - expect( typeof result ).toEqual( 'string' );
239 - expect( result ).toEqual( expectedHtml );
240 - } );
241 -
242 - it( "should allow a global that returns strings, with graceful failure on missing magic words", function() {
243 - var gM = mediaWiki.language.getMessageFunction();
244 - // passing this through jQuery and back to string, because browsers may have subtle differences, like the case of tag names.
245 - // a surrounding <SPAN> is needed for html() to work right
246 - var expectedHtml = $j( '<span>en_fail_magic: unknown operation "sietname"</span>' ).html();
247 - var result = gM( 'en_fail_magic' );
248 - expect( typeof result ).toEqual( 'string' );
249 - expect( result ).toEqual( expectedHtml );
250 - } );
251 -
252 -
253 - it( "should allow a jQuery plugin, with graceful failure", function() {
254 - $j.fn.msg = mediaWiki.language.getJqueryMessagePlugin();
255 - var $div = $j( '<div>' ).append( $j( '<p>' ).addClass( 'foo' ) );
256 - $div.find( '.foo' ).msg( 'en_fail' );
257 - // passing this through jQuery and back to string, because browsers may have subtle differences, like the case of tag names.
258 - // a surrounding <SPAN> is needed for html() to work right
259 - var expectedHtml = $j( '<span>en_fail: Parse error at position 20 in input: This should fail to {{parse</span>' ).html();
260 - var createdHtml = $div.find( '.foo' ).html();
261 - expect( createdHtml ).toEqual( expectedHtml );
262 - delete $j.fn.msg;
263 - } );
264 -
265 - } );
266 -
267 -
268 -
269 -
270 - describe( "test plurals and other language-specific functions", function() {
271 - /* copying some language definitions in here -- it's hard to make this test fast and reliable
272 - otherwise, and we don't want to have to know the mediawiki URL from this kind of test either.
273 - We also can't preload the langs for the test since they clobber the same namespace.
274 - In principle Roan said it was okay to change how languages worked so that didn't happen... maybe
275 - someday. We'd have to the same kind of importing of the default rules for most rules, or maybe
276 - come up with some kind of subclassing scheme for languages */
277 - var languageClasses = {
278 - ar: {
279 - /**
280 - * Arabic (العربية) language functions
281 - */
282 -
283 - convertPlural: function( count, forms ) {
284 - forms = mediaWiki.language.preConvertPlural( forms, 6 );
285 - if ( count === 0 ) {
286 - return forms[0];
287 - }
288 - if ( count == 1 ) {
289 - return forms[1];
290 - }
291 - if ( count == 2 ) {
292 - return forms[2];
293 - }
294 - if ( count % 100 >= 3 && count % 100 <= 10 ) {
295 - return forms[3];
296 - }
297 - if ( count % 100 >= 11 && count % 100 <= 99 ) {
298 - return forms[4];
299 - }
300 - return forms[5];
301 - },
302 -
303 - digitTransformTable: {
304 - '0': '٠', // &#x0660;
305 - '1': '١', // &#x0661;
306 - '2': '٢', // &#x0662;
307 - '3': '٣', // &#x0663;
308 - '4': '٤', // &#x0664;
309 - '5': '٥', // &#x0665;
310 - '6': '٦', // &#x0666;
311 - '7': '٧', // &#x0667;
312 - '8': '٨', // &#x0668;
313 - '9': '٩', // &#x0669;
314 - '.': '٫', // &#x066b; wrong table ?
315 - ',': '٬' // &#x066c;
316 - }
317 -
318 - },
319 - en: { },
320 - fr: {
321 - convertPlural: function( count, forms ) {
322 - forms = mediaWiki.language.preConvertPlural( forms, 2 );
323 - return ( count <= 1 ) ? forms[0] : forms[1];
324 - }
325 - },
326 - jp: { },
327 - zh: { }
328 - };
329 -
330 - /* simulate how the language classes override, or don't, the standard functions in mediaWiki.language */
331 - $j.each( languageClasses, function( langCode, rules ) {
332 - $j.each( [ 'convertPlural', 'convertNumber' ], function( i, propertyName ) {
333 - if ( typeof rules[ propertyName ] === 'undefined' ) {
334 - rules[ propertyName ] = mediaWiki.language[ propertyName ];
335 - }
336 - } );
337 - } );
338 -
339 - $j.each( jasmineMsgSpec, function( i, test ) {
340 - it( "should parse " + test.name, function() {
341 - // using language override so we don't have to muck with global namespace
342 - var parser = new mediaWiki.language.parser( { language: languageClasses[ test.lang ] } );
343 - var parsedHtml = parser.parse( test.key, test.args ).html();
344 - expect( parsedHtml ).toEqual( test.result );
345 - } );
346 - } );
347 -
348 - } );
349 -
350 - } );
351 -} )( jQuery );
Index: trunk/extensions/UploadWizard/test/jasmine/spec/mediawiki.language.parser.spec.data.js
@@ -1,488 +0,0 @@
2 -// This file stores the results from the PHP parser for certain messages and arguments,
3 -// so we can test the equivalent Javascript libraries.
4 -// Last generated with makeLanguageSpec.php at 2011-01-28T02:04:09+00:00
5 -
6 -mediaWiki.messages.set( {
7 - "en_undelete_short": "Undelete {{PLURAL:$1|one edit|$1 edits}}",
8 - "en_category-subcat-count": "{{PLURAL:$2|This category has only the following subcategory.|This category has the following {{PLURAL:$1|subcategory|$1 subcategories}}, out of $2 total.}}",
9 - "fr_undelete_short": "Restaurer $1 modification{{PLURAL:$1||s}}",
10 - "fr_category-subcat-count": "Cette cat\u00e9gorie comprend {{PLURAL:$2|la sous-cat\u00e9gorie|$2 sous-cat\u00e9gories, dont {{PLURAL:$1|celle|les $1}}}} ci-dessous.",
11 - "ar_undelete_short": "\u0627\u0633\u062a\u0631\u062c\u0627\u0639 {{PLURAL:$1|\u062a\u0639\u062f\u064a\u0644 \u0648\u0627\u062d\u062f|\u062a\u0639\u062f\u064a\u0644\u064a\u0646|$1 \u062a\u0639\u062f\u064a\u0644\u0627\u062a|$1 \u062a\u0639\u062f\u064a\u0644|$1 \u062a\u0639\u062f\u064a\u0644\u0627}}",
12 - "ar_category-subcat-count": "{{PLURAL:$2|\u0644\u0627 \u062a\u0635\u0627\u0646\u064a\u0641 \u0641\u0631\u0639\u064a\u0629 \u0641\u064a \u0647\u0630\u0627 \u0627\u0644\u062a\u0635\u0646\u064a\u0641|\u0647\u0630\u0627 \u0627\u0644\u062a\u0635\u0646\u064a\u0641 \u0641\u064a\u0647 \u0627\u0644\u062a\u0635\u0646\u064a\u0641 \u0627\u0644\u0641\u0631\u0639\u064a \u0627\u0644\u062a\u0627\u0644\u064a \u0641\u0642\u0637.|\u0647\u0630\u0627 \u0627\u0644\u062a\u0635\u0646\u064a\u0641 \u0641\u064a\u0647 {{PLURAL:$1||\u0647\u0630\u0627 \u0627\u0644\u062a\u0635\u0646\u064a\u0641 \u0627\u0644\u0641\u0631\u0639\u064a|\u0647\u0630\u064a\u0646 \u0627\u0644\u062a\u0635\u0646\u064a\u0641\u064a\u0646 \u0627\u0644\u0641\u0631\u0639\u064a\u064a\u0646|\u0647\u0630\u0647 \u0627\u0644$1 \u062a\u0635\u0627\u0646\u064a\u0641 \u0627\u0644\u0641\u0631\u0639\u064a\u0629|\u0647\u0630\u0647 \u0627\u0644$1 \u062a\u0635\u0646\u064a\u0641\u0627 \u0641\u0631\u0639\u064a\u0627|\u0647\u0630\u0647 \u0627\u0644$1 \u062a\u0635\u0646\u064a\u0641 \u0641\u0631\u0639\u064a}}\u060c \u0645\u0646 \u0625\u062c\u0645\u0627\u0644\u064a $2.}}",
13 - "jp_undelete_short": "Undelete {{PLURAL:$1|one edit|$1 edits}}",
14 - "jp_category-subcat-count": "{{PLURAL:$2|This category has only the following subcategory.|This category has the following {{PLURAL:$1|subcategory|$1 subcategories}}, out of $2 total.}}",
15 - "zh_undelete_short": "\u6062\u590d\u88ab\u5220\u9664\u7684$1\u9879\u4fee\u8ba2",
16 - "zh_category-subcat-count": "{{PLURAL:$2|\u672c\u5206\u7c7b\u53ea\u6709\u4e0b\u5217\u4e00\u4e2a\u5b50\u5206\u7c7b\u3002|\u672c\u5206\u7c7b\u5305\u542b\u4e0b\u5217$1\u4e2a\u5b50\u5206\u7c7b\uff0c\u5171\u6709$2\u4e2a\u5b50\u5206\u7c7b\u3002}}"
17 -} );
18 -var jasmineMsgSpec = [
19 - {
20 - "name": "en undelete_short 0",
21 - "key": "en_undelete_short",
22 - "args": [
23 - 0
24 - ],
25 - "result": "Undelete 0 edits",
26 - "lang": "en"
27 - },
28 - {
29 - "name": "en undelete_short 1",
30 - "key": "en_undelete_short",
31 - "args": [
32 - 1
33 - ],
34 - "result": "Undelete one edit",
35 - "lang": "en"
36 - },
37 - {
38 - "name": "en undelete_short 2",
39 - "key": "en_undelete_short",
40 - "args": [
41 - 2
42 - ],
43 - "result": "Undelete 2 edits",
44 - "lang": "en"
45 - },
46 - {
47 - "name": "en undelete_short 5",
48 - "key": "en_undelete_short",
49 - "args": [
50 - 5
51 - ],
52 - "result": "Undelete 5 edits",
53 - "lang": "en"
54 - },
55 - {
56 - "name": "en undelete_short 21",
57 - "key": "en_undelete_short",
58 - "args": [
59 - 21
60 - ],
61 - "result": "Undelete 21 edits",
62 - "lang": "en"
63 - },
64 - {
65 - "name": "en undelete_short 101",
66 - "key": "en_undelete_short",
67 - "args": [
68 - 101
69 - ],
70 - "result": "Undelete 101 edits",
71 - "lang": "en"
72 - },
73 - {
74 - "name": "en category-subcat-count 0,10",
75 - "key": "en_category-subcat-count",
76 - "args": [
77 - 0,
78 - 10
79 - ],
80 - "result": "This category has the following 0 subcategories, out of 10 total.",
81 - "lang": "en"
82 - },
83 - {
84 - "name": "en category-subcat-count 1,1",
85 - "key": "en_category-subcat-count",
86 - "args": [
87 - 1,
88 - 1
89 - ],
90 - "result": "This category has only the following subcategory.",
91 - "lang": "en"
92 - },
93 - {
94 - "name": "en category-subcat-count 1,2",
95 - "key": "en_category-subcat-count",
96 - "args": [
97 - 1,
98 - 2
99 - ],
100 - "result": "This category has the following subcategory, out of 2 total.",
101 - "lang": "en"
102 - },
103 - {
104 - "name": "en category-subcat-count 3,30",
105 - "key": "en_category-subcat-count",
106 - "args": [
107 - 3,
108 - 30
109 - ],
110 - "result": "This category has the following 3 subcategories, out of 30 total.",
111 - "lang": "en"
112 - },
113 - {
114 - "name": "fr undelete_short 0",
115 - "key": "fr_undelete_short",
116 - "args": [
117 - 0
118 - ],
119 - "result": "Restaurer 0 modification",
120 - "lang": "fr"
121 - },
122 - {
123 - "name": "fr undelete_short 1",
124 - "key": "fr_undelete_short",
125 - "args": [
126 - 1
127 - ],
128 - "result": "Restaurer 1 modification",
129 - "lang": "fr"
130 - },
131 - {
132 - "name": "fr undelete_short 2",
133 - "key": "fr_undelete_short",
134 - "args": [
135 - 2
136 - ],
137 - "result": "Restaurer 2 modifications",
138 - "lang": "fr"
139 - },
140 - {
141 - "name": "fr undelete_short 5",
142 - "key": "fr_undelete_short",
143 - "args": [
144 - 5
145 - ],
146 - "result": "Restaurer 5 modifications",
147 - "lang": "fr"
148 - },
149 - {
150 - "name": "fr undelete_short 21",
151 - "key": "fr_undelete_short",
152 - "args": [
153 - 21
154 - ],
155 - "result": "Restaurer 21 modifications",
156 - "lang": "fr"
157 - },
158 - {
159 - "name": "fr undelete_short 101",
160 - "key": "fr_undelete_short",
161 - "args": [
162 - 101
163 - ],
164 - "result": "Restaurer 101 modifications",
165 - "lang": "fr"
166 - },
167 - {
168 - "name": "fr category-subcat-count 0,10",
169 - "key": "fr_category-subcat-count",
170 - "args": [
171 - 0,
172 - 10
173 - ],
174 - "result": "Cette cat\u00e9gorie comprend 10 sous-cat\u00e9gories, dont celle ci-dessous.",
175 - "lang": "fr"
176 - },
177 - {
178 - "name": "fr category-subcat-count 1,1",
179 - "key": "fr_category-subcat-count",
180 - "args": [
181 - 1,
182 - 1
183 - ],
184 - "result": "Cette cat\u00e9gorie comprend la sous-cat\u00e9gorie ci-dessous.",
185 - "lang": "fr"
186 - },
187 - {
188 - "name": "fr category-subcat-count 1,2",
189 - "key": "fr_category-subcat-count",
190 - "args": [
191 - 1,
192 - 2
193 - ],
194 - "result": "Cette cat\u00e9gorie comprend 2 sous-cat\u00e9gories, dont celle ci-dessous.",
195 - "lang": "fr"
196 - },
197 - {
198 - "name": "fr category-subcat-count 3,30",
199 - "key": "fr_category-subcat-count",
200 - "args": [
201 - 3,
202 - 30
203 - ],
204 - "result": "Cette cat\u00e9gorie comprend 30 sous-cat\u00e9gories, dont les 3 ci-dessous.",
205 - "lang": "fr"
206 - },
207 - {
208 - "name": "ar undelete_short 0",
209 - "key": "ar_undelete_short",
210 - "args": [
211 - 0
212 - ],
213 - "result": "\u0627\u0633\u062a\u0631\u062c\u0627\u0639 \u062a\u0639\u062f\u064a\u0644 \u0648\u0627\u062d\u062f",
214 - "lang": "ar"
215 - },
216 - {
217 - "name": "ar undelete_short 1",
218 - "key": "ar_undelete_short",
219 - "args": [
220 - 1
221 - ],
222 - "result": "\u0627\u0633\u062a\u0631\u062c\u0627\u0639 \u062a\u0639\u062f\u064a\u0644\u064a\u0646",
223 - "lang": "ar"
224 - },
225 - {
226 - "name": "ar undelete_short 2",
227 - "key": "ar_undelete_short",
228 - "args": [
229 - 2
230 - ],
231 - "result": "\u0627\u0633\u062a\u0631\u062c\u0627\u0639 2 \u062a\u0639\u062f\u064a\u0644\u0627\u062a",
232 - "lang": "ar"
233 - },
234 - {
235 - "name": "ar undelete_short 5",
236 - "key": "ar_undelete_short",
237 - "args": [
238 - 5
239 - ],
240 - "result": "\u0627\u0633\u062a\u0631\u062c\u0627\u0639 5 \u062a\u0639\u062f\u064a\u0644",
241 - "lang": "ar"
242 - },
243 - {
244 - "name": "ar undelete_short 21",
245 - "key": "ar_undelete_short",
246 - "args": [
247 - 21
248 - ],
249 - "result": "\u0627\u0633\u062a\u0631\u062c\u0627\u0639 21 \u062a\u0639\u062f\u064a\u0644\u0627",
250 - "lang": "ar"
251 - },
252 - {
253 - "name": "ar undelete_short 101",
254 - "key": "ar_undelete_short",
255 - "args": [
256 - 101
257 - ],
258 - "result": "\u0627\u0633\u062a\u0631\u062c\u0627\u0639 101 \u062a\u0639\u062f\u064a\u0644\u0627",
259 - "lang": "ar"
260 - },
261 - {
262 - "name": "ar category-subcat-count 0,10",
263 - "key": "ar_category-subcat-count",
264 - "args": [
265 - 0,
266 - 10
267 - ],
268 - "result": "\u0647\u0630\u0627 \u0627\u0644\u062a\u0635\u0646\u064a\u0641 \u0641\u064a\u0647 \u060c \u0645\u0646 \u0625\u062c\u0645\u0627\u0644\u064a 10.",
269 - "lang": "ar"
270 - },
271 - {
272 - "name": "ar category-subcat-count 1,1",
273 - "key": "ar_category-subcat-count",
274 - "args": [
275 - 1,
276 - 1
277 - ],
278 - "result": "\u0647\u0630\u0627 \u0627\u0644\u062a\u0635\u0646\u064a\u0641 \u0641\u064a\u0647 \u0627\u0644\u062a\u0635\u0646\u064a\u0641 \u0627\u0644\u0641\u0631\u0639\u064a \u0627\u0644\u062a\u0627\u0644\u064a \u0641\u0642\u0637.",
279 - "lang": "ar"
280 - },
281 - {
282 - "name": "ar category-subcat-count 1,2",
283 - "key": "ar_category-subcat-count",
284 - "args": [
285 - 1,
286 - 2
287 - ],
288 - "result": "\u0647\u0630\u0627 \u0627\u0644\u062a\u0635\u0646\u064a\u0641 \u0641\u064a\u0647 \u0647\u0630\u0627 \u0627\u0644\u062a\u0635\u0646\u064a\u0641 \u0627\u0644\u0641\u0631\u0639\u064a\u060c \u0645\u0646 \u0625\u062c\u0645\u0627\u0644\u064a 2.",
289 - "lang": "ar"
290 - },
291 - {
292 - "name": "ar category-subcat-count 3,30",
293 - "key": "ar_category-subcat-count",
294 - "args": [
295 - 3,
296 - 30
297 - ],
298 - "result": "\u0647\u0630\u0627 \u0627\u0644\u062a\u0635\u0646\u064a\u0641 \u0641\u064a\u0647 \u0647\u0630\u0647 \u0627\u06443 \u062a\u0635\u0627\u0646\u064a\u0641 \u0627\u0644\u0641\u0631\u0639\u064a\u0629\u060c \u0645\u0646 \u0625\u062c\u0645\u0627\u0644\u064a 30.",
299 - "lang": "ar"
300 - },
301 - {
302 - "name": "jp undelete_short 0",
303 - "key": "jp_undelete_short",
304 - "args": [
305 - 0
306 - ],
307 - "result": "Undelete 0 edits",
308 - "lang": "jp"
309 - },
310 - {
311 - "name": "jp undelete_short 1",
312 - "key": "jp_undelete_short",
313 - "args": [
314 - 1
315 - ],
316 - "result": "Undelete one edit",
317 - "lang": "jp"
318 - },
319 - {
320 - "name": "jp undelete_short 2",
321 - "key": "jp_undelete_short",
322 - "args": [
323 - 2
324 - ],
325 - "result": "Undelete 2 edits",
326 - "lang": "jp"
327 - },
328 - {
329 - "name": "jp undelete_short 5",
330 - "key": "jp_undelete_short",
331 - "args": [
332 - 5
333 - ],
334 - "result": "Undelete 5 edits",
335 - "lang": "jp"
336 - },
337 - {
338 - "name": "jp undelete_short 21",
339 - "key": "jp_undelete_short",
340 - "args": [
341 - 21
342 - ],
343 - "result": "Undelete 21 edits",
344 - "lang": "jp"
345 - },
346 - {
347 - "name": "jp undelete_short 101",
348 - "key": "jp_undelete_short",
349 - "args": [
350 - 101
351 - ],
352 - "result": "Undelete 101 edits",
353 - "lang": "jp"
354 - },
355 - {
356 - "name": "jp category-subcat-count 0,10",
357 - "key": "jp_category-subcat-count",
358 - "args": [
359 - 0,
360 - 10
361 - ],
362 - "result": "This category has the following 0 subcategories, out of 10 total.",
363 - "lang": "jp"
364 - },
365 - {
366 - "name": "jp category-subcat-count 1,1",
367 - "key": "jp_category-subcat-count",
368 - "args": [
369 - 1,
370 - 1
371 - ],
372 - "result": "This category has only the following subcategory.",
373 - "lang": "jp"
374 - },
375 - {
376 - "name": "jp category-subcat-count 1,2",
377 - "key": "jp_category-subcat-count",
378 - "args": [
379 - 1,
380 - 2
381 - ],
382 - "result": "This category has the following subcategory, out of 2 total.",
383 - "lang": "jp"
384 - },
385 - {
386 - "name": "jp category-subcat-count 3,30",
387 - "key": "jp_category-subcat-count",
388 - "args": [
389 - 3,
390 - 30
391 - ],
392 - "result": "This category has the following 3 subcategories, out of 30 total.",
393 - "lang": "jp"
394 - },
395 - {
396 - "name": "zh undelete_short 0",
397 - "key": "zh_undelete_short",
398 - "args": [
399 - 0
400 - ],
401 - "result": "\u6062\u590d\u88ab\u5220\u9664\u76840\u9879\u4fee\u8ba2",
402 - "lang": "zh"
403 - },
404 - {
405 - "name": "zh undelete_short 1",
406 - "key": "zh_undelete_short",
407 - "args": [
408 - 1
409 - ],
410 - "result": "\u6062\u590d\u88ab\u5220\u9664\u76841\u9879\u4fee\u8ba2",
411 - "lang": "zh"
412 - },
413 - {
414 - "name": "zh undelete_short 2",
415 - "key": "zh_undelete_short",
416 - "args": [
417 - 2
418 - ],
419 - "result": "\u6062\u590d\u88ab\u5220\u9664\u76842\u9879\u4fee\u8ba2",
420 - "lang": "zh"
421 - },
422 - {
423 - "name": "zh undelete_short 5",
424 - "key": "zh_undelete_short",
425 - "args": [
426 - 5
427 - ],
428 - "result": "\u6062\u590d\u88ab\u5220\u9664\u76845\u9879\u4fee\u8ba2",
429 - "lang": "zh"
430 - },
431 - {
432 - "name": "zh undelete_short 21",
433 - "key": "zh_undelete_short",
434 - "args": [
435 - 21
436 - ],
437 - "result": "\u6062\u590d\u88ab\u5220\u9664\u768421\u9879\u4fee\u8ba2",
438 - "lang": "zh"
439 - },
440 - {
441 - "name": "zh undelete_short 101",
442 - "key": "zh_undelete_short",
443 - "args": [
444 - 101
445 - ],
446 - "result": "\u6062\u590d\u88ab\u5220\u9664\u7684101\u9879\u4fee\u8ba2",
447 - "lang": "zh"
448 - },
449 - {
450 - "name": "zh category-subcat-count 0,10",
451 - "key": "zh_category-subcat-count",
452 - "args": [
453 - 0,
454 - 10
455 - ],
456 - "result": "\u672c\u5206\u7c7b\u5305\u542b\u4e0b\u52170\u4e2a\u5b50\u5206\u7c7b\uff0c\u5171\u670910\u4e2a\u5b50\u5206\u7c7b\u3002",
457 - "lang": "zh"
458 - },
459 - {
460 - "name": "zh category-subcat-count 1,1",
461 - "key": "zh_category-subcat-count",
462 - "args": [
463 - 1,
464 - 1
465 - ],
466 - "result": "\u672c\u5206\u7c7b\u53ea\u6709\u4e0b\u5217\u4e00\u4e2a\u5b50\u5206\u7c7b\u3002",
467 - "lang": "zh"
468 - },
469 - {
470 - "name": "zh category-subcat-count 1,2",
471 - "key": "zh_category-subcat-count",
472 - "args": [
473 - 1,
474 - 2
475 - ],
476 - "result": "\u672c\u5206\u7c7b\u5305\u542b\u4e0b\u52171\u4e2a\u5b50\u5206\u7c7b\uff0c\u5171\u67092\u4e2a\u5b50\u5206\u7c7b\u3002",
477 - "lang": "zh"
478 - },
479 - {
480 - "name": "zh category-subcat-count 3,30",
481 - "key": "zh_category-subcat-count",
482 - "args": [
483 - 3,
484 - 30
485 - ],
486 - "result": "\u672c\u5206\u7c7b\u5305\u542b\u4e0b\u52173\u4e2a\u5b50\u5206\u7c7b\uff0c\u5171\u670930\u4e2a\u5b50\u5206\u7c7b\u3002",
487 - "lang": "zh"
488 - }
489 -];
Index: trunk/extensions/UploadWizard/UploadWizardHooks.php
@@ -10,10 +10,6 @@
1111 public static $modules = array(
1212 // n.b. we tend not to use mediawiki.language functions, they are better implemented in mediawiki.language.parser.
1313 // however, loading mediawiki.language will a) create the namespace b) load the language files with convertPlural for the current language and all.
14 - 'ext.uploadwizard.mediawiki.language.parser' => array(
15 - 'dependencies' => array( 'mediawiki.language', 'mediawiki.util' ),
16 - 'scripts' => 'resources/mediawiki.language.parser.js'
17 - ),
1814 'ext.uploadWizard' => array(
1915 'dependencies' => array(
2016 'jquery.autoEllipsis',
@@ -27,7 +23,7 @@
2824 'mediawiki.Uri',
2925 'mediawiki.util',
3026 'mediawiki.libs.jpegmeta',
31 - 'ext.uploadwizard.mediawiki.language.parser',
 27+ 'mediawiki.jqueryMsg',
3228 'mediawiki.api',
3329 'mediawiki.api.edit',
3430 'mediawiki.api.category',
Index: trunk/extensions/UploadWizard/resources/mediawiki.language.parser.peg
@@ -1,76 +0,0 @@
2 -/* PEG grammar for a subset of wikitext, useful in the MediaWiki frontend */
3 -
4 -start
5 - = e:expression* { return e.length > 1 ? [ "CONCAT" ].concat(e) : e[0]; }
6 -
7 -expression
8 - = template
9 - / link
10 - / extlink
11 - / replacement
12 - / literal
13 -
14 -paramExpression
15 - = template
16 - / link
17 - / extlink
18 - / replacement
19 - / literalWithoutBar
20 -
21 -template
22 - = "{{" t:templateContents "}}" { return t; }
23 -
24 -templateContents
25 - = twr:templateWithReplacement p:templateParam* { return twr.concat(p) }
26 - / t:templateName p:templateParam* { return p.length ? [ t, p ] : [ t ] }
27 -
28 -templateWithReplacement
29 - = t:templateName ":" r:replacement { return [ t, r ] }
30 -
31 -templateParam
32 - = "|" e:paramExpression* { return e.length > 1 ? [ "CONCAT" ].concat(e) : e[0]; }
33 -
34 -templateName
35 - = tn:[A-Za-z_]+ { return tn.join('').toUpperCase() }
36 -
37 -link
38 - = "[[" w:expression "]]" { return [ 'WLINK', w ]; }
39 -
40 -extlink
41 - = "[" url:url whitespace text:expression "]" { return [ 'LINK', url, text ] }
42 -
43 -url
44 - = url:[^ ]+ { return url.join(''); }
45 -
46 -whitespace
47 - = [ ]+
48 -
49 -replacement
50 - = '$' digits:digits { return [ 'REPLACE', parseInt( digits, 10 ) - 1 ] }
51 -
52 -digits
53 - = [0-9]+
54 -
55 -literal
56 - = lit:escapedOrRegularLiteral+ { return lit.join(''); }
57 -
58 -literalWithoutBar
59 - = lit:escapedOrLiteralWithoutBar+ { return lit.join(''); }
60 -
61 -escapedOrRegularLiteral
62 - = escapedLiteral
63 - / regularLiteral
64 -
65 -escapedOrLiteralWithoutBar
66 - = escapedLiteral
67 - / regularLiteralWithoutBar
68 -
69 -escapedLiteral
70 - = "\\" escaped:. { return escaped; }
71 -
72 -regularLiteral
73 - = [^{}\[\]$\\]
74 -
75 -regularLiteralWithoutBar
76 - = [^{}\[\]$\\|]
77 -
Index: trunk/extensions/UploadWizard/resources/mediawiki.language.parser.js
@@ -1,637 +0,0 @@
2 -/**
3 - * Experimental advanced wikitext parser-emitter.
4 - * See: http://www.mediawiki.org/wiki/Extension:UploadWizard/MessageParser for docs
5 - *
6 - * @author neilk@wikimedia.org
7 - */
8 -
9 -( function( mw, $j ) {
10 -
11 - /**
12 - * Given parser options, return a function that parses a key and replacements, returning jQuery object
13 - * @param {Object} parser options
14 - * @return {Function} accepting ( String message key, String replacement1, String replacement2 ... ) and returning {jQuery}
15 - */
16 - function getFailableParserFn( options ) {
17 - var parser = new mw.language.parser( options );
18 - /**
19 - * Try to parse a key and optional replacements, returning a jQuery object that may be a tree of jQuery nodes.
20 - * If there was an error parsing, return the key and the error message (wrapped in jQuery). This should put the error right into
21 - * the interface, without causing the page to halt script execution, and it hopefully should be clearer how to fix it.
22 - *
23 - * @param {Array} first element is the key, replacements may be in array in 2nd element, or remaining elements.
24 - * @return {jQuery}
25 - */
26 - return function( args ) {
27 - var key = args[0];
28 - var replacements = $j.isArray( args[1] ) ? args[1] : $j.makeArray( args ).slice( 1 );
29 - try {
30 - return parser.parse( key, replacements );
31 - } catch ( e ) {
32 - return $j( '<span></span>' ).append( key + ': ' + e.message );
33 - }
34 - };
35 - }
36 -
37 - /**
38 - * Class method.
39 - * Returns a function suitable for use as a global, to construct strings from the message key (and optional replacements).
40 - * e.g.
41 - * window.gM = mediaWiki.parser.getMessageFunction( options );
42 - * $j( 'p#headline' ).html( gM( 'hello-user', username ) );
43 - *
44 - * Like the old gM() function this returns only strings, so it destroys any bindings. If you want to preserve bindings use the
45 - * jQuery plugin version instead. This is only included for backwards compatibility with gM().
46 - *
47 - * @param {Array} parser options
48 - * @return {Function} function suitable for assigning to window.gM
49 - */
50 - mw.language.getMessageFunction = function( options ) {
51 - var failableParserFn = getFailableParserFn( options );
52 - /**
53 - * N.B. replacements are variadic arguments or an array in second parameter. In other words:
54 - * somefunction(a, b, c, d)
55 - * is equivalent to
56 - * somefunction(a, [b, c, d])
57 - *
58 - * @param {String} message key
59 - * @param {Array} optional replacements (can also specify variadically)
60 - * @return {String} rendered HTML as string
61 - */
62 - return function( /* key, replacements */ ) {
63 - return failableParserFn( arguments ).html();
64 - };
65 - };
66 -
67 - /**
68 - * Class method.
69 - * Returns a jQuery plugin which parses the message in the message key, doing replacements optionally, and appends the nodes to
70 - * the current selector. Bindings to passed-in jquery elements are preserved. Functions become click handlers for [$1 linktext] links.
71 - * e.g.
72 - * $j.fn.msg = mediaWiki.parser.getJqueryPlugin( options );
73 - * var userlink = $j( '<a>' ).click( function() { alert( "hello!!") } );
74 - * $j( 'p#headline' ).msg( 'hello-user', userlink );
75 - *
76 - * @param {Array} parser options
77 - * @return {Function} function suitable for assigning to jQuery plugin, such as $j.fn.msg
78 - */
79 - mw.language.getJqueryMessagePlugin = function( options ) {
80 - var failableParserFn = getFailableParserFn( options );
81 - /**
82 - * N.B. replacements are variadic arguments or an array in second parameter. In other words:
83 - * somefunction(a, b, c, d)
84 - * is equivalent to
85 - * somefunction(a, [b, c, d])
86 - *
87 - * We append to 'this', which in a jQuery plugin context will be the selected elements.
88 - * @param {String} message key
89 - * @param {Array} optional replacements (can also specify variadically)
90 - * @return {jQuery} this
91 - */
92 - return function( /* key, replacements */ ) {
93 - var $target = this.empty();
94 - $j.each( failableParserFn( arguments ).contents(), function( i, node ) {
95 - $target.append( node );
96 - } );
97 - return $target;
98 - };
99 - };
100 -
101 - var parserDefaults = {
102 - 'magic' : {},
103 - 'messages' : mw.messages,
104 - 'language' : mw.language
105 - };
106 -
107 - /**
108 - * The parser itself.
109 - * Describes an object, whose primary duty is to .parse() message keys.
110 - * @param {Array} options
111 - */
112 - mw.language.parser = function( options ) {
113 - this.settings = $j.extend( {}, parserDefaults, options );
114 - this.emitter = new mw.language.htmlEmitter( this.settings.language, this.settings.magic );
115 - };
116 -
117 - mw.language.parser.prototype = {
118 -
119 - // cache, map of mediaWiki message key to the AST of the message. In most cases, the message is a string so this is identical.
120 - // (This is why we would like to move this functionality server-side).
121 - astCache: {},
122 -
123 - /**
124 - * Where the magic happens.
125 - * Parses a message from the key, and swaps in replacements as necessary, wraps in jQuery
126 - * If an error is thrown, returns original key, and logs the error
127 - * @param {String} message key
128 - * @param {Array} replacements for $1, $2... $n
129 - * @return {jQuery}
130 - */
131 - parse: function( key, replacements ) {
132 - return this.emitter.emit( this.getAst( key ), replacements );
133 - },
134 -
135 - /**
136 - * Fetch the message string associated with a key, return parsed structure. Memoized.
137 - * Note that we pass '[' + key + ']' back for a missing message here.
138 - * @param {String} key
139 - * @return {String|Array} string of '[key]' if message missing, simple string if possible, array of arrays if needs parsing
140 - */
141 - getAst: function( key ) {
142 - if ( typeof this.astCache[ key ] === 'undefined' ) {
143 - var wikiText = this.settings.messages.get( key );
144 - if ( typeof wikiText !== 'string' ) {
145 - wikiText = "\\[" + key + "\\]";
146 - }
147 - this.astCache[ key ] = this.wikiTextToAst( wikiText );
148 - }
149 - return this.astCache[ key ];
150 - },
151 -
152 - /*
153 - * Parses the input wikiText into an abstract syntax tree, essentially an s-expression.
154 - *
155 - * CAVEAT: This does not parse all wikitext. It could be more efficient, but it's pretty good already.
156 - * n.b. We want to move this functionality to the server. Nothing here is required to be on the client.
157 - *
158 - * @param {String} message string wikitext
159 - * @throws Error
160 - * @return {Mixed} abstract syntax tree
161 - */
162 - wikiTextToAst: function( input ) {
163 -
164 - // Indicates current position in input as we parse through it.
165 - // Shared among all parsing functions below.
166 - var pos = 0;
167 -
168 - // =========================================================
169 - // parsing combinators - could be a library on its own
170 - // =========================================================
171 -
172 -
173 - // Try parsers until one works, if none work return null
174 - function choice( ps ) {
175 - return function() {
176 - for ( var i = 0; i < ps.length; i++ ) {
177 - var result = ps[i]();
178 - if ( result !== null ) {
179 - return result;
180 - }
181 - }
182 - return null;
183 - };
184 - }
185 -
186 - // try several ps in a row, all must succeed or return null
187 - // this is the only eager one
188 - function sequence( ps ) {
189 - var originalPos = pos;
190 - var result = [];
191 - for ( var i = 0; i < ps.length; i++ ) {
192 - var res = ps[i]();
193 - if ( res === null ) {
194 - pos = originalPos;
195 - return null;
196 - }
197 - result.push( res );
198 - }
199 - return result;
200 - }
201 -
202 - // run the same parser over and over until it fails.
203 - // must succeed a minimum of n times or return null
204 - function nOrMore( n, p ) {
205 - return function() {
206 - var originalPos = pos;
207 - var result = [];
208 - var parsed = p();
209 - while ( parsed !== null ) {
210 - result.push( parsed );
211 - parsed = p();
212 - }
213 - if ( result.length < n ) {
214 - pos = originalPos;
215 - return null;
216 - }
217 - return result;
218 - };
219 - }
220 -
221 - // There is a general pattern -- parse a thing, if that worked, apply transform, otherwise return null.
222 - // But using this as a combinator seems to cause problems when combined with nOrMore().
223 - // May be some scoping issue
224 - function transform( p, fn ) {
225 - return function() {
226 - var result = p();
227 - return result === null ? null : fn( result );
228 - };
229 - }
230 -
231 - // Helpers -- just make ps out of simpler JS builtin types
232 -
233 - function makeStringParser( s ) {
234 - var len = s.length;
235 - return function() {
236 - var result = null;
237 - if ( input.substr( pos, len ) === s ) {
238 - result = s;
239 - pos += len;
240 - }
241 - return result;
242 - };
243 - }
244 -
245 - function makeRegexParser( regex ) {
246 - return function() {
247 - var matches = input.substr( pos ).match( regex );
248 - if ( matches === null ) {
249 - return null;
250 - }
251 - pos += matches[0].length;
252 - return matches[0];
253 - };
254 - }
255 -
256 -
257 - /**
258 - * ===================================================================
259 - * General patterns above this line -- wikitext specific parsers below
260 - * ===================================================================
261 - */
262 -
263 - // Parsing functions follow. All parsing functions work like this:
264 - // They don't accept any arguments.
265 - // Instead, they just operate non destructively on the string 'input'
266 - // As they can consume parts of the string, they advance the shared variable pos,
267 - // and return tokens (or whatever else they want to return).
268 -
269 - // some things are defined as closures and other things as ordinary functions
270 - // converting everything to a closure makes it a lot harder to debug... errors pop up
271 - // but some debuggers can't tell you exactly where they come from. Also the mutually
272 - // recursive functions seem not to work in all browsers then. (Tested IE6-7, Opera, Safari, FF)
273 - // This may be because, to save code, memoization was removed
274 -
275 -
276 - var regularLiteral = makeRegexParser( /^[^{}[\]$\\]/ );
277 - var regularLiteralWithoutBar = makeRegexParser(/^[^{}[\]$\\|]/);
278 - var regularLiteralWithoutSpace = makeRegexParser(/^[^{}[\]$\s]/);
279 -
280 - var backslash = makeStringParser( "\\" );
281 - var anyCharacter = makeRegexParser( /^./ );
282 -
283 - function escapedLiteral() {
284 - var result = sequence( [
285 - backslash,
286 - anyCharacter
287 - ] );
288 - return result === null ? null : result[1];
289 - }
290 -
291 - var escapedOrLiteralWithoutSpace = choice( [
292 - escapedLiteral,
293 - regularLiteralWithoutSpace
294 - ] );
295 -
296 - var escapedOrLiteralWithoutBar = choice( [
297 - escapedLiteral,
298 - regularLiteralWithoutBar
299 - ] );
300 -
301 - var escapedOrRegularLiteral = choice( [
302 - escapedLiteral,
303 - regularLiteral
304 - ] );
305 -
306 - // Used to define "literals" without spaces, in space-delimited situations
307 - function literalWithoutSpace() {
308 - var result = nOrMore( 1, escapedOrLiteralWithoutSpace )();
309 - return result === null ? null : result.join('');
310 - }
311 -
312 - // Used to define "literals" within template parameters. The pipe character is the parameter delimeter, so by default
313 - // it is not a literal in the parameter
314 - function literalWithoutBar() {
315 - var result = nOrMore( 1, escapedOrLiteralWithoutBar )();
316 - return result === null ? null : result.join('');
317 - }
318 -
319 - function literal() {
320 - var result = nOrMore( 1, escapedOrRegularLiteral )();
321 - return result === null ? null : result.join('');
322 - }
323 -
324 - var whitespace = makeRegexParser( /^\s+/ );
325 - var dollar = makeStringParser( '$' );
326 - var digits = makeRegexParser( /^\d+/ );
327 -
328 - function replacement() {
329 - var result = sequence( [
330 - dollar,
331 - digits
332 - ] );
333 - if ( result === null ) {
334 - return null;
335 - }
336 - return [ 'REPLACE', parseInt( result[1], 10 ) - 1 ];
337 - }
338 -
339 -
340 - var openExtlink = makeStringParser( '[' );
341 - var closeExtlink = makeStringParser( ']' );
342 -
343 - // this extlink MUST have inner text, e.g. [foo] not allowed; [foo bar] is allowed
344 - function extlink() {
345 - var result = null;
346 - var parsedResult = sequence( [
347 - openExtlink,
348 - nonWhitespaceExpression,
349 - whitespace,
350 - expression,
351 - closeExtlink
352 - ] );
353 - if ( parsedResult !== null ) {
354 - result = [ 'LINK', parsedResult[1], parsedResult[3] ];
355 - }
356 - return result;
357 - }
358 -
359 - var openLink = makeStringParser( '[[' );
360 - var closeLink = makeStringParser( ']]' );
361 -
362 - function link() {
363 - var result = null;
364 - var parsedResult = sequence( [
365 - openLink,
366 - expression,
367 - closeLink
368 - ] );
369 - if ( parsedResult !== null ) {
370 - result = [ 'WLINK', parsedResult[1] ];
371 - }
372 - return result;
373 - }
374 -
375 - var templateName = transform(
376 - // see $wgLegalTitleChars
377 - // not allowing : due to the need to catch "PLURAL:$1"
378 - makeRegexParser( /^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+-]+/ ),
379 - function( result ) { return result.toString(); }
380 - );
381 -
382 - function templateParam() {
383 - var result = sequence( [
384 - pipe,
385 - nOrMore( 0, paramExpression )
386 - ] );
387 - if ( result === null ) {
388 - return null;
389 - }
390 - var expr = result[1];
391 - // use a "CONCAT" operator if there are multiple nodes, otherwise return the first node, raw.
392 - return expr.length > 1 ? [ "CONCAT" ].concat( expr ) : expr[0];
393 - }
394 -
395 - var pipe = makeStringParser( '|' );
396 -
397 - function templateWithReplacement() {
398 - var result = sequence( [
399 - templateName,
400 - colon,
401 - replacement
402 - ] );
403 - return result === null ? null : [ result[0], result[2] ];
404 - }
405 -
406 - var colon = makeStringParser(':');
407 -
408 - var templateContents = choice( [
409 - function() {
410 - var res = sequence( [
411 - templateWithReplacement,
412 - nOrMore( 0, templateParam )
413 - ] );
414 - return res === null ? null : res[0].concat( res[1] );
415 - },
416 - function() {
417 - var res = sequence( [
418 - templateName,
419 - nOrMore( 0, templateParam )
420 - ] );
421 - if ( res === null ) {
422 - return null;
423 - }
424 - return [ res[0] ].concat( res[1] );
425 - }
426 - ] );
427 -
428 - var openTemplate = makeStringParser('{{');
429 - var closeTemplate = makeStringParser('}}');
430 -
431 - function template() {
432 - var result = sequence( [
433 - openTemplate,
434 - templateContents,
435 - closeTemplate
436 - ] );
437 - return result === null ? null : result[1];
438 - }
439 -
440 - var nonWhitespaceExpression = choice( [
441 - template,
442 - link,
443 - extlink,
444 - replacement,
445 - literalWithoutSpace
446 - ] );
447 -
448 - var paramExpression = choice( [
449 - template,
450 - link,
451 - extlink,
452 - replacement,
453 - literalWithoutBar
454 - ] );
455 -
456 - var expression = choice( [
457 - template,
458 - link,
459 - extlink,
460 - replacement,
461 - literal
462 - ] );
463 -
464 - function start() {
465 - var result = nOrMore( 0, expression )();
466 - if ( result === null ) {
467 - return null;
468 - }
469 - return [ "CONCAT" ].concat( result );
470 - }
471 -
472 - // everything above this point is supposed to be stateless/static, but
473 - // I am deferring the work of turning it into prototypes & objects. It's quite fast enough
474 -
475 - // finally let's do some actual work...
476 -
477 - var result = start();
478 -
479 - /*
480 - * For success, the p must have gotten to the end of the input
481 - * and returned a non-null.
482 - * n.b. This is part of language infrastructure, so we do not throw an internationalizable message.
483 - */
484 - if (result === null || pos !== input.length) {
485 - throw new Error( "Parse error at position " + pos.toString() + " in input: " + input );
486 - }
487 - return result;
488 - }
489 -
490 - };
491 -
492 - /**
493 - * htmlEmitter - object which primarily exists to emit HTML from parser ASTs
494 - */
495 - mw.language.htmlEmitter = function( language, magic ) {
496 - this.language = language;
497 - var _this = this;
498 -
499 - $j.each( magic, function( key, val ) {
500 - _this[ key.toLowerCase() ] = function() { return val; };
501 - } );
502 -
503 - /**
504 - * (We put this method definition here, and not in prototype, to make sure it's not overwritten by any magic.)
505 - * Walk entire node structure, applying replacements and template functions when appropriate
506 - * @param {Mixed} abstract syntax tree (top node or subnode)
507 - * @param {Array} replacements for $1, $2, ... $n
508 - * @return {Mixed} single-string node or array of nodes suitable for jQuery appending
509 - */
510 - this.emit = function( node, replacements ) {
511 - var ret = null;
512 - var _this = this;
513 - switch( typeof node ) {
514 - case 'string':
515 - case 'number':
516 - ret = node;
517 - break;
518 - case 'object': // node is an array of nodes
519 - var subnodes = $j.map( node.slice( 1 ), function( n ) {
520 - return _this.emit( n, replacements );
521 - } );
522 - var operation = node[0].toLowerCase();
523 - if ( typeof _this[operation] === 'function' ) {
524 - ret = _this[ operation ]( subnodes, replacements );
525 - } else {
526 - throw new Error( 'unknown operation "' + operation + '"' );
527 - }
528 - break;
529 - case 'undefined':
530 - // Parsing the empty string (as an entire expression, or as a paramExpression in a template) results in undefined
531 - // Perhaps a more clever parser can detect this, and return the empty string? Or is that useful information?
532 - // The logical thing is probably to return the empty string here when we encounter undefined.
533 - ret = '';
534 - break;
535 - default:
536 - throw new Error( 'unexpected type in AST: ' + typeof node );
537 - }
538 - return ret;
539 - };
540 -
541 - };
542 -
543 - // For everything in input that follows double-open-curly braces, there should be an equivalent parser
544 - // function. For instance {{PLURAL ... }} will be processed by 'plural'.
545 - // If you have 'magic words' then configure the parser to have them upon creation.
546 - //
547 - // An emitter method takes the parent node, the array of subnodes and the array of replacements (the values that $1, $2... should translate to).
548 - // Note: all such functions must be pure, with the exception of referring to other pure functions via this.language (convertPlural and so on)
549 - mw.language.htmlEmitter.prototype = {
550 -
551 - /**
552 - * Parsing has been applied depth-first we can assume that all nodes here are single nodes
553 - * Must return a single node to parents -- a jQuery with synthetic span
554 - * However, unwrap any other synthetic spans in our children and pass them upwards
555 - * @param {Array} nodes - mixed, some single nodes, some arrays of nodes
556 - * @return {jQuery}
557 - */
558 - concat: function( nodes ) {
559 - var span = $j( '<span>' ).addClass( 'mediaWiki_htmlEmitter' );
560 - $j.each( nodes, function( i, node ) {
561 - if ( node instanceof jQuery && node.hasClass( 'mediaWiki_htmlEmitter' ) ) {
562 - $j.each( node.contents(), function( j, childNode ) {
563 - span.append( childNode );
564 - } );
565 - } else {
566 - // strings, integers, anything else
567 - span.append( node );
568 - }
569 - } );
570 - return span;
571 - },
572 -
573 - /**
574 - * Return replacement of correct index, or string if unavailable.
575 - * Note that we expect the parsed parameter to be zero-based. i.e. $1 should have become [ 0 ].
576 - * if the specified parameter is not found return the same string
577 - * (e.g. "$99" -> parameter 98 -> not found -> return "$99" )
578 - * TODO throw error if nodes.length > 1 ?
579 - * @param {Array} of one element, integer, n >= 0
580 - * @return {String} replacement
581 - */
582 - replace: function( nodes, replacements ) {
583 - var index = parseInt( nodes[0], 10 );
584 - return index < replacements.length ? replacements[index] : '$' + ( index + 1 );
585 - },
586 -
587 - /**
588 - * Transform wiki-link
589 - * TODO unimplemented
590 - */
591 - wlink: function( nodes ) {
592 - return "unimplemented";
593 - },
594 -
595 - /**
596 - * Transform parsed structure into external link
597 - * If the href is a jQuery object, treat it as "enclosing" the link text.
598 - * ... function, treat it as the click handler
599 - * ... string, treat it as a URI
600 - * TODO: throw an error if nodes.length > 2 ?
601 - * @param {Array} of two elements, {jQuery|Function|String} and {String}
602 - * @return {jQuery}
603 - */
604 - link: function( nodes ) {
605 - var arg = nodes[0];
606 - var contents = nodes[1];
607 - var $el;
608 - if ( arg instanceof jQuery ) {
609 - $el = arg;
610 - } else {
611 - $el = $j( '<a>' );
612 - if ( typeof arg === 'function' ) {
613 - $el.click( arg ).attr( 'href', '#' );
614 - } else {
615 - $el.attr( 'href', arg.toString() );
616 - }
617 - }
618 - $el.append( contents );
619 - return $el;
620 - },
621 -
622 - /**
623 - * Transform parsed structure into pluralization
624 - * n.b. The first node may be a non-integer (for instance, a string representing an Arabic number).
625 - * So convert it back with the current language's convertNumber.
626 - * @param {Array} of nodes, [ {String|Number}, {String}, {String} ... ]
627 - * @return {String} selected pluralized form according to current language
628 - */
629 - plural: function( nodes ) {
630 - var count = parseInt( this.language.convertNumber( nodes[0], true ), 10 );
631 - var forms = nodes.slice(1);
632 - return forms.length ? this.language.convertPlural( count, forms ) : '';
633 - }
634 -
635 - };
636 -
637 -
638 -} )( mediaWiki, jQuery );
Index: trunk/extensions/UploadWizard/UploadWizardPage.js
@@ -20,12 +20,5 @@
2121 };
2222
2323 jQuery( document ).ready( function() {
24 - // add "magic" to Language template parser for keywords
25 - var options = { magic: { 'SITENAME' : wgSiteName } };
26 -
27 - window.gM = mediaWiki.language.getMessageFunction( options );
28 - $j.fn.msg = mediaWiki.language.getJqueryMessagePlugin( options );
29 -
30 - // show page.
3124 mw.UploadWizardPage();
3225 } );

Follow-up revisions

RevisionCommit summaryAuthorDate
r105974moved language library to core mediawiki.jqueryMsgneilk03:03, 13 December 2011

Status & tagging log