registration: Allow extensions to specify which MW core versions they require
[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.state = Upload.State.NEW;
59
60 this.imageinfo = undefined;
61 }
62
63 UP = Upload.prototype;
64
65 /**
66 * Set the text of the file page, to be created on file upload.
67 * @param {string} text
68 */
69 UP.setText = function ( text ) {
70 this.text = text;
71 };
72
73 /**
74 * Set the filename, to be finalized on upload.
75 * @param {string} filename
76 */
77 UP.setFilename = function ( filename ) {
78 this.filename = filename;
79 };
80
81 /**
82 * Sets the filename based on the filename as it was on the upload.
83 */
84 UP.setFilenameFromFile = function () {
85 if ( this.file.nodeType && this.file.nodeType === Node.ELEMENT_NODE ) {
86 // File input element, use getBasename to cut out the path
87 this.setFilename( this.getBasename( this.file.value ) );
88 } else if ( this.file.name && this.file.lastModified ) {
89 // HTML5 FileAPI File object, but use getBasename to be safe
90 this.setFilename( this.getBasename( this.file.name ) );
91 }
92 };
93
94 /**
95 * Set the file to be uploaded.
96 * @param {HTMLInputElement|File} file
97 */
98 UP.setFile = function ( file ) {
99 this.file = file;
100 };
101
102 /**
103 * Set whether the file should be watchlisted after upload.
104 * @param {boolean} watchlist
105 */
106 UP.setWatchlist = function ( watchlist ) {
107 this.watchlist = watchlist;
108 };
109
110 /**
111 * Set the edit comment for the upload.
112 * @param {string} comment
113 */
114 UP.setComment = function ( comment ) {
115 this.comment = comment;
116 };
117
118 /**
119 * Get the text of the file page, to be created on file upload.
120 * @return {string}
121 */
122 UP.getText = function () {
123 return this.text;
124 };
125
126 /**
127 * Get the filename, to be finalized on upload.
128 * @return {string}
129 */
130 UP.getFilename = function () {
131 return this.filename;
132 };
133
134 /**
135 * Get the file being uploaded.
136 * @return {HTMLInputElement|File}
137 */
138 UP.getFile = function () {
139 return this.file;
140 };
141
142 /**
143 * Get the boolean for whether the file will be watchlisted after upload.
144 * @return {boolean}
145 */
146 UP.getWatchlist = function () {
147 return this.watchlist;
148 };
149
150 /**
151 * Get the current value of the edit comment for the upload.
152 * @return {string}
153 */
154 UP.getComment = function () {
155 return this.comment;
156 };
157
158 /**
159 * Gets the base filename from a path name.
160 * @param {string} path
161 * @return {string}
162 */
163 UP.getBasename = function ( path ) {
164 if ( path === undefined || path === null ) {
165 return '';
166 }
167
168 // Find the index of the last path separator in the
169 // path, and add 1. Then, take the entire string after that.
170 return path.slice(
171 Math.max(
172 path.lastIndexOf( '/' ),
173 path.lastIndexOf( '\\' )
174 ) + 1
175 );
176 };
177
178 /**
179 * Gets the state of the upload.
180 * @return {mw.Upload.State}
181 */
182 UP.getState = function () {
183 return this.state;
184 };
185
186 /**
187 * Get the imageinfo object for the finished upload.
188 * Only available once the upload is finished! Don't try to get it
189 * beforehand.
190 * @return {Object|undefined}
191 */
192 UP.getImageInfo = function () {
193 return this.imageinfo;
194 };
195
196 /**
197 * Upload the file directly.
198 * @return {jQuery.Promise}
199 */
200 UP.upload = function () {
201 var upload = this;
202
203 if ( !this.file ) {
204 return $.Deferred().reject( 'No file to upload. Call setFile to add one.' );
205 }
206
207 if ( !this.filename ) {
208 return $.Deferred().reject( 'No filename set. Call setFilename to add one.' );
209 }
210
211 this.state = Upload.State.UPLOADING;
212
213 return this.api.upload( this.file, {
214 watchlist: ( this.watchlist === true ) ? 1 : undefined,
215 comment: this.comment,
216 filename: this.filename,
217 text: this.text
218 } ).then( function ( result ) {
219 upload.state = Upload.State.UPLOADED;
220 upload.imageinfo = result.upload.imageinfo;
221 return result;
222 }, function () {
223 upload.state = Upload.State.ERROR;
224 } );
225 };
226
227 /**
228 * Upload the file to the stash to be completed later.
229 * @return {jQuery.Promise}
230 */
231 UP.uploadToStash = function () {
232 var upload = this;
233
234 if ( !this.file ) {
235 return $.Deferred().reject( 'No file to upload. Call setFile to add one.' );
236 }
237
238 if ( !this.filename ) {
239 this.setFilenameFromFile();
240 }
241
242 this.state = Upload.State.UPLOADING;
243
244 this.stashPromise = this.api.uploadToStash( this.file, {
245 filename: this.filename
246 } ).then( function ( finishStash ) {
247 upload.state = Upload.State.STASHED;
248 return finishStash;
249 }, function () {
250 upload.state = Upload.State.ERROR;
251 } );
252
253 return this.stashPromise;
254 };
255
256 /**
257 * Finish a stash upload.
258 * @return {jQuery.Promise}
259 */
260 UP.finishStashUpload = function () {
261 var upload = this;
262
263 if ( !this.stashPromise ) {
264 return $.Deferred().reject( 'This upload has not been stashed, please upload it to the stash first.' );
265 }
266
267 return this.stashPromise.then( function ( finishStash ) {
268 upload.state = Upload.State.UPLOADING;
269
270 return finishStash( {
271 watchlist: ( upload.watchlist === true ) ? 1 : undefined,
272 comment: upload.getComment(),
273 filename: upload.getFilename(),
274 text: upload.getText()
275 } ).then( function () {
276 upload.state = Upload.State.UPLOADED;
277 }, function () {
278 upload.state = Upload.State.ERROR;
279 } );
280 } );
281 };
282
283 /**
284 * @enum mw.Upload.State
285 * State of uploads represented in simple terms.
286 */
287 Upload.State = {
288 /** Upload not yet started */
289 NEW: 0,
290
291 /** Upload finished, but there was a warning */
292 WARNING: 1,
293
294 /** Upload finished, but there was an error */
295 ERROR: 2,
296
297 /** Upload in progress */
298 UPLOADING: 3,
299
300 /** Upload finished, but not published, call #finishStashUpload */
301 STASHED: 4,
302
303 /** Upload finished and published */
304 UPLOADED: 5
305 };
306
307 mw.Upload = Upload;
308 }( mediaWiki, jQuery ) );