mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-09-26 19:56:49 +00:00
Refactor Transclusion and Meta dialogs to use BookletLayout
Use OOJS-UI's newly-extended paged dialogs (in e08eb2a03b) to refactor how the Transclusion and Meta dialogs work, splitting out the code for each of the panels into its own file and simplifying extensibility. The Meta dialog (ve.ui.MWMetaDialog) now has two self-managing panels: * ve.ui.MWCategoriesPage for categories and the default sort key * ve.ui.MWLanguagesPage for language links The Transclusion dialog (ve.ui.MWTransclusionDialog) now has four: * ve.ui.MWTemplatePage for a template's primary panel * ve.ui.MWTemplateParameterPage for each parameter of a template * ve.ui.MWTemplatePlaceholderPage for a placeholder to insert a template * ve.ui.MWTransclusionContentPage for non-template transclusion Additionally, the Transclusion dialog has been slightly cleaned up: * Replace add/remove events with replace events in transclusion model * Actually return and resolve a promise (as documented) * Get rid of "origin" info in template models * Add method for adding required parts TODO: * Decide how and when we will choose between advanced transclusion and template dialogs * Work out design issues with how template descriptions will be visible and how adding parameters will work if only showing parameters in outline * Add preview to template dialog * Consider ways to further improve pages for use in continuous mode WARNING: * Right now the template dialog gets overridden by the advanced transclusion dialog because they have the same symbolic name and the latter is registered later than the former. To test the template dialog, just change the symbolic name of the advanced transclusion dialog. Change-Id: I51e74b322aec9a4c3918e6f792bdb3d318060979
This commit is contained in:
parent
49d108003d
commit
51e096d6f2
|
@ -134,6 +134,10 @@
|
|||
{
|
||||
"name": "Dialogs",
|
||||
"classes": ["ve.ui.*Dialog"]
|
||||
},
|
||||
{
|
||||
"name": "Pages",
|
||||
"classes": ["ve.ui.*Page"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -474,12 +474,21 @@ $wgResourceModules += array(
|
|||
've-mw/ui/widgets/ve.ui.MWReferenceResultWidget.js',
|
||||
've-mw/ui/widgets/ve.ui.MWTitleInputWidget.js',
|
||||
|
||||
've-mw/ui/pages/ve.ui.MWCategoriesPage.js',
|
||||
've-mw/ui/pages/ve.ui.MWLanguagesPage.js',
|
||||
've-mw/ui/pages/ve.ui.MWTemplatePage.js',
|
||||
've-mw/ui/pages/ve.ui.MWTemplateParameterPage.js',
|
||||
've-mw/ui/pages/ve.ui.MWTemplatePlaceholderPage.js',
|
||||
've-mw/ui/pages/ve.ui.MWTransclusionContentPage.js',
|
||||
|
||||
've-mw/ui/dialogs/ve.ui.MWSaveDialog.js',
|
||||
've-mw/ui/dialogs/ve.ui.MWMetaDialog.js',
|
||||
've-mw/ui/dialogs/ve.ui.MWBetaWelcomeDialog.js',
|
||||
've-mw/ui/dialogs/ve.ui.MWMediaInsertDialog.js',
|
||||
've-mw/ui/dialogs/ve.ui.MWMediaEditDialog.js',
|
||||
've-mw/ui/dialogs/ve.ui.MWTransclusionDialog.js',
|
||||
've-mw/ui/dialogs/ve.ui.MWTemplateDialog.js',
|
||||
've-mw/ui/dialogs/ve.ui.MWAdvancedTransclusionDialog.js',
|
||||
've-mw/ui/dialogs/ve.ui.MWReferenceListDialog.js',
|
||||
've-mw/ui/dialogs/ve.ui.MWReferenceDialog.js',
|
||||
|
||||
|
|
|
@ -18,15 +18,13 @@
|
|||
* @param {Object} target Template target
|
||||
* @param {string} target.wt Original wikitext of target
|
||||
* @param {string} [target.href] Hypertext reference to target
|
||||
* @param {string} [origin] Origin of part, e.g. 'data' or 'user'
|
||||
*/
|
||||
ve.dm.MWTemplateModel = function VeDmMWTemplateModel( transclusion, target, origin ) {
|
||||
ve.dm.MWTemplateModel = function VeDmMWTemplateModel( transclusion, target ) {
|
||||
// Parent constructor
|
||||
ve.dm.MWTransclusionPartModel.call( this, transclusion );
|
||||
|
||||
// Properties
|
||||
this.target = target;
|
||||
this.origin = origin;
|
||||
|
||||
// TODO: Either here or in uses of this constructor we need to validate the title
|
||||
this.title = ( target.href && target.href.replace( /^(\.\.?\/)*/, '' ) ) || null;
|
||||
|
@ -106,26 +104,15 @@ ve.dm.MWTemplateModel.newFromName = function ( transclusion, name ) {
|
|||
/**
|
||||
* Get template target.
|
||||
*
|
||||
* @method
|
||||
* @returns {Object} Template target
|
||||
*/
|
||||
ve.dm.MWTemplateModel.prototype.getTarget = function () {
|
||||
return this.target;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get template origin, e.g. 'user' or 'data'.
|
||||
*
|
||||
* @returns {string} Origin
|
||||
*/
|
||||
ve.dm.MWTemplateModel.prototype.getOrigin = function () {
|
||||
return this.origin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get template title.
|
||||
*
|
||||
* @method
|
||||
* @returns {string|null} Template title, if available
|
||||
*/
|
||||
ve.dm.MWTemplateModel.prototype.getTitle = function () {
|
||||
|
@ -135,7 +122,6 @@ ve.dm.MWTemplateModel.prototype.getTitle = function () {
|
|||
/**
|
||||
* Get template specification.
|
||||
*
|
||||
* @method
|
||||
* @returns {ve.dm.MWTemplateSpecModel} Template specification
|
||||
*/
|
||||
ve.dm.MWTemplateModel.prototype.getSpec = function () {
|
||||
|
@ -145,7 +131,6 @@ ve.dm.MWTemplateModel.prototype.getSpec = function () {
|
|||
/**
|
||||
* Get all params.
|
||||
*
|
||||
* @method
|
||||
* @returns {Object.<string,ve.dm.MWTemplateParameterModel>} Parameters keyed by name
|
||||
*/
|
||||
ve.dm.MWTemplateModel.prototype.getParameters = function () {
|
||||
|
@ -155,7 +140,6 @@ ve.dm.MWTemplateModel.prototype.getParameters = function () {
|
|||
/**
|
||||
* Get a parameter.
|
||||
*
|
||||
* @method
|
||||
* @param {string} name Parameter name
|
||||
* @returns {ve.dm.MWTemplateParameterModel} Parameter
|
||||
*/
|
||||
|
@ -166,7 +150,6 @@ ve.dm.MWTemplateModel.prototype.getParameter = function ( name ) {
|
|||
/**
|
||||
* Check if a parameter exists.
|
||||
*
|
||||
* @method
|
||||
* @param {string} name Parameter name
|
||||
* @returns {boolean} Parameter exists
|
||||
*/
|
||||
|
@ -203,7 +186,6 @@ ve.dm.MWTemplateModel.prototype.hasParameter = function ( name ) {
|
|||
* Numeric names, whether strings or real numbers, are placed at the begining, followed by
|
||||
* alphabetically sorted names.
|
||||
*
|
||||
* @method
|
||||
* @returns {string[]} List of parameter names
|
||||
*/
|
||||
ve.dm.MWTemplateModel.prototype.getParameterNames = function () {
|
||||
|
@ -233,7 +215,6 @@ ve.dm.MWTemplateModel.prototype.getParameterNames = function () {
|
|||
/**
|
||||
* Add a parameter to template.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTemplateParameterModel} param Parameter to add
|
||||
* @fires add
|
||||
*/
|
||||
|
@ -248,7 +229,6 @@ ve.dm.MWTemplateModel.prototype.addParameter = function ( param ) {
|
|||
/**
|
||||
* Remove parameter from template.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTemplateParameterModel} param Parameter to remove
|
||||
* @fires remove
|
||||
*/
|
||||
|
@ -261,9 +241,25 @@ ve.dm.MWTemplateModel.prototype.removeParameter = function ( param ) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Set original data, to be used as a base for serialization.
|
||||
* Add all non-existing required parameters, if any.
|
||||
*
|
||||
* @method
|
||||
*/
|
||||
ve.dm.MWTemplateModel.prototype.addRequiredParameters = function () {
|
||||
var i, len,
|
||||
spec = this.getSpec(),
|
||||
names = spec.getParameterNames();
|
||||
|
||||
for ( i = 0, len = names.length; i < len; i++ ) {
|
||||
if ( !this.params[name] && spec.isParameterRequired( names[i] ) ) {
|
||||
this.addParameter( new ve.dm.MWTemplateParameterModel( this, names[i] ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set original data, to be used as a base for serialization.
|
||||
*
|
||||
* @returns {Object} Template data
|
||||
*/
|
||||
ve.dm.MWTemplateModel.prototype.setOriginalData = function ( data ) {
|
||||
|
|
|
@ -37,13 +37,9 @@ OO.mixinClass( ve.dm.MWTransclusionModel, OO.EventEmitter );
|
|||
/* Events */
|
||||
|
||||
/**
|
||||
* @event add
|
||||
* @param {ve.dm.MWTransclusionPartModel} part Added part
|
||||
*/
|
||||
|
||||
/**
|
||||
* @event remove
|
||||
* @param {ve.dm.MWTransclusionPartModel} part Removed part
|
||||
* @event replace
|
||||
* @param {ve.dm.MWTransclusionPartModel|null} removed Removed part
|
||||
* @param {ve.dm.MWTransclusionPartModel|null} added Added part
|
||||
*/
|
||||
|
||||
/* Methods */
|
||||
|
@ -51,12 +47,12 @@ OO.mixinClass( ve.dm.MWTransclusionModel, OO.EventEmitter );
|
|||
/**
|
||||
* Load from transclusion data, and fetch spec from server.
|
||||
*
|
||||
* @method
|
||||
* @param {Object} data Transclusion data
|
||||
* @returns {jQuery.Promise} Promise, resolved when spec is loaded
|
||||
*/
|
||||
ve.dm.MWTransclusionModel.prototype.load = function ( data ) {
|
||||
var i, len, part;
|
||||
var i, len, part, deferred,
|
||||
promises = [];
|
||||
|
||||
// Convert single part format to multi-part format
|
||||
// Parsoid doesn't use this format any more, but we accept it for backwards compatibility
|
||||
|
@ -68,48 +64,66 @@ ve.dm.MWTransclusionModel.prototype.load = function ( data ) {
|
|||
for ( i = 0, len = data.parts.length; i < len; i++ ) {
|
||||
part = data.parts[i];
|
||||
if ( part.template ) {
|
||||
this.queue.push(
|
||||
{ 'part': ve.dm.MWTemplateModel.newFromData( this, part.template ) }
|
||||
);
|
||||
deferred = $.Deferred();
|
||||
promises.push( deferred.promise() );
|
||||
this.queue.push( {
|
||||
'add': ve.dm.MWTemplateModel.newFromData( this, part.template ),
|
||||
'deferred': deferred
|
||||
} );
|
||||
} else if ( typeof part === 'string' ) {
|
||||
this.queue.push(
|
||||
{ 'part': new ve.dm.MWTransclusionContentModel( this, part, 'data' ) }
|
||||
);
|
||||
deferred = $.Deferred();
|
||||
promises.push( deferred.promise() );
|
||||
this.queue.push( {
|
||||
'add': new ve.dm.MWTransclusionContentModel( this, part, 'data' ),
|
||||
'deferred': deferred
|
||||
} );
|
||||
}
|
||||
}
|
||||
setTimeout( ve.bind( this.fetch, this ) );
|
||||
}
|
||||
|
||||
return $.when.apply( $, promises );
|
||||
};
|
||||
|
||||
/**
|
||||
* Process one or more queue items.
|
||||
*
|
||||
* @method
|
||||
* @param {Object[]} queue List of objects containing parts to add and optionally indexes to add
|
||||
* them at, if no index is given parts will be added at the end
|
||||
* @fires add For each item added
|
||||
* @fires replace For each item added
|
||||
*/
|
||||
ve.dm.MWTransclusionModel.prototype.process = function ( queue ) {
|
||||
var i, len, item, title, index;
|
||||
var i, len, item, title, index,
|
||||
remove = 0;
|
||||
|
||||
for ( i = 0, len = queue.length; i < len; i++ ) {
|
||||
item = queue[i];
|
||||
if ( item.part instanceof ve.dm.MWTemplateModel ) {
|
||||
title = item.part.getTitle();
|
||||
if ( item.add instanceof ve.dm.MWTemplateModel ) {
|
||||
title = item.add.getTitle();
|
||||
if ( hasOwn.call( specCache, title ) && specCache[title] ) {
|
||||
item.part.getSpec().extend( specCache[title] );
|
||||
item.add.getSpec().extend( specCache[title] );
|
||||
}
|
||||
}
|
||||
// Auto-remove if already existing
|
||||
index = ve.indexOf( item.part, this.parts );
|
||||
index = ve.indexOf( item.add, this.parts );
|
||||
if ( index !== -1 ) {
|
||||
this.parts.splice( index, 1 );
|
||||
this.emit( 'remove', item.part );
|
||||
this.emit( 'replace', item.add, null );
|
||||
}
|
||||
// Add at index, or end if none was given
|
||||
index = item.index === undefined ? this.parts.length : item.index;
|
||||
this.parts.splice( index, 0, item.part );
|
||||
this.emit( 'add', item.part );
|
||||
if ( item.index !== undefined ) {
|
||||
index = item.index;
|
||||
} else if ( item.remove ) {
|
||||
index = ve.indexOf( item.remove, this.parts );
|
||||
if ( index !== -1 ) {
|
||||
remove = 1;
|
||||
}
|
||||
}
|
||||
if ( index === undefined || index === -1 ) {
|
||||
index = this.parts.length;
|
||||
}
|
||||
this.parts.splice( index, remove, item.add );
|
||||
this.emit( 'replace', item.remove || null, item.add );
|
||||
// Resolve promises
|
||||
if ( item.deferred ) {
|
||||
item.deferred.resolve();
|
||||
|
@ -134,8 +148,8 @@ ve.dm.MWTransclusionModel.prototype.fetch = function () {
|
|||
// Get unique list of template titles that aren't already loaded
|
||||
for ( i = 0, len = queue.length; i < len; i++ ) {
|
||||
item = queue[i];
|
||||
if ( item.part instanceof ve.dm.MWTemplateModel ) {
|
||||
title = item.part.getTitle();
|
||||
if ( item.add instanceof ve.dm.MWTemplateModel ) {
|
||||
title = item.add.getTitle();
|
||||
if (
|
||||
// Skip titles that don't have a resolvable href
|
||||
title &&
|
||||
|
@ -227,7 +241,6 @@ ve.dm.MWTransclusionModel.prototype.abortRequests = function () {
|
|||
/**
|
||||
* Get plain object representation of template transclusion.
|
||||
*
|
||||
* @method
|
||||
* @returns {Object|null} Plain object representation, or null if empty
|
||||
*/
|
||||
ve.dm.MWTransclusionModel.prototype.getPlainObject = function () {
|
||||
|
@ -254,19 +267,45 @@ ve.dm.MWTransclusionModel.prototype.getPlainObject = function () {
|
|||
*
|
||||
* This is used to give parts unique IDs, and returns a different value each time it's called.
|
||||
*
|
||||
* @method
|
||||
* @returns {number} Unique ID
|
||||
*/
|
||||
ve.dm.MWTransclusionModel.prototype.getUniquePartId = function () {
|
||||
return this.uid++;
|
||||
};
|
||||
|
||||
/**
|
||||
* Replace part.
|
||||
*
|
||||
* Replace asynchonously.
|
||||
*
|
||||
* @param {ve.dm.MWTransclusionPartModel} remove Part to remove
|
||||
* @param {ve.dm.MWTransclusionPartModel} add Part to add
|
||||
* @throws {Error} If part to remove is not valid
|
||||
* @throws {Error} If part to add is not valid
|
||||
* @returns {jQuery.Promise} Promise, resolved when part is added
|
||||
*/
|
||||
ve.dm.MWTransclusionModel.prototype.replacePart = function ( remove, add ) {
|
||||
var deferred = $.Deferred();
|
||||
if (
|
||||
!( remove instanceof ve.dm.MWTransclusionPartModel ) ||
|
||||
!( add instanceof ve.dm.MWTransclusionPartModel )
|
||||
) {
|
||||
throw new Error( 'Invalid transclusion part' );
|
||||
}
|
||||
this.queue.push( { 'remove': remove, 'add': add, 'deferred': deferred } );
|
||||
|
||||
// Fetch on next yield to process items in the queue together, subsequent calls to fetch will
|
||||
// have no effect because the queue will be clear
|
||||
setTimeout( ve.bind( this.fetch, this ) );
|
||||
|
||||
return deferred.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add part.
|
||||
*
|
||||
* Added asynchronously, but order is preserved.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTransclusionPartModel} part Part to add
|
||||
* @param {number} [index] Specific index to add content at, defaults to the end
|
||||
* @throws {Error} If part is not valid
|
||||
|
@ -277,7 +316,7 @@ ve.dm.MWTransclusionModel.prototype.addPart = function ( part, index ) {
|
|||
if ( !( part instanceof ve.dm.MWTransclusionPartModel ) ) {
|
||||
throw new Error( 'Invalid transclusion part' );
|
||||
}
|
||||
this.queue.push( { 'part': part, 'index': index, 'deferred': deferred } );
|
||||
this.queue.push( { 'add': part, 'index': index, 'deferred': deferred } );
|
||||
|
||||
// Fetch on next yield to process items in the queue together, subsequent calls to fetch will
|
||||
// have no effect because the queue will be clear
|
||||
|
@ -289,22 +328,20 @@ ve.dm.MWTransclusionModel.prototype.addPart = function ( part, index ) {
|
|||
/**
|
||||
* Remove a part.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTransclusionPartModel} part Part to remove
|
||||
* @fires remove
|
||||
* @fires replace
|
||||
*/
|
||||
ve.dm.MWTransclusionModel.prototype.removePart = function ( part ) {
|
||||
var index = ve.indexOf( part, this.parts );
|
||||
if ( index !== -1 ) {
|
||||
this.parts.splice( index, 1 );
|
||||
this.emit( 'remove', part );
|
||||
this.emit( 'replace', part, null );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all parts.
|
||||
*
|
||||
* @method
|
||||
* @returns {ve.dm.MWTransclusionPartModel[]} Parts in transclusion
|
||||
*/
|
||||
ve.dm.MWTransclusionModel.prototype.getParts = function () {
|
||||
|
@ -316,7 +353,6 @@ ve.dm.MWTransclusionModel.prototype.getParts = function () {
|
|||
*
|
||||
* Matching is performed against the first section of the `id`, delimited by a '/'.
|
||||
*
|
||||
* @method
|
||||
* @param {string} id Part ID
|
||||
* @returns {ve.dm.MWTransclusionPartModel|null} Part with matching ID, if found
|
||||
*/
|
||||
|
@ -334,4 +370,36 @@ ve.dm.MWTransclusionModel.prototype.getPartFromId = function ( id ) {
|
|||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the index of a part or parameter.
|
||||
*
|
||||
* Indexes are linear depth-first addresses in the transclusion tree.
|
||||
*
|
||||
* @param {ve.dm.MWTransclusionPartModel|ve.dm.MWTemplateParameterModel} model Part or parameter
|
||||
* @returns {number} Page index of model
|
||||
*/
|
||||
ve.dm.MWTransclusionModel.prototype.getIndex = function ( model ) {
|
||||
var i, iLen, j, jLen, part, names,
|
||||
parts = this.parts,
|
||||
index = 0;
|
||||
|
||||
for ( i = 0, iLen = parts.length; i < iLen; i++ ) {
|
||||
part = parts[i];
|
||||
if ( part === model ) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
if ( part instanceof ve.dm.MWTemplateModel ) {
|
||||
names = part.getParameterNames();
|
||||
for ( j = 0, jLen = names.length; j < jLen; j++ ) {
|
||||
if ( part.getParameter( names[j] ) === model ) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
}() );
|
||||
|
|
131
modules/ve-mw/ui/dialogs/ve.ui.MWAdvancedTransclusionDialog.js
Normal file
131
modules/ve-mw/ui/dialogs/ve.ui.MWAdvancedTransclusionDialog.js
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* VisualEditor user interface MWAdvancedTransclusionDialog class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dialog for inserting and editing MediaWiki transclusions.
|
||||
*
|
||||
* @class
|
||||
* @extends ve.ui.MWTransclusionDialog
|
||||
*
|
||||
* @constructor
|
||||
* @param {ve.ui.WindowSet} windowSet Window set this dialog is part of
|
||||
* @param {Object} [config] Configuration options
|
||||
*/
|
||||
ve.ui.MWAdvancedTransclusionDialog = function VeUiMWAdvancedTransclusionDialog( windowSet, config ) {
|
||||
// Parent constructor
|
||||
ve.ui.MWTransclusionDialog.call( this, windowSet, config );
|
||||
|
||||
// Properties
|
||||
this.node = null;
|
||||
this.transclusion = null;
|
||||
this.loaded = false;
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
OO.inheritClass( ve.ui.MWAdvancedTransclusionDialog, ve.ui.MWTransclusionDialog );
|
||||
|
||||
/* Static Properties */
|
||||
|
||||
ve.ui.MWAdvancedTransclusionDialog.static.name = 'transclusion';
|
||||
|
||||
ve.ui.MWAdvancedTransclusionDialog.static.titleMessage = 'visualeditor-dialog-transclusion-title';
|
||||
|
||||
ve.ui.MWAdvancedTransclusionDialog.static.icon = 'template';
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Handle outline controls move events.
|
||||
*
|
||||
* @param {number} places Number of places to move the selected item
|
||||
*/
|
||||
ve.ui.MWAdvancedTransclusionDialog.prototype.onOutlineControlsMove = function ( places ) {
|
||||
var part, index, name, promise,
|
||||
parts = this.transclusion.getParts(),
|
||||
item = this.bookletLayout.getOutline().getSelectedItem();
|
||||
|
||||
if ( item ) {
|
||||
name = item.getData();
|
||||
part = this.transclusion.getPartFromId( name );
|
||||
index = ve.indexOf( part, parts );
|
||||
// Auto-removes part from old location
|
||||
promise = this.transclusion.addPart( part, index + places );
|
||||
if ( this.loaded ) {
|
||||
promise.done( ve.bind( this.setPageByName, this, part.getId() ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle outline controls add events.
|
||||
*
|
||||
* @param {string} type Type of item to add
|
||||
*/
|
||||
ve.ui.MWAdvancedTransclusionDialog.prototype.onOutlineControlsAdd = function ( type ) {
|
||||
var part, parts, item, index, promise;
|
||||
|
||||
if ( type === 'content' ) {
|
||||
part = new ve.dm.MWTransclusionContentModel( this.transclusion, '', 'user' );
|
||||
} else if ( type === 'template' ) {
|
||||
part = new ve.dm.MWTemplatePlaceholderModel( this.transclusion, 'user' );
|
||||
}
|
||||
if ( part ) {
|
||||
parts = this.transclusion.getParts();
|
||||
item = this.bookletLayout.getOutline().getSelectedItem();
|
||||
index = item ?
|
||||
ve.indexOf( this.transclusion.getPartFromId( item.getData() ), parts ) + 1 :
|
||||
parts.length;
|
||||
promise = this.transclusion.addPart( part, index );
|
||||
if ( this.loaded ) {
|
||||
promise.done( ve.bind( this.setPageByName, this, part.getId() ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ve.ui.MWAdvancedTransclusionDialog.prototype.getBookletLayout = function () {
|
||||
return new OO.ui.BookletLayout( {
|
||||
'$': this.$,
|
||||
'continuous': true,
|
||||
'autoFocus': true,
|
||||
'outlined': true,
|
||||
'editable': true,
|
||||
'adders': [
|
||||
{
|
||||
'name': 'template',
|
||||
'icon': 'template',
|
||||
'title': ve.msg( 'visualeditor-dialog-transclusion-add-template' )
|
||||
},
|
||||
{
|
||||
'name': 'content',
|
||||
'icon': 'source',
|
||||
'title': ve.msg( 'visualeditor-dialog-transclusion-add-content' )
|
||||
}
|
||||
]
|
||||
} );
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ve.ui.MWAdvancedTransclusionDialog.prototype.initialize = function () {
|
||||
// Parent method
|
||||
ve.ui.MWTransclusionDialog.prototype.initialize.call( this );
|
||||
|
||||
// Events
|
||||
this.bookletLayout.getOutlineControls().connect( this, {
|
||||
'move': 'onOutlineControlsMove',
|
||||
'add': 'onOutlineControlsAdd'
|
||||
} );
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.ui.dialogFactory.register( ve.ui.MWAdvancedTransclusionDialog );
|
|
@ -5,8 +5,6 @@
|
|||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/*global mw*/
|
||||
|
||||
/**
|
||||
* Dialog for editing MediaWiki page meta information.
|
||||
*
|
||||
|
@ -20,17 +18,6 @@
|
|||
ve.ui.MWMetaDialog = function VeUiMWMetaDialog( windowSet, config ) {
|
||||
// Parent constructor
|
||||
ve.ui.MWDialog.call( this, windowSet, config );
|
||||
|
||||
// Properties
|
||||
this.metaList = this.surface.getModel().metaList;
|
||||
this.defaultSortKeyTouched = false;
|
||||
this.fallbackDefaultSortKey = mw.config.get( 'wgTitle' );
|
||||
|
||||
// Events
|
||||
this.metaList.connect( this, {
|
||||
'insert': 'onMetaListInsert',
|
||||
'remove': 'onMetaListRemove'
|
||||
} );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
@ -47,341 +34,32 @@ ve.ui.MWMetaDialog.static.icon = 'settings';
|
|||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Handle language items being loaded.
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.onAllLanuageItemsSuccess = function ( deferred, response ) {
|
||||
var i, iLen, languages = [], langlinks = response.query.pages[response.query.pageids[0]].langlinks;
|
||||
if ( langlinks ) {
|
||||
for ( i = 0, iLen = langlinks.length; i < iLen; i++ ) {
|
||||
languages.push( {
|
||||
'lang': langlinks[i].lang,
|
||||
'title': langlinks[i]['*'],
|
||||
'metaItem': null
|
||||
} );
|
||||
}
|
||||
}
|
||||
deferred.resolve( languages );
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle language items failing to be loaded.
|
||||
*
|
||||
* TODO: This error function should probably not be empty.
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.onAllLanuageItemsError = function () {};
|
||||
|
||||
/**
|
||||
* Handle category default sort change events.
|
||||
*
|
||||
* @param {string} value Default sort value
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.onDefaultSortChange = function ( value ) {
|
||||
this.categoryWidget.setDefaultSortKey( value === '' ? this.fallbackDefaultSortKey : value );
|
||||
this.defaultSortKeyTouched = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts new category into meta list
|
||||
*
|
||||
* @param {Object} item
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.onNewCategory = function ( item ) {
|
||||
// Insert new metaList item
|
||||
this.insertMetaListItem( this.getCategoryItemForInsertion( item ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes and re-inserts updated category widget item
|
||||
*
|
||||
* @param {Object} item
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.onUpdateSortKey = function ( item ) {
|
||||
// Replace meta item with updated one
|
||||
item.metaItem.replaceWith( this.getCategoryItemForInsertion( item, item.metaItem.getElement() ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Bound to MetaList insert event for adding meta dialog components.
|
||||
*
|
||||
* @param {ve.dm.MetaItem} metaItem
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.onMetaListInsert = function ( metaItem ) {
|
||||
// Responsible for adding UI components
|
||||
if ( metaItem.element.type === 'mwCategory' ) {
|
||||
this.categoryWidget.addItems(
|
||||
[ this.getCategoryItemFromMetaListItem( metaItem ) ],
|
||||
this.metaList.findItem( metaItem.getOffset(), metaItem.getIndex(), 'mwCategory' )
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Bound to MetaList insert event for removing meta dialog components.
|
||||
*
|
||||
* @param {ve.dm.MetaItem} metaItem
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.onMetaListRemove = function ( metaItem ) {
|
||||
var item;
|
||||
|
||||
if ( metaItem.element.type === 'mwCategory' ) {
|
||||
item = this.getCategoryItemFromMetaListItem( metaItem );
|
||||
this.categoryWidget.removeItems( [item.value] );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get default sort key item.
|
||||
*
|
||||
* @returns {string} Default sort key item
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.getDefaultSortKeyItem = function () {
|
||||
var items = this.metaList.getItemsInGroup( 'mwDefaultSort' );
|
||||
return items.length ? items[0] : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get array of category items from meta list
|
||||
*
|
||||
* @returns {Object[]} items
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.getCategoryItems = function () {
|
||||
var i,
|
||||
items = [],
|
||||
categories = this.metaList.getItemsInGroup( 'mwCategory' );
|
||||
|
||||
// Loop through MwCategories and build out items
|
||||
for ( i = 0; i < categories.length; i++ ) {
|
||||
items.push( this.getCategoryItemFromMetaListItem( categories[i] ) );
|
||||
}
|
||||
return items;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets category item from meta list item
|
||||
*
|
||||
* @param {ve.dm.MWCategoryMetaItem} metaItem
|
||||
* @returns {Object} item
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.getCategoryItemFromMetaListItem = function ( metaItem ) {
|
||||
var title = mw.Title.newFromText( metaItem.element.attributes.category ),
|
||||
value = title ? title.getMainText() : '';
|
||||
|
||||
return {
|
||||
'name': metaItem.element.attributes.category,
|
||||
'value': value,
|
||||
// TODO: sortkey is lcase, make consistent throughout CategoryWidget
|
||||
'sortKey': metaItem.element.attributes.sortkey,
|
||||
'metaItem': metaItem
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get metaList like object to insert from item
|
||||
*
|
||||
* @param {Object} item category widget item
|
||||
* @param {Object} [oldData] Metadata object that was previously associated with this item, if any
|
||||
* @returns {Object} metaBase
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.getCategoryItemForInsertion = function ( item, oldData ) {
|
||||
var newData = {
|
||||
'attributes': { 'category': item.name, 'sortkey': item.sortKey || '' },
|
||||
'type': 'mwCategory'
|
||||
};
|
||||
if ( oldData ) {
|
||||
return ve.extendObject( {}, oldData, newData );
|
||||
}
|
||||
return newData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets language item from meta list item
|
||||
*
|
||||
* @param {ve.dm.MWLanguageMetaItem} metaItem
|
||||
* @returns {Object} item
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.getLanguageItemFromMetaListItem = function ( metaItem ) {
|
||||
// TODO: get real values from metaItem once Parsoid actually provides them - bug 48970
|
||||
return {
|
||||
'lang': 'lang',
|
||||
'title': 'title',
|
||||
'metaItem': metaItem
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get array of language items from meta list
|
||||
*
|
||||
* @returns {Object[]} items
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.getLocalLanguageItems = function () {
|
||||
var i,
|
||||
items = [],
|
||||
languages = this.metaList.getItemsInGroup( 'mwLanguage' ),
|
||||
languageslength = languages.length;
|
||||
|
||||
// Loop through MWLanguages and build out items
|
||||
|
||||
for ( i = 0; i < languageslength; i++ ) {
|
||||
items.push( this.getLanguageItemFromMetaListItem( languages[i] ) );
|
||||
}
|
||||
return items;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get array of language items from meta list
|
||||
*
|
||||
* @returns {jQuery.Promise}
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.getAllLanguageItems = function () {
|
||||
var deferred = $.Deferred();
|
||||
// TODO: Detect paging token if results exceed limit
|
||||
$.ajax( {
|
||||
'url': mw.util.wikiScript( 'api' ),
|
||||
'data': {
|
||||
'action': 'query',
|
||||
'prop': 'langlinks',
|
||||
'lllimit': 500,
|
||||
'titles': mw.config.get( 'wgTitle' ),
|
||||
'indexpageids': 1,
|
||||
'format': 'json'
|
||||
},
|
||||
'dataType': 'json',
|
||||
'type': 'POST',
|
||||
// Wait up to 100 seconds before giving up
|
||||
'timeout': 100000,
|
||||
'cache': 'false'
|
||||
} )
|
||||
.done( ve.bind( this.onAllLanuageItemsSuccess, this, deferred ) )
|
||||
.fail( ve.bind( this.onAllLanuageItemsError, this, deferred ) );
|
||||
return deferred.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts a meta list item
|
||||
*
|
||||
* @param {Object} metaBase meta list insert object
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.insertMetaListItem = function ( metaBase ) {
|
||||
this.metaList.insertMeta( metaBase );
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.initialize = function () {
|
||||
var languagePromise;
|
||||
|
||||
// Parent method
|
||||
ve.ui.MWDialog.prototype.initialize.call( this );
|
||||
|
||||
// Properties
|
||||
this.pagedOutlineLayout = new OO.ui.PagedOutlineLayout( { '$': this.$ } );
|
||||
this.categoriesFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-meta-categories-data-label' ),
|
||||
'icon': 'tag'
|
||||
} );
|
||||
this.categoryOptionsFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-meta-categories-options' ),
|
||||
'icon': 'settings'
|
||||
} );
|
||||
this.categoryWidget = new ve.ui.MWCategoryWidget( {
|
||||
'$': this.$, '$overlay': this.$overlay
|
||||
} );
|
||||
this.defaultSortInput = new OO.ui.TextInputWidget( {
|
||||
'$': this.$, 'placeholder': this.fallbackDefaultSortKey
|
||||
} );
|
||||
this.defaultSortLabel = new OO.ui.InputLabelWidget( {
|
||||
'$': this.$,
|
||||
'input': this.defaultSortInput,
|
||||
'label': ve.msg( 'visualeditor-dialog-meta-categories-defaultsort-label' )
|
||||
} );
|
||||
this.languagesFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-meta-languages-label' ),
|
||||
'icon': 'language'
|
||||
} );
|
||||
this.bookletLayout = new OO.ui.BookletLayout( { '$': this.$, 'outlined': true } );
|
||||
this.applyButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-action-apply' ),
|
||||
'flags': ['primary']
|
||||
} );
|
||||
this.categoriesPage = new ve.ui.MWCategoriesPage( this.surface, 'categories', {
|
||||
'$': this.$, '$overlay': this.$overlay
|
||||
} );
|
||||
this.languagesPage = new ve.ui.MWLanguagesPage( 'languages', { '$': this.$ } );
|
||||
|
||||
// Events
|
||||
this.categoryWidget.connect( this, {
|
||||
'newCategory': 'onNewCategory',
|
||||
'updateSortkey': 'onUpdateSortKey'
|
||||
} );
|
||||
this.defaultSortInput.connect( this, {
|
||||
'change': 'onDefaultSortChange'
|
||||
} );
|
||||
this.applyButton.connect( this, { 'click': [ 'close', { 'action': 'apply' } ] } );
|
||||
|
||||
// Initialization
|
||||
this.categoryWidget.addItems( this.getCategoryItems() );
|
||||
|
||||
this.$body.append( this.pagedOutlineLayout.$element );
|
||||
this.$body.append( this.bookletLayout.$element );
|
||||
this.$foot.append( this.applyButton.$element );
|
||||
|
||||
this.pagedOutlineLayout.addPage( 'categories', {
|
||||
'$content': [ this.categoriesFieldset.$element, this.categoryOptionsFieldset.$element ],
|
||||
'label': ve.msg( 'visualeditor-dialog-meta-categories-section' ),
|
||||
'icon': 'tag'
|
||||
} ).addPage( 'languages', {
|
||||
'$content': this.languagesFieldset.$element,
|
||||
'label': ve.msg( 'visualeditor-dialog-meta-languages-section' ),
|
||||
'icon': 'language'
|
||||
} );
|
||||
|
||||
this.categoriesFieldset.$element.append( this.categoryWidget.$element );
|
||||
this.categoryOptionsFieldset.$element.append(
|
||||
this.defaultSortLabel.$element,
|
||||
this.defaultSortInput.$element
|
||||
);
|
||||
this.languagesFieldset.$element.append(
|
||||
this.$( '<span>' )
|
||||
.text( ve.msg( 'visualeditor-dialog-meta-languages-readonlynote' ) )
|
||||
);
|
||||
|
||||
languagePromise = this.getAllLanguageItems();
|
||||
languagePromise.done( ve.bind( function ( languages ) {
|
||||
var i, $languagesTable = this.$( '<table>' ), languageslength = languages.length;
|
||||
|
||||
$languagesTable
|
||||
.addClass( 've-ui-mwMetaDialog-languages-table' )
|
||||
.append( this.$( '<tr>' )
|
||||
.append(
|
||||
this.$( '<th>' )
|
||||
.append( ve.msg( 'visualeditor-dialog-meta-languages-code-label' ) )
|
||||
)
|
||||
.append(
|
||||
this.$( '<th>' )
|
||||
.append( ve.msg( 'visualeditor-dialog-meta-languages-link-label' ) )
|
||||
)
|
||||
);
|
||||
|
||||
for ( i = 0; i < languageslength; i++ ) {
|
||||
languages[i].safelang = languages[i].lang;
|
||||
languages[i].dir = 'auto';
|
||||
if ( $.uls ) {
|
||||
// site codes don't always represent official language codes
|
||||
// using real language code instead of a dummy ('redirect' in ULS' terminology)
|
||||
languages[i].safelang = $.uls.data.isRedirect( languages[i].lang ) || languages[i].lang;
|
||||
languages[i].dir = $.uls.data.getDir( languages[i].safelang );
|
||||
}
|
||||
$languagesTable
|
||||
.append( this.$( '<tr>' )
|
||||
.append( this.$( '<td>' ).append( languages[i].lang ) )
|
||||
.append( this.$( '<td>' ).append( languages[i].title )
|
||||
.attr( 'lang', languages[i].safelang )
|
||||
.attr( 'dir', languages[i].dir ) )
|
||||
);
|
||||
}
|
||||
|
||||
this.languagesFieldset.$element.append( $languagesTable );
|
||||
}, this ) );
|
||||
this.bookletLayout.addPages( [ this.categoriesPage, this.languagesPage ] );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -394,49 +72,25 @@ ve.ui.MWMetaDialog.prototype.setup = function ( data ) {
|
|||
// Data initialization
|
||||
data = data || {};
|
||||
|
||||
var surfaceModel = this.surface.getModel(),
|
||||
categoryWidget = this.categoryWidget,
|
||||
defaultSortKeyItem = this.getDefaultSortKeyItem();
|
||||
var surfaceModel = this.surface.getModel();
|
||||
|
||||
if ( data.page && this.pagedOutlineLayout.getPage( data.page ) ) {
|
||||
this.pagedOutlineLayout.setPage( data.page );
|
||||
if ( data.page && this.bookletLayout.getPage( data.page ) ) {
|
||||
this.bookletLayout.setPage( data.page );
|
||||
}
|
||||
|
||||
this.defaultSortInput.setValue(
|
||||
defaultSortKeyItem ? defaultSortKeyItem.getAttribute( 'content' ) : ''
|
||||
);
|
||||
this.defaultSortKeyTouched = false;
|
||||
|
||||
// Force all previous transactions to be separate from this history state
|
||||
surfaceModel.breakpoint();
|
||||
surfaceModel.stopHistoryTracking();
|
||||
|
||||
// Update input position once visible
|
||||
setTimeout( function () {
|
||||
categoryWidget.fitInput();
|
||||
} );
|
||||
this.categoriesPage.setup( data );
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ve.ui.MWMetaDialog.prototype.teardown = function ( data ) {
|
||||
// Data initialization
|
||||
data = data || {};
|
||||
|
||||
var hasTransactions,
|
||||
surfaceModel = this.surface.getModel(),
|
||||
|
||||
// Category sort key items
|
||||
currentDefaultSortKeyItem = this.getDefaultSortKeyItem(),
|
||||
newDefaultSortKey = this.defaultSortInput.getValue(),
|
||||
newDefaultSortKeyData = {
|
||||
'type': 'mwDefaultSort',
|
||||
'attributes': { 'content': newDefaultSortKey }
|
||||
};
|
||||
|
||||
// Place transactions made while dialog was open in a common history state
|
||||
hasTransactions = surfaceModel.breakpoint();
|
||||
var surfaceModel = this.surface.getModel(),
|
||||
hasTransactions = surfaceModel.breakpoint();
|
||||
|
||||
// Undo everything done in the dialog and prevent redoing those changes
|
||||
if ( data.action === 'cancel' && hasTransactions ) {
|
||||
|
@ -444,28 +98,10 @@ ve.ui.MWMetaDialog.prototype.teardown = function ( data ) {
|
|||
surfaceModel.truncateUndoStack();
|
||||
}
|
||||
|
||||
// Alter the default sort key iff it's been touched & is actually different
|
||||
if ( this.defaultSortKeyTouched ) {
|
||||
if ( newDefaultSortKey === '' ) {
|
||||
if ( currentDefaultSortKeyItem ) {
|
||||
currentDefaultSortKeyItem.remove();
|
||||
}
|
||||
} else {
|
||||
if ( !currentDefaultSortKeyItem ) {
|
||||
this.metaList.insertMeta( newDefaultSortKeyData );
|
||||
} else if ( currentDefaultSortKeyItem.getValue() !== newDefaultSortKey ) {
|
||||
currentDefaultSortKeyItem.replaceWith(
|
||||
ve.extendObject( true, {},
|
||||
currentDefaultSortKeyItem.getElement(),
|
||||
newDefaultSortKeyData
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.categoriesPage.teardown( data );
|
||||
|
||||
// Return to normal tracking behavior
|
||||
surfaceModel.startHistoryTracking();
|
||||
this.surface.getModel().startHistoryTracking();
|
||||
|
||||
// Parent method
|
||||
ve.ui.MWDialog.prototype.teardown.call( this, data );
|
||||
|
|
|
@ -140,7 +140,7 @@ ve.ui.MWReferenceDialog.prototype.initialize = function () {
|
|||
ve.ui.MWDialog.prototype.initialize.call( this );
|
||||
|
||||
// Properties
|
||||
this.panels = new OO.ui.StackPanelLayout( { '$': this.$ } );
|
||||
this.panels = new OO.ui.StackLayout( { '$': this.$ } );
|
||||
this.editPanel = new OO.ui.PanelLayout( {
|
||||
'$': this.$, 'scrollable': true, 'padded': true
|
||||
} );
|
||||
|
@ -187,14 +187,14 @@ ve.ui.MWReferenceDialog.prototype.initialize = function () {
|
|||
this.backButton.$element.show();
|
||||
this.insertButton.$element.hide();
|
||||
this.selectButton.$element.hide();
|
||||
this.panels.showItem( this.searchPanel );
|
||||
this.panels.setItem( this.searchPanel );
|
||||
this.search.getQuery().$input.focus().select();
|
||||
} } );
|
||||
this.backButton.connect( this, { 'click': function () {
|
||||
this.backButton.$element.hide();
|
||||
this.insertButton.$element.show();
|
||||
this.selectButton.$element.show();
|
||||
this.panels.showItem( this.editPanel );
|
||||
this.panels.setItem( this.editPanel );
|
||||
this.editPanel.$element.find( '.ve-ce-documentNode' ).focus();
|
||||
} } );
|
||||
this.search.connect( this, { 'select': 'onSearchSelect' } );
|
||||
|
@ -234,7 +234,7 @@ ve.ui.MWReferenceDialog.prototype.setup = function ( data ) {
|
|||
this.selectButton.$element.show();
|
||||
}
|
||||
this.backButton.$element.hide();
|
||||
this.panels.showItem( this.editPanel );
|
||||
this.panels.setItem( this.editPanel );
|
||||
this.useReference( ref );
|
||||
this.search.buildIndex();
|
||||
this.selectButton.setDisabled( !this.search.getResults().getItems().length );
|
||||
|
|
|
@ -159,7 +159,7 @@ ve.ui.MWSaveDialog.prototype.swapPanel = function ( panel ) {
|
|||
}
|
||||
|
||||
// Show the target panel
|
||||
this.panel.showItem( panelObj );
|
||||
this.panel.setItem( panelObj );
|
||||
|
||||
mw.hook( 've.saveDialog.stateChanged' ).fire();
|
||||
|
||||
|
@ -344,7 +344,7 @@ ve.ui.MWSaveDialog.prototype.initialize = function () {
|
|||
this.nochangesPanel.$element.append( this.$noChanges );
|
||||
|
||||
// Panel stack
|
||||
this.panel = new OO.ui.StackPanelLayout( { '$': this.$, 'scrollable': true } );
|
||||
this.panel = new OO.ui.StackLayout( { '$': this.$, 'scrollable': true } );
|
||||
this.panel.$element.addClass( 've-ui-mwSaveDialog-panel' );
|
||||
this.panel.addItems( [this.savePanel, this.reviewPanel, this.conflictPanel, this.nochangesPanel], 0 );
|
||||
|
||||
|
|
56
modules/ve-mw/ui/dialogs/ve.ui.MWTemplateDialog.js
Normal file
56
modules/ve-mw/ui/dialogs/ve.ui.MWTemplateDialog.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* VisualEditor user interface MWTemplateDialog class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* Dialog for editing MediaWiki templates.
|
||||
*
|
||||
* @class
|
||||
* @extends ve.ui.MWTransclusionDialog
|
||||
*
|
||||
* @constructor
|
||||
* @param {ve.ui.WindowSet} windowSet Window set this dialog is part of
|
||||
* @param {Object} [config] Configuration options
|
||||
*/
|
||||
ve.ui.MWTemplateDialog = function VeUiMWTemplateDialog( windowSet, config ) {
|
||||
// Parent constructor
|
||||
ve.ui.MWTransclusionDialog.call( this, windowSet, config );
|
||||
|
||||
// Properties
|
||||
this.node = null;
|
||||
this.transclusion = null;
|
||||
this.loaded = false;
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
OO.inheritClass( ve.ui.MWTemplateDialog, ve.ui.MWTransclusionDialog );
|
||||
|
||||
/* Static Properties */
|
||||
|
||||
ve.ui.MWTemplateDialog.static.name = 'transclusion';
|
||||
|
||||
ve.ui.MWTemplateDialog.static.titleMessage = 'visualeditor-dialog-transclusion-title';
|
||||
|
||||
ve.ui.MWTemplateDialog.static.icon = 'template';
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ve.ui.MWTemplateDialog.prototype.getBookletLayout = function () {
|
||||
return new OO.ui.BookletLayout( {
|
||||
'$': this.$,
|
||||
'continuous': true,
|
||||
'autoFocus': true,
|
||||
'outlined': true
|
||||
} );
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.ui.dialogFactory.register( ve.ui.MWTemplateDialog );
|
|
@ -1,4 +1,4 @@
|
|||
/*!
|
||||
/*
|
||||
* VisualEditor user interface MWTransclusionDialog class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
|
@ -22,7 +22,7 @@ ve.ui.MWTransclusionDialog = function VeUiMWTransclusionDialog( windowSet, confi
|
|||
// Properties
|
||||
this.node = null;
|
||||
this.transclusion = null;
|
||||
this.pending = [];
|
||||
this.loaded = false;
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
@ -31,457 +31,105 @@ OO.inheritClass( ve.ui.MWTransclusionDialog, ve.ui.MWDialog );
|
|||
|
||||
/* Static Properties */
|
||||
|
||||
ve.ui.MWTransclusionDialog.static.name = 'transclusion';
|
||||
|
||||
ve.ui.MWTransclusionDialog.static.titleMessage = 'visualeditor-dialog-transclusion-title';
|
||||
|
||||
ve.ui.MWTransclusionDialog.static.icon = 'template';
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Handle add part events.
|
||||
* Handle parts being replaced.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTransclusionPartModel} part Added part
|
||||
* @param {ve.dm.MWTransclusionPartModel} removed Removed part
|
||||
* @param {ve.dm.MWTransclusionPartModel} added Added part
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.onAddPart = function ( part ) {
|
||||
var i, len, page, params, names, pending, item, spec;
|
||||
ve.ui.MWTransclusionDialog.prototype.onReplacePart = function ( removed, added ) {
|
||||
var i, len, page, name, names, params, selected,
|
||||
removePages = [],
|
||||
select = false;
|
||||
|
||||
if ( part instanceof ve.dm.MWTemplateModel ) {
|
||||
page = this.getTemplatePage( part );
|
||||
} else if ( part instanceof ve.dm.MWTransclusionContentModel ) {
|
||||
page = this.getContentPage( part );
|
||||
} else if ( part instanceof ve.dm.MWTemplatePlaceholderModel ) {
|
||||
page = this.getPlaceholderPage( part );
|
||||
}
|
||||
if ( page ) {
|
||||
page.index = this.getPageIndex( part );
|
||||
this.pagedOutlineLayout.addPage( part.getId(), page );
|
||||
// Add existing params to templates
|
||||
if ( part instanceof ve.dm.MWTemplateModel ) {
|
||||
names = part.getParameterNames();
|
||||
params = part.getParameters();
|
||||
for ( i = 0, len = names.length; i < len; i++ ) {
|
||||
this.onAddParameter( params[names[i]] );
|
||||
if ( removed ) {
|
||||
// Remove parameter pages of removed templates
|
||||
if ( removed instanceof ve.dm.MWTemplateModel ) {
|
||||
params = removed.getParameters();
|
||||
for ( name in params ) {
|
||||
removePages.push( this.bookletLayout.getPage( params[name].getId() ) );
|
||||
}
|
||||
part.connect( this, { 'add': 'onAddParameter', 'remove': 'onRemoveParameter' } );
|
||||
removed.disconnect( this );
|
||||
}
|
||||
}
|
||||
|
||||
// Add required params to user created templates
|
||||
if ( part instanceof ve.dm.MWTemplateModel && part.getOrigin() === 'user' ) {
|
||||
spec = part.getSpec();
|
||||
names = spec.getParameterNames();
|
||||
for ( i = 0, len = names.length; i < len; i++ ) {
|
||||
// Only add required params
|
||||
if ( spec.isParameterRequired( names[i] ) ) {
|
||||
part.addParameter( new ve.dm.MWTemplateParameterModel( part, names[i] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve pending placeholder
|
||||
for ( i = 0, len = this.pending.length; i < len; i++ ) {
|
||||
pending = this.pending[i];
|
||||
if ( pending.part === part ) {
|
||||
if ( this.outlined ) {
|
||||
// Auto-select new part if placeholder is still selected
|
||||
item = this.pagedOutlineLayout.getOutline().getSelectedItem();
|
||||
if ( item.getData() === pending.placeholder.getId() ) {
|
||||
this.setPageByName( part.getId() );
|
||||
selected = this.bookletLayout.getOutline().getSelectedItem();
|
||||
if ( selected && removed.getId() === selected.getData() ) {
|
||||
select = true;
|
||||
}
|
||||
}
|
||||
removePages.push( this.bookletLayout.getPage( removed.getId() ) );
|
||||
this.bookletLayout.removePages( removePages );
|
||||
}
|
||||
|
||||
if ( added ) {
|
||||
if ( added instanceof ve.dm.MWTemplateModel ) {
|
||||
page = new ve.ui.MWTemplatePage( added, added.getId(), { '$': this.$ } );
|
||||
} else if ( added instanceof ve.dm.MWTransclusionContentModel ) {
|
||||
page = new ve.ui.MWTransclusionContentPage( added, added.getId(), { '$': this.$ } );
|
||||
} else if ( added instanceof ve.dm.MWTemplatePlaceholderModel ) {
|
||||
page = new ve.ui.MWTemplatePlaceholderPage( added, added.getId(), { '$': this.$ } );
|
||||
}
|
||||
if ( page ) {
|
||||
this.bookletLayout.addPages( [ page ], this.transclusion.getIndex( added ) );
|
||||
if ( select && this.loaded ) {
|
||||
this.setPageByName( added.getId() );
|
||||
}
|
||||
// Add existing params to templates (the template might be being moved)
|
||||
if ( added instanceof ve.dm.MWTemplateModel ) {
|
||||
names = added.getParameterNames();
|
||||
params = added.getParameters();
|
||||
for ( i = 0, len = names.length; i < len; i++ ) {
|
||||
this.onAddParameter( params[names[i]] );
|
||||
}
|
||||
added.connect( this, { 'add': 'onAddParameter', 'remove': 'onRemoveParameter' } );
|
||||
}
|
||||
|
||||
// Add required params to user created templates
|
||||
if ( added instanceof ve.dm.MWTemplateModel && this.loaded ) {
|
||||
added.addRequiredParameters();
|
||||
}
|
||||
// Cleanup
|
||||
pending.placeholder.remove();
|
||||
this.pending.splice( i, 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle remove part events.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTransclusionPartModel} part Removed part
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.onRemovePart = function ( part ) {
|
||||
var name, params;
|
||||
|
||||
if ( part instanceof ve.dm.MWTemplateModel ) {
|
||||
params = part.getParameters();
|
||||
for ( name in params ) {
|
||||
this.pagedOutlineLayout.removePage( params[name].getId() );
|
||||
}
|
||||
part.disconnect( this );
|
||||
}
|
||||
this.pagedOutlineLayout.removePage( part.getId() );
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle add param events.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTemplateParameterModel} param Added param
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.onAddParameter = function ( param ) {
|
||||
var page = this.getParameterPage( param );
|
||||
page.index = this.getPageIndex( param );
|
||||
this.pagedOutlineLayout.addPage( param.getId(), page );
|
||||
var page = new ve.ui.MWTemplateParameterPage( param, param.getId(), { '$': this.$ } );
|
||||
this.bookletLayout.addPages( [ page ], this.transclusion.getIndex( param ) );
|
||||
if ( this.loaded ) {
|
||||
this.setPageByName( param.getId() );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle remove param events.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTemplateParameterModel} param Removed param
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.onRemoveParameter = function ( param ) {
|
||||
this.pagedOutlineLayout.removePage( param.getId() );
|
||||
// Return to template page
|
||||
this.setPageByName( param.getTemplate().getId() );
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle outline controls move events.
|
||||
*
|
||||
* @method
|
||||
* @param {number} places Number of places to move the selected item
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.onOutlineControlsMove = function ( places ) {
|
||||
var part, index, name,
|
||||
parts = this.transclusion.getParts(),
|
||||
item = this.pagedOutlineLayout.getOutline().getSelectedItem();
|
||||
|
||||
if ( item ) {
|
||||
name = item.getData();
|
||||
part = this.transclusion.getPartFromId( name );
|
||||
index = ve.indexOf( part, parts );
|
||||
// Auto-removes part from old location
|
||||
this.transclusion.addPart( part, index + places )
|
||||
.done( ve.bind( this.setPageByName, this, part.getId() ) );
|
||||
this.bookletLayout.removePages( [ this.bookletLayout.getPage( param.getId() ) ] );
|
||||
if ( this.loaded ) {
|
||||
// Return to template page
|
||||
this.setPageByName( param.getTemplate().getId() );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle outline controls add events.
|
||||
* Get an outlined booklet layout widget.
|
||||
*
|
||||
* @method
|
||||
* @param {string} type Type of item to add
|
||||
* @return {OO.ui.BookletLayout} Configured widget
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.onOutlineControlsAdd = function ( type ) {
|
||||
var part;
|
||||
|
||||
if ( type === 'content' ) {
|
||||
part = new ve.dm.MWTransclusionContentModel( this.transclusion, '', 'user' );
|
||||
} else if ( type === 'template' ) {
|
||||
part = new ve.dm.MWTemplatePlaceholderModel( this.transclusion, 'user' );
|
||||
}
|
||||
if ( part ) {
|
||||
this.transclusion.addPart( part, this.getPartInsertionIndex() )
|
||||
.done( ve.bind( this.setPageByName, this, part.getId() ) );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get an index for part insertion.
|
||||
*
|
||||
* @method
|
||||
* @returns {number} Index to insert new parts at
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.getPartInsertionIndex = function () {
|
||||
var parts = this.transclusion.getParts(),
|
||||
item = this.pagedOutlineLayout.getOutline().getSelectedItem();
|
||||
|
||||
if ( item ) {
|
||||
return ve.indexOf( this.transclusion.getPartFromId( item.getData() ), parts ) + 1;
|
||||
}
|
||||
return parts.length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the page index of an item.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTransclusionPartModel|ve.dm.MWTemplateParameterModel} item Part or parameter
|
||||
* @returns {number} Page index of item
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.getPageIndex = function ( item ) {
|
||||
// Build pages from parts
|
||||
var i, iLen, j, jLen, part, names,
|
||||
parts = this.transclusion.getParts(),
|
||||
index = 0;
|
||||
|
||||
// Populate pages
|
||||
for ( i = 0, iLen = parts.length; i < iLen; i++ ) {
|
||||
part = parts[i];
|
||||
if ( part === item ) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
if ( part instanceof ve.dm.MWTemplateModel ) {
|
||||
names = part.getParameterNames();
|
||||
for ( j = 0, jLen = names.length; j < jLen; j++ ) {
|
||||
if ( part.getParameter( names[j] ) === item ) {
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get page for transclusion content.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTransclusionContentModel} content Content model
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.getContentPage = function ( content ) {
|
||||
var valueFieldset, textInput, optionsFieldset, removeButton;
|
||||
|
||||
valueFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-content' ),
|
||||
'icon': 'source'
|
||||
} );
|
||||
|
||||
textInput = new OO.ui.TextInputWidget( { '$': this.$, 'multiline': true } );
|
||||
textInput.setValue( content.getValue() );
|
||||
textInput.connect( this, { 'change': function () {
|
||||
content.setValue( textInput.getValue() );
|
||||
} } );
|
||||
textInput.$element.addClass( 've-ui-mwTransclusionDialog-input' );
|
||||
valueFieldset.$element.append( textInput.$element );
|
||||
|
||||
optionsFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-options' ),
|
||||
'icon': 'settings'
|
||||
} );
|
||||
|
||||
removeButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-remove-content' ),
|
||||
'flags': ['destructive']
|
||||
} );
|
||||
removeButton.connect( this, { 'click': function () {
|
||||
content.remove();
|
||||
} } );
|
||||
optionsFieldset.$element.append( removeButton.$element );
|
||||
|
||||
return {
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-content' ),
|
||||
'icon': 'source',
|
||||
'$content': valueFieldset.$element.add( optionsFieldset.$element ),
|
||||
'moveable': true
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get page for a template.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTemplateModel} template Template model
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.getTemplatePage = function ( template ) {
|
||||
var infoFieldset, addParameterFieldset, addParameterSearch, optionsFieldset,
|
||||
removeButton,
|
||||
spec = template.getSpec(),
|
||||
label = spec.getLabel(),
|
||||
description = spec.getDescription();
|
||||
|
||||
function addParameter( name ) {
|
||||
var param;
|
||||
|
||||
if ( name ) {
|
||||
param = new ve.dm.MWTemplateParameterModel( template, name );
|
||||
template.addParameter( param );
|
||||
addParameterSearch.query.setValue();
|
||||
this.setPageByName( param.getId() );
|
||||
}
|
||||
}
|
||||
|
||||
infoFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': label,
|
||||
'icon': 'template'
|
||||
} );
|
||||
|
||||
if ( description ) {
|
||||
infoFieldset.$element.append( this.$( '<div>' ).text( description ) );
|
||||
}
|
||||
|
||||
addParameterFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-add-param' ),
|
||||
'icon': 'parameter'
|
||||
} );
|
||||
addParameterFieldset.$element.addClass( 've-ui-mwTransclusionDialog-addParameterFieldset' );
|
||||
addParameterSearch = new ve.ui.MWParameterSearchWidget( template, { '$': this.$ } );
|
||||
addParameterSearch.connect( this, { 'select': addParameter } );
|
||||
addParameterFieldset.$element.append( addParameterSearch.$element );
|
||||
|
||||
optionsFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-options' ),
|
||||
'icon': 'settings'
|
||||
} );
|
||||
|
||||
removeButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-remove-template' ),
|
||||
'flags': ['destructive']
|
||||
} );
|
||||
removeButton.connect( this, { 'click': function () {
|
||||
template.remove();
|
||||
} } );
|
||||
optionsFieldset.$element.append( removeButton.$element );
|
||||
|
||||
return {
|
||||
'label': label,
|
||||
'icon': 'template',
|
||||
'$content': infoFieldset.$element.add( addParameterFieldset.$element ).add( optionsFieldset.$element ),
|
||||
'moveable': true
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get page for a parameter.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTemplateParameterModel} parameter Parameter model
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.getParameterPage = function ( parameter ) {
|
||||
var valueFieldset, optionsFieldset, textInput, inputLabel, removeButton,
|
||||
spec = parameter.getTemplate().getSpec(),
|
||||
name = parameter.getName(),
|
||||
label = spec.getParameterLabel( name ),
|
||||
description = spec.getParameterDescription( name );
|
||||
|
||||
valueFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': label,
|
||||
'icon': 'parameter'
|
||||
} );
|
||||
|
||||
if ( description ) {
|
||||
inputLabel = new OO.ui.InputLabelWidget( {
|
||||
'$': this.$,
|
||||
'input': textInput,
|
||||
'label': description
|
||||
} );
|
||||
valueFieldset.$element.append( inputLabel.$element );
|
||||
}
|
||||
|
||||
textInput = new OO.ui.TextInputWidget( { '$': this.$, 'multiline': true } );
|
||||
textInput.setValue( parameter.getValue() );
|
||||
textInput.connect( this, { 'change': function () {
|
||||
parameter.setValue( textInput.getValue() );
|
||||
} } );
|
||||
textInput.$element.addClass( 've-ui-mwTransclusionDialog-input' );
|
||||
valueFieldset.$element.append( textInput.$element );
|
||||
|
||||
optionsFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-options' ),
|
||||
'icon': 'settings'
|
||||
} );
|
||||
|
||||
removeButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-remove-param' ),
|
||||
'flags': ['destructive']
|
||||
} );
|
||||
removeButton.connect( this, { 'click': function () {
|
||||
parameter.remove();
|
||||
} } );
|
||||
optionsFieldset.$element.append( removeButton.$element );
|
||||
|
||||
// TODO: Use spec.required
|
||||
// TODO: Use spec.deprecation
|
||||
// TODO: Use spec.default
|
||||
// TODO: Use spec.type
|
||||
|
||||
return {
|
||||
'label': label,
|
||||
'icon': 'parameter',
|
||||
'level': 1,
|
||||
'$content': valueFieldset.$element.add( optionsFieldset.$element )
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get page for a parameter.
|
||||
*
|
||||
* @method
|
||||
* @param {ve.dm.MWTemplateParameterModel} parameter Parameter model
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.getPlaceholderPage = function ( placeholder ) {
|
||||
var addTemplateFieldset, addTemplateInput, addTemplateButton, optionsFieldset, removeButton,
|
||||
label = ve.msg( 'visualeditor-dialog-transclusion-placeholder' );
|
||||
|
||||
function addTemplate() {
|
||||
var parts = placeholder.getTransclusion().getParts(),
|
||||
part = ve.dm.MWTemplateModel.newFromName(
|
||||
this.transclusion, addTemplateInput.getValue()
|
||||
);
|
||||
|
||||
this.transclusion.addPart( part, ve.indexOf( placeholder, parts ) );
|
||||
this.pending.push( { 'part': part, 'placeholder': placeholder } );
|
||||
addTemplateInput.pushPending();
|
||||
addTemplateButton.setDisabled( true );
|
||||
removeButton.setDisabled( true );
|
||||
}
|
||||
|
||||
addTemplateFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': label,
|
||||
'icon': 'template'
|
||||
} );
|
||||
addTemplateFieldset.$element.addClass( 've-ui-mwTransclusionDialog-addTemplateFieldset' );
|
||||
|
||||
addTemplateInput = new ve.ui.MWTitleInputWidget( {
|
||||
'$': this.$, '$overlay': this.$overlay, 'namespace': 10
|
||||
} );
|
||||
addTemplateButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-add-template' ),
|
||||
'flags': ['constructive'],
|
||||
'disabled': true
|
||||
} );
|
||||
addTemplateInput.connect( this, {
|
||||
'change': function () {
|
||||
addTemplateButton.setDisabled( addTemplateInput.getValue() === '' );
|
||||
},
|
||||
'enter': addTemplate
|
||||
} );
|
||||
addTemplateButton.connect( this, { 'click': addTemplate } );
|
||||
addTemplateFieldset.$element.append( addTemplateInput.$element, addTemplateButton.$element );
|
||||
|
||||
optionsFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-options' ),
|
||||
'icon': 'settings'
|
||||
} );
|
||||
|
||||
removeButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-remove-template' ),
|
||||
'flags': ['destructive']
|
||||
} );
|
||||
removeButton.connect( this, { 'click': function () {
|
||||
placeholder.remove();
|
||||
} } );
|
||||
optionsFieldset.$element.append( removeButton.$element );
|
||||
|
||||
return {
|
||||
'label': this.$( '<span>' )
|
||||
.addClass( 've-ui-mwTransclusionDialog-placeholder-label' )
|
||||
.text( label ),
|
||||
'icon': 'template',
|
||||
'$content': addTemplateFieldset.$element.add( optionsFieldset.$element )
|
||||
};
|
||||
ve.ui.MWTransclusionDialog.prototype.getBookletLayout = function () {
|
||||
return new OO.ui.BookletLayout( { '$': this.$, 'continuous': true } );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -489,13 +137,16 @@ ve.ui.MWTransclusionDialog.prototype.getPlaceholderPage = function ( placeholder
|
|||
*
|
||||
* Page names are always the ID of the part or param they represent.
|
||||
*
|
||||
* @method
|
||||
* @param {string} name Page name
|
||||
*/
|
||||
ve.ui.MWTransclusionDialog.prototype.setPageByName = function ( name ) {
|
||||
this.pagedOutlineLayout.getOutline().selectItem(
|
||||
this.pagedOutlineLayout.getOutline().getItemFromData( name )
|
||||
);
|
||||
if ( this.outlined ) {
|
||||
this.bookletLayout.getOutline().selectItem(
|
||||
this.bookletLayout.getOutline().getItemFromData( name )
|
||||
);
|
||||
} else {
|
||||
this.bookletLayout.setPage( name );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -507,34 +158,17 @@ ve.ui.MWTransclusionDialog.prototype.initialize = function () {
|
|||
|
||||
// Properties
|
||||
this.applyButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$, 'label': ve.msg( 'visualeditor-dialog-action-apply' ), 'flags': ['primary']
|
||||
} );
|
||||
this.pagedOutlineLayout = new OO.ui.PagedOutlineLayout( {
|
||||
'$': this.$,
|
||||
'editable': true,
|
||||
'adders': [
|
||||
{
|
||||
'name': 'template',
|
||||
'icon': 'template',
|
||||
'title': ve.msg( 'visualeditor-dialog-transclusion-add-template' )
|
||||
},
|
||||
{
|
||||
'name': 'content',
|
||||
'icon': 'source',
|
||||
'title': ve.msg( 'visualeditor-dialog-transclusion-add-content' )
|
||||
}
|
||||
]
|
||||
'label': ve.msg( 'visualeditor-dialog-action-apply' ),
|
||||
'flags': ['primary']
|
||||
} );
|
||||
this.bookletLayout = this.getBookletLayout();
|
||||
|
||||
// Events
|
||||
this.pagedOutlineLayout.getOutlineControls().connect( this, {
|
||||
'move': 'onOutlineControlsMove',
|
||||
'add': 'onOutlineControlsAdd'
|
||||
} );
|
||||
this.applyButton.connect( this, { 'click': [ 'close', { 'action': 'apply' } ] } );
|
||||
|
||||
// Initialization
|
||||
this.$body.append( this.pagedOutlineLayout.$element );
|
||||
this.$body.append( this.bookletLayout.$element );
|
||||
this.$foot.append( this.applyButton.$element );
|
||||
};
|
||||
|
||||
|
@ -550,17 +184,21 @@ ve.ui.MWTransclusionDialog.prototype.setup = function ( data ) {
|
|||
|
||||
// Properties
|
||||
this.transclusion = new ve.dm.MWTransclusionModel();
|
||||
this.loaded = false;
|
||||
|
||||
// Events
|
||||
this.transclusion.connect( this, { 'add': 'onAddPart', 'remove': 'onRemovePart' } );
|
||||
this.transclusion.connect( this, { 'replace': 'onReplacePart' } );
|
||||
|
||||
// Initialization
|
||||
if ( this.node instanceof ve.ce.MWTransclusionNode ) {
|
||||
this.transclusion.load( ve.copy( this.node.getModel().getAttribute( 'mw' ) ) );
|
||||
this.transclusion
|
||||
.load( ve.copy( this.node.getModel().getAttribute( 'mw' ) ) )
|
||||
.always( ve.bind( function () {
|
||||
this.loaded = true;
|
||||
}, this ) );
|
||||
} else {
|
||||
this.transclusion.addPart(
|
||||
new ve.dm.MWTemplatePlaceholderModel( this.transclusion, 'user' )
|
||||
);
|
||||
this.loaded = true;
|
||||
this.transclusion.addPart( new ve.dm.MWTemplatePlaceholderModel( this.transclusion ) );
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -598,7 +236,7 @@ ve.ui.MWTransclusionDialog.prototype.teardown = function ( data ) {
|
|||
this.transclusion.disconnect( this );
|
||||
this.transclusion.abortRequests();
|
||||
this.transclusion = null;
|
||||
this.pagedOutlineLayout.clearPages();
|
||||
this.bookletLayout.clearPages();
|
||||
this.node = null;
|
||||
this.content = null;
|
||||
|
||||
|
@ -606,6 +244,3 @@ ve.ui.MWTransclusionDialog.prototype.teardown = function ( data ) {
|
|||
ve.ui.MWDialog.prototype.teardown.call( this, data );
|
||||
};
|
||||
|
||||
/* Registration */
|
||||
|
||||
ve.ui.dialogFactory.register( ve.ui.MWTransclusionDialog );
|
||||
|
|
264
modules/ve-mw/ui/pages/ve.ui.MWCategoriesPage.js
Normal file
264
modules/ve-mw/ui/pages/ve.ui.MWCategoriesPage.js
Normal file
|
@ -0,0 +1,264 @@
|
|||
/*!
|
||||
* VisualEditor user interface MWCategoriesPage class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/*global mw*/
|
||||
|
||||
/**
|
||||
* MediaWiki meta dialog categories page.
|
||||
*
|
||||
* @class
|
||||
* @extends OO.ui.PageLayout
|
||||
*
|
||||
* @constructor
|
||||
* @param {ve.ui.Surface} surface Surface being worked on
|
||||
* @param {string} name Unique symbolic name of page
|
||||
* @param {Object} [config] Configuration options
|
||||
* @cfg {jQuery} [$overlay] Overlay to render category settings popups in
|
||||
*/
|
||||
ve.ui.MWCategoriesPage = function VeUiMWCategoriesPage( surface, name, config ) {
|
||||
// Configuration initialization
|
||||
config = ve.extendObject( config, { 'icon': 'tag' } );
|
||||
|
||||
// Parent constructor
|
||||
OO.ui.PageLayout.call( this, name, config );
|
||||
|
||||
// Properties
|
||||
this.surface = surface;
|
||||
this.metaList = this.surface.getModel().metaList;
|
||||
this.defaultSortKeyTouched = false;
|
||||
this.fallbackDefaultSortKey = mw.config.get( 'wgTitle' );
|
||||
this.label = ve.msg( 'visualeditor-dialog-meta-categories-section' );
|
||||
this.categoriesFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-meta-categories-data-label' ),
|
||||
'icon': 'tag'
|
||||
} );
|
||||
this.categoryOptionsFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-meta-categories-options' ),
|
||||
'icon': 'settings'
|
||||
} );
|
||||
this.categoryWidget = new ve.ui.MWCategoryWidget( {
|
||||
'$': this.$, '$overlay': config.$overlay
|
||||
} );
|
||||
this.defaultSortInput = new OO.ui.TextInputWidget( {
|
||||
'$': this.$, 'placeholder': this.fallbackDefaultSortKey
|
||||
} );
|
||||
this.defaultSortLabel = new OO.ui.InputLabelWidget( {
|
||||
'$': this.$,
|
||||
'input': this.defaultSortInput,
|
||||
'label': ve.msg( 'visualeditor-dialog-meta-categories-defaultsort-label' )
|
||||
} );
|
||||
|
||||
// Events
|
||||
this.metaList.connect( this, {
|
||||
'insert': 'onMetaListInsert',
|
||||
'remove': 'onMetaListRemove'
|
||||
} );
|
||||
this.categoryWidget.connect( this, {
|
||||
'newCategory': 'onNewCategory',
|
||||
'updateSortkey': 'onUpdateSortKey'
|
||||
} );
|
||||
this.defaultSortInput.connect( this, {
|
||||
'change': 'onDefaultSortChange'
|
||||
} );
|
||||
|
||||
// Initialization
|
||||
this.categoryWidget.addItems( this.getCategoryItems() );
|
||||
this.categoriesFieldset.$element.append( this.categoryWidget.$element );
|
||||
this.categoryOptionsFieldset.$element.append(
|
||||
this.defaultSortLabel.$element,
|
||||
this.defaultSortInput.$element
|
||||
);
|
||||
this.$element.append( this.categoriesFieldset.$element, this.categoryOptionsFieldset.$element );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
OO.inheritClass( ve.ui.MWCategoriesPage, OO.ui.PageLayout );
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Handle category default sort change events.
|
||||
*
|
||||
* @param {string} value Default sort value
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.onDefaultSortChange = function ( value ) {
|
||||
this.categoryWidget.setDefaultSortKey( value === '' ? this.fallbackDefaultSortKey : value );
|
||||
this.defaultSortKeyTouched = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts new category into meta list
|
||||
*
|
||||
* @param {Object} item
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.onNewCategory = function ( item ) {
|
||||
// Insert new metaList item
|
||||
this.insertMetaListItem( this.getCategoryItemForInsertion( item ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes and re-inserts updated category widget item
|
||||
*
|
||||
* @param {Object} item
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.onUpdateSortKey = function ( item ) {
|
||||
// Replace meta item with updated one
|
||||
item.metaItem.replaceWith( this.getCategoryItemForInsertion( item, item.metaItem.getElement() ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Bound to MetaList insert event for adding meta dialog components.
|
||||
*
|
||||
* @param {Object} ve.dm.MetaItem
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.onMetaListInsert = function ( metaItem ) {
|
||||
// Responsible for adding UI components
|
||||
if ( metaItem.element.type === 'mwCategory' ) {
|
||||
this.categoryWidget.addItems(
|
||||
[ this.getCategoryItemFromMetaListItem( metaItem ) ],
|
||||
this.metaList.findItem( metaItem.getOffset(), metaItem.getIndex(), 'mwCategory' )
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Bound to MetaList insert event for removing meta dialog components.
|
||||
*
|
||||
* @param {Object} ve.dm.MetaItem
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.onMetaListRemove = function ( metaItem ) {
|
||||
var item;
|
||||
|
||||
if ( metaItem.element.type === 'mwCategory' ) {
|
||||
item = this.getCategoryItemFromMetaListItem( metaItem );
|
||||
this.categoryWidget.removeItems( [item.value] );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get default sort key item.
|
||||
*
|
||||
* @returns {string} Default sort key item
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.getDefaultSortKeyItem = function () {
|
||||
var items = this.metaList.getItemsInGroup( 'mwDefaultSort' );
|
||||
return items.length ? items[0] : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get array of category items from meta list
|
||||
*
|
||||
* @returns {Object[]} items
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.getCategoryItems = function () {
|
||||
var i,
|
||||
items = [],
|
||||
categories = this.metaList.getItemsInGroup( 'mwCategory' );
|
||||
|
||||
// Loop through MwCategories and build out items
|
||||
for ( i = 0; i < categories.length; i++ ) {
|
||||
items.push( this.getCategoryItemFromMetaListItem( categories[i] ) );
|
||||
}
|
||||
return items;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets category item from meta list item
|
||||
*
|
||||
* @param {Object} ve.dm.MWCategoryMetaItem
|
||||
* @returns {Object} item
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.getCategoryItemFromMetaListItem = function ( metaItem ) {
|
||||
var title = mw.Title.newFromText( metaItem.element.attributes.category ),
|
||||
value = title ? title.getMainText() : '';
|
||||
|
||||
return {
|
||||
'name': metaItem.element.attributes.category,
|
||||
'value': value,
|
||||
// TODO: sortkey is lcase, make consistent throughout CategoryWidget
|
||||
'sortKey': metaItem.element.attributes.sortkey,
|
||||
'metaItem': metaItem
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get metaList like object to insert from item
|
||||
*
|
||||
* @param {Object} item category widget item
|
||||
* @param {Object} [oldData] Metadata object that was previously associated with this item, if any
|
||||
* @returns {Object} metaBase
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.getCategoryItemForInsertion = function ( item, oldData ) {
|
||||
var newData = {
|
||||
'attributes': { 'category': item.name, 'sortkey': item.sortKey || '' },
|
||||
'type': 'mwCategory'
|
||||
};
|
||||
if ( oldData ) {
|
||||
return ve.extendObject( {}, oldData, newData );
|
||||
}
|
||||
return newData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts a meta list item
|
||||
*
|
||||
* @param {Object} metaBase meta list insert object
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.insertMetaListItem = function ( metaBase ) {
|
||||
this.metaList.insertMeta( metaBase );
|
||||
};
|
||||
|
||||
/**
|
||||
* Set up the page. This is called when the MWMetaDialog is set up.
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.setup = function () {
|
||||
var defaultSortKeyItem = this.getDefaultSortKeyItem();
|
||||
|
||||
this.defaultSortInput.setValue(
|
||||
defaultSortKeyItem ? defaultSortKeyItem.getAttribute( 'content' ) : ''
|
||||
);
|
||||
this.defaultSortKeyTouched = false;
|
||||
|
||||
// Update input position once visible
|
||||
setTimeout( ve.bind( function () {
|
||||
this.categoryWidget.fitInput();
|
||||
}, this ) );
|
||||
};
|
||||
|
||||
/**
|
||||
* Tear down the page. This is called when the MWMetaDialog is torn down.
|
||||
*/
|
||||
ve.ui.MWCategoriesPage.prototype.teardown = function () {
|
||||
var currentDefaultSortKeyItem = this.getDefaultSortKeyItem(),
|
||||
newDefaultSortKey = this.defaultSortInput.getValue(),
|
||||
newDefaultSortKeyData = {
|
||||
'type': 'mwDefaultSort',
|
||||
'attributes': { 'content': newDefaultSortKey }
|
||||
};
|
||||
|
||||
// Alter the default sort key iff it's been touched & is actually different
|
||||
if ( this.defaultSortKeyTouched ) {
|
||||
if ( newDefaultSortKey === '' ) {
|
||||
if ( currentDefaultSortKeyItem ) {
|
||||
currentDefaultSortKeyItem.remove();
|
||||
}
|
||||
} else {
|
||||
if ( !currentDefaultSortKeyItem ) {
|
||||
this.metaList.insertMeta( newDefaultSortKeyData );
|
||||
} else if ( currentDefaultSortKeyItem.getAttribute( 'content' ) !== newDefaultSortKey ) {
|
||||
currentDefaultSortKeyItem.replaceWith(
|
||||
ve.extendObject( true, {},
|
||||
currentDefaultSortKeyItem.getElement(),
|
||||
newDefaultSortKeyData
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
173
modules/ve-mw/ui/pages/ve.ui.MWLanguagesPage.js
Normal file
173
modules/ve-mw/ui/pages/ve.ui.MWLanguagesPage.js
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*!
|
||||
* VisualEditor user interface MWLanguagesPage class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/*global mw*/
|
||||
|
||||
/**
|
||||
* MediaWiki meta dialog Languages page.
|
||||
*
|
||||
* @class
|
||||
* @extends OO.ui.PageLayout
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} name Unique symbolic name of page
|
||||
* @param {Object} [config] Configuration options
|
||||
*/
|
||||
ve.ui.MWLanguagesPage = function VeUiMWLanguagesPage( name, config ) {
|
||||
// Configuration initialization
|
||||
config = ve.extendObject( config, { 'icon': 'language' } );
|
||||
|
||||
// Parent constructor
|
||||
OO.ui.PageLayout.call( this, name, config );
|
||||
|
||||
// Properties
|
||||
this.label = ve.msg( 'visualeditor-dialog-meta-languages-section' );
|
||||
this.languagesFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-meta-languages-label' ),
|
||||
'icon': 'language'
|
||||
} );
|
||||
|
||||
// Initialization
|
||||
this.languagesFieldset.$element.append(
|
||||
this.$( '<span>' )
|
||||
.text( ve.msg( 'visualeditor-dialog-meta-languages-readonlynote' ) )
|
||||
);
|
||||
this.$element.append( this.languagesFieldset.$element );
|
||||
|
||||
this.getAllLanguageItems().done( ve.bind( this.onLoadLanguageData, this ) );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
OO.inheritClass( ve.ui.MWLanguagesPage, OO.ui.PageLayout );
|
||||
|
||||
/* Methods */
|
||||
|
||||
ve.ui.MWLanguagesPage.prototype.onLoadLanguageData = function ( languages ) {
|
||||
var i, $languagesTable = this.$( '<table>' ), languageslength = languages.length;
|
||||
|
||||
$languagesTable
|
||||
.addClass( 've-ui-MWLanguagesPage-languages-table' )
|
||||
.append( this.$( '<tr>' )
|
||||
.append(
|
||||
this.$( '<th>' )
|
||||
.append( ve.msg( 'visualeditor-dialog-meta-languages-code-label' ) )
|
||||
)
|
||||
.append(
|
||||
this.$( '<th>' )
|
||||
.append( ve.msg( 'visualeditor-dialog-meta-languages-link-label' ) )
|
||||
)
|
||||
);
|
||||
|
||||
for ( i = 0; i < languageslength; i++ ) {
|
||||
languages[i].safelang = languages[i].lang;
|
||||
languages[i].dir = 'auto';
|
||||
if ( $.uls ) {
|
||||
// site codes don't always represent official language codes
|
||||
// using real language code instead of a dummy ('redirect' in ULS' terminology)
|
||||
languages[i].safelang = $.uls.data.isRedirect( languages[i].lang ) || languages[i].lang;
|
||||
languages[i].dir = $.uls.data.getDir( languages[i].safelang );
|
||||
}
|
||||
$languagesTable
|
||||
.append( this.$( '<tr>' )
|
||||
.append( this.$( '<td>' ).append( languages[i].lang ) )
|
||||
.append( this.$( '<td>' ).append( languages[i].title )
|
||||
.attr( 'lang', languages[i].safelang )
|
||||
.attr( 'dir', languages[i].dir ) )
|
||||
);
|
||||
}
|
||||
|
||||
this.languagesFieldset.$element.append( $languagesTable );
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle language items being loaded.
|
||||
*/
|
||||
ve.ui.MWLanguagesPage.prototype.onAllLanuageItemsSuccess = function ( deferred, response ) {
|
||||
var i, iLen, languages = [], langlinks = response.query.pages[response.query.pageids[0]].langlinks;
|
||||
if ( langlinks ) {
|
||||
for ( i = 0, iLen = langlinks.length; i < iLen; i++ ) {
|
||||
languages.push( {
|
||||
'lang': langlinks[i].lang,
|
||||
'title': langlinks[i]['*'],
|
||||
'metaItem': null
|
||||
} );
|
||||
}
|
||||
}
|
||||
deferred.resolve( languages );
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets language item from meta list item
|
||||
*
|
||||
* @param {Object} ve.dm.MWLanguageMetaItem
|
||||
* @returns {Object} item
|
||||
*/
|
||||
ve.ui.MWLanguagesPage.prototype.getLanguageItemFromMetaListItem = function ( metaItem ) {
|
||||
// TODO: get real values from metaItem once Parsoid actually provides them - bug 48970
|
||||
return {
|
||||
'lang': 'lang',
|
||||
'title': 'title',
|
||||
'metaItem': metaItem
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get array of language items from meta list
|
||||
*
|
||||
* @returns {Object[]} items
|
||||
*/
|
||||
ve.ui.MWLanguagesPage.prototype.getLocalLanguageItems = function () {
|
||||
var i,
|
||||
items = [],
|
||||
languages = this.metaList.getItemsInGroup( 'mwLanguage' ),
|
||||
languageslength = languages.length;
|
||||
|
||||
// Loop through MWLanguages and build out items
|
||||
|
||||
for ( i = 0; i < languageslength; i++ ) {
|
||||
items.push( this.getLanguageItemFromMetaListItem( languages[i] ) );
|
||||
}
|
||||
return items;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get array of language items from meta list
|
||||
*
|
||||
* @returns {jQuery.Promise}
|
||||
*/
|
||||
ve.ui.MWLanguagesPage.prototype.getAllLanguageItems = function () {
|
||||
var deferred = $.Deferred();
|
||||
// TODO: Detect paging token if results exceed limit
|
||||
$.ajax( {
|
||||
'url': mw.util.wikiScript( 'api' ),
|
||||
'data': {
|
||||
'action': 'query',
|
||||
'prop': 'langlinks',
|
||||
'lllimit': 500,
|
||||
'titles': mw.config.get( 'wgTitle' ),
|
||||
'indexpageids': 1,
|
||||
'format': 'json'
|
||||
},
|
||||
'dataType': 'json',
|
||||
'type': 'POST',
|
||||
// Wait up to 100 seconds before giving up
|
||||
'timeout': 100000,
|
||||
'cache': 'false'
|
||||
} )
|
||||
.done( ve.bind( this.onAllLanuageItemsSuccess, this, deferred ) )
|
||||
.fail( ve.bind( this.onAllLanuageItemsError, this, deferred ) );
|
||||
return deferred.promise();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle language items failing to be loaded.
|
||||
*
|
||||
* TODO: This error function should probably not be empty.
|
||||
*/
|
||||
ve.ui.MWLanguagesPage.prototype.onAllLanuageItemsError = function () {};
|
79
modules/ve-mw/ui/pages/ve.ui.MWTemplatePage.js
Normal file
79
modules/ve-mw/ui/pages/ve.ui.MWTemplatePage.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*!
|
||||
* VisualEditor user interface MWTemplatePage class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* MediaWiki transclusion dialog template page.
|
||||
*
|
||||
* @class
|
||||
* @extends OO.ui.PageLayout
|
||||
*
|
||||
* @constructor
|
||||
* @param {ve.dm.MWTemplateModel} parameter Template
|
||||
* @param {string} name Unique symbolic name of page
|
||||
* @param {Object} [config] Configuration options
|
||||
*/
|
||||
ve.ui.MWTemplatePage = function VeUiMWTemplatePage( template, name, config ) {
|
||||
// Configuration initialization
|
||||
config = ve.extendObject( config, { 'icon': 'template', 'moveable': true, 'level': 0 } );
|
||||
|
||||
// Parent constructor
|
||||
OO.ui.PageLayout.call( this, name, config );
|
||||
|
||||
// Properties
|
||||
this.template = template;
|
||||
this.spec = this.template.getSpec();
|
||||
this.label = this.spec.getLabel();
|
||||
this.addParameterSearch = new ve.ui.MWParameterSearchWidget( this.template, { '$': this.$ } )
|
||||
.connect( this, { 'select': 'onParameterSelect' } );
|
||||
this.removeButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-remove-template' ),
|
||||
'flags': ['destructive'],
|
||||
'classes': [ 've-ui-mwTransclusionDialog-removeButton' ]
|
||||
} )
|
||||
.connect( this, { 'click': 'onRemoveButtonClick' } );
|
||||
this.infoFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': this.spec.getLabel(),
|
||||
'icon': 'template',
|
||||
'$content': this.$( '<div>' ).text( this.template.getSpec().getDescription() || '' )
|
||||
} );
|
||||
this.addParameterFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-add-param' ),
|
||||
'icon': 'parameter',
|
||||
'classes': [ 've-ui-mwTransclusionDialog-addParameterFieldset' ],
|
||||
'$content': this.addParameterSearch.$element
|
||||
} );
|
||||
|
||||
// Initialization
|
||||
this.$element.append(
|
||||
this.infoFieldset.$element,
|
||||
this.addParameterFieldset.$element,
|
||||
this.removeButton.$element
|
||||
);
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
OO.inheritClass( ve.ui.MWTemplatePage, OO.ui.PageLayout );
|
||||
|
||||
/* Methods */
|
||||
|
||||
ve.ui.MWTemplatePage.prototype.onParameterSelect = function ( name ) {
|
||||
var param;
|
||||
|
||||
if ( name ) {
|
||||
param = new ve.dm.MWTemplateParameterModel( this.template, name );
|
||||
this.template.addParameter( param );
|
||||
this.addParameterSearch.query.setValue();
|
||||
}
|
||||
};
|
||||
|
||||
ve.ui.MWTemplatePage.prototype.onRemoveButtonClick = function () {
|
||||
this.template.remove();
|
||||
};
|
77
modules/ve-mw/ui/pages/ve.ui.MWTemplateParameterPage.js
Normal file
77
modules/ve-mw/ui/pages/ve.ui.MWTemplateParameterPage.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*!
|
||||
* VisualEditor user interface MWTemplateParameterPage class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* MediaWiki transclusion dialog template page.
|
||||
*
|
||||
* @class
|
||||
* @extends OO.ui.PageLayout
|
||||
*
|
||||
* @constructor
|
||||
* @param {ve.dm.MWTemplateParameterModel} parameter Template parameter
|
||||
* @param {string} name Unique symbolic name of page
|
||||
* @param {Object} [config] Configuration options
|
||||
*/
|
||||
ve.ui.MWTemplateParameterPage = function VeUiMWTemplateParameter( parameter, name, config ) {
|
||||
// Configuration initialization
|
||||
config = ve.extendObject( config, { 'icon': 'parameter', 'moveable': false, 'level': 1 } );
|
||||
|
||||
// Parent constructor
|
||||
OO.ui.PageLayout.call( this, name, config );
|
||||
|
||||
// Properties
|
||||
this.parameter = parameter;
|
||||
this.spec = parameter.getTemplate().getSpec();
|
||||
this.label = this.spec.getParameterLabel( this.parameter.getName() );
|
||||
this.textInput = new OO.ui.TextInputWidget( {
|
||||
'$': this.$,
|
||||
'multiline': true,
|
||||
'classes': [ 've-ui-mwTransclusionDialog-input' ]
|
||||
} )
|
||||
.setValue( this.parameter.getValue() )
|
||||
.connect( this, { 'change': 'onTextInputChange' } );
|
||||
this.inputLabel = new OO.ui.InputLabelWidget( {
|
||||
'$': this.$,
|
||||
'input': this.textInput,
|
||||
'label': this.spec.getParameterDescription( this.parameter.getName() ) || ''
|
||||
} );
|
||||
this.removeButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-remove-param' ),
|
||||
'flags': ['destructive'],
|
||||
'classes': [ 've-ui-mwTransclusionDialog-removeButton' ]
|
||||
} )
|
||||
.connect( this, { 'click': 'onRemoveButtonClick' } );
|
||||
this.valueFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': this.spec.getParameterLabel( this.parameter.getName() ),
|
||||
'icon': 'parameter',
|
||||
'$content': this.inputLabel.$element.add( this.textInput.$element )
|
||||
} );
|
||||
|
||||
// TODO: Use spec.required
|
||||
// TODO: Use spec.deprecation
|
||||
// TODO: Use spec.default
|
||||
// TODO: Use spec.type
|
||||
|
||||
// Initialization
|
||||
this.$element.append( this.valueFieldset.$element, this.removeButton.$element );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
OO.inheritClass( ve.ui.MWTemplateParameterPage, OO.ui.PageLayout );
|
||||
|
||||
/* Methods */
|
||||
|
||||
ve.ui.MWTemplateParameterPage.prototype.onTextInputChange = function () {
|
||||
this.parameter.setValue( this.textInput.getValue() );
|
||||
};
|
||||
|
||||
ve.ui.MWTemplateParameterPage.prototype.onRemoveButtonClick = function () {
|
||||
this.parameter.remove();
|
||||
};
|
87
modules/ve-mw/ui/pages/ve.ui.MWTemplatePlaceholderPage.js
Normal file
87
modules/ve-mw/ui/pages/ve.ui.MWTemplatePlaceholderPage.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*!
|
||||
* VisualEditor user interface MWTemplatePlaceholderPage class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* MediaWiki transclusion dialog placeholder page.
|
||||
*
|
||||
* @class
|
||||
* @extends OO.ui.PageLayout
|
||||
*
|
||||
* @constructor
|
||||
* @param {ve.dm.MWTemplatePlaceholderModel} placeholder Template placeholder
|
||||
* @param {string} name Unique symbolic name of page
|
||||
* @param {Object} [config] Configuration options
|
||||
*/
|
||||
ve.ui.MWTemplatePlaceholderPage = function VeUiMWTemplatePlaceholder( placeholder, name, config ) {
|
||||
// Configuration initialization
|
||||
config = ve.extendObject( config, { 'icon': 'template', 'moveable': true, 'level': 0 } );
|
||||
|
||||
// Parent constructor
|
||||
OO.ui.PageLayout.call( this, name, config );
|
||||
|
||||
// Properties
|
||||
this.placeholder = placeholder;
|
||||
this.label = this.$( '<span>' )
|
||||
.addClass( 've-ui-mwTransclusionDialog-placeholder-label' )
|
||||
.text( ve.msg( 'visualeditor-dialog-transclusion-placeholder' ) );
|
||||
this.addTemplateInput = new ve.ui.MWTitleInputWidget( {
|
||||
'$': this.$, '$overlay': this.$overlay, 'namespace': 10
|
||||
} )
|
||||
.connect( this, {
|
||||
'change': 'onTemplateInputChange',
|
||||
'enter': 'onAddTemplate'
|
||||
} );
|
||||
this.addTemplateButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-add-template' ),
|
||||
'flags': ['constructive'],
|
||||
'disabled': true
|
||||
} )
|
||||
.connect( this, { 'click': 'onAddTemplate' } );
|
||||
this.removeButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-remove-template' ),
|
||||
'flags': ['destructive'],
|
||||
'classes': [ 've-ui-mwTransclusionDialog-removeButton' ]
|
||||
} )
|
||||
.connect( this, { 'click': 'onRemoveButtonClick' } );
|
||||
this.addTemplateFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-placeholder' ),
|
||||
'icon': 'template',
|
||||
'classes': [ 've-ui-mwTransclusionDialog-addTemplateFieldset' ],
|
||||
'$content': this.addTemplateInput.$element.add( this.addTemplateButton.$element )
|
||||
} );
|
||||
|
||||
// Initialization
|
||||
this.$element.append( this.addTemplateFieldset.$element, this.removeButton.$element );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
OO.inheritClass( ve.ui.MWTemplatePlaceholderPage, OO.ui.PageLayout );
|
||||
|
||||
/* Methods */
|
||||
|
||||
ve.ui.MWTemplatePlaceholderPage.prototype.onAddTemplate = function () {
|
||||
var transclusion = this.placeholder.getTransclusion(),
|
||||
parts = this.placeholder.getTransclusion().getParts(),
|
||||
part = ve.dm.MWTemplateModel.newFromName( transclusion, this.addTemplateInput.getValue() );
|
||||
|
||||
transclusion.replacePart( this.placeholder, part, ve.indexOf( this.placeholder, parts ) );
|
||||
this.addTemplateInput.pushPending();
|
||||
this.addTemplateButton.setDisabled( true );
|
||||
this.removeButton.setDisabled( true );
|
||||
};
|
||||
|
||||
ve.ui.MWTemplatePlaceholderPage.prototype.onTemplateInputChange = function ( value ) {
|
||||
this.addTemplateButton.setDisabled( value === '' );
|
||||
};
|
||||
|
||||
ve.ui.MWTemplatePlaceholderPage.prototype.onRemoveButtonClick = function () {
|
||||
this.placeholder.remove();
|
||||
};
|
67
modules/ve-mw/ui/pages/ve.ui.MWTransclusionContentPage.js
Normal file
67
modules/ve-mw/ui/pages/ve.ui.MWTransclusionContentPage.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*!
|
||||
* VisualEditor user interface MWTransclusionContentPage class.
|
||||
*
|
||||
* @copyright 2011-2013 VisualEditor Team and others; see AUTHORS.txt
|
||||
* @license The MIT License (MIT); see LICENSE.txt
|
||||
*/
|
||||
|
||||
/**
|
||||
* MediaWiki transclusion dialog content page.
|
||||
*
|
||||
* @class
|
||||
* @extends OO.ui.PageLayout
|
||||
*
|
||||
* @constructor
|
||||
* @param {ve.dm.MWTransclusionContentModel} content Transclusion content
|
||||
* @param {string} name Unique symbolic name of page
|
||||
* @param {Object} [config] Configuration options
|
||||
*/
|
||||
ve.ui.MWTransclusionContentPage = function VeUiMWTransclusionContent( content, name, config ) {
|
||||
// Configuration initialization
|
||||
config = ve.extendObject( config, { 'icon': 'source', 'moveable': true, 'level': 0 } );
|
||||
|
||||
// Parent constructor
|
||||
OO.ui.PageLayout.call( this, name, config );
|
||||
|
||||
// Properties
|
||||
this.content = content;
|
||||
this.label = ve.msg( 'visualeditor-dialog-transclusion-content' );
|
||||
this.textInput = new OO.ui.TextInputWidget( {
|
||||
'$': this.$,
|
||||
'multiline': true,
|
||||
'classes': [ 've-ui-mwTransclusionDialog-input' ]
|
||||
} )
|
||||
.setValue( this.content.getValue() )
|
||||
.connect( this, { 'change': 'onTextInputChange' } );
|
||||
this.removeButton = new OO.ui.PushButtonWidget( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-remove-content' ),
|
||||
'flags': [ 'destructive' ],
|
||||
'classes': [ 've-ui-mwTransclusionDialog-removeButton' ]
|
||||
} )
|
||||
.connect( this, { 'click': 'onRemoveButtonClick' } );
|
||||
this.valueFieldset = new OO.ui.FieldsetLayout( {
|
||||
'$': this.$,
|
||||
'label': ve.msg( 'visualeditor-dialog-transclusion-content' ),
|
||||
'icon': 'source',
|
||||
'$content': this.textInput.$element
|
||||
} );
|
||||
|
||||
// Initialization
|
||||
this.$element.append( this.valueFieldset.$element, this.removeButton.$element );
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
OO.inheritClass( ve.ui.MWTransclusionContentPage, OO.ui.PageLayout );
|
||||
|
||||
/* Methods */
|
||||
|
||||
|
||||
ve.ui.MWTransclusionContentPage.prototype.onTextInputChange = function () {
|
||||
this.content.setValue( this.textInput.getValue() );
|
||||
};
|
||||
|
||||
ve.ui.MWTransclusionContentPage.prototype.onRemoveButtonClick = function () {
|
||||
this.content.remove();
|
||||
};
|
|
@ -27,6 +27,16 @@
|
|||
font-style: italic;
|
||||
}
|
||||
|
||||
.oo-ui-fieldsetLayout + .ve-ui-mwTransclusionDialog-addParameterFieldset {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ve-ui-mwTransclusionDialog-addParameterFieldset > legend.oo-ui-labeledElement-label {
|
||||
font-size: 1.25em;
|
||||
padding-left: 2em;
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
.ve-ui-mwTransclusionDialog-addParameterFieldset .ve-ui-mwParameterSearchWidget {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
@ -39,6 +49,12 @@
|
|||
padding-bottom: 5em;
|
||||
}
|
||||
|
||||
.ve-ui-mwTransclusionDialog-removeButton {
|
||||
position: absolute;
|
||||
right: 1.5em;
|
||||
top: 1em;
|
||||
}
|
||||
|
||||
/* ve.ui.MWMetaDialog */
|
||||
|
||||
.ve-ui-mwMetaDialog-languages-table {
|
||||
|
|
Loading…
Reference in a new issue