Jdlrobson has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/54069


Change subject: Rewrite createOverlay to use an Overlay View that is stackable
......................................................................

Rewrite createOverlay to use an Overlay View that is stackable

This refactors overlays so that you can have more than one
open at a time. When closed they are removed from the stack
until you escape back to the page content.

PhotoUploaderPreview now creates an instance of Overlay
- this is probably not perfect but it keeps this patchset as
simplistic as possible.

Change-Id: I26dc25cf47a2a964e7353a05a0fb4400388b79bf
---
M MobileFrontend.php
M includes/MobileFrontend.hooks.php
M javascripts/common/mf-navigation.js
M javascripts/modules/mf-languages.js
M javascripts/modules/mf-photo.js
M less/common/mf-navigation.less
M less/modules/mf-tables.less
M less/specials/userlogin.less
M stylesheets/common/mf-navigation.css
M stylesheets/modules/mf-tables.css
M stylesheets/specials/userlogin.css
A templates/overlay.html
A tests/js/common/mf-navigation.js
M tests/js/fixtures-templates.js
14 files changed, 213 insertions(+), 114 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MobileFrontend 
refs/changes/69/54069/1

diff --git a/MobileFrontend.php b/MobileFrontend.php
index fa74116..0d956aa 100644
--- a/MobileFrontend.php
+++ b/MobileFrontend.php
@@ -215,6 +215,7 @@
        'templates' => array(
                'photoCopyrightDialog',
                'leadPhoto',
+               'overlay',
                'photoUploader',
                'photoUploadPreview',
                'ctaDrawer'
diff --git a/includes/MobileFrontend.hooks.php 
b/includes/MobileFrontend.hooks.php
index 8eb85ee..3533bb5 100644
--- a/includes/MobileFrontend.hooks.php
+++ b/includes/MobileFrontend.hooks.php
@@ -190,6 +190,7 @@
                                'javascripts/modules/mf-toggle-dynamic.js',
                                'javascripts/actions/mf-edit.js', 
'tests/js/test_mf-edit.js',
                                'javascripts/common/mf-navigation.js',
+                               'tests/js/common/mf-navigation.js',
                                'javascripts/common/mf-notification.js',
                                'javascripts/modules/mf-photo.js', 
'tests/js/test_mf-photo.js',
                                'javascripts/modules/mf-references.js', 
'tests/js/test_references.js',
diff --git a/javascripts/common/mf-navigation.js 
b/javascripts/common/mf-navigation.js
index 8a524f9..f0ce27a 100644
--- a/javascripts/common/mf-navigation.js
+++ b/javascripts/common/mf-navigation.js
@@ -5,6 +5,7 @@
                u = M.utils, mfePrefix = M.prefix,
                lastScrollTopPosition = 0,
                inBeta = mw.config.get( 'wgMFMode' ) === 'beta',
+               Overlay,
                Drawer, CtaDrawer;
 
        Drawer = View.extend( {
@@ -55,41 +56,90 @@
                template: M.template.get( 'ctaDrawer' )
        } );
 
-       function getOverlay() {
-               return document.getElementById( 'mw-mf-overlay' );
+       /**
+        * Returns any overlays that have been added to the DOM
+        *
+        * @return {jQuery.object} All open overlays
+        */
+       function getOverlays() {
+               return $( '.mw-mf-overlay' );
        }
 
-       function closeOverlay( ) {
-               $( 'html' ).removeClass( 'overlay' );
-               window.scrollTo( document.body.scrollLeft, 
lastScrollTopPosition );
-               M.history.replaceHash( '#' );
+       /**
+        * Hides all overlays except last added overlay
+        */
+       function showTopOverlay() {
+               $( '.mw-mf-overlay' ).hide().last().show();
+       }
+
+       /**
+        * Attempts to escape back to article from overlay mode
+        *
+        * @return {Boolean} Whether attempt succeeded
+        */
+       function escapeOverlayMode() {
+               // Make sure all open overlays are closed before returning to 
article
+               if ( getOverlays().length === 0 ) {
+                       $( 'html' ).removeClass( 'overlay' );
+                       window.scrollTo( document.body.scrollLeft, 
lastScrollTopPosition );
+                       M.history.replaceHash( '#' );
+                       return true;
+               } else {
+                       showTopOverlay();
+                       return false;
+               }
        }
 
        function showOverlay() {
+               showTopOverlay();
                lastScrollTopPosition = document.body.scrollTop;
-               $( 'html' ).addClass( 'overlay' );
-               window.scrollTo( 0, 0 ); // scroll right to top
-               $( 'html' ).removeClass( 'navigationEnabled' );
+               $( 'html' ).addClass( 'overlay' ).
+                       removeClass( 'navigationEnabled' );
+
+               // skip the URL bar if possible
+               window.scrollTo( 0, 1 );
        }
 
-       // FIXME: redo using view
+       Overlay = View.extend( {
+               defaults: {
+                       heading: '',
+                       content: '',
+                       closeMsg: mw.msg( 'mobile-frontend-overlay-escape' )
+               },
+               template: M.template.get( 'overlay' ),
+               initialize: function() {
+                       var self = this;
+                       this.$( '.close' ).click( function( ev ) {
+                               ev.preventDefault();
+                               self.close();
+                       } );
+               },
+               show: function() {
+                       showOverlay();
+               },
+               close: function() {
+                       this.$el.remove();
+                       escapeOverlayMode();
+               }
+       } );
+
+       /**
+        * Convenience function for creating an overlay
+        *
+        * @param {String|jQuery.Object} heading: Text or html to appear in 
header of overlay
+        * @param {String|jQuery.Object} contents: Html to appear in body of 
overlay
+        * @return {Overlay} View representing overlay
+        */
        function createOverlay( heading, contents, options ) {
                options = options || {};
-               var overlay = document.getElementById( mfePrefix + 'overlay' );
+               options.heading = typeof heading === 'string' ? '<h2>' + 
heading + '</h2>' :
+                       $( '<div>' ).append( heading ).html();
+               options.content = $( '<div>' ).append( contents ).html();
+               // FIXME: Set this as a default
+               options.el = $( '<div class="mw-mf-overlay">' ).appendTo( 
document.body )[ 0 ];
+               var overlay = new Overlay( options );
                M.history.pushState( options.hash || '#mw-mf-overlay' );
-               showOverlay();
-               $( overlay ).empty();
-               $( '<div class="header">' ).appendTo( '#' + mfePrefix + 
'overlay' );
-               $( '<button id="close"></button>' ).text( mw.msg( 
'mobile-frontend-overlay-escape' ) ).
-                       addClass( 'escapeOverlay' ).
-                       click( closeOverlay ).appendTo( '#' + mfePrefix + 
'overlay .header' );
-               if( typeof heading === 'string' ) {
-                       heading = $( '<h2 />' ).text( heading );
-               }
-               $( heading ).appendTo( '#' + mfePrefix + 'overlay .header' );
-               $( overlay ).append( contents );
-               $( overlay ).find( '.close' ).click( closeOverlay );
-
+               overlay.show();
                return overlay;
        }
 
@@ -137,7 +187,7 @@
 
                M.on( 'history-change', function( curPage ) {
                        if ( curPage.hash === '#' || curPage.hash === '' || 
curPage.hash === '#_' ) {
-                               closeOverlay();
+                               escapeOverlayMode();
                                closeNavigation();
                        } else if ( curPage.hash === '#mw-mf-page-left' ) {
                                toggleNavigation();
@@ -182,10 +232,9 @@
 
        return {
                CtaDrawer: CtaDrawer,
-               closeOverlay: closeOverlay,
+               Overlay: Overlay,
                createOverlay: createOverlay,
                getPageMenu: getPageMenu,
-               getOverlay: getOverlay,
                showOverlay: showOverlay
        };
 }( jQuery ));
diff --git a/javascripts/modules/mf-languages.js 
b/javascripts/modules/mf-languages.js
index a804d4a..7b85dbd 100644
--- a/javascripts/modules/mf-languages.js
+++ b/javascripts/modules/mf-languages.js
@@ -57,11 +57,11 @@
                        text( mw.msg( 'mobile-frontend-language-footer' ) 
).appendTo( $footer );
 
                overlay = createOverlay( $search, $wrapper, { hash: 
'#mw-mf-overlay-language' } );
-               $lists = $( overlay ).find( 'ul' );
+               $lists = overlay.$( 'ul' );
                $lists.find( 'a' ).on( 'click', function() {
                        M.emit( 'language-select', $( this ).attr( 'lang' ) );
                } );
-               $( overlay ).find( '.search' ).on( 'keyup', function() {
+               overlay.$( '.search' ).on( 'keyup', function() {
                        var matches = filterLists( $lists, 
this.value.toLowerCase() );
                        if ( matches > 0 ) {
                                $footer.hide();
diff --git a/javascripts/modules/mf-photo.js b/javascripts/modules/mf-photo.js
index 3dbcd96..3d34cb0 100644
--- a/javascripts/modules/mf-photo.js
+++ b/javascripts/modules/mf-photo.js
@@ -182,13 +182,17 @@
 
                initialize: function() {
                        var self = this,
-                               $description = this.$( 'textarea' ),
-                               $submitButton = this.$( 'button.submit' );
+                               $overlay, $description, $submitButton;
+
+                       this.overlay = nav.createOverlay( '',  this.$el );
+                       $overlay = this.overlay.$el;
+
+                       $description = $overlay.find( 'textarea' );
+                       $submitButton = $overlay.find( 'button.submit' );
                        this.$description = $description;
 
                        // make license links open in separate tabs
-                       this.$( '.license a' ).attr( 'target', '_blank' );
-
+                       $overlay.find( '.license a' ).attr( 'target', '_blank' 
);
                        // use input event too, Firefox doesn't fire keyup on 
many devices:
                        // https://bugzilla.mozilla.org/show_bug.cgi?id=737658
                        $description.on( 'keyup input', function() {
@@ -202,7 +206,7 @@
                        $submitButton.on( 'click', function() {
                                self.emit( 'submit' );
                        } );
-                       this.$( 'button.cancel' ).on( 'click', function() {
+                       $overlay.find( 'button.cancel' ).on( 'click', 
function() {
                                self.emit( 'cancel' );
                        } );
                },
@@ -214,6 +218,7 @@
                setImageUrl: function( url ) {
                        var msg = mw.msg( 'mobile-frontend-image-ownership',
                                mw.config.get( 'wgUserName' ) ),
+                               $overlay = this.overlay.$el,
                                data = {
                                        bulletPoints: [
                                                { text: mw.msg( 
'mobile-frontend-image-ownership-bullet-one' ) },
@@ -224,14 +229,14 @@
                                        leadText: msg
                                };
                        this.imageUrl = url;
-                       this.$( '.loading' ).removeClass( 'loading' ).text( msg 
);
+                       $overlay.find( '.loading' ).removeClass( 'loading' 
).text( msg );
                        $( '<a class="help">' ).
                                text( mw.msg( 
'mobile-frontend-image-ownership-help' ) ).
                                click( function() {
                                        nav.createOverlay( '',  $( 
M.template.get( 'photoCopyrightDialog' ).render( data ) ) );
                                } ).
-                               appendTo( this.$( 'p' ) );
-                       $( '<img>' ).attr( 'src', url ).prependTo( this.$( 
'.photoPreview' ) );
+                               appendTo( $overlay.find( 'p' ) );
+                       $( '<img>' ).attr( 'src', url ).prependTo( 
$overlay.find( '.photoPreview' ) );
                }
        } );
 
@@ -362,15 +367,14 @@
                                        preview.
                                                on( 'cancel', function() {
                                                        self.log( { action: 
'previewCancel' } );
-                                                       nav.closeOverlay();
+                                                       preview.overlay.close();
                                                } ).
                                                on( 'submit', function() {
                                                        self.log( { action: 
'previewSubmit' } );
                                                        self._submit();
                                                } );
 
-                                       // FIXME: replace if we make overlay an 
object (and inherit from it?)
-                                       nav.createOverlay( null, preview.$el );
+                                       preview.overlay.show();
                                        // skip the URL bar if possible
                                        window.scrollTo( 0, 1 );
 
@@ -391,7 +395,7 @@
                                progressPopup = new PhotoUploadProgress();
 
                        this.emit( 'start' );
-                       nav.closeOverlay();
+                       this.preview.overlay.close();
                        popup.show( progressPopup.$el, 'locked noButton 
loading' );
                        progressPopup.on( 'cancel', function() {
                                api.abort();
diff --git a/less/common/mf-navigation.less b/less/common/mf-navigation.less
index 0709356..6fb2056 100644
--- a/less/common/mf-navigation.less
+++ b/less/common/mf-navigation.less
@@ -56,7 +56,8 @@
        min-height: 100%;
 }
 
-#mw-mf-header, #mw-mf-overlay .header {
+#mw-mf-header,
+.mw-mf-overlay .header {
        color: black;
        .vertical-gradient( @searchBoxColor, @searchBoxColorTo );
        border-bottom: 1px solid #e2e2e2;
@@ -215,23 +216,15 @@
        overflow: hidden;
 }
 
-#mw-mf-overlay {
-       display: none;
-}
-
-#mw-mf-overlay li {
-       text-align: left;
-}
-
-#mw-mf-overlay .mw-mf-overlay-footer a {
-       display: inline;
-}
-
 .escapeOverlay {
        display: none;
 }
 
 html.overlay {
+       .mw-mf-overlay {
+               display: block;
+       }
+
        #mw-mf-viewport {
                display: none;
        }
@@ -256,11 +249,17 @@
        }
 }
 
-.overlay #mw-mf-overlay {
-       display: block;
-}
+.mw-mf-overlay {
+       display: none;
 
-#mw-mf-overlay {
+       .mw-mf-overlay-header {
+               background-color: #F1F1F1;
+       }
+
+       li {
+               text-align: left;
+       }
+
        div.content {
                padding-bottom: 20px;
        }
@@ -285,6 +284,23 @@
 
                button {
                        margin: 0 5px;
+               }
+       }
+
+       .mw-mf-overlay-footer,
+       .mw-mf-overlay-header {
+               padding: 4px 8px 4px @searchBarPaddingLeft;
+
+               margin: 0;
+               font-size: 0.8em;
+               color: #666;
+               border-bottom: solid 1px #eee;
+       }
+
+       .mw-mf-overlay-footer {
+               a {
+                       display: inline;
+                       color: #002BB8;
                }
        }
 }
@@ -400,26 +416,6 @@
 #mw-mf-page-left,
 #mw-mf-page-center {
        min-height: 100%;
-}
-
-#mw-mf-overlay {
-       .mw-mf-overlay-footer,
-       .mw-mf-overlay-header {
-               padding: 4px 8px 4px @searchBarPaddingLeft;
-
-               margin: 0;
-               font-size: 0.8em;
-               color: #666;
-               border-bottom: solid 1px #eee;
-       }
-}
-
-#mw-mf-overlay .mw-mf-overlay-footer a {
-       color: #002BB8;
-}
-
-#mw-mf-overlay .mw-mf-overlay-header {
-       background-color: #F1F1F1;
 }
 
 .supportsPositionFixed #mw-mf-page-center {
diff --git a/less/modules/mf-tables.less b/less/modules/mf-tables.less
index 88fd456..c084f8c 100644
--- a/less/modules/mf-tables.less
+++ b/less/modules/mf-tables.less
@@ -1,6 +1,6 @@
 @import "../mf-mixins.less";
 
-#mw-mf-overlay {
+.mw-mf-overlay {
        .tableContent > table {
                        .boxshadow( -4px, 10px, 10px, -1px, #aaa );
        }
diff --git a/less/specials/userlogin.less b/less/specials/userlogin.less
index 18a44f5..43fbb77 100644
--- a/less/specials/userlogin.less
+++ b/less/specials/userlogin.less
@@ -7,7 +7,7 @@
                background-color: @overlayContentBackground;
        }
 
-       #mw-mf-overlay {
+       .mw-mf-overlay {
                .header {
                        h1 {
                                padding-left: @overlayHeadingIndent;
diff --git a/stylesheets/common/mf-navigation.css 
b/stylesheets/common/mf-navigation.css
index 96b6d0c..8dba013 100644
--- a/stylesheets/common/mf-navigation.css
+++ b/stylesheets/common/mf-navigation.css
@@ -163,7 +163,7 @@
   min-height: 100%;
 }
 #mw-mf-header,
-#mw-mf-overlay .header {
+.mw-mf-overlay .header {
   color: black;
   background-color: #f3f3f3;
   background-image: -moz-linear-gradient(top, #fafafa 0, #f3f3f3 100%);
@@ -174,7 +174,7 @@
   border-bottom: 1px solid #e2e2e2;
 }
 #mw-mf-header h1,
-#mw-mf-overlay .header h1 {
+.mw-mf-overlay .header h1 {
   color: #000000;
   line-height: 46px;
   margin: 0;
@@ -294,17 +294,11 @@
   height: 100%;
   overflow: hidden;
 }
-#mw-mf-overlay {
-  display: none;
-}
-#mw-mf-overlay li {
-  text-align: left;
-}
-#mw-mf-overlay .mw-mf-overlay-footer a {
-  display: inline;
-}
 .escapeOverlay {
   display: none;
+}
+html.overlay .mw-mf-overlay {
+  display: block;
 }
 html.overlay #mw-mf-viewport {
   display: none;
@@ -349,20 +343,26 @@
 html.overlay ul.content li.preferred {
   font-weight: bold;
 }
-.overlay #mw-mf-overlay {
-  display: block;
+.mw-mf-overlay {
+  display: none;
 }
-#mw-mf-overlay div.content {
+.mw-mf-overlay .mw-mf-overlay-header {
+  background-color: #F1F1F1;
+}
+.mw-mf-overlay li {
+  text-align: left;
+}
+.mw-mf-overlay div.content {
   padding-bottom: 20px;
 }
-#mw-mf-overlay .content {
+.mw-mf-overlay .content {
   margin-top: 14pt;
 }
-#mw-mf-overlay .content .close {
+.mw-mf-overlay .content .close {
   display: block;
   margin: auto;
 }
-#mw-mf-overlay .buttonBar {
+.mw-mf-overlay .buttonBar {
   position: fixed;
   bottom: 0;
   width: 100%;
@@ -371,8 +371,20 @@
   background: #f3f3f3;
   text-align: center;
 }
-#mw-mf-overlay .buttonBar button {
+.mw-mf-overlay .buttonBar button {
   margin: 0 5px;
+}
+.mw-mf-overlay .mw-mf-overlay-footer,
+.mw-mf-overlay .mw-mf-overlay-header {
+  padding: 4px 8px 4px 40px;
+  margin: 0;
+  font-size: 0.8em;
+  color: #666;
+  border-bottom: solid 1px #eee;
+}
+.mw-mf-overlay .mw-mf-overlay-footer a {
+  display: inline;
+  color: #002BB8;
 }
 .alert {
   padding: 0.5em 1em;
@@ -473,20 +485,6 @@
 #mw-mf-page-left,
 #mw-mf-page-center {
   min-height: 100%;
-}
-#mw-mf-overlay .mw-mf-overlay-footer,
-#mw-mf-overlay .mw-mf-overlay-header {
-  padding: 4px 8px 4px 40px;
-  margin: 0;
-  font-size: 0.8em;
-  color: #666;
-  border-bottom: solid 1px #eee;
-}
-#mw-mf-overlay .mw-mf-overlay-footer a {
-  color: #002BB8;
-}
-#mw-mf-overlay .mw-mf-overlay-header {
-  background-color: #F1F1F1;
 }
 .supportsPositionFixed #mw-mf-page-center {
   min-height: none !important;
diff --git a/stylesheets/modules/mf-tables.css 
b/stylesheets/modules/mf-tables.css
index 81d1b8d..246f21d 100644
--- a/stylesheets/modules/mf-tables.css
+++ b/stylesheets/modules/mf-tables.css
@@ -1,4 +1,4 @@
-#mw-mf-overlay .tableContent > table {
+.mw-mf-overlay .tableContent > table {
   box-shadow: -4px 10px 10px -1px #aaaaaa;
 }
 #content table.expando {
diff --git a/stylesheets/specials/userlogin.css 
b/stylesheets/specials/userlogin.css
index fa29e01..c6db02a 100644
--- a/stylesheets/specials/userlogin.css
+++ b/stylesheets/specials/userlogin.css
@@ -1,7 +1,7 @@
 .specialPage.overlay body {
   background-color: #ffffff;
 }
-.specialPage.overlay #mw-mf-overlay .header h1 {
+.specialPage.overlay .mw-mf-overlay .header h1 {
   padding-left: 0.4em;
 }
 #mw-mf-login .watermark,
diff --git a/templates/overlay.html b/templates/overlay.html
new file mode 100644
index 0000000..093bb5e
--- /dev/null
+++ b/templates/overlay.html
@@ -0,0 +1,5 @@
+<div class="header">
+       <button class="close escapeOverlay">{{closeMsg}}</button>
+       {{{heading}}}
+</div>
+{{{content}}}
diff --git a/tests/js/common/mf-navigation.js b/tests/js/common/mf-navigation.js
new file mode 100644
index 0000000..5ddb38c
--- /dev/null
+++ b/tests/js/common/mf-navigation.js
@@ -0,0 +1,44 @@
+( function( nav, $ ) {
+
+module( 'MobileFrontend: mf-navigation.js', {} );
+
+test( 'Simple overlay', function() {
+       var overlay = nav.createOverlay( 'Title', 'Text' );
+       strictEqual( overlay.$el[0].parentNode, document.body, 'In DOM' );
+       strictEqual( overlay.$el.html(), '<h2>Title</h2>/Text' );
+} );
+
+test( 'HTML overlay', function() {
+       var overlay = nav.createOverlay( $( '<div>Awesome: <input></div>' ),
+               $('<div class="content">YO</div>' ) );
+       strictEqual( overlay.$el.html(), '<div>Awesome: <input></div>/<div 
class="content">YO</div>' );
+} );
+
+test( 'Close overlay', function() {
+       var overlay = nav.createOverlay( 'Title', 'Text' );
+       overlay.close();
+       strictEqual( overlay.$el[0].parentNode, null, 'No longer in DOM' );
+} );
+
+test( 'Stacked overlays', function() {
+       var overlay = nav.createOverlay( 'Overlay 1', 'Text' ),
+               overlayTwo = nav.createOverlay( 'Overlay 2', 'Text' );
+       overlay.show();
+       overlayTwo.show();
+       strictEqual( $( 'html' ).hasClass( 'overlay' ), true, 'In overlay mode' 
);
+       strictEqual( overlayTwo.$el.is( ':visible' ), true,
+               'The second overlay is the active one' );
+       strictEqual( overlay.$el.is( ':visible' ), false,
+               'The first overlay is marked as inactive' );
+
+       // now close the top stacked one...
+       overlayTwo.close();
+       strictEqual( overlayTwo.$el[0].parentNode, null, 'No longer in DOM' );
+       strictEqual( overlay.$el[0].parentNode, document.body, 'Still in DOM' );
+       strictEqual( overlay.$el.is( ':visible' ), true,
+               'The first overlay is now active' );
+       strictEqual( $( 'html' ).hasClass( 'overlay' ), true, 'Still in overlay 
mode' );
+} );
+
+
+} )( mw.mobileFrontend.require( 'navigation' ), jQuery );
diff --git a/tests/js/fixtures-templates.js b/tests/js/fixtures-templates.js
index f49f256..0cb2c96 100644
--- a/tests/js/fixtures-templates.js
+++ b/tests/js/fixtures-templates.js
@@ -2,3 +2,4 @@
 mw.mobileFrontend.template.add( 'leadPhoto', '' );
 mw.mobileFrontend.template.add( 'photoUploader', '' );
 mw.mobileFrontend.template.add( 'ctaDrawer', '' );
+mw.mobileFrontend.template.add( 'overlay', '{{{heading}}}/{{{content}}}' );

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I26dc25cf47a2a964e7353a05a0fb4400388b79bf
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Jdlrobson <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to