Jdlrobson has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/332928 )

Change subject: Hygiene: Skin moved to mobile.frontend library
......................................................................

Hygiene: Skin moved to mobile.frontend library

Change-Id: Ieb5cb151c009b40f41b4f6acbd0f33feac876914
---
R build_resources/mobile.frontend/Skin.js
M build_resources/mobile.frontend/index.js
M extension.json
M includes/MobileFrontend.hooks.php
M resources/mobile.frontend/index.js
M resources/skins.minerva.scripts/preInit.js
R tests/qunit/mobile.frontend/test_Skin.js
7 files changed, 393 insertions(+), 8 deletions(-)


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

diff --git a/resources/mobile.startup/Skin.js 
b/build_resources/mobile.frontend/Skin.js
similarity index 98%
rename from resources/mobile.startup/Skin.js
rename to build_resources/mobile.frontend/Skin.js
index 7b4579d..d6fddb1 100644
--- a/resources/mobile.startup/Skin.js
+++ b/build_resources/mobile.frontend/Skin.js
@@ -1,8 +1,8 @@
 ( function ( M, $ ) {
 
-       var browser = mw.mf.Browser.getSingleton(),
-               View = mw.mf.View,
-               icons = mw.mf.icons;
+       var browser = require( './Browser' ).getSingleton(),
+               View = require( './View' ),
+               icons = require( './icons' );
 
        /**
         * Get the id of the section $el belongs to.
@@ -372,6 +372,6 @@
        } );
 
        Skin.getSectionId = getSectionId;
-       M.define( 'mobile.startup/Skin', Skin );
+       module.exports = Skin;
 
 }( mw.mobileFrontend, jQuery ) );
diff --git a/build_resources/mobile.frontend/index.js 
b/build_resources/mobile.frontend/index.js
index 5d3dfc3..3f1415c 100644
--- a/build_resources/mobile.frontend/index.js
+++ b/build_resources/mobile.frontend/index.js
@@ -2,6 +2,7 @@
        icons: require( './icons' ),
        Browser: require( './Browser' ),
        Icon: require( './Icon' ),
+       Skin: require( './Skin' ),
        View: require( './View' ),
        util: require( './util.js' )
 };
diff --git a/extension.json b/extension.json
index e1b8ea2..95c08f4 100644
--- a/extension.json
+++ b/extension.json
@@ -579,7 +579,6 @@
                                "resources/mobile.startup/Section.js",
                                "resources/mobile.startup/Thumbnail.js",
                                "resources/mobile.startup/Page.js",
-                               "resources/mobile.startup/Skin.js",
                                "resources/mobile.startup/OverlayManager.js"
                        ],
                        "position": "bottom"
diff --git a/includes/MobileFrontend.hooks.php 
b/includes/MobileFrontend.hooks.php
index fb6013f..4b1f637 100644
--- a/includes/MobileFrontend.hooks.php
+++ b/includes/MobileFrontend.hooks.php
@@ -329,6 +329,7 @@
                $dependencies[] = 'mobile.frontend';
                $testFiles[] = 'tests/qunit/mobile.frontend/test_browser.js';
                $testFiles[] = 'tests/qunit/mobile.frontend/test_View.js';
+               $testFiles[] = 'tests/qunit/mobile.frontend/test_Skin.js';
 
                $testModule = [
                        'dependencies' => $dependencies,
diff --git a/resources/mobile.frontend/index.js 
b/resources/mobile.frontend/index.js
index 99fe584..3ab9cb8 100644
--- a/resources/mobile.frontend/index.js
+++ b/resources/mobile.frontend/index.js
@@ -48,8 +48,9 @@
                icons: __webpack_require__( 1 ),
                Browser: __webpack_require__( 8 ),
                Icon: __webpack_require__( 2 ),
+               Skin: __webpack_require__( 9 ),
                View: __webpack_require__( 3 ),
-               util: __webpack_require__( 9 )
+               util: __webpack_require__( 10 )
        };
 
 
@@ -1571,6 +1572,389 @@
 
 /***/ },
 /* 9 */
+/***/ function(module, exports, __webpack_require__) {
+
+       ( function ( M, $ ) {
+
+               var browser = __webpack_require__( 8 ).getSingleton(),
+                       View = __webpack_require__( 3 ),
+                       icons = __webpack_require__( 1 );
+
+               /**
+                * Get the id of the section $el belongs to.
+                * @param {jQuery.Object} $el
+                * @return {string|null} either the anchor (id attribute of the 
section heading
+                *  or null if none found)
+                * @ignore
+                */
+               function getSectionId( $el ) {
+                       var id,
+                               hSelector = 'h1,h2,h3,h4,h5,h6',
+                               $parent = $el.parent(),
+                               // e.g. matches Subheading in
+                               // <h2>H</h2><div><h3 
id="subheading">Subh</h3><a class="element"></a></div>
+                               $heading = $el.prevAll( hSelector ).eq( 0 );
+
+                       if ( $heading.length ) {
+                               id = $heading.find( '.mw-headline' ).attr( 'id' 
);
+                               if ( id ) {
+                                       return id;
+                               }
+                       }
+                       if ( $parent.length ) {
+                               // if we couldnt find a sibling heading, check 
the sibling of the parents
+                               // consider <div><h2 /><div><$el/></div></div>
+                               return getSectionId( $parent );
+                       } else {
+                               return null;
+                       }
+               }
+
+               /**
+                * Representation of the current skin being rendered.
+                *
+                * @class Skin
+                * @extends View
+                * @uses Browser
+                * @uses Page
+                *
+                * @constructor
+                * @param {Object} options Configuration options
+                */
+               function Skin( options ) {
+                       var self = this;
+
+                       this.page = options.page;
+                       this.name = options.name;
+                       this.mainMenu = options.mainMenu;
+                       View.call( this, options );
+                       // Must be run after merging with defaults as must be 
defined.
+                       this.tabletModules = options.tabletModules;
+                       this.referencesGateway = options.referencesGateway;
+
+                       /**
+                        * Tests current window size and if suitable loads 
styles and scripts specific for larger devices
+                        *
+                        * @method
+                        * @ignore
+                        */
+                       function loadWideScreenModules() {
+                               if ( browser.isWideScreen() ) {
+                                       // Adjust screen for tablets
+                                       if ( self.page.inNamespace( '' ) ) {
+                                               mw.loader.using( 
self.tabletModules ).always( function () {
+                                                       self.off( '_resize' );
+                                                       self.emit.call( self, 
'changed' );
+                                               } );
+                                       }
+                               }
+                       }
+                       M.on( 'resize', $.proxy( this, 'emit', '_resize' ) );
+                       this.on( '_resize', loadWideScreenModules );
+                       this.emit( '_resize' );
+
+                       if (
+                               !mw.config.get( 'wgImagesDisabled' ) &&
+                               mw.config.get( 'wgMFLazyLoadImages' )
+                       ) {
+                               $( function () {
+                                       self.loadImages();
+                               } );
+                       }
+
+                       if ( mw.config.get( 'wgMFLazyLoadReferences' ) ) {
+                               M.on( 'before-section-toggled', $.proxy( 
this.lazyLoadReferences, this ) );
+                       }
+               }
+
+               OO.mfExtend( Skin, View, {
+                       /**
+                        * @inheritdoc
+                        * Skin contains components that we do not control
+                        */
+                       isBorderBox: false,
+                       /**
+                        * @inheritdoc
+                        * @cfg {Object} defaults Default options hash.
+                        * @cfg {Page} defaults.page page the skin is currently 
rendering
+                        * @cfg {Array} defaults.tabletModules modules to load 
when in tablet
+                        * @cfg {MainMenu} defaults.mainMenu instance of the 
mainMenu
+                        * @cfg {ReferencesGateway} defaults.referencesGateway 
instance of references gateway
+                        */
+                       defaults: {
+                               page: undefined,
+                               tabletModules: [],
+                               mainMenu: undefined
+                       },
+
+                       /**
+                        * @inheritdoc
+                        */
+                       events: {},
+
+                       /**
+                        * Close navigation if content tapped
+                        * @param {jQuery.Event} ev
+                        * @private
+                        */
+                       _onPageCenterClick: function ( ev ) {
+                               var $target = $( ev.target );
+
+                               // Make sure the menu is open and we are not 
clicking on the menu button
+                               if (
+                                       this.mainMenu.isOpen() &&
+                                       !$target.hasClass( 'main-menu-button' )
+                               ) {
+                                       this.mainMenu.closeNavigationDrawers();
+                                       ev.preventDefault();
+                               }
+                       },
+
+                       /**
+                        * @inheritdoc
+                        */
+                       postRender: function () {
+                               var $el = this.$el;
+                               if ( browser.supportsAnimations() ) {
+                                       $el.addClass( 'animations' );
+                               }
+                               if ( browser.supportsTouchEvents() ) {
+                                       $el.addClass( 'touch-events' );
+                               }
+                               $( '<div class="transparent-shield 
cloaked-element">' ).appendTo( '#mw-mf-page-center' );
+                               /**
+                                * @event changed
+                                * Fired when appearance of skin changes.
+                                */
+                               this.emit( 'changed' );
+                               // FIXME: Move back into events when T98200 
resolved
+                               this.$( '#mw-mf-page-center' ).on( 'click',
+                                       $.proxy( this, '_onPageCenterClick' ) );
+                       },
+
+                       /**
+                        * Return the instance of MainMenu
+                        * @return {MainMenu}
+                        */
+                       getMainMenu: function () {
+                               return this.mainMenu;
+                       },
+
+                       /**
+                        * Load images on demand
+                        * @param {jQuery.Object} [$container] The container 
that should be
+                        *  searched for image placeholders. Defaults to 
"#content".
+                        */
+                       loadImages: function ( $container ) {
+                               var self = this,
+                                       offset = $( window ).height() * 1.5,
+                                       imagePlaceholders;
+
+                               $container = $container || this.$( '#content' );
+                               imagePlaceholders = $container.find( 
'.lazy-image-placeholder' ).toArray();
+
+                               /**
+                                * Load remaining images in viewport
+                                */
+                               function _loadImages() {
+
+                                       imagePlaceholders = $.grep( 
imagePlaceholders, function ( placeholder ) {
+                                               var $placeholder = $( 
placeholder );
+
+                                               if (
+                                                       
mw.viewport.isElementCloseToViewport( placeholder, offset ) &&
+                                                       // If a placeholder is 
an inline element without a height attribute set it will record as hidden
+                                                       // to circumvent this 
we also need to test the height (see T143768).
+                                                       ( $placeholder.is( 
':visible' ) || $placeholder.height() === 0 )
+                                               ) {
+                                                       self.loadImage( 
$placeholder );
+                                                       return false;
+                                               }
+
+                                               return true;
+                                       } );
+
+                                       if ( !imagePlaceholders.length ) {
+                                               M.off( 'scroll:throttled', 
_loadImages );
+                                               M.off( 'resize:throttled', 
_loadImages );
+                                               M.off( 'section-toggled', 
_loadImages );
+                                               self.off( 'changed', 
_loadImages );
+                                       }
+
+                               }
+
+                               M.on( 'scroll:throttled', _loadImages );
+                               M.on( 'resize:throttled', _loadImages );
+                               M.on( 'section-toggled', _loadImages );
+                               this.on( 'changed', _loadImages );
+
+                               _loadImages();
+                       },
+
+                       /**
+                        * Load an image on demand
+                        * @param {jQuery.Object} $placeholder
+                        */
+                       loadImage: function ( $placeholder ) {
+                               var
+                                       width = $placeholder.attr( 'data-width' 
),
+                                       height = $placeholder.attr( 
'data-height' ),
+                                       // Image will start downloading
+                                       $downloadingImage = $( '<img/>' );
+
+                               // When the image has loaded
+                               $downloadingImage.on( 'load', function () {
+                                       // Swap the HTML inside the placeholder 
(to keep the layout and
+                                       // dimensions the same and not trigger 
layouts
+                                       $placeholder.empty().append( 
$downloadingImage );
+                                       // Set the loaded class after insertion 
of the HTML to trigger the
+                                       // animations.
+                                       $placeholder.addClass( 'loaded' );
+                               } );
+
+                               // Trigger image download after binding the 
load handler
+                               $downloadingImage.attr( {
+                                       'class': $placeholder.attr( 
'data-class' ),
+                                       width: width,
+                                       height: height,
+                                       src: $placeholder.attr( 'data-src' ),
+                                       alt: $placeholder.attr( 'data-alt' ),
+                                       srcset: $placeholder.attr( 
'data-srcset' )
+                               } );
+                       },
+
+                       /**
+                        * Load the references section content from API if it's 
not already loaded.
+                        *
+                        * All references tags content will be loaded per 
section.
+                        *
+                        * @param {Object} data Information about the section. 
It's in the following form:
+                        * {
+                        *     @property {string} page,
+                        *     @property {boolean} wasExpanded,
+                        *     @property {jQuery.Object} $heading,
+                        *     @property {boolean} isReferenceSection
+                        * }
+                        * @return {jQuery.Deferred} rejected when not a 
reference section.
+                        */
+                       lazyLoadReferences: function ( data ) {
+                               var $content, $spinner,
+                                       gateway = this.referencesGateway,
+                                       self = this;
+
+                               // If the section was expanded before toggling, 
do not load anything as
+                               // section is being collapsed now.
+                               // Also return early if lazy loading is not 
required or the section is
+                               // not a reference section
+                               if (
+                                       data.wasExpanded ||
+                                       !data.isReferenceSection
+                               ) {
+                                       return;
+                               }
+
+                               $content = data.$heading.next();
+
+                               if ( !$content.data( 'are-references-loaded' ) 
) {
+                                       $content.children().addClass( 'hidden' 
);
+                                       $spinner = $( 
icons.spinner().toHtmlString() ).prependTo( $content );
+
+                                       // First ensure we retrieve all of the 
possible lists
+                                       return gateway.getReferencesLists( 
data.page )
+                                               .done( function () {
+                                                       var lastId;
+
+                                                       $content.find( 
'.mf-lazy-references-placeholder' ).each( function () {
+                                                               var 
refListIndex = 0,
+                                                                       
$placeholder = $( this ),
+                                                                       // 
search for id of the collapsible heading
+                                                                       id = 
getSectionId( $placeholder );
+
+                                                               if ( lastId !== 
id ) {
+                                                                       // If 
the placeholder belongs to a new section reset index
+                                                                       
refListIndex = 0;
+                                                                       lastId 
= id;
+                                                               } else {
+                                                                       // 
otherwise increment it
+                                                                       
refListIndex++;
+                                                               }
+
+                                                               if ( id ) {
+                                                                       
gateway.getReferencesList( data.page, id ).done( function ( refListElements ) {
+                                                                               
// Note if no section html is provided no substitution will happen so user is
+                                                                               
// forced to rely on placeholder link.
+                                                                               
if ( refListElements && refListElements[refListIndex] ) {
+                                                                               
        $placeholder.replaceWith( refListElements[refListIndex] );
+                                                                               
}
+                                                                       } );
+                                                               }
+                                                       } );
+                                                       // Show the section now 
the references lists have been placed.
+                                                       $spinner.remove();
+                                                       
$content.children().removeClass( 'hidden' );
+                                                       /**
+                                                        * @event 
references-loaded
+                                                        * Fired when 
references list is loaded into the HTML
+                                                        */
+                                                       self.emit( 
'references-loaded', self.page );
+                                               } )
+                                               .fail( function () {
+                                                       $spinner.remove();
+                                                       // unhide on a failure
+                                                       
$content.children().removeClass( 'hidden' );
+                                               } )
+                                               .always( function () {
+                                                       // lazy load images if 
any
+                                                       self.loadImages( 
$content );
+                                                       // Do not attempt 
further loading even if we're unable to load this time.
+                                                       $content.data( 
'are-references-loaded', 1 );
+                                               } );
+                               } else {
+                                       return $.Deferred().reject();
+                               }
+                       },
+
+                       /**
+                        * Returns the appropriate license message including 
links/name to
+                        * terms of use (if any) and license page
+                        * @return {string}
+                        */
+                       getLicenseMsg: function () {
+                               var licenseMsg,
+                                       mfLicense = mw.config.get( 
'wgMFLicense' ),
+                                       licensePlural = 
mw.language.convertNumber( mfLicense.plural );
+
+                               if ( mfLicense.link ) {
+                                       if ( $( '#footer-places-terms-use' 
).length > 0 ) {
+                                               licenseMsg = mw.msg(
+                                                       
'mobile-frontend-editor-licensing-with-terms',
+                                                       mw.message(
+                                                               
'mobile-frontend-editor-terms-link',
+                                                               $( 
'#footer-places-terms-use a' ).attr( 'href' )
+                                                       ).parse(),
+                                                       mfLicense.link,
+                                                       licensePlural
+                                               );
+                                       } else {
+                                               licenseMsg = mw.msg(
+                                                       
'mobile-frontend-editor-licensing',
+                                                       mfLicense.link,
+                                                       licensePlural
+                                               );
+                                       }
+                               }
+                               return licenseMsg;
+                       }
+               } );
+
+               Skin.getSectionId = getSectionId;
+               module.exports = Skin;
+
+       }( mw.mobileFrontend, jQuery ) );
+
+
+/***/ },
+/* 10 */
 /***/ function(module, exports) {
 
        var util;
diff --git a/resources/skins.minerva.scripts/preInit.js 
b/resources/skins.minerva.scripts/preInit.js
index c92fc7e..8f0f361 100644
--- a/resources/skins.minerva.scripts/preInit.js
+++ b/resources/skins.minerva.scripts/preInit.js
@@ -14,7 +14,7 @@
                Page = M.require( 'mobile.startup/Page' ),
                mainMenu = M.require( 'skins.minerva.scripts.top/mainMenu' ),
                toast = M.require( 'mobile.toast/toast' ),
-               Skin = M.require( 'mobile.startup/Skin' ),
+               Skin = mw.mf.Skin,
                ReferencesMobileViewGateway = M.require(
                        'mobile.references.gateway/ReferencesMobileViewGateway'
                ),
diff --git a/tests/qunit/mobile.startup/test_Skin.js 
b/tests/qunit/mobile.frontend/test_Skin.js
similarity index 98%
rename from tests/qunit/mobile.startup/test_Skin.js
rename to tests/qunit/mobile.frontend/test_Skin.js
index 7cab79d..b2e4c50 100644
--- a/tests/qunit/mobile.startup/test_Skin.js
+++ b/tests/qunit/mobile.frontend/test_Skin.js
@@ -1,6 +1,6 @@
 ( function ( M, $ ) {
        var Page = M.require( 'mobile.startup/Page' ),
-               Skin = M.require( 'mobile.startup/Skin' );
+               Skin = mw.mf.Skin;
 
        QUnit.module( 'MobileFrontend Skin.js', {
                setup: function () {

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ieb5cb151c009b40f41b4f6acbd0f33feac876914
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: mfui
Gerrit-Owner: Jdlrobson <jrob...@wikimedia.org>

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

Reply via email to