2 * VisualEditor user interface MWCitationDialog class.
4 * @copyright 2011-2017 Cite VisualEditor Team and others; see AUTHORS.txt
5 * @license The MIT License (MIT); see LICENSE.txt
9 * Dialog for inserting and editing MediaWiki citations.
12 * @extends ve.ui.MWTemplateDialog
15 * @param {Object} [config] Configuration options
17 ve.ui.MWCitationDialog = function VeUiMWCitationDialog( config ) {
19 ve.ui.MWCitationDialog.super.call( this, config );
22 this.referenceModel = null;
23 this.referenceNode = null;
29 OO.inheritClass( ve.ui.MWCitationDialog, ve.ui.MWTemplateDialog );
31 /* Static Properties */
33 ve.ui.MWCitationDialog.static.name = 'citation';
38 * Get the reference node to be edited.
40 * @return {ve.dm.MWReferenceNode|null} Reference node to be edited, null if none exists
42 ve.ui.MWCitationDialog.prototype.getReferenceNode = function () {
43 var selectedNode = this.getFragment().getSelectedNode();
45 if ( selectedNode instanceof ve.dm.MWReferenceNode ) {
55 ve.ui.MWCitationDialog.prototype.getSelectedNode = function () {
56 var branches, leaves, transclusionNode,
57 referenceNode = this.getReferenceNode();
59 if ( referenceNode ) {
60 branches = referenceNode.getInternalItem().getChildren();
62 branches.length === 1 &&
63 branches[ 0 ].canContainContent() &&
64 branches[ 0 ].getChildren();
65 transclusionNode = leaves &&
66 leaves.length === 1 &&
67 leaves[ 0 ] instanceof ve.dm.MWTransclusionNode &&
71 return transclusionNode || null;
77 ve.ui.MWCitationDialog.prototype.initialize = function ( data ) {
79 ve.ui.MWCitationDialog.super.prototype.initialize.call( this, data );
81 // HACK: Use the same styling as single-mode transclusion dialog - this should be generalized
82 this.$content.addClass( 've-ui-mwTransclusionDialog-single' );
88 ve.ui.MWCitationDialog.prototype.getSetupProcess = function ( data ) {
89 return ve.ui.MWCitationDialog.super.prototype.getSetupProcess.call( this, data )
92 this.inDialog = data.inDialog;
95 if ( this.selectedNode ) {
96 this.referenceNode = this.getReferenceNode();
97 if ( this.referenceNode ) {
98 this.referenceModel = ve.dm.MWReferenceModel.static.newFromReferenceNode(
103 this.actions.forEach( { actions: 'insert' }, function ( action ) {
104 action.setLabel( ve.msg( 'visualeditor-dialog-action-insert' ) );
109 ve.ui.MWCitationDialog.prototype.onTransclusionReady = function () {
111 ve.ui.MWCitationDialog.super.prototype.onTransclusionReady.call( this );
113 if ( !this.hasUsefulParameter() ) {
114 this.actions.setAbilities( { apply: false, insert: false } );
121 ve.ui.MWCitationDialog.prototype.setPageByName = function ( param ) {
122 var hasUsefulParameter = this.hasUsefulParameter();
125 ve.ui.MWCitationDialog.super.prototype.setPageByName.call( this, param );
127 this.actions.setAbilities( { apply: hasUsefulParameter, insert: hasUsefulParameter } );
133 ve.ui.MWCitationDialog.prototype.onAddParameterBeforeLoad = function ( page ) {
135 hasUsefulParameter = this.hasUsefulParameter();
138 page.valueInput.on( 'change', function () {
139 dialog.actions.setAbilities( { apply: hasUsefulParameter, insert: hasUsefulParameter } );
144 * Works out whether there are any set parameters that aren't just placeholders
148 ve.ui.MWCitationDialog.prototype.hasUsefulParameter = function () {
149 var foundUseful = false;
150 $.each( this.bookletLayout.pages, function () {
152 this instanceof ve.ui.MWParameterPage &&
153 ( !this.preLoad || this.valueInput.getValue() !== '' )
165 ve.ui.MWCitationDialog.prototype.getActionProcess = function ( action ) {
168 this.inDialog !== 'reference' &&
169 ( action === 'apply' || action === 'insert' )
171 return new OO.ui.Process( function () {
172 var deferred = $.Deferred();
173 dialog.checkRequiredParameters().done( function () {
175 surfaceModel = dialog.getFragment().getSurface(),
176 doc = surfaceModel.getDocument(),
177 internalList = doc.getInternalList(),
178 obj = dialog.transclusionModel.getPlainObject();
180 if ( !dialog.referenceModel ) {
181 // Collapse returns a new fragment, so update dialog.fragment
182 dialog.fragment = dialog.getFragment().collapseToEnd();
183 dialog.referenceModel = new ve.dm.MWReferenceModel( doc );
184 dialog.referenceModel.insertInternalItem( surfaceModel );
185 dialog.referenceModel.insertReferenceNode( dialog.getFragment() );
188 item = dialog.referenceModel.findInternalItem( surfaceModel );
190 if ( dialog.selectedNode ) {
191 dialog.transclusionModel.updateTransclusionNode(
192 surfaceModel, dialog.selectedNode
194 } else if ( obj !== null ) {
195 dialog.transclusionModel.insertTransclusionNode(
196 // HACK: This is trying to place the cursor inside the first content branch
197 // node but this theoretically not a safe assumption - in practice, the
198 // citation dialog will only reach this code if we are inserting (not
199 // updating) a transclusion, so the referenceModel will have already
200 // initialized the internal node with a paragraph - getting the range of the
201 // item covers the entire paragraph so we have to get the range of it's
202 // first (and empty) child
203 dialog.getFragment().clone(
204 new ve.dm.LinearSelection( doc, item.getChildren()[ 0 ].getRange() )
211 // HACK: Scorch the earth - this is only needed because without it, the references list
212 // won't re-render properly, and can be removed once someone fixes that
213 dialog.referenceModel.setDocument(
215 internalList.getItemNode( dialog.referenceModel.getListIndex() ).getRange()
218 dialog.referenceModel.updateInternalItem( surfaceModel );
220 dialog.close( { action: action } );
221 } ).always( deferred.resolve );
228 return ve.ui.MWCitationDialog.super.prototype.getActionProcess.call( this, action );
234 ve.ui.MWCitationDialog.prototype.getTeardownProcess = function ( data ) {
235 return ve.ui.MWCitationDialog.super.prototype.getTeardownProcess.call( this, data )
236 .first( function () {
238 this.referenceModel = null;
239 this.referenceNode = null;