Jdlrobson has uploaded a new change for review.
https://gerrit.wikimedia.org/r/243353
Change subject: WIP: Beta: Serve only lead section on page load
......................................................................
WIP: Beta: Serve only lead section on page load
When scrolled after a certain threshold only then do we load
the rest of the content.
Changes:
* Introduce MobileParser for beta mode. It gets the lead section in a much
cleaner fashion. Only lead is now served in beta
* Mobile view API not impacted
* Sections can now be rendered via JavaScript
* Full site link at bottom of page for non-js users and slow connections
TODO
* Table of contents gets toggling enabled on it twice
Bug: T100258
Change-Id: I1913ef6d6b92db73ebb9f55ae63a5552a077bd8d
---
M MobileFrontend.php
M includes/MobileFrontend.hooks.php
M includes/Resources.php
R includes/parser/MobileFormatter.php
A includes/parser/MobileParser.php
M includes/skins/MinervaTemplateBeta.php
M resources/mobile.references/references.js
M resources/mobile.startup/Page.js
M resources/mobile.startup/Section.hogan
M resources/mobile.startup/Section.js
A resources/mobile.startup/heading.hogan
A resources/mobile.startup/page.less
A resources/mobile.startup/pending.gif
M resources/mobile.toc/TableOfContents.js
M resources/skins.minerva.editor/init.js
M resources/skins.minerva.scripts/init.js
M resources/skins.minerva.scripts/preInit.js
M resources/skins.minerva.tablet.scripts/toc.js
M resources/skins.minerva.toggling/init.js
M tests/qunit/mobile.startup/test_PageApi.js
20 files changed, 245 insertions(+), 24 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MobileFrontend
refs/changes/53/243353/1
diff --git a/MobileFrontend.php b/MobileFrontend.php
index c79cde5..6af3a4a 100644
--- a/MobileFrontend.php
+++ b/MobileFrontend.php
@@ -54,7 +54,9 @@
'DeviceDetection' => 'DeviceDetection',
'HtmlDeviceProperties' => 'DeviceDetection',
'MobileContext' => 'MobileContext',
- 'MobileFormatter' => 'MobileFormatter',
+
+ 'MobileParser' => 'parser/MobileParser',
+ 'MobileFormatter' => 'parser/MobileFormatter',
'MobileCollection' => 'models/MobileCollection',
'MobilePage' => 'models/MobilePage',
@@ -149,6 +151,7 @@
$wgHooks['LoginFormValidErrorMessages'][] =
'MobileFrontendHooks::onLoginFormValidErrorMessages';
$wgHooks['ResourceLoaderGetLessVars'][] =
'MobileFrontendHooks::onResourceLoaderGetLessVars';
$wgHooks['SkinPreloadExistence'][] =
'MobileFrontendHooks::onSkinPreloadExistence';
+$wgHooks['ContentGetParserOutput'][] =
'MobileFrontendHooks::onContentGetParserOutput';
$wgSpecialPages += array(
'History' => 'SpecialMobileHistory',
diff --git a/includes/MobileFrontend.hooks.php
b/includes/MobileFrontend.hooks.php
index 38ad8e8..23b3ca3 100644
--- a/includes/MobileFrontend.hooks.php
+++ b/includes/MobileFrontend.hooks.php
@@ -1022,8 +1022,35 @@
}
}
}
- // Enable wrapped sections
- $po->setText( ExtMobileFrontend::DOMParse( $outputPage,
$po->getText() ) );
+ if ( $context->isBetaGroupMember() ) {
+ // Serve the lead section unless directed
otherwise, then serve everything.
+ if ( !$outputPage->getRequest()->getBool(
'fullsite', false ) ) {
+ $po->setText( $po->getProperty(
'mf-lead-section' ) );
+ }
+ } else {
+ $po->setText( ExtMobileFrontend::DOMParse(
$outputPage, $po->getText() ) );
+ }
+ }
+ return true;
+ }
+
+ /**
+ * @see
https://www.mediawiki.org/wiki/Manual:Hooks/ContentGetParserOutput
+ *
+ * @param string $content
+ * @param Title $title
+ * @param integer $revId
+ * @param ParserOptions $options
+ * @param bool $generateHtml
+ * @param ParserOutput $po
+ * @return bool
+ */
+ public static function onContentGetParserOutput( $content, $title,
$revId, $options, $generateHtml, &$output ) {
+ global $wgParser, $wgParserConf;
+ $context = MobileContext::singleton();
+ // hack. Swap out the parser so we only parse lead sections.
+ if ( $context->shouldDisplayMobileView() &&
$context->isBetaGroupMember() ) {
+ $wgParser = new MobileParser( $wgParserConf );
}
return true;
}
diff --git a/includes/Resources.php b/includes/Resources.php
index bea7c07..f48016c 100644
--- a/includes/Resources.php
+++ b/includes/Resources.php
@@ -413,6 +413,8 @@
'button.hogan' =>
'resources/mobile.startup/button.hogan',
),
'messages' => array(
+ // Sections.js
+ 'edit',
// icons.js
'mobile-frontend-loading-message',
'mobile-frontend-console-recruit',
@@ -422,6 +424,7 @@
'mobile-frontend-editor-terms-link',
),
'styles' => array(
+ 'resources/mobile.startup/page.less',
'resources/mobile.startup/panel.less',
),
'scripts' => array(
diff --git a/includes/MobileFormatter.php b/includes/parser/MobileFormatter.php
similarity index 100%
rename from includes/MobileFormatter.php
rename to includes/parser/MobileFormatter.php
diff --git a/includes/parser/MobileParser.php b/includes/parser/MobileParser.php
new file mode 100644
index 0000000..d0da28f
--- /dev/null
+++ b/includes/parser/MobileParser.php
@@ -0,0 +1,53 @@
+<?php
+class MobileParser extends Parser {
+ protected $leadSectionMode = false;
+
+ /**
+ * @param array $conf
+ */
+ public function __construct( $conf = array() ) {
+ $this->leadSectionMode = isset( $conf['leadonly'] );
+ return parent::__construct( $conf );
+ }
+
+ /**
+ * Convert user input to HTML
+ * Do not call this function recursively.
+ *
+ * @param string $text Text we want to parse
+ * @param Title $title
+ * @param ParserOptions $options
+ * @param bool $linestart
+ * @param bool $clearState
+ * @param int $revid Number to pass in {{REVISIONID}}
+ * @return ParserOutputInterface A ParserOutput
+ */
+ public function parse( $text, Title $title, ParserOptions $options,
$lineStart = true, $clearState = true, $revId = null ) {
+ $po = parent::parse( $text, $title, $options, $lineStart,
$clearState, $revId );
+ if ( !$this->leadSectionMode ) {
+ $leadParser = new MobileParser( array( 'leadonly' =>
true ) );
+ $po->setProperty( 'mf-lead-section',
+ '<div>' . $leadParser->parse( $text, $title,
$options, $lineStart, $clearState, $revId )->getText() . '</div>' );
+ }
+ return $po;
+ }
+
+ /**
+ * Parse headers and return html
+ *
+ * @private
+ *
+ * @param string $text
+ *
+ * @return string
+ */
+ public function doHeadings( $text ) {
+ if ( $this->leadSectionMode ) {
+ // This will currently break references, languages and
other things...
+ // So be it!
+ return substr( $text, 0, strpos( $text, '=' ) );
+ } else {
+ return parent::doHeadings( $text );
+ }
+ }
+}
\ No newline at end of file
diff --git a/includes/skins/MinervaTemplateBeta.php
b/includes/skins/MinervaTemplateBeta.php
index 9380a92..d0084a4 100644
--- a/includes/skins/MinervaTemplateBeta.php
+++ b/includes/skins/MinervaTemplateBeta.php
@@ -19,6 +19,22 @@
}
/**
+ * Renders the content of a page
+ * @param array $data Data used to build the page
+ */
+ protected function renderContent( $data ) {
+ parent::renderContent( $data );
+ echo Html::openElement( 'div', array( 'class' => 'post-content
end-of-page-marker' ) )
+ . Html::element( 'a',
+ array(
+ 'href' =>
$this->getSkin()->getTitle()->getLocalUrl( array( 'fullsite' => true ) ),
+ ),
+ // FIXME: i18n
+ 'View full site' )
+ . Html::closeElement( 'div' );
+ }
+
+ /**
* Get category button if categories are present
* @return array A map of the button's friendly name, "categories" to
its
* spec if the button can be displayed.
diff --git a/resources/mobile.references/references.js
b/resources/mobile.references/references.js
index 371c865..db6fae6 100644
--- a/resources/mobile.references/references.js
+++ b/resources/mobile.references/references.js
@@ -1,6 +1,5 @@
( function ( M, $ ) {
var ReferencesDrawer, drawer;
-
/**
* Return the matched reference among the children of ol.references
* @method
diff --git a/resources/mobile.startup/Page.js b/resources/mobile.startup/Page.js
index 8d25371..4be90e4 100644
--- a/resources/mobile.startup/Page.js
+++ b/resources/mobile.startup/Page.js
@@ -10,6 +10,7 @@
*
* @class Page
* @uses Section
+ * @uses InfiniteScroll
* @extends View
*/
Page = View.extend( {
@@ -33,6 +34,8 @@
* @cfg {Number} defaults.thumbnail.width of image in pixels.
* @cfg {Number} defaults.thumbnail.height of image in pixels.
* @cfg {String} defaults.thumbnail.source url for image
+ * @cfg {Api} defaults.api when provided only the lead section
needs to be provided and all other sections will
+ * be defer loaded.
*/
defaults: {
id: 0,
@@ -62,7 +65,8 @@
* @inheritdoc
*/
initialize: function ( options ) {
- var thumb;
+ var thumb,
+ self = this;
// Fallback if no displayTitle provided
options.displayTitle = options.displayTitle ||
options.title;
@@ -81,6 +85,59 @@
this.thumbnail.isLandscape = thumb.width >
thumb.height;
}
this.wikidataDescription = options.wikidataDescription;
+ if ( options.api ) {
+ this.api = options.api;
+ mw.loader.using( 'mobile.infiniteScroll'
).done( $.proxy( self, '_setupScrolling' ) );
+ }
+ },
+
+ /**
+ * Lazy load content beyond the lead section and update the
Page object
+ */
+ lazyLoad: function () {
+ var self = this;
+ this.$( '.end-of-page-marker' ).addClass( 'pending' );
+ this.api.getPage( this.options.title, null,
'allbutlead', 2592000, mw.config.get( 'wgRevisionId' ) ).done( function (
pageOptions ) {
+ // keep the lead.
+ pageOptions.sections =
self.options.sections.slice( 0, 1 ).concat( pageOptions.sections );
+ self.options = $.extend( self.options,
pageOptions );
+ self.compile.call( self );
+ /**
+ * @event loaded
+ */
+ self.emit( 'loaded' );
+ self._renderPostLeadSection();
+ self.$( '.end-of-page-marker' ).remove();
+ } );
+ },
+
+ /**
+ * Setup infinite scrolling on a lazy loaded page.
+ * @private
+ */
+ _setupScrolling: function () {
+ var InfiniteScroll = M.require(
'mobile.infiniteScroll/InfiniteScroll' );
+ this.infiniteScroll = new InfiniteScroll( 1000 );
+ this.infiniteScroll.setElement( this.$el );
+ this.infiniteScroll.once( 'load', $.proxy( this,
'lazyLoad' ) );
+ },
+
+ /**
+ * Render sections beyond the lead section. Callback for
infinite scroller.
+ * To start off this is very dumb and just appends all
children. In future it should
+ * simply append headings and then load the contents of the
section when clicked to
+ * avoid loading all images.
+ * @private
+ */
+ _renderPostLeadSection: function () {
+ var self = this;
+ $.each( this.sections.reverse(), function ( i, section
) {
+ section.$el.children().insertAfter(
self.getLeadSectionElement() );
+ } );
+ /**
+ * @event rendered
+ */
+ this.emit( 'rendered' );
},
/**
@@ -99,7 +156,7 @@
* @return {jQuery.Object}
*/
getLeadSectionElement: function () {
- return this.$( '> div > div' ).eq( 0 );
+ return this.$( '.content > div > div' ).eq( 0 );
},
/**
@@ -205,6 +262,13 @@
* @inheritdoc
*/
preRender: function () {
+ this.compile();
+ },
+
+ /**
+ * Compiles list of sections to Section classes. Adds caching.
+ */
+ compile: function () {
var self = this;
this.sections = [];
this._sectionLookup = {};
diff --git a/resources/mobile.startup/Section.hogan
b/resources/mobile.startup/Section.hogan
index 0ad5fdb..81d42cf 100644
--- a/resources/mobile.startup/Section.hogan
+++ b/resources/mobile.startup/Section.hogan
@@ -1,2 +1,2 @@
-{{#line}}<h{{level}} id="{{anchor}}">{{{line}}}</h{{level}}>{{/line}}
-{{{text}}}
+{{#line}}<h{{level}}><span class="mw-headline"
id="{{anchor}}">{{{line}}}</span>{{#editIcon}}{{>icon}}{{/editIcon}}</h{{level}}>{{/line}}
+<div>{{{text}}}</div>
diff --git a/resources/mobile.startup/Section.js
b/resources/mobile.startup/Section.js
index 5ee523d..5048728 100644
--- a/resources/mobile.startup/Section.js
+++ b/resources/mobile.startup/Section.js
@@ -2,6 +2,7 @@
var View = M.require( 'mobile.view/View' ),
Section,
+ Icon = M.require( 'mobile.startup/Icon' ),
icons = M.require( 'mobile.startup/icons' );
/**
@@ -11,13 +12,18 @@
*/
Section = View.extend( {
template: mw.template.get( 'mobile.startup', 'Section.hogan' ),
+ templatePartials: {
+ icon: Icon.prototype.template
+ },
/**
* @cfg {Object} defaults Default options hash.
* @cfg {String} defaults.text Section text.
+ * @cfg {Object} defaults.editIcon options for an edit button.
* @cfg {String} defaults.spinner HTML of the spinner icon.
*/
defaults: {
line: undefined,
+ editIcon: undefined,
text: '',
spinner: icons.spinner().toHtmlString()
},
@@ -29,6 +35,13 @@
this.text = options.text;
this.hasReferences = options.hasReferences || false;
this.id = options.id || null;
+ options.editIcon = new Icon( {
+ href: '#/editor/' + options.id,
+ name: 'edit-enabled',
+ title: mw.msg( 'edit' ),
+ label: mw.msg( 'edit' ),
+ additionalClassNames: 'edit-page'
+ } ).options;
this.anchor = options.anchor;
this.children = [];
$.each( options.children || [], function () {
diff --git a/resources/mobile.startup/heading.hogan
b/resources/mobile.startup/heading.hogan
new file mode 100644
index 0000000..0933979
--- /dev/null
+++ b/resources/mobile.startup/heading.hogan
@@ -0,0 +1 @@
+{{#line}}<h{{level}}
id="{{anchor}}"><span>{{{line}}}</span>{{#editIcon}}{{>icon}}{{/editIcon}}</h{{level}}>{{/line}}
\ No newline at end of file
diff --git a/resources/mobile.startup/page.less
b/resources/mobile.startup/page.less
new file mode 100644
index 0000000..5de18e2
--- /dev/null
+++ b/resources/mobile.startup/page.less
@@ -0,0 +1,12 @@
+@import "minerva.mixins";
+@import "minerva.variables";
+
+// Put in infinite scroll?
+.end-of-page-marker.pending {
+ .background-image( 'pending.gif' );
+ min-height: 100px;
+ clear: both;
+ a {
+ display: none;
+ }
+}
diff --git a/resources/mobile.startup/pending.gif
b/resources/mobile.startup/pending.gif
new file mode 100644
index 0000000..1194eed
--- /dev/null
+++ b/resources/mobile.startup/pending.gif
Binary files differ
diff --git a/resources/mobile.toc/TableOfContents.js
b/resources/mobile.toc/TableOfContents.js
index 9978c65..67b7d2a 100644
--- a/resources/mobile.toc/TableOfContents.js
+++ b/resources/mobile.toc/TableOfContents.js
@@ -1,5 +1,6 @@
( function ( M ) {
var TableOfContents,
+ toggle = M.require( 'mobile.toggle/toggle' ),
SchemaMobileWebClickTracking = M.require(
'mobile.loggingSchemas/SchemaMobileWebClickTracking' ),
uiSchema = new SchemaMobileWebClickTracking( {},
'MobileWebUIClickTracking' ),
View = M.require( 'mobile.view/View' ),
@@ -36,6 +37,10 @@
'click h2': 'onTocToggle',
'click a': 'onLinkClick'
},
+ /** @inheritdoc */
+ postRender: function () {
+ toggle.enable( this.$el, 'toc-' );
+ },
/**
* Log toggling the header
*/
diff --git a/resources/skins.minerva.editor/init.js
b/resources/skins.minerva.editor/init.js
index e62352b..42f95b3 100644
--- a/resources/skins.minerva.editor/init.js
+++ b/resources/skins.minerva.editor/init.js
@@ -359,11 +359,14 @@
// Cta's will be rendered in EditorOverlay, if
anonymous editing is enabled.
if ( mw.config.get( 'wgMFEditorOptions'
).anonymousEditing ) {
init();
+ currentPage.on( 'rendered', init );
} else {
initCta();
+ currentPage.on( 'rendered', initCta );
}
} else {
init();
+ currentPage.on( 'rendered', init );
}
}
diff --git a/resources/skins.minerva.scripts/init.js
b/resources/skins.minerva.scripts/init.js
index 7f0ea58..d22fed0 100644
--- a/resources/skins.minerva.scripts/init.js
+++ b/resources/skins.minerva.scripts/init.js
@@ -142,6 +142,10 @@
M.on( 'edit-preview', function ( overlay ) {
cleanuptemplates.init( overlay.$el );
} );
+ page.on( 'rendered', function () {
+ references.setup();
+ cleanuptemplates.init( page );
+ } );
// let the interested parties know whether the panel is shown
mw.track( 'minerva.betaoptin', {
diff --git a/resources/skins.minerva.scripts/preInit.js
b/resources/skins.minerva.scripts/preInit.js
index 8ee7906..d9b0594 100644
--- a/resources/skins.minerva.scripts/preInit.js
+++ b/resources/skins.minerva.scripts/preInit.js
@@ -11,6 +11,8 @@
pageApi = new PageApi(),
Page = M.require( 'mobile.startup/Page' ),
mainMenu = M.require( 'mobile.head/mainMenu' ),
+ context = M.require( 'mobile.context/context' ),
+ isLazyLoadEnabled = context.isBetaGroupMember(),
Skin = M.require( 'mobile.startup/Skin' );
skin = new Skin( {
@@ -47,13 +49,16 @@
* @ignore
*/
function loadCurrentPage() {
- var permissions = mw.config.get( 'wgRestrictionEdit', [] ),
+ var options,
+ permissions = mw.config.get( 'wgRestrictionEdit', [] ),
$content = $( '#content #bodyContent' );
+
if ( permissions.length === 0 ) {
permissions.push( '*' );
}
- currentPage = new Page( {
- el: $content,
+
+ options = {
+ el: '#content',
title: mw.config.get( 'wgPageName' ).replace( /_/g, ' '
),
protection: {
edit: permissions
@@ -63,7 +68,12 @@
sections: pageApi.getSectionsFromHTML( $content ),
id: mw.config.get( 'wgArticleId' ),
namespaceNumber: mw.config.get( 'wgNamespaceNumber' )
- } );
+ };
+
+ if ( isLazyLoadEnabled ) {
+ options.api = pageApi;
+ }
+ currentPage = new Page( options );
return currentPage;
}
diff --git a/resources/skins.minerva.tablet.scripts/toc.js
b/resources/skins.minerva.tablet.scripts/toc.js
index b131989..0ab27db 100644
--- a/resources/skins.minerva.tablet.scripts/toc.js
+++ b/resources/skins.minerva.tablet.scripts/toc.js
@@ -1,6 +1,6 @@
( function ( M, $ ) {
- var TableOfContents = M.require( 'mobile.toc/TableOfContents' ),
- toggle = M.require( 'mobile.toggle/toggle' );
+ var toc, page,
+ TableOfContents = M.require( 'mobile.toc/TableOfContents' );
/**
* Create TableOfContents if the given Page has sections and is not the
main page
@@ -10,12 +10,15 @@
* @ignore
*/
function init( page ) {
- var toc,
- sections = page.getSections(),
+ var sections = page.getSections(),
$toc = $( '#toc' ),
// TODO: remove wgTOC when caches with old HTML expire
enableToc = mw.config.get( 'wgMFTocEnabled' ) ||
mw.config.get( 'wgTOC' );
+ // Remove any existing table of contents
+ if ( toc ) {
+ toc.remove();
+ }
if ( enableToc ||
// Fallback for old cached HTML, added 26 June, 2014
( enableToc === null && sections.length > 0 &&
!page.isMainPage() ) ) {
@@ -31,13 +34,15 @@
// otherwise append it to the lead section
toc.appendTo( page.getLeadSectionElement() );
}
- toggle.enable( toc.$el, 'toc-' );
}
}
// add a ToC only for "view" action (user is reading a page)
if ( mw.config.get( 'wgAction' ) === 'view' ) {
- init( M.getCurrentPage() );
+ page = M.getCurrentPage();
+ init( page );
+ page.once( 'loaded', function () {
+ init( page );
+ } );
}
-
}( mw.mobileFrontend, jQuery ) );
diff --git a/resources/skins.minerva.toggling/init.js
b/resources/skins.minerva.toggling/init.js
index 7cb8aa7..d6a7980 100644
--- a/resources/skins.minerva.toggling/init.js
+++ b/resources/skins.minerva.toggling/init.js
@@ -30,4 +30,7 @@
) {
init( $contentContainer, 'content-', page );
}
+ page.on( 'rendered', function () {
+ init( $contentContainer, 'content-', page );
+ } );
}( mw.mobileFrontend, jQuery ) );
diff --git a/tests/qunit/mobile.startup/test_PageApi.js
b/tests/qunit/mobile.startup/test_PageApi.js
index 8a64dca..407a958 100644
--- a/tests/qunit/mobile.startup/test_PageApi.js
+++ b/tests/qunit/mobile.startup/test_PageApi.js
@@ -83,7 +83,7 @@
line: '1',
anchor: '1',
id: 1,
- text: '<p>Text of 1\n</p><h2
id="1.1"><i>1.1</i></h2>\n<p>Text of 1.1\n</p>\n',
+ text: '<p>Text of 1\n</p><h2
id="1.1"><span><i>1.1</i></span></h2>\n<div><p>Text of 1.1\n</p></div>\n',
children: [
{
level: '2',
@@ -100,7 +100,7 @@
line: '2',
anchor: '2',
id: 3,
- text: '<p>Text of 2\n</p><h2
id="2.1">2.1</h2>\n<p>Text of 2.1\n</p>\n',
+ text: '<p>Text of 2\n</p><h2
id="2.1"><span>2.1</span></h2>\n<div><p>Text of 2.1\n</p></div>\n',
children: [
{
level: '2',
@@ -196,7 +196,7 @@
line: 'Aaa section',
anchor: 'Aaa_section',
id: 1,
- text: 'aaa content<h3
id="Subaaa_section">Subaaa section</h3>\nsubaaa content\n',
+ text: 'aaa content<h3
id="Subaaa_section"><span>Subaaa section</span></h3>\n<div>subaaa
content</div>\n',
children: [
{
level: '3',
@@ -394,7 +394,7 @@
line: 'A1',
level: '2',
anchor: '1.0',
- text: '<h3 id="">A2.1</h3>\n\n',
+ text: '<h3
id=""><span>A2.1</span></h3>\n<div></div>\n',
children: [ {
line: 'A2.1',
level: '3',
@@ -414,7 +414,7 @@
line: 'A2',
level: '1',
anchor: '',
- text: '<h2 id="">A2.1</h2>\n\n',
+ text: '<h2
id=""><span>A2.1</span></h2>\n<div></div>\n',
children: [ {
line: 'A2.1',
level: '2',
--
To view, visit https://gerrit.wikimedia.org/r/243353
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I1913ef6d6b92db73ebb9f55ae63a5552a077bd8d
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