Follow-up r83218: Added var declarations. Html escape the largefileserver message...
[lhc/web/wiklou.git] / resources / mediawiki.special / mediawiki.special.upload.js
1 /*
2 * JavaScript for Special:Upload
3 * Note that additional code still lives in skins/common/upload.js
4 */
5
6 /**
7 * Add a preview to the upload form
8 */
9 jQuery( function( $ ) {
10 /**
11 * Is the FileAPI available with sufficient functionality?
12 */
13 function hasFileAPI(){
14 return typeof window.FileReader !== 'undefined';
15 }
16
17 /**
18 * Check if this is a recognizable image type...
19 * Also excludes files over 10M to avoid going insane on memory usage.
20 *
21 * @todo is there a way we can ask the browser what's supported in <img>s?
22 *
23 * @param {File} file
24 * @return boolean
25 */
26 function fileIsPreviewable( file ) {
27 var known = ['image/png', 'image/gif', 'image/jpeg', 'image/svg+xml'],
28 tooHuge = 10 * 1024 * 1024;
29 return ( $.inArray( file.type, known ) !== -1 ) && file.size > 0 && file.size < tooHuge;
30 }
31
32 /**
33 * Show a thumbnail preview of PNG, JPEG, GIF, and SVG files prior to upload
34 * in browsers supporting HTML5 FileAPI.
35 *
36 * As of this writing, known good:
37 * - Firefox 3.6+
38 * - Chrome 7.something
39 *
40 * @todo check file size limits and warn of likely failures
41 *
42 * @param {File} file
43 */
44 function showPreview( file ) {
45 var previewSize = 180,
46 thumb = $( '<div id="mw-upload-thumbnail" class="thumb tright">' +
47 '<div class="thumbinner">' +
48 '<canvas width="' + previewSize + '" height="' + previewSize + '" ></canvas>' +
49 '<div class="thumbcaption"><div class="filename"></div><div class="fileinfo"></div></div>' +
50 '</div>' +
51 '</div>' );
52 thumb.find( '.filename' ).text( file.name ).end()
53 .find( '.fileinfo' ).text( prettySize( file.size ) ).end();
54
55 var ctx = thumb.find( 'canvas' )[0].getContext( '2d' ),
56 spinner = new Image();
57 spinner.onload = function() {
58 ctx.drawImage( spinner, (previewSize - spinner.width) / 2,
59 (previewSize - spinner.height) / 2 );
60 };
61 spinner.src = mw.config.get( 'wgScriptPath' ) + '/skins/common/images/spinner.gif';
62 $( '#mw-htmlform-source' ).parent().prepend( thumb );
63
64 var meta;
65 fetchPreview( file, function( dataURL ) {
66 var img = new Image(),
67 rotation = 0;
68
69 if ( meta && meta.tiff && meta.tiff.Orientation ) {
70 rotation = (360 - function () {
71 // See includes/media/Bitmap.php
72 switch ( meta.tiff.Orientation.value ) {
73 case 8:
74 return 90;
75 case 3:
76 return 180;
77 case 6:
78 return 270;
79 default:
80 return 0;
81 }
82 }() ) % 360;
83 }
84
85 img.onload = function() {
86 // Fit the image within the previewSizexpreviewSize box
87 if ( img.width > img.height ) {
88 width = previewSize;
89 height = img.height / img.width * previewSize;
90 } else {
91 height = previewSize;
92 width = img.width / img.height * previewSize;
93 }
94 // Determine the offset required to center the image
95 dx = (180 - width) / 2;
96 dy = (180 - height) / 2;
97 switch ( rotation ) {
98 // If a rotation is applied, the direction of the axis
99 // changes as well. You can derive the values below by
100 // drawing on paper an axis system, rotate it and see
101 // where the positive axis direction is
102 case 0:
103 x = dx;
104 y = dy;
105 break;
106 case 90:
107
108 x = dx;
109 y = dy - previewSize;
110 break;
111 case 180:
112 x = dx - previewSize;
113 y = dy - previewSize;
114 break;
115 case 270:
116 x = dx - previewSize;
117 y = dy;
118 break;
119 }
120
121 ctx.clearRect( 0, 0, 180, 180 );
122 ctx.rotate( rotation / 180 * Math.PI );
123 ctx.drawImage( img, x, y, width, height );
124
125 // Image size
126 var info = mw.msg( 'widthheight', img.width, img.height ) +
127 ', ' + prettySize( file.size );
128 $( '#mw-upload-thumbnail .fileinfo' ).text( info );
129 };
130 img.src = dataURL;
131 }, mw.config.get( 'wgFileCanRotate' ) ? function ( data ) {
132 try {
133 meta = mw.util.jpegmeta( data, file.fileName );
134 meta._binary_data = null;
135 } catch ( e ) {
136 meta = null;
137 }
138 } : null );
139 }
140
141 /**
142 * Start loading a file into memory; when complete, pass it as a
143 * data URL to the callback function. If the callbackBinary is set it will
144 * first be read as binary and afterwards as data URL. Useful if you want
145 * to do preprocessing on the binary data first.
146 *
147 * @param {File} file
148 * @param {function} callback
149 * @param {function} callbackBinary
150 */
151 function fetchPreview( file, callback, callbackBinary ) {
152 var reader = new FileReader();
153 reader.onload = function() {
154 if ( callbackBinary ) {
155 callbackBinary( reader.result );
156 reader.onload = function() {
157 callback( reader.result );
158 };
159 reader.readAsDataURL( file );
160 } else {
161 callback( reader.result );
162 }
163 };
164 if ( callbackBinary ) {
165 reader.readAsBinaryString( file );
166 } else {
167 reader.readAsDataURL( file );
168 }
169 }
170
171 /**
172 * Format a file size attractively.
173 * @todo match numeric formatting
174 *
175 * @param {number} s
176 * @return string
177 */
178 function prettySize( s ) {
179 var sizes = ['size-bytes', 'size-kilobytes', 'size-megabytes', 'size-gigabytes'];
180 while ( s >= 1024 && sizes.length > 1 ) {
181 s /= 1024;
182 sizes = sizes.slice( 1 );
183 }
184 return mw.msg( sizes[0], Math.round( s ) );
185 }
186
187 /**
188 * Clear the file upload preview area.
189 */
190 function clearPreview() {
191 $( '#mw-upload-thumbnail' ).remove();
192 }
193
194 /**
195 * Check if the file does not exceed the maximum size
196 */
197 function checkMaxUploadSize( file ) {
198 function getMaxUploadSize( type ) {
199 var sizes = mw.config.get( 'wgMaxUploadSize' );
200 if ( sizes[type] !== undefined ) {
201 return sizes[type];
202 }
203 return sizes['*'];
204 }
205 $( '.mw-upload-source-error' ).remove();
206
207 var maxSize = getMaxUploadSize( 'file' );
208 if ( file.size > maxSize ) {
209 var error = $( '<p class="error mw-upload-source-error" id="wpSourceTypeFile-error">' +
210 mw.message( 'largefileserver', file.size, maxSize ).escaped() + '</p>' );
211 $( '#wpUploadFile' ).after( error );
212 return false;
213 }
214 return true;
215 }
216
217
218 /**
219 * Initialization
220 */
221 if ( hasFileAPI() ) {
222 // Update thumbnail when the file selection control is updated.
223 $( '#wpUploadFile' ).change( function() {
224 clearPreview();
225 if ( this.files && this.files.length ) {
226 // Note: would need to be updated to handle multiple files.
227 var file = this.files[0];
228
229 if ( !checkMaxUploadSize( file ) ) {
230 return;
231 }
232
233 if ( fileIsPreviewable( file ) ) {
234 showPreview( file );
235 }
236 }
237 } );
238 }
239 } );
240
241 /**
242 * Disable all upload source fields except the selected one
243 */
244 jQuery( function ( $ ) {
245 var rows = $( '.mw-htmlform-field-UploadSourceField' );
246 for ( var i = rows.length; i; i-- ) {
247 var row = rows[i - 1];
248 $( 'input[name="wpSourceType"]', row ).change( function () {
249 var currentRow = row; // Store current row in our own scope
250 return function () {
251 $( '.mw-upload-source-error' ).remove();
252 if ( this.checked ) {
253 // Disable all inputs
254 $( 'input[name!="wpSourceType"]', rows ).attr( 'disabled', true );
255 // Re-enable the current one
256 $( 'input', currentRow ).attr( 'disabled', false );
257 }
258 };
259 }() );
260 }
261 } );
262