Merge "mediawiki.Upload: Add #getApi method, use it instead of undocumented properties"
[lhc/web/wiklou.git] / resources / src / mediawiki / mediawiki.ForeignStructuredUpload.BookletLayout.js
1 /*global moment */
2 ( function ( $, mw ) {
3
4 /**
5 * mw.ForeignStructuredUpload.BookletLayout encapsulates the process
6 * of uploading a file to MediaWiki using the mw.ForeignStructuredUpload model.
7 *
8 * var uploadDialog = new mw.Upload.Dialog( {
9 * bookletClass: mw.ForeignStructuredUpload.BookletLayout,
10 * booklet: {
11 * target: 'local'
12 * }
13 * } );
14 * var windowManager = new OO.ui.WindowManager();
15 * $( 'body' ).append( windowManager.$element );
16 * windowManager.addWindows( [ uploadDialog ] );
17 *
18 * @class mw.ForeignStructuredUpload.BookletLayout
19 * @uses mw.ForeignStructuredUpload
20 * @extends mw.Upload.BookletLayout
21 * @cfg {string} [target] Used to choose the target repository.
22 * If nothing is passed, the {@link mw.ForeignUpload#property-target default} is used.
23 */
24 mw.ForeignStructuredUpload.BookletLayout = function ( config ) {
25 config = config || {};
26 // Parent constructor
27 mw.ForeignStructuredUpload.BookletLayout.parent.call( this, config );
28
29 this.target = config.target;
30 };
31
32 /* Setup */
33
34 OO.inheritClass( mw.ForeignStructuredUpload.BookletLayout, mw.Upload.BookletLayout );
35
36 /* Uploading */
37
38 /**
39 * @inheritdoc
40 */
41 mw.ForeignStructuredUpload.BookletLayout.prototype.initialize = function () {
42 var deferred = $.Deferred();
43 mw.ForeignStructuredUpload.BookletLayout.parent.prototype.initialize.call( this )
44 .done( function () {
45 // Point the CategorySelector to the right wiki
46 this.upload.getApi().done( function ( api ) {
47 // If this is a ForeignApi, it will have a apiUrl, otherwise we don't need to do anything
48 if ( api.apiUrl ) {
49 // Can't reuse the same object, CategorySelector calls #abort on its mw.Api instance
50 this.categoriesWidget.api = new mw.ForeignApi( api.apiUrl );
51 }
52 deferred.resolve();
53 }.bind( this ) );
54 }.bind( this ) );
55 return deferred.promise();
56 };
57
58 /**
59 * Returns a {@link mw.ForeignStructuredUpload mw.ForeignStructuredUpload}
60 * with the {@link #cfg-target target} specified in config.
61 *
62 * @protected
63 * @return {mw.Upload}
64 */
65 mw.ForeignStructuredUpload.BookletLayout.prototype.createUpload = function () {
66 return new mw.ForeignStructuredUpload( this.target );
67 };
68
69 /* Form renderers */
70
71 /**
72 * @inheritdoc
73 */
74 mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm = function () {
75 var
76 query = /[?&]uploadbucket=(\d)/.exec( location.search ),
77 isTestEnabled = !!mw.config.get( 'wgForeignUploadTestEnabled' ),
78 defaultBucket = mw.config.get( 'wgForeignUploadTestDefault' ) || 1,
79 userId = mw.config.get( 'wgUserId' );
80
81 if ( query && query[ 1 ] ) {
82 // Testing and debugging
83 this.shouldRecordBucket = false;
84 this.bucket = Number( query[ 1 ] );
85 } else if ( !userId || !isTestEnabled ) {
86 // a) Anonymous user. This can actually happen, because our software sucks.
87 // b) Test is not enabled on this wiki.
88 // In either case, display the old interface and don't record bucket on uploads.
89 this.shouldRecordBucket = false;
90 this.bucket = defaultBucket;
91 } else {
92 // Regular logged in user on a wiki where the test is running
93 this.shouldRecordBucket = true;
94 this.bucket = ( userId % 4 ) + 1; // 1, 2, 3, 4
95 }
96
97 return this[ 'renderUploadForm' + this.bucket ]();
98 };
99
100 /**
101 * Test option 1, the original one. See T120867.
102 */
103 mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm1 = function () {
104 var fieldset, $ownWorkMessage, $notOwnWorkMessage,
105 onUploadFormChange,
106 ownWorkMessage, notOwnWorkMessage, notOwnWorkLocal,
107 validTargets = mw.config.get( 'wgForeignUploadTargets' ),
108 target = this.target || validTargets[ 0 ] || 'local',
109 layout = this;
110
111 // Temporary override to make my life easier during A/B test
112 target = 'shared';
113
114 // foreign-structured-upload-form-label-own-work-message-local
115 // foreign-structured-upload-form-label-own-work-message-shared
116 ownWorkMessage = mw.message( 'foreign-structured-upload-form-label-own-work-message-' + target );
117 // foreign-structured-upload-form-label-not-own-work-message-local
118 // foreign-structured-upload-form-label-not-own-work-message-shared
119 notOwnWorkMessage = mw.message( 'foreign-structured-upload-form-label-not-own-work-message-' + target );
120 // foreign-structured-upload-form-label-not-own-work-local-local
121 // foreign-structured-upload-form-label-not-own-work-local-shared
122 notOwnWorkLocal = mw.message( 'foreign-structured-upload-form-label-not-own-work-local-' + target );
123
124 if ( !ownWorkMessage.exists() ) {
125 ownWorkMessage = mw.message( 'foreign-structured-upload-form-label-own-work-message-default' );
126 }
127 if ( !notOwnWorkMessage.exists() ) {
128 notOwnWorkMessage = mw.message( 'foreign-structured-upload-form-label-not-own-work-message-default' );
129 }
130 if ( !notOwnWorkLocal.exists() ) {
131 notOwnWorkLocal = mw.message( 'foreign-structured-upload-form-label-not-own-work-local-default' );
132 }
133
134 $ownWorkMessage = $( '<p>' ).html( ownWorkMessage.parse() )
135 .addClass( 'mw-foreignStructuredUpload-bookletLayout-license' );
136 $notOwnWorkMessage = $( '<div>' ).append(
137 $( '<p>' ).html( notOwnWorkMessage.parse() ),
138 $( '<p>' ).html( notOwnWorkLocal.parse() )
139 );
140 $ownWorkMessage.add( $notOwnWorkMessage ).find( 'a' )
141 .attr( 'target', '_blank' )
142 .on( 'click', function ( e ) {
143 // Some stupid code is trying to prevent default on all clicks, which causes the links to
144 // not be openable, don't let it
145 e.stopPropagation();
146 } );
147
148 this.selectFileWidget = new OO.ui.SelectFileWidget();
149 this.messageLabel = new OO.ui.LabelWidget( {
150 label: $notOwnWorkMessage
151 } );
152 this.ownWorkCheckbox = new OO.ui.CheckboxInputWidget().on( 'change', function ( on ) {
153 layout.messageLabel.toggle( !on );
154 } );
155
156 fieldset = new OO.ui.FieldsetLayout();
157 fieldset.addItems( [
158 new OO.ui.FieldLayout( this.selectFileWidget, {
159 align: 'top',
160 label: mw.msg( 'upload-form-label-select-file' )
161 } ),
162 new OO.ui.FieldLayout( this.ownWorkCheckbox, {
163 align: 'inline',
164 label: $( '<div>' ).append(
165 $( '<p>' ).text( mw.msg( 'foreign-structured-upload-form-label-own-work' ) ),
166 $ownWorkMessage
167 )
168 } ),
169 new OO.ui.FieldLayout( this.messageLabel, {
170 align: 'top'
171 } )
172 ] );
173 this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
174
175 onUploadFormChange = function () {
176 var file = this.selectFileWidget.getValue(),
177 ownWork = this.ownWorkCheckbox.isSelected(),
178 valid = !!file && ownWork;
179 this.emit( 'uploadValid', valid );
180 };
181
182 // Validation
183 this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
184 this.ownWorkCheckbox.on( 'change', onUploadFormChange.bind( this ) );
185
186 this.selectFileWidget.on( 'change', function () {
187 var file = layout.getFile();
188
189 // Set the date to lastModified once we have the file
190 if ( layout.getDateFromLastModified( file ) !== undefined ) {
191 layout.dateWidget.setValue( layout.getDateFromLastModified( file ) );
192 }
193
194 // Check if we have EXIF data and set to that where available
195 layout.getDateFromExif( file ).done( function ( date ) {
196 layout.dateWidget.setValue( date );
197 } );
198 } );
199
200 return this.uploadForm;
201 };
202
203 /**
204 * Test option 2, idea A from T121021. See T120867.
205 */
206 mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm2 = function () {
207 var fieldset, checkboxes, fields, onUploadFormChange;
208
209 this.selectFileWidget = new OO.ui.SelectFileWidget();
210 this.licenseCheckboxes = checkboxes = [
211 new OO.ui.CheckboxInputWidget(),
212 new OO.ui.CheckboxInputWidget(),
213 new OO.ui.CheckboxInputWidget(),
214 new OO.ui.CheckboxInputWidget()
215 ];
216
217 fields = [
218 new OO.ui.FieldLayout( this.selectFileWidget, {
219 align: 'top',
220 label: mw.msg( 'upload-form-label-select-file' )
221 } ),
222 new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
223 label: mw.message( 'foreign-structured-upload-form-2-label-intro' ).parseDom()
224 } ), {
225 align: 'top'
226 } ),
227 new OO.ui.FieldLayout( checkboxes[ 0 ], {
228 align: 'inline',
229 classes: [
230 'mw-foreignStructuredUpload-bookletLayout-withicon',
231 'mw-foreignStructuredUpload-bookletLayout-ownwork'
232 ],
233 label: mw.message( 'foreign-structured-upload-form-2-label-ownwork' ).parseDom()
234 } ),
235 new OO.ui.FieldLayout( checkboxes[ 1 ], {
236 align: 'inline',
237 classes: [
238 'mw-foreignStructuredUpload-bookletLayout-withicon',
239 'mw-foreignStructuredUpload-bookletLayout-noderiv'
240 ],
241 label: mw.message( 'foreign-structured-upload-form-2-label-noderiv' ).parseDom()
242 } ),
243 new OO.ui.FieldLayout( checkboxes[ 2 ], {
244 align: 'inline',
245 classes: [
246 'mw-foreignStructuredUpload-bookletLayout-withicon',
247 'mw-foreignStructuredUpload-bookletLayout-useful'
248 ],
249 label: mw.message( 'foreign-structured-upload-form-2-label-useful' ).parseDom()
250 } ),
251 new OO.ui.FieldLayout( checkboxes[ 3 ], {
252 align: 'inline',
253 classes: [
254 'mw-foreignStructuredUpload-bookletLayout-withicon',
255 'mw-foreignStructuredUpload-bookletLayout-ccbysa'
256 ],
257 label: mw.message( 'foreign-structured-upload-form-2-label-ccbysa' ).parseDom()
258 } ),
259 new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
260 label: $()
261 .add( $( '<p>' ).msg( 'foreign-structured-upload-form-2-label-alternative' ) )
262 .add( $( '<p>' ).msg( 'foreign-structured-upload-form-2-label-termsofuse' )
263 .addClass( 'mw-foreignStructuredUpload-bookletLayout-license' ) )
264 } ), {
265 align: 'top'
266 } )
267 ];
268
269 fieldset = new OO.ui.FieldsetLayout( { items: fields } );
270 this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
271
272 this.uploadForm.$element.find( 'a' )
273 .attr( 'target', '_blank' )
274 .on( 'click', function ( e ) {
275 // Some stupid code is trying to prevent default on all clicks, which causes the links to
276 // not be openable, don't let it
277 e.stopPropagation();
278 } );
279
280 onUploadFormChange = function () {
281 var file = this.selectFileWidget.getValue(),
282 checks = checkboxes.every( function ( checkbox ) {
283 return checkbox.isSelected();
284 } ),
285 valid = !!file && checks;
286 this.emit( 'uploadValid', valid );
287 };
288
289 // Validation
290 this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
291 checkboxes[ 0 ].on( 'change', onUploadFormChange.bind( this ) );
292 checkboxes[ 1 ].on( 'change', onUploadFormChange.bind( this ) );
293 checkboxes[ 2 ].on( 'change', onUploadFormChange.bind( this ) );
294 checkboxes[ 3 ].on( 'change', onUploadFormChange.bind( this ) );
295
296 return this.uploadForm;
297 };
298
299 /**
300 * Test option 3, idea D from T121021. See T120867.
301 */
302 mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm3 = function () {
303 var ownWorkCheckbox, fieldset, yesMsg, noMsg, selects, selectFields,
304 alternativeField, fields, onUploadFormChange;
305
306 this.selectFileWidget = new OO.ui.SelectFileWidget();
307 this.ownWorkCheckbox = ownWorkCheckbox = new OO.ui.CheckboxInputWidget();
308
309 yesMsg = mw.message( 'foreign-structured-upload-form-3-label-yes' ).text();
310 noMsg = mw.message( 'foreign-structured-upload-form-3-label-no' ).text();
311 selects = [
312 new OO.ui.RadioSelectWidget( {
313 items: [
314 new OO.ui.RadioOptionWidget( { data: false, label: yesMsg } ),
315 new OO.ui.RadioOptionWidget( { data: true, label: noMsg } )
316 ]
317 } ),
318 new OO.ui.RadioSelectWidget( {
319 items: [
320 new OO.ui.RadioOptionWidget( { data: true, label: yesMsg } ),
321 new OO.ui.RadioOptionWidget( { data: false, label: noMsg } )
322 ]
323 } ),
324 new OO.ui.RadioSelectWidget( {
325 items: [
326 new OO.ui.RadioOptionWidget( { data: false, label: yesMsg } ),
327 new OO.ui.RadioOptionWidget( { data: true, label: noMsg } )
328 ]
329 } )
330 ];
331
332 this.licenseSelectFields = selectFields = [
333 new OO.ui.FieldLayout( selects[ 0 ], {
334 align: 'top',
335 classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
336 label: mw.message( 'foreign-structured-upload-form-3-label-question-website' ).parseDom()
337 } ),
338 new OO.ui.FieldLayout( selects[ 1 ], {
339 align: 'top',
340 classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
341 label: mw.message( 'foreign-structured-upload-form-3-label-question-ownwork' ).parseDom()
342 } ).toggle( false ),
343 new OO.ui.FieldLayout( selects[ 2 ], {
344 align: 'top',
345 classes: [ 'mw-foreignStructuredUpload-bookletLayout-question' ],
346 label: mw.message( 'foreign-structured-upload-form-3-label-question-noderiv' ).parseDom()
347 } ).toggle( false )
348 ];
349
350 alternativeField = new OO.ui.FieldLayout( new OO.ui.LabelWidget( {
351 label: mw.message( 'foreign-structured-upload-form-3-label-alternative' ).parseDom()
352 } ), {
353 align: 'top'
354 } ).toggle( false );
355
356 // Choosing the right answer to each question shows the next question.
357 // Switching to wrong answer hides all subsequent questions.
358 selects.forEach( function ( select, i ) {
359 select.on( 'choose', function ( selectedOption ) {
360 var isRightAnswer = !!selectedOption.getData();
361 alternativeField.toggle( !isRightAnswer );
362 if ( i + 1 === selectFields.length ) {
363 // Last question
364 return;
365 }
366 if ( isRightAnswer ) {
367 selectFields[ i + 1 ].toggle( true );
368 } else {
369 selectFields.slice( i + 1 ).forEach( function ( field ) {
370 field.fieldWidget.selectItem( null );
371 field.toggle( false );
372 } );
373 }
374 } );
375 } );
376
377 fields = [
378 new OO.ui.FieldLayout( this.selectFileWidget, {
379 align: 'top',
380 label: mw.msg( 'upload-form-label-select-file' )
381 } ),
382 selectFields[ 0 ],
383 selectFields[ 1 ],
384 selectFields[ 2 ],
385 alternativeField,
386 new OO.ui.FieldLayout( ownWorkCheckbox, {
387 classes: [ 'mw-foreignStructuredUpload-bookletLayout-checkbox' ],
388 align: 'inline',
389 label: mw.message( 'foreign-structured-upload-form-label-own-work-message-shared' ).parseDom()
390 } )
391 ];
392
393 // Must be done late, after it's been associated with the FieldLayout
394 ownWorkCheckbox.setDisabled( true );
395
396 fieldset = new OO.ui.FieldsetLayout( { items: fields } );
397 this.uploadForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
398
399 this.uploadForm.$element.find( 'a' )
400 .attr( 'target', '_blank' )
401 .on( 'click', function ( e ) {
402 // Some stupid code is trying to prevent default on all clicks, which causes the links to
403 // not be openable, don't let it
404 e.stopPropagation();
405 } );
406
407 onUploadFormChange = function () {
408 var file = this.selectFileWidget.getValue(),
409 checkbox = ownWorkCheckbox.isSelected(),
410 rightAnswers = selects.every( function ( select ) {
411 return select.getSelectedItem() && !!select.getSelectedItem().getData();
412 } ),
413 valid = !!file && checkbox && rightAnswers;
414 ownWorkCheckbox.setDisabled( !rightAnswers );
415 if ( !rightAnswers ) {
416 ownWorkCheckbox.setSelected( false );
417 }
418 this.emit( 'uploadValid', valid );
419 };
420
421 // Validation
422 this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
423 this.ownWorkCheckbox.on( 'change', onUploadFormChange.bind( this ) );
424 selects[ 0 ].on( 'choose', onUploadFormChange.bind( this ) );
425 selects[ 1 ].on( 'choose', onUploadFormChange.bind( this ) );
426 selects[ 2 ].on( 'choose', onUploadFormChange.bind( this ) );
427
428 return this.uploadForm;
429 };
430
431 /**
432 * Test option 4, idea E from T121021. See T120867.
433 */
434 mw.ForeignStructuredUpload.BookletLayout.prototype.renderUploadForm4 = function () {
435 var fieldset, $guide;
436 this.renderUploadForm1();
437 fieldset = this.uploadForm.getItems()[ 0 ];
438
439 $guide = mw.template.get( 'mediawiki.ForeignStructuredUpload.BookletLayout', 'guide.html' ).render();
440 $guide.find( '.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-good span' )
441 .msg( 'foreign-structured-upload-form-4-label-good' );
442 $guide.find( '.mw-foreignStructuredUpload-bookletLayout-guide-text-wrapper-bad span' )
443 .msg( 'foreign-structured-upload-form-4-label-bad' );
444
445 // Note the index, we insert after the SelectFileWidget field
446 fieldset.addItems( [
447 new OO.ui.FieldLayout( new OO.ui.Widget( {
448 $content: $guide
449 } ), {
450 align: 'top'
451 } )
452 ], 1 );
453
454 // Hook for custom styles
455 fieldset.getItems()[ 2 ].$element.addClass( 'mw-foreignStructuredUpload-bookletLayout-guide-checkbox' );
456
457 // Streamline: remove mention of local Special:Upload
458 fieldset.getItems()[ 3 ].$element.find( 'p' ).last().remove();
459
460 return this.uploadForm;
461 };
462
463 /**
464 * @inheritdoc
465 */
466 mw.ForeignStructuredUpload.BookletLayout.prototype.onUploadFormChange = function () {};
467
468 /**
469 * @inheritdoc
470 */
471 mw.ForeignStructuredUpload.BookletLayout.prototype.renderInfoForm = function () {
472 var fieldset;
473
474 this.filenameWidget = new OO.ui.TextInputWidget( {
475 required: true,
476 validate: /.+/
477 } );
478 this.descriptionWidget = new OO.ui.TextInputWidget( {
479 required: true,
480 validate: /\S+/,
481 multiline: true,
482 autosize: true
483 } );
484 this.categoriesWidget = new mw.widgets.CategorySelector( {
485 // Can't be done here because we don't know the target wiki yet... done in #initialize.
486 // api: new mw.ForeignApi( ... ),
487 $overlay: this.$overlay
488 } );
489 this.dateWidget = new mw.widgets.DateInputWidget( {
490 $overlay: this.$overlay,
491 required: true,
492 mustBeBefore: moment().add( 1, 'day' ).locale( 'en' ).format( 'YYYY-MM-DD' ) // Tomorrow
493 } );
494
495 fieldset = new OO.ui.FieldsetLayout( {
496 label: mw.msg( 'upload-form-label-infoform-title' )
497 } );
498 fieldset.addItems( [
499 new OO.ui.FieldLayout( this.filenameWidget, {
500 label: mw.msg( 'upload-form-label-infoform-name' ),
501 align: 'top',
502 help: mw.msg( 'upload-form-label-infoform-name-tooltip' )
503 } ),
504 new OO.ui.FieldLayout( this.descriptionWidget, {
505 label: mw.msg( 'upload-form-label-infoform-description' ),
506 align: 'top',
507 help: mw.msg( 'upload-form-label-infoform-description-tooltip' )
508 } ),
509 new OO.ui.FieldLayout( this.categoriesWidget, {
510 label: mw.msg( 'foreign-structured-upload-form-label-infoform-categories' ),
511 align: 'top'
512 } ),
513 new OO.ui.FieldLayout( this.dateWidget, {
514 label: mw.msg( 'foreign-structured-upload-form-label-infoform-date' ),
515 align: 'top'
516 } )
517 ] );
518 this.infoForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
519
520 // Validation
521 this.filenameWidget.on( 'change', this.onInfoFormChange.bind( this ) );
522 this.descriptionWidget.on( 'change', this.onInfoFormChange.bind( this ) );
523 this.dateWidget.on( 'change', this.onInfoFormChange.bind( this ) );
524
525 return this.infoForm;
526 };
527
528 /**
529 * @inheritdoc
530 */
531 mw.ForeignStructuredUpload.BookletLayout.prototype.onInfoFormChange = function () {
532 var layout = this;
533 $.when(
534 this.filenameWidget.getValidity(),
535 this.descriptionWidget.getValidity(),
536 this.dateWidget.getValidity()
537 ).done( function () {
538 layout.emit( 'infoValid', true );
539 } ).fail( function () {
540 layout.emit( 'infoValid', false );
541 } );
542 };
543
544 /* Getters */
545
546 /**
547 * @inheritdoc
548 */
549 mw.ForeignStructuredUpload.BookletLayout.prototype.getText = function () {
550 var language = mw.config.get( 'wgContentLanguage' );
551 this.upload.clearDescriptions();
552 this.upload.addDescription( language, this.descriptionWidget.getValue() );
553 this.upload.setDate( this.dateWidget.getValue() );
554 this.upload.clearCategories();
555 this.upload.addCategories( this.categoriesWidget.getItemsData() );
556 return this.upload.getText();
557 };
558
559 /**
560 * Get original date from EXIF data
561 *
562 * @param {Object} file
563 * @return {jQuery.Promise} Promise resolved with the EXIF date
564 */
565 mw.ForeignStructuredUpload.BookletLayout.prototype.getDateFromExif = function ( file ) {
566 var fileReader,
567 deferred = $.Deferred();
568
569 if ( file && file.type === 'image/jpeg' ) {
570 fileReader = new FileReader();
571 fileReader.onload = function () {
572 var fileStr, arr, i, metadata;
573
574 if ( typeof fileReader.result === 'string' ) {
575 fileStr = fileReader.result;
576 } else {
577 // Array buffer; convert to binary string for the library.
578 arr = new Uint8Array( fileReader.result );
579 fileStr = '';
580 for ( i = 0; i < arr.byteLength; i++ ) {
581 fileStr += String.fromCharCode( arr[ i ] );
582 }
583 }
584
585 try {
586 metadata = mw.libs.jpegmeta( this.result, file.name );
587 } catch ( e ) {
588 metadata = null;
589 }
590
591 if ( metadata !== null && metadata.exif !== undefined && metadata.exif.DateTimeOriginal ) {
592 deferred.resolve( moment( metadata.exif.DateTimeOriginal, 'YYYY:MM:DD' ).format( 'YYYY-MM-DD' ) );
593 } else {
594 deferred.reject();
595 }
596 };
597
598 if ( 'readAsBinaryString' in fileReader ) {
599 fileReader.readAsBinaryString( file );
600 } else if ( 'readAsArrayBuffer' in fileReader ) {
601 fileReader.readAsArrayBuffer( file );
602 } else {
603 // We should never get here
604 deferred.reject();
605 throw new Error( 'Cannot read thumbnail as binary string or array buffer.' );
606 }
607 }
608
609 return deferred.promise();
610 };
611
612 /**
613 * Get last modified date from file
614 *
615 * @param {Object} file
616 * @return {Object} Last modified date from file
617 */
618 mw.ForeignStructuredUpload.BookletLayout.prototype.getDateFromLastModified = function ( file ) {
619 if ( file && file.lastModified ) {
620 return moment( file.lastModified ).format( 'YYYY-MM-DD' );
621 }
622 };
623
624 /* Setters */
625
626 /**
627 * @inheritdoc
628 */
629 mw.ForeignStructuredUpload.BookletLayout.prototype.clear = function () {
630 mw.ForeignStructuredUpload.BookletLayout.parent.prototype.clear.call( this );
631
632 if ( this.ownWorkCheckbox ) {
633 this.ownWorkCheckbox.setSelected( false );
634 }
635 if ( this.licenseCheckboxes ) {
636 this.licenseCheckboxes.forEach( function ( checkbox ) {
637 checkbox.setSelected( false );
638 } );
639 }
640 if ( this.licenseSelectFields ) {
641 this.licenseSelectFields.forEach( function ( field, i ) {
642 field.fieldWidget.selectItem( null );
643 if ( i !== 0 ) {
644 field.toggle( false );
645 }
646 } );
647 }
648
649 this.categoriesWidget.setItemsFromData( [] );
650 this.dateWidget.setValue( '' ).setValidityFlag( true );
651 };
652
653 }( jQuery, mediaWiki ) );