Abstract out editor manipulation form UI

Create target classes with get/setWikitext methods.

Also refactor some of the window manager code.

Bug: T152230
Change-Id: I7dcc137d79e029b69467ca282d8c52683e022598
This commit is contained in:
Ed Sanders 2016-12-03 16:18:51 +00:00
parent 6565ee4cf5
commit a586d54e93
4 changed files with 144 additions and 68 deletions

View file

@ -73,6 +73,7 @@
"ext.templateDataGenerator.ui": { "ext.templateDataGenerator.ui": {
"styles": "modules/ext.templateDataGenerator.ui.css", "styles": "modules/ext.templateDataGenerator.ui.css",
"scripts": [ "scripts": [
"modules/ext.templateDataGenerator.target.js",
"modules/ext.templateDataGenerator.ui.js", "modules/ext.templateDataGenerator.ui.js",
"modules/widgets/ext.templateDataGenerator.paramSelectWidget.js", "modules/widgets/ext.templateDataGenerator.paramSelectWidget.js",
"modules/widgets/ext.templateDataGenerator.paramWidget.js", "modules/widgets/ext.templateDataGenerator.paramWidget.js",
@ -165,6 +166,9 @@
"localBasePath": "", "localBasePath": "",
"remoteExtPath": "TemplateData" "remoteExtPath": "TemplateData"
}, },
"VisualEditorPluginModules": [
"ext.templateDataGenerator.editPage"
],
"config": { "config": {
"TemplateDataUseGUI": true "TemplateDataUseGUI": true
}, },

View file

@ -8,7 +8,7 @@
'use strict'; 'use strict';
$( function () { $( function () {
var pieces, isDocPage, var pieces, isDocPage, target,
pageName = mw.config.get( 'wgPageName' ), pageName = mw.config.get( 'wgPageName' ),
config = { config = {
pageName: pageName, pageName: pageName,
@ -16,26 +16,34 @@
}, },
$textbox = $( '#wpTextbox1' ); $textbox = $( '#wpTextbox1' );
// Check if there's an editor textarea and if we're in the proper namespace // Check if we're in the proper namespace
if ( $textbox.length && mw.config.get( 'wgCanonicalNamespace' ) === 'Template' ) { if ( mw.config.get( 'wgCanonicalNamespace' ) !== 'Template' ) {
pieces = pageName.split( '/' ); return;
isDocPage = pieces.length > 1 && pieces[ pieces.length - 1 ] === 'doc'; }
config = { pieces = pageName.split( '/' );
pageName: pageName, isDocPage = pieces.length > 1 && pieces[ pieces.length - 1 ] === 'doc';
isPageSubLevel: pieces.length > 1,
parentPage: pageName,
isDocPage: isDocPage
};
// Only if we are in a doc page do we set the parent page to config = {
// the one above. Otherwise, all parent pages are current pages pageName: pageName,
if ( isDocPage ) { isPageSubLevel: pieces.length > 1,
pieces.pop(); parentPage: pageName,
config.parentPage = pieces.join( '/' ); isDocPage: isDocPage
} };
// Only if we are in a doc page do we set the parent page to
// the one above. Otherwise, all parent pages are current pages
if ( isDocPage ) {
pieces.pop();
config.parentPage = pieces.join( '/' );
}
// Textbox wikitext editor
if ( $textbox.length ) {
// Prepare the editor // Prepare the editor
mw.libs.tdgUi.init( $( '#mw-content-text' ), $textbox, config ); target = new mw.TemplateData.TextareaTarget( $textbox ),
mw.libs.tdgUi.init( target, config );
$( '#mw-content-text' ).prepend( target.$element );
} }
} ); } );

View file

@ -0,0 +1,74 @@
/**
* Template data edit ui target
*
* @class
* @abstract
* @extends OO.ui.Element
* @mixin OO.EventEmitter
*
* @constructor
*/
mw.TemplateData.Target = function mwTemplateDataTarget() {
// Parent constructor
mw.TemplateData.Target.super.apply( this, arguments );
// Mixin constructor
OO.EventEmitter.call( this );
this.$element.addClass( 'tdg-editscreen-main' );
// TODO: Move more init code into this class
};
/* Inheritance */
OO.inheritClass( mw.TemplateData.Target, OO.ui.Element );
OO.mixinClass( mw.TemplateData.Target, OO.EventEmitter );
/* Methods */
/**
* Get wikitext from the editor
*
* @method
* @abstract
* @return {string} Wikitext
*/
mw.TemplateData.Target.prototype.getWikitext = null;
/**
* Write wikitext back to the target
*
* @method
* @abstract
* @param {string} newWikitext New wikitext
*/
mw.TemplateData.Target.prototype.setWikitext = null;
/**
* Textarea target
*
* @class
* @extends mw.TemplateData.Target
*
* @constructor
* @param {jQuery} $textarea Editor textarea
*/
mw.TemplateData.TextareaTarget = function mwTemplateDataTextareaTarget( $textarea ) {
// Parent constructor
mw.TemplateData.TextareaTarget.super.call( this );
this.$textarea = $textarea;
};
/* Inheritance */
OO.inheritClass( mw.TemplateData.TextareaTarget, mw.TemplateData.Target );
mw.TemplateData.TextareaTarget.prototype.getWikitext = function () {
return this.$textarea.val();
};
mw.TemplateData.TextareaTarget.prototype.setWikitext = function ( newWikitext ) {
this.$textarea.val( newWikitext );
};

View file

@ -1,5 +1,6 @@
( function () { ( function () {
'use strict'; 'use strict';
/** /**
* TemplateData Generator data model. * TemplateData Generator data model.
* This singleton is independent of any UI; it expects * This singleton is independent of any UI; it expects
@ -14,17 +15,17 @@
isDocPage, isDocPage,
pageName, pageName,
parentPage, parentPage,
$textbox, target,
originalWikitext,
// ooui Window Manager // ooui Window Manager
sourceHandler, sourceHandler,
tdgDialog, tdgDialog,
messageDialog,
windowManager,
// Edit window elements // Edit window elements
editOpenDialogButton, editOpenDialogButton,
editNoticeLabel, editNoticeLabel,
editArea, openEditDialog, onEditOpenDialogButton, editArea, openEditDialog, onEditOpenDialogButton,
replaceTemplateData, onDialogApply; replaceTemplateData, onDialogApply,
windowManager = OO.ui.getWindowManager();
editArea = { editArea = {
/** /**
@ -82,8 +83,10 @@
// Reset notice message // Reset notice message
editArea.resetNoticeMessage(); editArea.resetNoticeMessage();
originalWikitext = target.getWikitext();
// Build the model // Build the model
sourceHandler.buildModel( $textbox.val() ) sourceHandler.buildModel( originalWikitext )
.then( .then(
// Success // Success
function ( model ) { function ( model ) {
@ -92,9 +95,10 @@
// Failure // Failure
function () { function () {
// Open a message dialog // Open a message dialog
windowManager.openWindow( messageDialog, { windowManager.openWindow( 'messageDialog', {
title: mw.msg( 'templatedata-modal-title' ), title: mw.msg( 'templatedata-modal-title' ),
message: mw.msg( 'templatedata-errormsg-jsonbadformat' ), message: mw.msg( 'templatedata-errormsg-jsonbadformat' ),
verbose: true,
actions: [ actions: [
{ {
action: 'accept', action: 'accept',
@ -107,24 +111,21 @@
flags: 'safe' flags: 'safe'
} }
] ]
} ) } ).then( function ( opened ) {
.then( function ( opening ) { return opened.then( function ( closing ) {
return opening; return closing.then( function ( data ) {
} ) var model;
.then( function ( opened ) { if ( data && data.action === 'accept' ) {
return opened; // Open the dialog with an empty model
} ) model = mw.TemplateData.Model.static.newFromObject(
.then( function ( data ) { { params: {} },
var model; sourceHandler.getTemplateSourceCodeParams()
if ( data && data.action === 'accept' ) { );
// Open the dialog with an empty model openEditDialog( model );
model = mw.TemplateData.Model.static.newFromObject( }
{ params: {} }, } );
sourceHandler.getTemplateSourceCodeParams()
);
openEditDialog( model );
}
} ); } );
} );
} }
); );
}, },
@ -140,20 +141,19 @@
*/ */
replaceTemplateData = function ( newTemplateData ) { replaceTemplateData = function ( newTemplateData ) {
var finalOutput, var finalOutput,
fullWikitext = $textbox.val(),
endNoIncludeLength = '</noinclude>'.length, endNoIncludeLength = '</noinclude>'.length,
// NB: This pattern contains no matching groups: (). This avoids // NB: This pattern contains no matching groups: (). This avoids
// corruption if the template data JSON contains $1 etc. // corruption if the template data JSON contains $1 etc.
templatedataPattern = /<templatedata>[\s\S]*?<\/templatedata>/i; templatedataPattern = /<templatedata>[\s\S]*?<\/templatedata>/i;
if ( fullWikitext.match( templatedataPattern ) ) { if ( originalWikitext.match( templatedataPattern ) ) {
// <templatedata> exists. Replace it // <templatedata> exists. Replace it
finalOutput = fullWikitext.replace( finalOutput = originalWikitext.replace(
templatedataPattern, templatedataPattern,
'<templatedata>\n' + JSON.stringify( newTemplateData, null, '\t' ) + '\n</templatedata>' '<templatedata>\n' + JSON.stringify( newTemplateData, null, '\t' ) + '\n</templatedata>'
); );
} else { } else {
finalOutput = fullWikitext; finalOutput = originalWikitext;
if ( finalOutput.substr( -1 ) !== '\n' ) { if ( finalOutput.substr( -1 ) !== '\n' ) {
finalOutput += '\n'; finalOutput += '\n';
} }
@ -186,10 +186,10 @@
Object.keys( templateData ).length > 1 || Object.keys( templateData ).length > 1 ||
Object.keys( templateData.params ).length > 0 Object.keys( templateData.params ).length > 0
) { ) {
$textbox.val( replaceTemplateData( templateData ) ); target.setWikitext( replaceTemplateData( templateData ) );
} else { } else {
windowManager.closeWindow( windowManager.getCurrentWindow() ); windowManager.closeWindow( windowManager.getCurrentWindow() );
windowManager.openWindow( messageDialog, { windowManager.openWindow( 'messageDialog', {
title: mw.msg( 'templatedata-modal-title' ), title: mw.msg( 'templatedata-modal-title' ),
message: mw.msg( 'templatedata-errormsg-insertblank' ), message: mw.msg( 'templatedata-errormsg-insertblank' ),
actions: [ actions: [
@ -207,7 +207,7 @@
.then( function ( opened ) { return opened; } ) .then( function ( opened ) { return opened; } )
.then( function ( data ) { .then( function ( data ) {
if ( data && data.action === 'apply' ) { if ( data && data.action === 'apply' ) {
$textbox.val( replaceTemplateData( templateData ) ); target.setWikitext( replaceTemplateData( templateData ) );
} }
} ); } );
} }
@ -217,15 +217,14 @@
/** /**
* Initialize UI * Initialize UI
* *
* @param {jQuery} $container The container to attach UI buttons to * @param {mw.TemplateData.Target} target Edit UI target
* @param {jQuery} $editorTextbox The editor textbox to take the * @param {Object} userConfig Config options
* current wikitext from.
*/ */
init: function ( $container, $editorTextbox, userConfig ) { init: function ( editTarget, userConfig ) {
var $helpLink, relatedPage, var $helpLink, relatedPage,
config = userConfig; config = userConfig;
$textbox = $editorTextbox; target = editTarget;
pageName = config.pageName; pageName = config.pageName;
parentPage = config.parentPage; parentPage = config.parentPage;
@ -250,10 +249,8 @@
.text( mw.msg( 'templatedata-helplink' ) ); .text( mw.msg( 'templatedata-helplink' ) );
// Dialog // Dialog
windowManager = new OO.ui.WindowManager();
tdgDialog = new mw.TemplateData.Dialog( config ); tdgDialog = new mw.TemplateData.Dialog( config );
messageDialog = new OO.ui.MessageDialog(); windowManager.addWindows( [ tdgDialog ] );
windowManager.addWindows( [ tdgDialog, messageDialog ] );
sourceHandler = new mw.TemplateData.SourceHandler( { sourceHandler = new mw.TemplateData.SourceHandler( {
fullPageName: pageName, fullPageName: pageName,
@ -292,19 +289,12 @@
} }
} ); } );
// Prepend to container target.$element
$container .append(
.prepend( editOpenDialogButton.$element,
$( '<div>' ) $helpLink,
.addClass( 'tdg-editscreen-main' ) editNoticeLabel.$element
.append(
editOpenDialogButton.$element,
$helpLink,
editNoticeLabel.$element
)
); );
$( 'body' )
.append( windowManager.$element );
// UI events // UI events
editOpenDialogButton.connect( this, { click: onEditOpenDialogButton } ); editOpenDialogButton.connect( this, { click: onEditOpenDialogButton } );