jenkins-bot has submitted this change and it was merged.

Change subject: Load photo uploader dynamically
......................................................................


Load photo uploader dynamically

Refactor the code in the process (split the button and "controller"
code) and rename/move some of the templates so that it's clear where
they belong.

Initial page load (stable, desktop Chrome) before: 320KB, after: 303KB.

Bug: 48718
Change-Id: I1b167dd27ba0488f99cbd3a29287d8af95011647
---
M includes/Resources.php
M javascripts/modules/uploads/LeadPhoto.js
M javascripts/modules/uploads/LearnMoreOverlay.js
M javascripts/modules/uploads/NagOverlay.js
M javascripts/modules/uploads/PhotoUploadProgress.js
M javascripts/modules/uploads/PhotoUploader.js
A javascripts/modules/uploads/PhotoUploaderButton.js
M javascripts/modules/uploads/PhotoUploaderPreview.js
R templates/uploads/LeadPhoto.html
R templates/uploads/LeadPhotoUploaderButton.html
R templates/uploads/LearnMoreOverlay.html
R templates/uploads/NagOverlay.html
R templates/uploads/PhotoUploadPreview.html
R templates/uploads/PhotoUploadProgress.html
R templates/uploads/PhotoUploaderButton.html
15 files changed, 294 insertions(+), 237 deletions(-)

Approvals:
  JGonera: Looks good to me, approved
  Jdlrobson: Looks good to me, but someone else must approve
  jenkins-bot: Verified



diff --git a/includes/Resources.php b/includes/Resources.php
index d86f3c3..82ae251 100644
--- a/includes/Resources.php
+++ b/includes/Resources.php
@@ -156,20 +156,6 @@
 
        'mobile.stable.plumbing' => array(
                'messages' => array(
-                       // NagOverlay.js
-                       'mobile-frontend-photo-license' => array( 'parse' ),
-                       'mobile-frontend-photo-nag-1-bullet-1-heading',
-                       'mobile-frontend-photo-nag-1-bullet-1-text' => array( 
'parse' ),
-                       'mobile-frontend-photo-nag-1-bullet-2-heading',
-                       'mobile-frontend-photo-nag-1-bullet-2-text',
-                       'mobile-frontend-photo-nag-2-bullet-1-heading',
-                       'mobile-frontend-photo-nag-3-bullet-1-heading',
-                       'parentheses',
-                       'mobile-frontend-learn-more',
-                       'mobile-frontend-photo-nag-learn-more-heading',
-                       'mobile-frontend-photo-nag-learn-more-1' => array( 
'parse' ),
-                       'mobile-frontend-photo-nag-learn-more-2' => array( 
'parse' ),
-                       'mobile-frontend-photo-nag-learn-more-3' => array( 
'parse' ),
                        // page.js
                        'mobile-frontend-talk-overlay-header',
                        'mobile-frontend-language-article-heading',
@@ -189,27 +175,21 @@
                        'wikitext/commons-upload',
                        // LanguageOverlay.js
                        'overlays/languages',
-                       // leadphoto.js
-                       'leadPhoto',
                        'overlay',
                        'overlays/cleanup',
-                       'overlays/learnMore',
                        // search-2.js
                        'articleList',
                        'overlays/search/search',
                        // page.js
                        'page',
                        'languageSection',
-                       // PhotoUploader.js
+                       // PhotoUploaderButton.js
                        // For new page action menu
-                       'photoUploadAction',
-                       'photoUploader',
-                       // PhotoUploaderPreview.js
-                       'photoUploadPreview',
-                       // PhotoUploadProgress.js
-                       'photoUploadProgress',
-                       // NagOverlay.js
-                       'photoNag',
+                       'uploads/LeadPhotoUploaderButton',
+                       // FIXME: this should be in special.uploads.plumbing 
(need to split
+                       // code in PhotoUploaderButton.js into separate files 
too)
+                       'uploads/PhotoUploaderButton',
+
                        'ctaDrawer',
                        // mf-references.js
                        'ReferencesDrawer',
@@ -254,6 +234,80 @@
                        'mobile-frontend-editor-error-loading',
                        'mobile-frontend-editor-preview-header',
                        'mobile-frontend-editor-error-preview',
+               ),
+       ),
+
+       'mobile.uploads' => $wgMFMobileResourceBoilerplate + array(
+               'dependencies' => array(
+                       'mobile.stable',
+                       'mobile.uploads.plumbing',
+               ),
+               'scripts' => array(
+                       'javascripts/modules/uploads/LearnMoreOverlay.js',
+                       'javascripts/modules/uploads/PhotoApi.js',
+                       'javascripts/modules/uploads/NagOverlay.js',
+                       'javascripts/modules/uploads/PhotoUploadProgress.js',
+                       'javascripts/modules/uploads/PhotoUploaderPreview.js',
+                       'javascripts/modules/uploads/LeadPhoto.js',
+                       'javascripts/modules/uploads/PhotoUploader.js',
+               ),
+       ),
+
+       'mobile.uploads.plumbing' => array(
+               'class' => 'MFResourceLoaderModule',
+               'localBasePath' => $localBasePath,
+               'localTemplateBasePath' => $localBasePath . '/templates',
+               'templates' => array(
+                       'uploads/PhotoUploadPreview',
+                       'uploads/PhotoUploadProgress',
+                       'uploads/NagOverlay',
+                       'uploads/LearnMoreOverlay',
+                       'uploads/LeadPhoto',
+               ),
+               'messages' => array(
+                       'mobile-frontend-photo-upload-success-article',
+                       'mobile-frontend-photo-upload-error',
+
+                       // LearnMoreOverlay.js
+                       'mobile-frontend-photo-ownership-confirm',
+
+                       // PhotoApi.js
+                       'mobile-frontend-photo-article-edit-comment',
+                       'mobile-frontend-photo-article-donate-comment',
+                       'mobile-frontend-photo-upload-error-filename',
+                       'mobile-frontend-photo-upload-comment',
+
+                       // PhotoUploaderPreview.js
+                       'mobile-frontend-photo-ownership',
+                       'mobile-frontend-photo-ownership-help',
+                       'mobile-frontend-photo-caption-placeholder',
+                       'mobile-frontend-image-loading',
+                       'mobile-frontend-photo-submit',
+                       'mobile-frontend-photo-cancel',
+                       'mobile-frontend-photo-ownership-bullet-one',
+                       'mobile-frontend-photo-ownership-bullet-two',
+                       'mobile-frontend-photo-ownership-bullet-three',
+                       'mobile-frontend-photo-upload-error-file-type',
+
+                       // PhotoUploadProgress.js
+                       'mobile-frontend-image-uploading-wait',
+                       'mobile-frontend-image-uploading-long',
+                       'mobile-frontend-image-uploading-cancel',
+
+                       // NagOverlay.js
+                       'mobile-frontend-photo-license' => array( 'parse' ),
+                       'mobile-frontend-photo-nag-1-bullet-1-heading',
+                       'mobile-frontend-photo-nag-1-bullet-1-text' => array( 
'parse' ),
+                       'mobile-frontend-photo-nag-1-bullet-2-heading',
+                       'mobile-frontend-photo-nag-1-bullet-2-text',
+                       'mobile-frontend-photo-nag-2-bullet-1-heading',
+                       'mobile-frontend-photo-nag-3-bullet-1-heading',
+                       'parentheses',
+                       'mobile-frontend-learn-more',
+                       'mobile-frontend-photo-nag-learn-more-heading',
+                       'mobile-frontend-photo-nag-learn-more-1' => array( 
'parse' ),
+                       'mobile-frontend-photo-nag-learn-more-2' => array( 
'parse' ),
+                       'mobile-frontend-photo-nag-learn-more-3' => array( 
'parse' ),
                ),
        ),
 
@@ -432,13 +486,7 @@
                        'javascripts/common/notification.js',
                        'javascripts/views/page.js',
                        // Upload specific code
-                       'javascripts/modules/uploads/LearnMoreOverlay.js',
-                       'javascripts/modules/uploads/PhotoApi.js',
-                       'javascripts/modules/uploads/NagOverlay.js',
-                       'javascripts/modules/uploads/PhotoUploadProgress.js',
-                       'javascripts/modules/uploads/PhotoUploaderPreview.js',
-                       'javascripts/modules/uploads/LeadPhoto.js',
-                       'javascripts/modules/uploads/PhotoUploader.js',
+                       'javascripts/modules/uploads/PhotoUploaderButton.js',
                        // Language specific code
                        'javascripts/common/languages/LanguageOverlay.js',
                ),
@@ -449,36 +497,11 @@
                        'mobile-frontend-drawer-cancel',
                        'mobile-frontend-overlay-escape',
 
-                       // LearnMoreOverlay.js, newbie.js
-                       'mobile-frontend-photo-ownership-confirm',
-                       'cancel',
-
-                       // PhotoApi.js
-                       'mobile-frontend-photo-article-edit-comment',
-                       'mobile-frontend-photo-article-donate-comment',
-                       'mobile-frontend-photo-upload-error-filename',
-                       'mobile-frontend-photo-upload-comment',
-
-                       // PhotoUploader.js
-                       'mobile-frontend-photo-upload-error',
+                       // PhotoUploaderButton.js
                        'mobile-frontend-photo-upload-cta',
 
-                       // PhotoUploaderPreview.js
-                       'mobile-frontend-photo-ownership',
-                       'mobile-frontend-photo-ownership-help',
-                       'mobile-frontend-photo-caption-placeholder',
-                       'mobile-frontend-image-loading',
-                       'mobile-frontend-photo-submit',
-                       'mobile-frontend-photo-cancel',
-                       'mobile-frontend-photo-ownership-bullet-one',
-                       'mobile-frontend-photo-ownership-bullet-two',
-                       'mobile-frontend-photo-ownership-bullet-three',
-                       'mobile-frontend-photo-upload-error-file-type',
-
-                       // PhotoUploadProgress.js
-                       'mobile-frontend-image-uploading-wait',
-                       'mobile-frontend-image-uploading-long',
-                       'mobile-frontend-image-uploading-cancel',
+                       // LearnMoreOverlay.js, newbie.js
+                       'cancel',
 
                        // LanguageOverlay.js
                        'mobile-frontend-language-header',
@@ -530,7 +553,6 @@
                        'mobile-frontend-photo-upload-protected',
                        'mobile-frontend-photo-upload-anon',
                        'mobile-frontend-photo-upload-unavailable',
-                       'mobile-frontend-photo-upload-success-article',
                        'mobile-frontend-photo-upload',
 
                        // mf-watchstar.js
@@ -560,6 +582,8 @@
 
        /**
                * Special page modules
+               * FIXME: Remove the need for these by making more reusable CSS
+               * FIXME: Rename these modules in the interim to clarify that 
they are modules for use on special pages
                *
                * Note: Use correct names to ensure modules load on pages
                * Name must be the name of the special page lowercased prefixed 
by 'mobile.'
@@ -681,7 +705,7 @@
        ),
 
        // Special:Uploads
-       'mobile.uploads.plumbing' => $wgMFMobileResourceTemplateBoilerplate + 
array(
+       'mobile.special.uploads.plumbing' => 
$wgMFMobileResourceTemplateBoilerplate + array(
                'templates' => array(
                        'specials/uploads/carousel',
                        'specials/uploads/photo',
@@ -690,9 +714,10 @@
        ),
        'mobile.uploads.scripts' => $wgMFMobileResourceBoilerplate + array(
                'dependencies' => array(
-                       'mobile.uploads.plumbing',
+                       'mobile.special.uploads.plumbing',
                        'mobile.stable.styles',
                        'mobile.stable.common',
+                       'mobile.uploads',
                ),
                'messages' => array(
                        'mobile-frontend-photo-upload-generic',
diff --git a/javascripts/modules/uploads/LeadPhoto.js 
b/javascripts/modules/uploads/LeadPhoto.js
index a7fe59a..2aed30c 100644
--- a/javascripts/modules/uploads/LeadPhoto.js
+++ b/javascripts/modules/uploads/LeadPhoto.js
@@ -3,7 +3,7 @@
        var View = M.require( 'view' ), LeadPhoto;
 
        LeadPhoto = View.extend( {
-               template: M.template.get( 'leadPhoto' ),
+               template: M.template.get( 'uploads/LeadPhoto' ),
 
                animate: function() {
                        this.$el.hide().slideDown();
diff --git a/javascripts/modules/uploads/LearnMoreOverlay.js 
b/javascripts/modules/uploads/LearnMoreOverlay.js
index c843643..9a82fc2 100644
--- a/javascripts/modules/uploads/LearnMoreOverlay.js
+++ b/javascripts/modules/uploads/LearnMoreOverlay.js
@@ -4,7 +4,7 @@
                defaults: {
                        confirmMessage: mw.msg( 
'mobile-frontend-photo-ownership-confirm' )
                },
-               template: M.template.get( 'overlays/learnMore' )
+               template: M.template.get( 'uploads/LearnMoreOverlay' )
        } );
 
        M.define( 'modules/uploads/LearnMoreOverlay', LearnMoreOverlay );
diff --git a/javascripts/modules/uploads/NagOverlay.js 
b/javascripts/modules/uploads/NagOverlay.js
index a2511f3..f44dfcb 100644
--- a/javascripts/modules/uploads/NagOverlay.js
+++ b/javascripts/modules/uploads/NagOverlay.js
@@ -8,7 +8,7 @@
                        learnMore: mw.msg( 'parentheses', mw.msg( 
'mobile-frontend-learn-more' ) )
                },
 
-               template: M.template.get( 'photoNag' ),
+               template: M.template.get( 'uploads/NagOverlay' ),
 
                initialize: function( options ) {
                        this.learnMoreOverlay = new LearnMoreOverlay( {
diff --git a/javascripts/modules/uploads/PhotoUploadProgress.js 
b/javascripts/modules/uploads/PhotoUploadProgress.js
index ff2bd14..0b853cc 100644
--- a/javascripts/modules/uploads/PhotoUploadProgress.js
+++ b/javascripts/modules/uploads/PhotoUploadProgress.js
@@ -10,7 +10,7 @@
                        messageInterval: 10000
                },
 
-               template: M.template.get( 'photoUploadProgress' ),
+               template: M.template.get( 'uploads/PhotoUploadProgress' ),
                className: 'drawer position-fixed text loading',
                locked: true,
 
diff --git a/javascripts/modules/uploads/PhotoUploader.js 
b/javascripts/modules/uploads/PhotoUploader.js
index bf8a7a0..48d2e72 100644
--- a/javascripts/modules/uploads/PhotoUploader.js
+++ b/javascripts/modules/uploads/PhotoUploader.js
@@ -1,30 +1,11 @@
 ( function( M, $ ) {
-       var View = M.require( 'view' ),
+       var Class = M.require( 'Class' ),
                popup = M.require( 'notifications' ),
-               CtaDrawer = M.require( 'CtaDrawer' ),
                NagOverlay = M.require( 'modules/uploads/NagOverlay' ),
                PhotoApi = M.require( 'modules/uploads/PhotoApi' ),
                PhotoUploadProgress = M.require( 
'modules/uploads/PhotoUploadProgress' ),
                PhotoUploaderPreview = M.require( 
'modules/uploads/PhotoUploaderPreview' ),
-               PhotoUploader,
-               PhotoUploaderButton,
-               LeadPhoto = M.require( 'modules/uploads/LeadPhoto' ),
-               LeadPhotoUploaderButton;
-
-       function isSupported() {
-               // FIXME: create a module for browser detection stuff
-               // deal with known false positives which don't support file 
input (bug 47374)
-               if ( navigator.userAgent.match( /Windows Phone (OS 7|8.0)/ ) ) {
-                       return false;
-               }
-               var browserSupported = (
-                       typeof FileReader !== 'undefined' &&
-                       typeof FormData !== 'undefined' &&
-                       ($('<input type="file"/>').prop('type') === 'file') // 
Firefox OS 1.0 turns <input type="file"> into <input type="text">
-               );
-
-               return browserSupported && !mw.config.get( 'wgImagesDisabled', 
false );
-       }
+               PhotoUploader;
 
        function getLog( funnel ) {
                return function( data ) {
@@ -43,119 +24,39 @@
                };
        }
 
-       /**
-        * PhotoUploader is a component for uploading images to the wiki.
-        *
-        * @example
-        * <code>
-        * var photoUploader = new PhotoUploader( {
-        *     buttonCaption: 'Add a photo',
-        * } );
-        * photoUploader.
-        *     insertAfter( 'h1' ).
-        *     on( 'upload', function( fileName, url ) {
-        *         $( '.someImage' ).attr( 'src', url );
-        *     } );
-        * </code>
-        *
-        * @constructor
-        * @param {Object} options Uploader options.
-        * @param {string} options.buttonCaption Caption for the upload button.
-        * @param {boolean} options.insertInPage If the image should be 
prepended
-        * to the wikitext of a page specified by options.pageTitle.
-        * @param {string} options.pageTitle Title of the page to which the 
image
-        * belongs (image name will be based on this) and to which it should be
-        * prepended (if options.insertInPage is true).
-        * @fires PhotoUploader#start
-        * @fires PhotoUploader#success
-        * @fires PhotoUploader#error
-        * @fires PhotoUploader#cancel
-        */
-       /**
-        * Triggered when image upload starts.
-        *
-        * @event PhotoUploader#start
-        */
-       /**
-        * Triggered when image upload is finished successfully.
-        *
-        * @event PhotoUploader#success
-        * @property {Object} data Uploaded image data.
-        * @property {string} data.fileName Name of the uploaded image.
-        * @property {string} data.description Name of the uploaded image.
-        * @property {string} data.url URL to the uploaded image (can be a
-        * local data URL).
-        */
-       /**
-        * Triggered when image upload fails.
-        *
-        * @event PhotoUploader#error
-        */
-       /**
-        * Triggered when image upload is cancelled.
-        *
-        * @event PhotoUploader#cancel
-        */
-       PhotoUploader = View.extend( {
+       PhotoUploader = Class.extend( {
 
                initialize: function( options ) {
+                       var nagCount = parseInt( M.settings.getUserSetting( 
'uploadNagCount' ) || 0, 10 ),
+                               shouldNag = parseInt( mw.config.get( 
'wgUserEditCount' ), 10 ) < 3,
+                               fileReader = new FileReader(), nagOverlay, 
preview;
+
                        this.log = getLog( options.funnel );
-                       this._super( options );
-               },
+                       preview = this.preview = new PhotoUploaderPreview( { 
log: this.log } );
 
-               postRender: function() {
-                       var self = this, $input = this.$( 'input' ), ctaDrawer;
+                       this.options = options;
+                       this.parent = options.parent;
+                       this.file = options.file;
 
-                       // show CTA instead if not logged in
-                       if ( !M.isLoggedIn() ) {
-                               ctaDrawer = new CtaDrawer( {
-                                       content: mw.msg( 
'mobile-frontend-photo-upload-cta' ),
-                                       queryParams: {
-                                               campaign: 
'mobile_uploadPageActionCta',
-                                               returntoquery: 
'article_action=photo-upload'
-                                       }
-                               } );
-                               this.$el.click( function( ev ) {
-                                       ctaDrawer.show();
-                                       ev.preventDefault();
-                               } );
-                               return;
+                       // nag if never nagged and shouldNag and then keep 
nagging (3 times)
+                       if ( ( nagCount === 0 && shouldNag ) || ( nagCount > 0 
&& nagCount < 3 ) ) {
+                               // FIXME: possibly set self.preview.parent = 
nagOverlay when nagOverlay is present
+                               nagOverlay = this._showNagOverlay( nagCount );
+                       } else {
+                               this._showPreview();
                        }
 
-                       $input.
-                               // accept must be set via attr otherwise cannot 
use camera on Android
-                               attr( 'accept', 'image/*;' ).
-                               on( 'change', function() {
-                                       var nagCount = parseInt( 
M.settings.getUserSetting( 'uploadNagCount' ) || 0, 10 ),
-                                               shouldNag = parseInt( 
mw.config.get( 'wgUserEditCount' ), 10 ) < 3,
-                                               fileReader = new FileReader(), 
nagOverlay;
+                       fileReader.readAsDataURL( this.file );
+                       fileReader.onload = function() {
+                               var dataUrl = fileReader.result;
+                               // add mimetype if not present (some browsers 
need it, e.g. Android browser)
+                               dataUrl = dataUrl.replace( /^data:base64/, 
'data:image/jpeg;base64' );
 
-                                       self.preview = new 
PhotoUploaderPreview( { log: self.log } );
-
-                                       self.file = $input[0].files[0];
-                                       // clear so that change event is fired 
again when user selects the same file
-                                       $input.val( '' );
-
-                                       // nag if never nagged and shouldNag 
and then keep nagging (3 times)
-                                       if ( ( nagCount === 0 && shouldNag ) || 
( nagCount > 0 && nagCount < 3 ) ) {
-                                               // FIXME: possibly set 
self.preview.parent = nagOverlay when nagOverlay is present
-                                               nagOverlay = 
self._showNagOverlay( nagCount );
-                                       } else {
-                                               self._showPreview();
-                                       }
-
-                                       fileReader.readAsDataURL( self.file );
-                                       fileReader.onload = function() {
-                                               var dataUrl = fileReader.result;
-                                               // add mimetype if not present 
(some browsers need it, e.g. Android browser)
-                                               dataUrl = dataUrl.replace( 
/^data:base64/, 'data:image/jpeg;base64' );
-
-                                               if ( nagOverlay ) {
-                                                       nagOverlay.setImageUrl( 
dataUrl );
-                                               }
-                                               self.preview.setImageUrl( 
dataUrl );
-                                       };
-                               } );
+                               if ( nagOverlay ) {
+                                       nagOverlay.setImageUrl( dataUrl );
+                               }
+                               preview.setImageUrl( dataUrl );
+                       };
                },
 
                _showNagOverlay: function( nagCount ) {
@@ -217,13 +118,13 @@
                                api = new PhotoApi(),
                                progressPopup = new PhotoUploadProgress();
 
-                       this.emit( 'start' );
+                       this.parent.emit( 'start' );
                        this.preview.hide();
                        progressPopup.show();
                        progressPopup.on( 'cancel', function() {
                                api.abort();
                                self.log( { action: 'cancel' } );
-                               self.emit( 'cancel' );
+                               self.parent.emit( 'cancel' );
                        } );
 
                        api.save( {
@@ -234,7 +135,7 @@
                        } ).done( function( fileName, descriptionUrl ) {
                                progressPopup.hide();
                                self.log( { action: 'success' } );
-                               self.emit( 'success', {
+                               self.parent.emit( 'success', {
                                        fileName: fileName,
                                        description: description,
                                        descriptionUrl: descriptionUrl,
@@ -244,7 +145,7 @@
                                progressPopup.hide();
                                popup.show( msg || mw.msg( 
'mobile-frontend-photo-upload-error' ), 'toast error' );
                                self.log( { action: 'error', errorText: err } );
-                               self.emit( 'error' );
+                               self.parent.emit( 'error' );
                        } );
 
                        api.on( 'uploadProgress', function( value ) {
@@ -253,43 +154,6 @@
                }
        } );
 
-       // An extension of the PhotoUploader which instead of treating it as a 
button treats it as a full screen bar
-       PhotoUploaderButton = PhotoUploader.extend( {
-               template: M.template.get( 'photoUploader' ),
-               className: 'button photo'
-       } );
-
-       LeadPhotoUploaderButton = PhotoUploader.extend( {
-               template: M.template.get( 'photoUploadAction' ),
-               className: 'enabled',
-               initialize: function( options ) {
-                       var self = this;
-                       this._super( options );
-                       this.on( 'start', function() {
-                                       self.$el.removeClass( 'enabled' );
-                               } ).
-                               on( 'success', function( data ) {
-                                       popup.show( mw.msg( 
'mobile-frontend-photo-upload-success-article' ), 'toast' );
-                                       // FIXME: workaround for 
https://bugzilla.wikimedia.org/show_bug.cgi?id=43271
-                                       if ( !$( '#content_0' ).length ) {
-                                               $( '<div id="content_0" >' 
).insertAfter( $( '#section_0,#page-actions' ).last() );
-                                       }
-                                       new LeadPhoto( {
-                                               url: data.url,
-                                               pageUrl: data.descriptionUrl,
-                                               caption: data.description
-                                       } ).prependTo( '#content_0' );
-                               } ).
-                               on( 'error cancel', function() {
-                                       self.$el.addClass( 'enabled' );
-                               } );
-               }
-       } );
-
-       PhotoUploaderButton.isSupported = LeadPhotoUploaderButton.isSupported = 
isSupported();
-
-       // FIXME: should we allow more than one define() per file?
-       M.define( 'modules/uploads/PhotoUploaderButton', PhotoUploaderButton );
-       M.define( 'modules/uploads/LeadPhotoUploaderButton', 
LeadPhotoUploaderButton );
+       M.define( 'modules/uploads/PhotoUploader', PhotoUploader );
 
 }( mw.mobileFrontend, jQuery ) );
diff --git a/javascripts/modules/uploads/PhotoUploaderButton.js 
b/javascripts/modules/uploads/PhotoUploaderButton.js
new file mode 100644
index 0000000..5d289c6
--- /dev/null
+++ b/javascripts/modules/uploads/PhotoUploaderButton.js
@@ -0,0 +1,168 @@
+( function( M, $ ) {
+       var View = M.require( 'view' ),
+               popup = M.require( 'notifications' ),
+               CtaDrawer = M.require( 'CtaDrawer' ),
+               LoadingOverlay = M.require( 'LoadingOverlay' ),
+               PhotoUploaderButton,
+               LeadPhotoUploaderButton;
+
+       function isSupported() {
+               // FIXME: create a module for browser detection stuff
+               // deal with known false positives which don't support file 
input (bug 47374)
+               if ( navigator.userAgent.match( /Windows Phone (OS 7|8.0)/ ) ) {
+                       return false;
+               }
+               var browserSupported = (
+                       typeof FileReader !== 'undefined' &&
+                       typeof FormData !== 'undefined' &&
+                       ($('<input type="file"/>').prop('type') === 'file') // 
Firefox OS 1.0 turns <input type="file"> into <input type="text">
+               );
+
+               return browserSupported && !mw.config.get( 'wgImagesDisabled', 
false );
+       }
+
+       /**
+        * PhotoUploaderButton is a component for uploading images to the wiki.
+        *
+        * @example
+        * <code>
+        * var photoUploaderButton = new PhotoUploaderButton( {
+        *     buttonCaption: 'Add a photo',
+        * } );
+        * photoUploaderButton.
+        *     insertAfter( 'h1' ).
+        *     on( 'upload', function( fileName, url ) {
+        *         $( '.someImage' ).attr( 'src', url );
+        *     } );
+        * </code>
+        *
+        * @constructor
+        * @param {Object} options Uploader options.
+        * @param {string} options.buttonCaption Caption for the upload button.
+        * @param {boolean} options.insertInPage If the image should be 
prepended
+        * to the wikitext of a page specified by options.pageTitle.
+        * @param {string} options.pageTitle Title of the page to which the 
image
+        * belongs (image name will be based on this) and to which it should be
+        * prepended (if options.insertInPage is true).
+        * @param {string} options.funnel Funnel for EventLogging.
+        * @fires PhotoUploader#start
+        * @fires PhotoUploader#success
+        * @fires PhotoUploader#error
+        * @fires PhotoUploader#cancel
+        */
+       /**
+        * Triggered when image upload starts.
+        *
+        * @event PhotoUploader#start
+        */
+       /**
+        * Triggered when image upload is finished successfully.
+        *
+        * @event PhotoUploader#success
+        * @property {Object} data Uploaded image data.
+        * @property {string} data.fileName Name of the uploaded image.
+        * @property {string} data.description Name of the uploaded image.
+        * @property {string} data.url URL to the uploaded image (can be a
+        * local data URL).
+        */
+       /**
+        * Triggered when image upload fails.
+        *
+        * @event PhotoUploader#error
+        */
+       /**
+        * Triggered when image upload is cancelled.
+        *
+        * @event PhotoUploader#cancel
+        */
+       PhotoUploaderButton = View.extend( {
+               template: M.template.get( 'uploads/PhotoUploaderButton' ),
+               className: 'button photo',
+
+               initialize: function( options ) {
+                       this._super( options );
+               },
+
+               postRender: function() {
+                       var self = this, $input = this.$( 'input' ), ctaDrawer;
+
+                       // show CTA instead if not logged in
+                       if ( !M.isLoggedIn() ) {
+                               ctaDrawer = new CtaDrawer( {
+                                       content: mw.msg( 
'mobile-frontend-photo-upload-cta' ),
+                                       queryParams: {
+                                               campaign: 
'mobile_uploadPageActionCta',
+                                               returntoquery: 
'article_action=photo-upload'
+                                       }
+                               } );
+                               this.$el.click( function( ev ) {
+                                       ctaDrawer.show();
+                                       ev.preventDefault();
+                               } );
+                               return;
+                       }
+
+                       $input.
+                               // accept must be set via attr otherwise cannot 
use camera on Android
+                               attr( 'accept', 'image/*;' ).
+                               on( 'change', function() {
+                                       var options = $.extend( {}, 
self.options, {
+                                               file: $input[0].files[0],
+                                               parent: self
+                                       } ),
+                                               loadingOverlay = new 
LoadingOverlay();
+
+                                       loadingOverlay.show();
+                                       mw.loader.using( 'mobile.uploads', 
function() {
+                                               var PhotoUploader = M.require( 
'modules/uploads/PhotoUploader' );
+                                               loadingOverlay.hide();
+                                               new PhotoUploader( options );
+                                       } );
+
+                                       // clear so that change event is fired 
again when user selects the same file
+                                       $input.val( '' );
+                               } );
+               }
+       } );
+
+       LeadPhotoUploaderButton = PhotoUploaderButton.extend( {
+               template: M.template.get( 'uploads/LeadPhotoUploaderButton' ),
+               className: 'enabled',
+
+               initialize: function( options ) {
+                       var self = this;
+                       this._super( options );
+                       this.on( 'start', function() {
+                                       self.$el.removeClass( 'enabled' );
+                               } ).
+                               on( 'success', function( data ) {
+                                       popup.show( mw.msg( 
'mobile-frontend-photo-upload-success-article' ), 'toast' );
+                                       // FIXME: workaround for 
https://bugzilla.wikimedia.org/show_bug.cgi?id=43271
+                                       if ( !$( '#content_0' ).length ) {
+                                               $( '<div id="content_0" >' 
).insertAfter( $( '#section_0,#page-actions' ).last() );
+                                       }
+
+                                       // just in case, LeadPhoto should be 
loaded by now anyway
+                                       mw.loader.using( 'mobile.uploads', 
function() {
+                                               var LeadPhoto = M.require( 
'modules/uploads/LeadPhoto' );
+
+                                               new LeadPhoto( {
+                                                       url: data.url,
+                                                       pageUrl: 
data.descriptionUrl,
+                                                       caption: 
data.description
+                                               } ).prependTo( '#content_0' );
+                                       } );
+                               } ).
+                               on( 'error cancel', function() {
+                                       self.$el.addClass( 'enabled' );
+                               } );
+               }
+       } );
+
+       PhotoUploaderButton.isSupported = LeadPhotoUploaderButton.isSupported = 
isSupported();
+
+       // FIXME: should we allow more than one define() per file?
+       M.define( 'modules/uploads/PhotoUploaderButton', PhotoUploaderButton );
+       M.define( 'modules/uploads/LeadPhotoUploaderButton', 
LeadPhotoUploaderButton );
+
+}( mw.mobileFrontend, jQuery ) );
diff --git a/javascripts/modules/uploads/PhotoUploaderPreview.js 
b/javascripts/modules/uploads/PhotoUploaderPreview.js
index 53c2abe..a548874 100644
--- a/javascripts/modules/uploads/PhotoUploaderPreview.js
+++ b/javascripts/modules/uploads/PhotoUploaderPreview.js
@@ -18,7 +18,7 @@
 
                className: 'mw-mf-overlay photo-overlay',
 
-               template: M.template.get( 'photoUploadPreview' ),
+               template: M.template.get( 'uploads/PhotoUploadPreview' ),
 
                initialize: function( options ) {
                        this.log = options.log;
diff --git a/templates/leadPhoto.html b/templates/uploads/LeadPhoto.html
similarity index 100%
rename from templates/leadPhoto.html
rename to templates/uploads/LeadPhoto.html
diff --git a/templates/photoUploadAction.html 
b/templates/uploads/LeadPhotoUploaderButton.html
similarity index 100%
rename from templates/photoUploadAction.html
rename to templates/uploads/LeadPhotoUploaderButton.html
diff --git a/templates/overlays/learnMore.html 
b/templates/uploads/LearnMoreOverlay.html
similarity index 100%
rename from templates/overlays/learnMore.html
rename to templates/uploads/LearnMoreOverlay.html
diff --git a/templates/photoNag.html b/templates/uploads/NagOverlay.html
similarity index 100%
rename from templates/photoNag.html
rename to templates/uploads/NagOverlay.html
diff --git a/templates/photoUploadPreview.html 
b/templates/uploads/PhotoUploadPreview.html
similarity index 100%
rename from templates/photoUploadPreview.html
rename to templates/uploads/PhotoUploadPreview.html
diff --git a/templates/photoUploadProgress.html 
b/templates/uploads/PhotoUploadProgress.html
similarity index 100%
rename from templates/photoUploadProgress.html
rename to templates/uploads/PhotoUploadProgress.html
diff --git a/templates/photoUploader.html 
b/templates/uploads/PhotoUploaderButton.html
similarity index 100%
rename from templates/photoUploader.html
rename to templates/uploads/PhotoUploaderButton.html

-- 
To view, visit https://gerrit.wikimedia.org/r/84032
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I1b167dd27ba0488f99cbd3a29287d8af95011647
Gerrit-PatchSet: 5
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: JGonera <jgon...@wikimedia.org>
Gerrit-Reviewer: JGonera <jgon...@wikimedia.org>
Gerrit-Reviewer: Jdlrobson <jrob...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to