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