MinervaNeue/resources/skins.minerva.editor/init.js

306 lines
9.9 KiB
JavaScript

( function ( M, $ ) {
var
router = require( 'mediawiki.router' ),
overlayManager = M.require( 'skins.minerva.scripts/overlayManager' ),
loader = M.require( 'mobile.startup/rlModuleLoader' ),
skin = M.require( 'skins.minerva.scripts/skin' ),
currentPage = M.getCurrentPage(),
// #ca-edit, .mw-editsection are standard MediaWiki elements
// .edit-link comes from MobileFrontend user page creation CTA
$allEditLinks = $( '#ca-edit a, .mw-editsection a, .edit-link' ),
user = M.require( 'mobile.startup/user' ),
popup = M.require( 'mobile.startup/toast' ),
CtaDrawer = M.require( 'mobile.startup/CtaDrawer' ),
// FIXME: Disable on IE < 10 for time being
blacklisted = /MSIE \d\./.test( navigator.userAgent ),
contentModel = mw.config.get( 'wgPageContentModel' ),
isEditingSupported = router.isSupported() && !blacklisted,
// FIXME: Use currentPage.getId()
isNewPage = currentPage.options.id === 0,
isNewFile = currentPage.inNamespace( 'file' ) && isNewPage,
veConfig = mw.config.get( 'wgVisualEditorConfig' ),
// FIXME: Should we consider default site options and user prefs?
isVisualEditorEnabled = veConfig,
editorPath = /^\/editor\/(\d+|all)$/;
/**
* Event handler for edit link clicks. Will prevent default link
* behaviour and will not allow propagation
* @method
* @ignore
* @return {boolean}
*/
function onEditLinkClick() {
var section = ( new mw.Uri( this.href ) ).query.section || 'all';
router.navigate( '#/editor/' + section );
// prevent folding section when clicking Edit by stopping propagation
return false;
}
/**
* Retrieve the user's preferred editor setting. If none is set, return the default
* editor for this wiki.
* @method
* @ignore
* @return {string} Either 'VisualEditor' or 'SourceEditor'
*/
function getPreferredEditor() {
var preferredEditor = mw.storage.get( 'preferredEditor' );
if ( !preferredEditor ) {
// For now, we are going to ignore which editor is set as the default for the
// wiki and always default to the source editor. Once we decide to honor the
// default editor setting for the wiki, we'll want to use:
// visualEditorDefault = veConfig && veConfig.defaultUserOptions &&
// veConfig.defaultUserOptions.enable;
// return visualEditorDefault ? 'VisualEditor' : 'SourceEditor';
return 'SourceEditor';
}
return preferredEditor;
}
/**
* Initialize the edit button so that it launches the editor interface when clicked.
* @method
* @ignore
* @param {Page} page The page to edit.
*/
function setupEditor( page ) {
var uri, fragment, editorOverride, section,
isNewPage = page.options.id === 0,
leadSection = page.getLeadSectionElement();
$allEditLinks.on( 'click', onEditLinkClick );
overlayManager.add( editorPath, function ( sectionId ) {
var
$content = $( '#mw-content-text' ),
preferredEditor = getPreferredEditor(),
editorOptions = {
overlayManager: overlayManager,
api: new mw.Api(),
licenseMsg: skin.getLicenseMsg(),
title: page.title,
isAnon: user.isAnon(),
isNewPage: isNewPage,
isNewEditor: user.getEditCount() === 0,
oldId: mw.util.getParamValue( 'oldid' ),
contentLang: $content.attr( 'lang' ),
contentDir: $content.attr( 'dir' ),
sessionId: mw.user.generateRandomSessionId()
},
visualEditorNamespaces = veConfig && veConfig.namespaces,
initMechanism = mw.util.getParamValue( 'redlink' ) ? 'new' : 'click';
/**
* Log init event to edit schema.
* Need to log this from outside the Overlay object because that module
* won't have loaded yet.
* @private
* @ignore
* @param {string} editor name e.g. wikitext or visualeditor
* @method
*/
function logInit( editor ) {
// If MobileFrontend is not available this will not be possible so
// check first.
mw.loader.using( 'mobile.loggingSchemas.edit' ).then( function () {
mw.track( 'mf.schemaEdit', {
action: 'init',
type: 'section',
mechanism: initMechanism,
editor: editor,
editingSessionId: editorOptions.sessionId
} );
} );
}
/**
* Load source editor
* @private
* @ignore
* @method
* @returns {JQuery.Promise}
*/
function loadSourceEditor() {
logInit( 'wikitext' );
return loader.loadModule( 'mobile.editor.overlay' ).then( function () {
var EditorOverlay = M.require( 'mobile.editor.overlay/EditorOverlay' );
return new EditorOverlay( editorOptions );
} );
}
if ( sectionId !== 'all' ) {
editorOptions.sectionId = page.isWikiText() ? +sectionId : null;
}
// Check whether VisualEditor should be loaded
if ( isVisualEditorEnabled &&
// Only for pages with a wikitext content model
page.isWikiText() &&
// Only in enabled namespaces
$.inArray( mw.config.get( 'wgNamespaceNumber' ), visualEditorNamespaces ) > -1 &&
// Not on pages which are outputs of the Page Translation feature
mw.config.get( 'wgTranslatePageTranslation' ) !== 'translation' &&
(
// If the user prefers the VisualEditor or the user has no preference and
// the VisualEditor is the default editor for this wiki
preferredEditor === 'VisualEditor' ||
// We've loaded it via the URL for this request
editorOverride === 'VisualEditor'
) &&
editorOverride !== 'SourceEditor'
) {
logInit( 'visualeditor' );
return loader.loadModule( 'mobile.editor.ve' ).then( function () {
var VisualEditorOverlay = M.require( 'mobile.editor.ve/VisualEditorOverlay' );
return new VisualEditorOverlay( editorOptions );
}, loadSourceEditor );
} else {
return loadSourceEditor();
}
} );
// By default the editor opens section 0 (lead section). If lead section is empty, and
// there are sections on the page, open editor with section 1 instead.
// (Be careful not to do this when leadSection is null, as this means MobileFormatter
// has not been run and thus we could not identify the lead.)
section = 0;
if ( leadSection && !leadSection.text() && !isNewPage && page.getSections().length !== 0 ) {
section = 1;
}
$( '#ca-edit a' ).prop( 'href', function ( i, href ) {
var uri = new mw.Uri( href );
uri.query.section = section;
return uri.toString();
} );
if ( !router.getPath() && ( mw.util.getParamValue( 'veaction' ) || mw.util.getParamValue( 'action' ) === 'edit' ) ) {
if ( mw.util.getParamValue( 'veaction' ) === 'edit' ) {
editorOverride = 'VisualEditor';
} else if ( mw.util.getParamValue( 'veaction' ) === 'editsource' ) {
editorOverride = 'SourceEditor';
}
// else: action=edit, for which we allow the default to take effect
fragment = '#/editor/' + ( mw.util.getParamValue( 'section' ) || ( mw.util.getParamValue( 'action' ) === 'edit' && 'all' ) || '0' );
if ( window.history && history.pushState ) {
uri = mw.Uri();
delete uri.query.action;
delete uri.query.veaction;
delete uri.query.section;
// Note: replaceState rather than pushState, because we're
// just reformatting the URL to the equivalent-meaning for the
// mobile site.
history.replaceState( null, document.title, uri.toString() + fragment );
} else {
router.navigate( fragment );
}
}
}
/**
* Hide any section id icons in the page. This will not hide the edit icon in the page action
* menu.
* @method
* @ignore
*/
function hideSectionEditIcons() {
currentPage.$( '.mw-editsection' ).hide();
}
/**
* Show a drawer with log in / sign up buttons.
* @method
* @ignore
*/
function showLoginDrawer() {
var drawer = new CtaDrawer( {
content: mw.msg( 'mobile-frontend-editor-disabled-anon' ),
signupQueryParams: {
warning: 'mobile-frontend-watchlist-signup-action'
}
} );
$allEditLinks.on( 'click', function ( ev ) {
drawer.show();
ev.preventDefault();
return drawer;
} );
router.route( editorPath, function () {
drawer.show();
} );
router.checkRoute();
}
/**
* Setup the editor if the user can edit the page otherwise show a sorry toast.
* @method
* @ignore
*/
function init() {
var isReadOnly, isEditable, editErrorMessage, editRestrictions;
// see: https://www.mediawiki.org/wiki/Manual:Interface/JavaScript#Page-specific
isReadOnly = mw.config.get( 'wgMinervaReadOnly' );
isEditable = !isReadOnly && mw.config.get( 'wgIsProbablyEditable' );
if ( isEditable ) {
// Edit button updated in setupEditor.
setupEditor( currentPage );
} else {
hideSectionEditIcons();
editRestrictions = mw.config.get( 'wgRestrictionEdit' );
if ( mw.user.isAnon() && Array.isArray( editRestrictions ) && editRestrictions.indexOf( '*' ) !== -1 ) {
showLoginDrawer();
} else {
editErrorMessage = isReadOnly ? mw.msg( 'apierror-readonly' ) : mw.msg( 'mobile-frontend-editor-disabled' );
showSorryToast( editErrorMessage );
}
}
}
/**
* Show a toast message with sincere condolences.
* @method
* @ignore
* @param {string} msg Message for sorry message
*/
function showSorryToast( msg ) {
$allEditLinks.on( 'click', function ( ev ) {
popup.show( msg );
ev.preventDefault();
} );
router.route( editorPath, function () {
popup.show( msg );
} );
router.checkRoute();
}
if ( contentModel !== 'wikitext' ) {
// Only load the wikitext editor on wikitext. Otherwise we'll rely on the fallback behaviour
// (You can test this on MediaWiki:Common.css) ?action=edit url (T173800)
return;
}
if ( mw.util.getParamValue( 'undo' ) ) {
// Our fancy editor doesn't support undo, but we can rely on the fallback.
return;
}
if ( !isEditingSupported ) {
// Browser doesn't support mobile editor (or is blacklisted), use the fallback editor.
return;
}
if ( isNewFile ) {
// Is a new file page (enable upload image only) Bug 58311
showSorryToast( mw.msg( 'mobile-frontend-editor-uploadenable' ) );
} else {
// Edit button is currently hidden. A call to init() will update it as needed.
init();
}
}( mw.mobileFrontend, jQuery ) );