5 * mw.ForeignStructuredUpload.BookletLayout encapsulates the process
6 * of uploading a file to MediaWiki using the mw.ForeignStructuredUpload model.
8 * var uploadDialog = new mw.Upload.Dialog( {
9 * bookletClass: mw.ForeignStructuredUpload.BookletLayout,
14 * var windowManager = new OO.ui.WindowManager();
15 * $( 'body' ).append( windowManager.$element );
16 * windowManager.addWindows( [ uploadDialog ] );
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.
24 mw
.ForeignStructuredUpload
.BookletLayout = function ( config
) {
25 config
= config
|| {};
27 mw
.ForeignStructuredUpload
.BookletLayout
.parent
.call( this, config
);
29 this.target
= config
.target
;
34 OO
.inheritClass( mw
.ForeignStructuredUpload
.BookletLayout
, mw
.Upload
.BookletLayout
);
41 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.initialize = function () {
43 return mw
.ForeignStructuredUpload
.BookletLayout
.parent
.prototype.initialize
.call( this ).then(
45 // Point the CategorySelector to the right wiki
46 return booklet
.upload
.getApi().then(
48 // If this is a ForeignApi, it will have a apiUrl, otherwise we don't need to do anything
50 // Can't reuse the same object, CategorySelector calls #abort on its mw.Api instance
51 booklet
.categoriesWidget
.api
= new mw
.ForeignApi( api
.apiUrl
);
53 return $.Deferred().resolve();
56 return $.Deferred().resolve();
61 return $.Deferred().resolve();
67 * Returns a {@link mw.ForeignStructuredUpload mw.ForeignStructuredUpload}
68 * with the {@link #cfg-target target} specified in config.
73 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.createUpload = function () {
74 return new mw
.ForeignStructuredUpload( this.target
);
82 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.renderUploadForm = function () {
83 var fieldset
, $ownWorkMessage
, $notOwnWorkMessage
,
84 ownWorkMessage
, notOwnWorkMessage
, notOwnWorkLocal
,
85 validTargets
= mw
.config
.get( 'wgForeignUploadTargets' ),
86 target
= this.target
|| validTargets
[ 0 ] || 'local',
89 // foreign-structured-upload-form-label-own-work-message-local
90 // foreign-structured-upload-form-label-own-work-message-shared
91 ownWorkMessage
= mw
.message( 'foreign-structured-upload-form-label-own-work-message-' + target
);
92 // foreign-structured-upload-form-label-not-own-work-message-local
93 // foreign-structured-upload-form-label-not-own-work-message-shared
94 notOwnWorkMessage
= mw
.message( 'foreign-structured-upload-form-label-not-own-work-message-' + target
);
95 // foreign-structured-upload-form-label-not-own-work-local-local
96 // foreign-structured-upload-form-label-not-own-work-local-shared
97 notOwnWorkLocal
= mw
.message( 'foreign-structured-upload-form-label-not-own-work-local-' + target
);
99 if ( !ownWorkMessage
.exists() ) {
100 ownWorkMessage
= mw
.message( 'foreign-structured-upload-form-label-own-work-message-default' );
102 if ( !notOwnWorkMessage
.exists() ) {
103 notOwnWorkMessage
= mw
.message( 'foreign-structured-upload-form-label-not-own-work-message-default' );
105 if ( !notOwnWorkLocal
.exists() ) {
106 notOwnWorkLocal
= mw
.message( 'foreign-structured-upload-form-label-not-own-work-local-default' );
109 $ownWorkMessage
= $( '<p>' ).append( ownWorkMessage
.parseDom() )
110 .addClass( 'mw-foreignStructuredUpload-bookletLayout-license' );
111 $notOwnWorkMessage
= $( '<div>' ).append(
112 $( '<p>' ).append( notOwnWorkMessage
.parseDom() ),
113 $( '<p>' ).append( notOwnWorkLocal
.parseDom() )
115 $ownWorkMessage
.add( $notOwnWorkMessage
).find( 'a' ).attr( 'target', '_blank' );
117 this.selectFileWidget
= new OO
.ui
.SelectFileWidget( {
120 this.messageLabel
= new OO
.ui
.LabelWidget( {
121 label
: $notOwnWorkMessage
123 this.ownWorkCheckbox
= new OO
.ui
.CheckboxInputWidget().on( 'change', function ( on
) {
124 layout
.messageLabel
.toggle( !on
);
127 fieldset
= new OO
.ui
.FieldsetLayout();
129 new OO
.ui
.FieldLayout( this.selectFileWidget
, {
132 new OO
.ui
.FieldLayout( this.ownWorkCheckbox
, {
134 label
: $( '<div>' ).append(
135 $( '<p>' ).text( mw
.msg( 'foreign-structured-upload-form-label-own-work' ) ),
139 new OO
.ui
.FieldLayout( this.messageLabel
, {
143 this.uploadForm
= new OO
.ui
.FormLayout( { items
: [ fieldset
] } );
146 this.selectFileWidget
.on( 'change', this.onUploadFormChange
.bind( this ) );
147 this.ownWorkCheckbox
.on( 'change', this.onUploadFormChange
.bind( this ) );
149 this.selectFileWidget
.on( 'change', function () {
150 var file
= layout
.getFile();
152 // Set the date to lastModified once we have the file
153 if ( layout
.getDateFromLastModified( file
) !== undefined ) {
154 layout
.dateWidget
.setValue( layout
.getDateFromLastModified( file
) );
157 // Check if we have EXIF data and set to that where available
158 layout
.getDateFromExif( file
).done( function ( date
) {
159 layout
.dateWidget
.setValue( date
);
162 layout
.updateFilePreview();
165 return this.uploadForm
;
171 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.onUploadFormChange = function () {
172 var file
= this.selectFileWidget
.getValue(),
173 ownWork
= this.ownWorkCheckbox
.isSelected(),
174 valid
= !!file
&& ownWork
;
175 this.emit( 'uploadValid', valid
);
181 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.renderInfoForm = function () {
184 this.filePreview
= new OO
.ui
.Widget( {
185 classes
: [ 'mw-upload-bookletLayout-filePreview' ]
187 this.progressBarWidget
= new OO
.ui
.ProgressBarWidget( {
190 this.filePreview
.$element
.append( this.progressBarWidget
.$element
);
192 this.filenameWidget
= new OO
.ui
.TextInputWidget( {
196 this.descriptionWidget
= new OO
.ui
.TextInputWidget( {
202 this.categoriesWidget
= new mw
.widgets
.CategorySelector( {
203 // Can't be done here because we don't know the target wiki yet... done in #initialize.
204 // api: new mw.ForeignApi( ... ),
205 $overlay
: this.$overlay
207 this.dateWidget
= new mw
.widgets
.DateInputWidget( {
208 $overlay
: this.$overlay
,
210 mustBeBefore
: moment().add( 1, 'day' ).locale( 'en' ).format( 'YYYY-MM-DD' ) // Tomorrow
213 fieldset
= new OO
.ui
.FieldsetLayout( {
214 label
: mw
.msg( 'upload-form-label-infoform-title' )
217 new OO
.ui
.FieldLayout( this.filenameWidget
, {
218 label
: mw
.msg( 'upload-form-label-infoform-name' ),
220 classes
: [ 'mw-foreignStructuredUploa-bookletLayout-small-notice' ],
221 notices
: [ mw
.msg( 'upload-form-label-infoform-name-tooltip' ) ]
223 new OO
.ui
.FieldLayout( this.descriptionWidget
, {
224 label
: mw
.msg( 'upload-form-label-infoform-description' ),
226 classes
: [ 'mw-foreignStructuredUploa-bookletLayout-small-notice' ],
227 notices
: [ mw
.msg( 'upload-form-label-infoform-description-tooltip' ) ]
229 new OO
.ui
.FieldLayout( this.categoriesWidget
, {
230 label
: mw
.msg( 'foreign-structured-upload-form-label-infoform-categories' ),
233 new OO
.ui
.FieldLayout( this.dateWidget
, {
234 label
: mw
.msg( 'foreign-structured-upload-form-label-infoform-date' ),
238 this.infoForm
= new OO
.ui
.FormLayout( {
239 classes
: [ 'mw-upload-bookletLayout-infoForm' ],
240 items
: [ this.filePreview
, fieldset
]
244 this.filenameWidget
.on( 'change', this.onInfoFormChange
.bind( this ) );
245 this.descriptionWidget
.on( 'change', this.onInfoFormChange
.bind( this ) );
246 this.dateWidget
.on( 'change', this.onInfoFormChange
.bind( this ) );
248 this.on( 'fileUploadProgress', function ( progress
) {
249 this.progressBarWidget
.setProgress( progress
* 100 );
252 return this.infoForm
;
258 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.onInfoFormChange = function () {
261 this.filenameWidget
.getValidity(),
262 this.descriptionWidget
.getValidity(),
263 this.dateWidget
.getValidity()
264 ).done( function () {
265 layout
.emit( 'infoValid', true );
266 } ).fail( function () {
267 layout
.emit( 'infoValid', false );
276 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.getText = function () {
277 var language
= mw
.config
.get( 'wgContentLanguage' );
278 this.upload
.clearDescriptions();
279 this.upload
.addDescription( language
, this.descriptionWidget
.getValue() );
280 this.upload
.setDate( this.dateWidget
.getValue() );
281 this.upload
.clearCategories();
282 this.upload
.addCategories( this.categoriesWidget
.getItemsData() );
283 return this.upload
.getText();
287 * Get original date from EXIF data
289 * @param {Object} file
290 * @return {jQuery.Promise} Promise resolved with the EXIF date
292 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.getDateFromExif = function ( file
) {
294 deferred
= $.Deferred();
296 if ( file
&& file
.type
=== 'image/jpeg' ) {
297 fileReader
= new FileReader();
298 fileReader
.onload = function () {
299 var fileStr
, arr
, i
, metadata
;
301 if ( typeof fileReader
.result
=== 'string' ) {
302 fileStr
= fileReader
.result
;
304 // Array buffer; convert to binary string for the library.
305 arr
= new Uint8Array( fileReader
.result
);
307 for ( i
= 0; i
< arr
.byteLength
; i
++ ) {
308 fileStr
+= String
.fromCharCode( arr
[ i
] );
313 metadata
= mw
.libs
.jpegmeta( this.result
, file
.name
);
318 if ( metadata
!== null && metadata
.exif
!== undefined && metadata
.exif
.DateTimeOriginal
) {
319 deferred
.resolve( moment( metadata
.exif
.DateTimeOriginal
, 'YYYY:MM:DD' ).format( 'YYYY-MM-DD' ) );
325 if ( 'readAsBinaryString' in fileReader
) {
326 fileReader
.readAsBinaryString( file
);
327 } else if ( 'readAsArrayBuffer' in fileReader
) {
328 fileReader
.readAsArrayBuffer( file
);
330 // We should never get here
332 throw new Error( 'Cannot read thumbnail as binary string or array buffer.' );
336 return deferred
.promise();
340 * Get last modified date from file
342 * @param {Object} file
343 * @return {Object} Last modified date from file
345 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.getDateFromLastModified = function ( file
) {
346 if ( file
&& file
.lastModified
) {
347 return moment( file
.lastModified
).format( 'YYYY-MM-DD' );
356 mw
.ForeignStructuredUpload
.BookletLayout
.prototype.clear = function () {
357 mw
.ForeignStructuredUpload
.BookletLayout
.parent
.prototype.clear
.call( this );
359 this.ownWorkCheckbox
.setSelected( false );
360 this.categoriesWidget
.setItemsFromData( [] );
361 this.dateWidget
.setValue( '' ).setValidityFlag( true );
364 }( jQuery
, mediaWiki
) );