Merge "Remove register_globals and magic_quotes_* checks"
[lhc/web/wiklou.git] / resources / src / mediawiki / mediawiki.Upload.js
1 ( function ( mw, $ ) {
2 var UP;
3
4 /**
5 * @class mw.Upload
6 *
7 * Used to represent an upload in progress on the frontend.
8 * Most of the functionality is implemented in mw.Api.plugin.upload,
9 * but this model class will tie it together as well as let you perform
10 * actions in a logical way.
11 *
12 * A simple example:
13 *
14 * var file = new OO.ui.SelectFileWidget(),
15 * button = new OO.ui.ButtonWidget( { label: 'Save' } ),
16 * upload = new mw.Upload;
17 *
18 * button.on( 'click', function () {
19 * upload.setFile( file.getValue() );
20 * upload.setFilename( file.getValue().name );
21 * upload.upload();
22 * } );
23 *
24 * $( 'body' ).append( file.$element, button.$element );
25 *
26 * You can also choose to {@link #uploadToStash stash the upload} and
27 * {@link #finishStashUpload finalize} it later:
28 *
29 * var file, // Some file object
30 * upload = new mw.Upload,
31 * stashPromise = $.Deferred();
32 *
33 * upload.setFile( file );
34 * upload.uploadToStash().then( function () {
35 * stashPromise.resolve();
36 * } );
37 *
38 * stashPromise.then( function () {
39 * upload.setFilename( 'foo' );
40 * upload.setText( 'bar' );
41 * upload.finishStashUpload().then( function () {
42 * console.log( 'Done!' );
43 * } );
44 * } );
45 *
46 * @constructor
47 * @param {Object|mw.Api} [apiconfig] A mw.Api object (or subclass), or configuration
48 * to pass to the constructor of mw.Api.
49 */
50 function Upload( apiconfig ) {
51 this.api = ( apiconfig instanceof mw.Api ) ? apiconfig : new mw.Api( apiconfig );
52
53 this.watchlist = false;
54 this.text = '';
55 this.comment = '';
56 this.filename = null;
57 this.file = null;
58 this.setState( Upload.State.NEW );
59
60 this.imageinfo = undefined;
61 }
62
63 UP = Upload.prototype;
64
65 /**
66 * Get the mw.Api instance used by this Upload object.
67 *
68 * @return {jQuery.Promise}
69 * @return {Function} return.done
70 * @return {mw.Api} return.done.api
71 */
72 UP.getApi = function () {
73 return $.Deferred().resolve( this.api ).promise();
74 };
75
76 /**
77 * Set the text of the file page, to be created on file upload.
78 *
79 * @param {string} text
80 */
81 UP.setText = function ( text ) {
82 this.text = text;
83 };
84
85 /**
86 * Set the filename, to be finalized on upload.
87 *
88 * @param {string} filename
89 */
90 UP.setFilename = function ( filename ) {
91 this.filename = filename;
92 };
93
94 /**
95 * Sets the filename based on the filename as it was on the upload.
96 */
97 UP.setFilenameFromFile = function () {
98 var file = this.getFile();
99 if ( !file ) {
100 return;
101 }
102 if ( file.nodeType && file.nodeType === Node.ELEMENT_NODE ) {
103 // File input element, use getBasename to cut out the path
104 this.setFilename( this.getBasename( file.value ) );
105 } else if ( file.name ) {
106 // HTML5 FileAPI File object, but use getBasename to be safe
107 this.setFilename( this.getBasename( file.name ) );
108 } else {
109 // If we ever implement uploading files from clipboard, they might not have a name
110 this.setFilename( '?' );
111 }
112 };
113
114 /**
115 * Set the file to be uploaded.
116 *
117 * @param {HTMLInputElement|File|Blob} file
118 */
119 UP.setFile = function ( file ) {
120 this.file = file;
121 };
122
123 /**
124 * Set whether the file should be watchlisted after upload.
125 *
126 * @param {boolean} watchlist
127 */
128 UP.setWatchlist = function ( watchlist ) {
129 this.watchlist = watchlist;
130 };
131
132 /**
133 * Set the edit comment for the upload.
134 *
135 * @param {string} comment
136 */
137 UP.setComment = function ( comment ) {
138 this.comment = comment;
139 };
140
141 /**
142 * Get the text of the file page, to be created on file upload.
143 *
144 * @return {string}
145 */
146 UP.getText = function () {
147 return this.text;
148 };
149
150 /**
151 * Get the filename, to be finalized on upload.
152 *
153 * @return {string}
154 */
155 UP.getFilename = function () {
156 return this.filename;
157 };
158
159 /**
160 * Get the file being uploaded.
161 *
162 * @return {HTMLInputElement|File|Blob}
163 */
164 UP.getFile = function () {
165 return this.file;
166 };
167
168 /**
169 * Get the boolean for whether the file will be watchlisted after upload.
170 *
171 * @return {boolean}
172 */
173 UP.getWatchlist = function () {
174 return this.watchlist;
175 };
176
177 /**
178 * Get the current value of the edit comment for the upload.
179 *
180 * @return {string}
181 */
182 UP.getComment = function () {
183 return this.comment;
184 };
185
186 /**
187 * Gets the base filename from a path name.
188 *
189 * @param {string} path
190 * @return {string}
191 */
192 UP.getBasename = function ( path ) {
193 if ( path === undefined || path === null ) {
194 return '';
195 }
196
197 // Find the index of the last path separator in the
198 // path, and add 1. Then, take the entire string after that.
199 return path.slice(
200 Math.max(
201 path.lastIndexOf( '/' ),
202 path.lastIndexOf( '\\' )
203 ) + 1
204 );
205 };
206
207 /**
208 * Sets the state and state details (if any) of the upload.
209 *
210 * @param {mw.Upload.State} state
211 * @param {Object} stateDetails
212 */
213 UP.setState = function ( state, stateDetails ) {
214 this.state = state;
215 this.stateDetails = stateDetails;
216 };
217
218 /**
219 * Gets the state of the upload.
220 *
221 * @return {mw.Upload.State}
222 */
223 UP.getState = function () {
224 return this.state;
225 };
226
227 /**
228 * Gets details of the current state.
229 *
230 * @return {string}
231 */
232 UP.getStateDetails = function () {
233 return this.stateDetails;
234 };
235
236 /**
237 * Get the imageinfo object for the finished upload.
238 * Only available once the upload is finished! Don't try to get it
239 * beforehand.
240 *
241 * @return {Object|undefined}
242 */
243 UP.getImageInfo = function () {
244 return this.imageinfo;
245 };
246
247 /**
248 * Upload the file directly.
249 *
250 * @return {jQuery.Promise}
251 */
252 UP.upload = function () {
253 var upload = this;
254
255 if ( !this.getFile() ) {
256 return $.Deferred().reject( 'No file to upload. Call setFile to add one.' );
257 }
258
259 if ( !this.getFilename() ) {
260 return $.Deferred().reject( 'No filename set. Call setFilename to add one.' );
261 }
262
263 this.setState( Upload.State.UPLOADING );
264
265 return this.api.upload( this.getFile(), {
266 watchlist: ( this.getWatchlist() ) ? 1 : undefined,
267 comment: this.getComment(),
268 filename: this.getFilename(),
269 text: this.getText()
270 } ).then( function ( result ) {
271 upload.setState( Upload.State.UPLOADED );
272 upload.imageinfo = result.upload.imageinfo;
273 return result;
274 }, function ( errorCode, result ) {
275 if ( result && result.upload && result.upload.warnings ) {
276 upload.setState( Upload.State.WARNING, result );
277 } else {
278 upload.setState( Upload.State.ERROR, result );
279 }
280 return $.Deferred().reject( errorCode, result );
281 } );
282 };
283
284 /**
285 * Upload the file to the stash to be completed later.
286 *
287 * @return {jQuery.Promise}
288 */
289 UP.uploadToStash = function () {
290 var upload = this;
291
292 if ( !this.getFile() ) {
293 return $.Deferred().reject( 'No file to upload. Call setFile to add one.' );
294 }
295
296 if ( !this.getFilename() ) {
297 this.setFilenameFromFile();
298 }
299
300 this.setState( Upload.State.UPLOADING );
301
302 this.stashPromise = this.api.uploadToStash( this.getFile(), {
303 filename: this.getFilename()
304 } ).then( function ( finishStash ) {
305 upload.setState( Upload.State.STASHED );
306 return finishStash;
307 }, function ( errorCode, result ) {
308 if ( result && result.upload && result.upload.warnings ) {
309 upload.setState( Upload.State.WARNING, result );
310 } else {
311 upload.setState( Upload.State.ERROR, result );
312 }
313 return $.Deferred().reject( errorCode, result );
314 } );
315
316 return this.stashPromise;
317 };
318
319 /**
320 * Finish a stash upload.
321 *
322 * @return {jQuery.Promise}
323 */
324 UP.finishStashUpload = function () {
325 var upload = this;
326
327 if ( !this.stashPromise ) {
328 return $.Deferred().reject( 'This upload has not been stashed, please upload it to the stash first.' );
329 }
330
331 return this.stashPromise.then( function ( finishStash ) {
332 upload.setState( Upload.State.UPLOADING );
333
334 return finishStash( {
335 watchlist: ( upload.getWatchlist() ) ? 1 : undefined,
336 comment: upload.getComment(),
337 filename: upload.getFilename(),
338 text: upload.getText()
339 } ).then( function ( result ) {
340 upload.setState( Upload.State.UPLOADED );
341 upload.imageinfo = result.upload.imageinfo;
342 return result;
343 }, function ( errorCode, result ) {
344 if ( result && result.upload && result.upload.warnings ) {
345 upload.setState( Upload.State.WARNING, result );
346 } else {
347 upload.setState( Upload.State.ERROR, result );
348 }
349 return $.Deferred().reject( errorCode, result );
350 } );
351 } );
352 };
353
354 /**
355 * @enum mw.Upload.State
356 * State of uploads represented in simple terms.
357 */
358 Upload.State = {
359 /** Upload not yet started */
360 NEW: 0,
361
362 /** Upload finished, but there was a warning */
363 WARNING: 1,
364
365 /** Upload finished, but there was an error */
366 ERROR: 2,
367
368 /** Upload in progress */
369 UPLOADING: 3,
370
371 /** Upload finished, but not published, call #finishStashUpload */
372 STASHED: 4,
373
374 /** Upload finished and published */
375 UPLOADED: 5
376 };
377
378 mw.Upload = Upload;
379 }( mediaWiki, jQuery ) );