mediawiki-extensions-Visual.../modules/ve-mw/ui/dialogs/ve.ui.MWTransclusionDialog.js

643 lines
20 KiB
JavaScript
Raw Normal View History

/*!
* VisualEditor user interface MWTransclusionDialog class.
*
* @copyright See AUTHORS.txt
* @license The MIT License (MIT); see LICENSE.txt
*/
/**
* Dialog for inserting and editing MediaWiki transclusions, i.e. a sequence of one or more template
* invocations that strictly belong to each other (e.g. because they are unbalanced), possibly
* mixed with raw wikitext snippets.
*
* Note the base class {@see ve.ui.MWTemplateDialog} alone does not allow to manage more than a
* single template invocation. Most of the code for this feature set is exclusive to this subclass.
*
* @class
* @extends ve.ui.MWTemplateDialog
*
* @constructor
* @param {Object} [config] Configuration options
*/
ve.ui.MWTransclusionDialog = function VeUiMWTransclusionDialog( config ) {
// Parent constructor
ve.ui.MWTransclusionDialog.super.call( this, config );
The great directory split of 2013 Move all MW-specific files into the ve-mw directory, in preparation for moving them out into a separate repo. All MW-specific files were moved into a parallel directory structure in modules/ve-mw . Files with both generic and MW-specific things were split up. Files in ve/init/mw/ were moved to ve-mw/init/ rather than ve-mw/init/mw ; they're still named ve.init.mw.* but we should change that. Some of the test files for core classes had MW-specific test cases, so those were split up and the test runner was duplicated; we should refactor our tests to use data providers so we can add cases more easily. Split files: * ve.ce.Node.css * ve.ce.ContentBranchNode.test.js (MWEntityNode) * ve.ce.Document.test.js (some core test cases genericized) * ve.dm.InternalList.test.js (uses mwReference test document) * ve.dm.SurfaceFragment.test.js, ve.ui.FormatAction.test.js ** Made core tests use heading instead of mwHeading ** Updated core tests because normal headings don't break out of lists ** Moved test runners into ve.test.utils.js * ve.ui.Icons-*.css * ve.ui.Dialog.css (MW parts into ve.ui.MWDialog.css) * ve.ui.Tool.css * ve.ui.Widget.css (move ve-ui-rtl and ve-ui-ltr to ve.ui.css) ve.dm.Converter.test.js: Moved runner functions into ve.test.utils.js ve.dm.example.js: * Refactored createExampleDocument so mwExample can use it * Removed wgExtensionAssetsPath detection, moved into mw-preload.js * Genericized withMeta example document (original version copied to mwExample) * Moved references example document to mwExample ve.dm.mwExample.js: * Move withMeta and references example documents from ve.dm.example.js * Add createExampleDocument function ve-mw/test/index.php: Runner for MW-specific tests only ve-mw/test/mw-preload.js: Sets VE_TESTDIR for Special:JavaScriptTest only ve.ui.Window.js: * Remove magic path interpolation in addLocalStyleSheets() * Pass full(er) paths to addLocalStyleSheets(), here and in subclasses ve.ui.MWDialog.js: Subclass of Dialog that adds MW versions of stylesheets ve.ui.MW*Dialog.js: * Subclass MWDialog rather than Dialog * Load both core and MW versions of stylesheets that have both ve.ui.PagedDialog.js: Converted to a mixin rather than an abstract base class * Don't inherit ve.ui.Dialog * Rather than overriding initialize(), provide initializePages() which the host class is supposed to call from its initialize() * Rename onOutlineSelect to onPageOutlineSelect ve.ui.MWMetaDialog.js, ve.ui.MWTransclusionDialog.js: * Use PagedDialog as a mixin rather than a base class, inherit MWDialog bullet-icon.png: Unused, deleted Stuff we should do later: * Refactor tests to use data providers * Write utility function for SVG compat check * Separate omnibus CSS files such as ve.ui.Widget.css * Separate omnibus RL modules * Use icon classes in ViewPageTarget Change-Id: I1b28f8ba7f2d2513e5c634927a854686fb9dd5a5
2013-07-03 01:30:10 +00:00
// Properties
this.isSidebarExpanded = null;
this.hotkeyTriggers = {};
this.$element.on( 'keydown', this.onKeyDown.bind( this ) );
};
/* Inheritance */
OO.inheritClass( ve.ui.MWTransclusionDialog, ve.ui.MWTemplateDialog );
The great directory split of 2013 Move all MW-specific files into the ve-mw directory, in preparation for moving them out into a separate repo. All MW-specific files were moved into a parallel directory structure in modules/ve-mw . Files with both generic and MW-specific things were split up. Files in ve/init/mw/ were moved to ve-mw/init/ rather than ve-mw/init/mw ; they're still named ve.init.mw.* but we should change that. Some of the test files for core classes had MW-specific test cases, so those were split up and the test runner was duplicated; we should refactor our tests to use data providers so we can add cases more easily. Split files: * ve.ce.Node.css * ve.ce.ContentBranchNode.test.js (MWEntityNode) * ve.ce.Document.test.js (some core test cases genericized) * ve.dm.InternalList.test.js (uses mwReference test document) * ve.dm.SurfaceFragment.test.js, ve.ui.FormatAction.test.js ** Made core tests use heading instead of mwHeading ** Updated core tests because normal headings don't break out of lists ** Moved test runners into ve.test.utils.js * ve.ui.Icons-*.css * ve.ui.Dialog.css (MW parts into ve.ui.MWDialog.css) * ve.ui.Tool.css * ve.ui.Widget.css (move ve-ui-rtl and ve-ui-ltr to ve.ui.css) ve.dm.Converter.test.js: Moved runner functions into ve.test.utils.js ve.dm.example.js: * Refactored createExampleDocument so mwExample can use it * Removed wgExtensionAssetsPath detection, moved into mw-preload.js * Genericized withMeta example document (original version copied to mwExample) * Moved references example document to mwExample ve.dm.mwExample.js: * Move withMeta and references example documents from ve.dm.example.js * Add createExampleDocument function ve-mw/test/index.php: Runner for MW-specific tests only ve-mw/test/mw-preload.js: Sets VE_TESTDIR for Special:JavaScriptTest only ve.ui.Window.js: * Remove magic path interpolation in addLocalStyleSheets() * Pass full(er) paths to addLocalStyleSheets(), here and in subclasses ve.ui.MWDialog.js: Subclass of Dialog that adds MW versions of stylesheets ve.ui.MW*Dialog.js: * Subclass MWDialog rather than Dialog * Load both core and MW versions of stylesheets that have both ve.ui.PagedDialog.js: Converted to a mixin rather than an abstract base class * Don't inherit ve.ui.Dialog * Rather than overriding initialize(), provide initializePages() which the host class is supposed to call from its initialize() * Rename onOutlineSelect to onPageOutlineSelect ve.ui.MWMetaDialog.js, ve.ui.MWTransclusionDialog.js: * Use PagedDialog as a mixin rather than a base class, inherit MWDialog bullet-icon.png: Unused, deleted Stuff we should do later: * Refactor tests to use data providers * Write utility function for SVG compat check * Separate omnibus CSS files such as ve.ui.Widget.css * Separate omnibus RL modules * Use icon classes in ViewPageTarget Change-Id: I1b28f8ba7f2d2513e5c634927a854686fb9dd5a5
2013-07-03 01:30:10 +00:00
/* Static Properties */
ve.ui.MWTransclusionDialog.static.name = 'transclusion';
ve.ui.MWTransclusionDialog.static.size = 'larger';
ve.ui.MWTransclusionDialog.static.actions = [
...ve.ui.MWTemplateDialog.static.actions,
Update VE core submodule to master (f2277ea) New changes: 56de6f5 Localisation updates from https://translatewiki.net. f8bda64 Widgetise demo menu 6ac48d8 Localisation updates from https://translatewiki.net. 365e131 builderloader: Omit value for boolean "disabled" attribute per HTML5 706e4b3 Prevent double counting of DM nodes in getNodeAndOffset b141a7d Update OOjs UI to v0.1.0-pre (d2451ac748) c5b3921 Localisation updates from https://translatewiki.net. 1606983 Update reference to ConfirmationDialog to use MessageDialog Deletions: * Styles for ve.ui.MWBetaWelcomeDialog - not needed anymore because OO.ui.MessageDialog provides them * Styles for ve.ui.MWGalleryInspector - not needed anymore because ve.ui.MWExtensionInspector provides part of them and the rest are being replaced by programatic sizing Modifications: * ve.ui.MWLinkTargetInputWidget - Added support for validation and href getter * Split message between tool and dialog title for ve.ui.MWEditModeTool and ve.ui.MWWikitextSwitchConfirmDialog General changes: * Updated inheritance. * Added manager param to constructors of dialogs and inspectors. * Updated use of show/hide with toggle. * Added meaningful descriptions of dialog and inspector classes. * Configured dialog and inspector sizes statically. * Configured dialog action buttons statically. * Interfaced with OO.ui.ActionSet to control action buttons. * Moved applyChanges code into getActionProcess methods. * Always using .next in setup/ready process getters and .first in hold/teardown process getters. Change-Id: Ia74732e6e32c0808eee021f0a26225b9e6c3f971
2014-07-14 21:32:49 +00:00
{
action: 'mode',
// label is set by updateModeActionState
modes: [ 'edit', 'insert' ]
},
{
action: 'back',
label: OO.ui.deferMsg( 'visualeditor-dialog-action-goback' ),
modes: [ 'edit', 'insert' ],
flags: [ 'safe', 'back' ]
Update VE core submodule to master (f2277ea) New changes: 56de6f5 Localisation updates from https://translatewiki.net. f8bda64 Widgetise demo menu 6ac48d8 Localisation updates from https://translatewiki.net. 365e131 builderloader: Omit value for boolean "disabled" attribute per HTML5 706e4b3 Prevent double counting of DM nodes in getNodeAndOffset b141a7d Update OOjs UI to v0.1.0-pre (d2451ac748) c5b3921 Localisation updates from https://translatewiki.net. 1606983 Update reference to ConfirmationDialog to use MessageDialog Deletions: * Styles for ve.ui.MWBetaWelcomeDialog - not needed anymore because OO.ui.MessageDialog provides them * Styles for ve.ui.MWGalleryInspector - not needed anymore because ve.ui.MWExtensionInspector provides part of them and the rest are being replaced by programatic sizing Modifications: * ve.ui.MWLinkTargetInputWidget - Added support for validation and href getter * Split message between tool and dialog title for ve.ui.MWEditModeTool and ve.ui.MWWikitextSwitchConfirmDialog General changes: * Updated inheritance. * Added manager param to constructors of dialogs and inspectors. * Updated use of show/hide with toggle. * Added meaningful descriptions of dialog and inspector classes. * Configured dialog and inspector sizes statically. * Configured dialog action buttons statically. * Interfaced with OO.ui.ActionSet to control action buttons. * Moved applyChanges code into getActionProcess methods. * Always using .next in setup/ready process getters and .first in hold/teardown process getters. Change-Id: Ia74732e6e32c0808eee021f0a26225b9e6c3f971
2014-07-14 21:32:49 +00:00
}
];
Update VE core submodule to master (f2277ea) New changes: 56de6f5 Localisation updates from https://translatewiki.net. f8bda64 Widgetise demo menu 6ac48d8 Localisation updates from https://translatewiki.net. 365e131 builderloader: Omit value for boolean "disabled" attribute per HTML5 706e4b3 Prevent double counting of DM nodes in getNodeAndOffset b141a7d Update OOjs UI to v0.1.0-pre (d2451ac748) c5b3921 Localisation updates from https://translatewiki.net. 1606983 Update reference to ConfirmationDialog to use MessageDialog Deletions: * Styles for ve.ui.MWBetaWelcomeDialog - not needed anymore because OO.ui.MessageDialog provides them * Styles for ve.ui.MWGalleryInspector - not needed anymore because ve.ui.MWExtensionInspector provides part of them and the rest are being replaced by programatic sizing Modifications: * ve.ui.MWLinkTargetInputWidget - Added support for validation and href getter * Split message between tool and dialog title for ve.ui.MWEditModeTool and ve.ui.MWWikitextSwitchConfirmDialog General changes: * Updated inheritance. * Added manager param to constructors of dialogs and inspectors. * Updated use of show/hide with toggle. * Added meaningful descriptions of dialog and inspector classes. * Configured dialog and inspector sizes statically. * Configured dialog action buttons statically. * Interfaced with OO.ui.ActionSet to control action buttons. * Moved applyChanges code into getActionProcess methods. * Always using .next in setup/ready process getters and .first in hold/teardown process getters. Change-Id: Ia74732e6e32c0808eee021f0a26225b9e6c3f971
2014-07-14 21:32:49 +00:00
ve.ui.MWTransclusionDialog.static.smallScreenMaxWidth = 540;
/* Static Methods */
/**
* @return {boolean}
*/
ve.ui.MWTransclusionDialog.static.isSmallScreen = function () {
return $( window ).width() <= ve.ui.MWTransclusionDialog.static.smallScreenMaxWidth;
};
/* Methods */
/**
* Handle outline controls move events.
*
* @private
* @param {number} places Number of places to move the selected item
*/
ve.ui.MWTransclusionDialog.prototype.onOutlineControlsMove = function ( places ) {
const part = this.transclusionModel.getPartFromId( this.bookletLayout.getSelectedTopLevelPartId() );
if ( !part ) {
return;
}
const newPlace = this.transclusionModel.getParts().indexOf( part ) + places;
if ( newPlace < 0 || newPlace >= this.transclusionModel.getParts().length ) {
return;
}
// Move part to new location, and if dialog is loaded switch to new part page
const promise = this.transclusionModel.addPart( part, newPlace );
if ( this.loaded && !this.preventReselection ) {
// FIXME: Should be handled internally {@see ve.ui.MWTwoPaneTransclusionDialogLayout}
promise.done( this.bookletLayout.focusPart.bind( this.bookletLayout, part.getId() ) );
}
};
/**
* Handle outline controls remove events.
*
* @private
*/
ve.ui.MWTransclusionDialog.prototype.onOutlineControlsRemove = function () {
const controls = this.bookletLayout.getOutlineControls();
// T301914: Safe-guard for when a keyboard shortcut triggers this, instead of the actual button
if ( !controls.isVisible() ||
!controls.removeButton.isVisible() ||
controls.removeButton.isDisabled()
) {
return;
}
const partId = this.bookletLayout.getSelectedTopLevelPartId(),
part = this.transclusionModel.getPartFromId( partId );
if ( part ) {
this.transclusionModel.removePart( part );
}
};
/**
* Create a new template part at the end of the transclusion.
*
* @private
*/
ve.ui.MWTransclusionDialog.prototype.addTemplatePlaceholder = function () {
this.addPart( new ve.dm.MWTemplatePlaceholderModel( this.transclusionModel ) );
};
/**
* Handle add wikitext button click or hotkey events.
*
* @private
*/
ve.ui.MWTransclusionDialog.prototype.addWikitext = function () {
this.addPart( new ve.dm.MWTransclusionContentModel( this.transclusionModel ) );
};
/**
* Handle add parameter hotkey events.
*
* @private
* @param {jQuery.Event} e Key down event
*/
ve.ui.MWTransclusionDialog.prototype.addParameter = function ( e ) {
// Check if the focus was in e.g. a parameter list or filter input when the hotkey was pressed
let partId = this.bookletLayout.sidebar.findPartIdContainingElement( e.target ),
part = this.transclusionModel.getPartFromId( partId );
if ( !( part instanceof ve.dm.MWTemplateModel ) ) {
// Otherwise add to the template that's currently selected via its title or parameter
partId = this.bookletLayout.getTopLevelPartIdForSelection();
part = this.transclusionModel.getPartFromId( partId );
}
if ( this.transclusionModel.isSingleTemplate() ) {
part = this.transclusionModel.getParts()[ 0 ];
}
if ( !( part instanceof ve.dm.MWTemplateModel ) ) {
return;
}
// TODO: Use a distinct class for placeholder model rather than
// these magical "empty" constants.
const placeholderParameter = new ve.dm.MWParameterModel( part );
part.addParameter( placeholderParameter );
this.bookletLayout.focusPart( placeholderParameter.getId() );
this.autoExpandSidebar();
};
/**
* @inheritdoc
*/
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
2013-12-02 20:10:55 +00:00
ve.ui.MWTransclusionDialog.prototype.onReplacePart = function ( removed, added ) {
ve.ui.MWTransclusionDialog.super.prototype.onReplacePart.call( this, removed, added );
const parts = this.transclusionModel.getParts();
Update VE core submodule to master (f2277ea) New changes: 56de6f5 Localisation updates from https://translatewiki.net. f8bda64 Widgetise demo menu 6ac48d8 Localisation updates from https://translatewiki.net. 365e131 builderloader: Omit value for boolean "disabled" attribute per HTML5 706e4b3 Prevent double counting of DM nodes in getNodeAndOffset b141a7d Update OOjs UI to v0.1.0-pre (d2451ac748) c5b3921 Localisation updates from https://translatewiki.net. 1606983 Update reference to ConfirmationDialog to use MessageDialog Deletions: * Styles for ve.ui.MWBetaWelcomeDialog - not needed anymore because OO.ui.MessageDialog provides them * Styles for ve.ui.MWGalleryInspector - not needed anymore because ve.ui.MWExtensionInspector provides part of them and the rest are being replaced by programatic sizing Modifications: * ve.ui.MWLinkTargetInputWidget - Added support for validation and href getter * Split message between tool and dialog title for ve.ui.MWEditModeTool and ve.ui.MWWikitextSwitchConfirmDialog General changes: * Updated inheritance. * Added manager param to constructors of dialogs and inspectors. * Updated use of show/hide with toggle. * Added meaningful descriptions of dialog and inspector classes. * Configured dialog and inspector sizes statically. * Configured dialog action buttons statically. * Interfaced with OO.ui.ActionSet to control action buttons. * Moved applyChanges code into getActionProcess methods. * Always using .next in setup/ready process getters and .first in hold/teardown process getters. Change-Id: Ia74732e6e32c0808eee021f0a26225b9e6c3f971
2014-07-14 21:32:49 +00:00
if ( parts.length === 0 ) {
this.addPart( new ve.dm.MWTemplatePlaceholderModel( this.transclusionModel ) );
} else if ( parts.length > 1 ) {
this.$element.removeClass( 've-ui-mwTransclusionDialog-single-transclusion' );
}
// multipart message
this.bookletLayout.stackLayout.$element.prepend( this.multipartMessage.$element );
this.multipartMessage.toggle( parts.length > 1 );
this.autoExpandSidebar();
this.updateModeActionState();
this.updateActionSet();
};
/**
* @private
*/
ve.ui.MWTransclusionDialog.prototype.setupHotkeyTriggers = function () {
// Lower-case modifier and key names as specified in {@see ve.ui.Trigger}
const isMac = ve.getSystemPlatform() === 'mac',
meta = isMac ? 'meta+' : 'ctrl+';
const hotkeys = {
addTemplate: meta + 'd',
addWikitext: meta + 'shift+y',
addParameter: meta + 'shift+d',
moveUp: meta + 'shift+up',
moveDown: meta + 'shift+down',
remove: meta + 'delete',
removeBackspace: meta + 'backspace'
};
const notInTextFields = /^(?!INPUT|TEXTAREA)/i;
this.connectHotKeyBinding( hotkeys.addTemplate, this.addTemplatePlaceholder.bind( this ) );
this.connectHotKeyBinding( hotkeys.addWikitext, this.addWikitext.bind( this ) );
this.connectHotKeyBinding( hotkeys.addParameter, this.addParameter.bind( this ) );
this.connectHotKeyBinding( hotkeys.moveUp, this.onOutlineControlsMove.bind( this, -1 ), notInTextFields );
this.connectHotKeyBinding( hotkeys.moveDown, this.onOutlineControlsMove.bind( this, 1 ), notInTextFields );
this.connectHotKeyBinding( hotkeys.remove, this.onOutlineControlsRemove.bind( this ), notInTextFields );
if ( isMac ) {
this.connectHotKeyBinding( hotkeys.removeBackspace, this.onOutlineControlsRemove.bind( this ), notInTextFields );
}
const controls = this.bookletLayout.getOutlineControls();
this.addHotkeyToTitle( controls.addTemplateButton, hotkeys.addTemplate );
this.addHotkeyToTitle( controls.addWikitextButton, hotkeys.addWikitext );
this.addHotkeyToTitle( controls.upButton, hotkeys.moveUp );
this.addHotkeyToTitle( controls.downButton, hotkeys.moveDown );
this.addHotkeyToTitle( controls.removeButton, hotkeys.remove );
};
/**
* @private
* @param {string} hotkey
* @param {Function} handler
* @param {RegExp} [validTypes]
*/
ve.ui.MWTransclusionDialog.prototype.connectHotKeyBinding = function ( hotkey, handler, validTypes ) {
this.hotkeyTriggers[ hotkey ] = {
handler: handler,
validTypes: validTypes
};
};
/**
* @private
* @param {OO.ui.mixin.TitledElement} element
* @param {string} hotkey
*/
ve.ui.MWTransclusionDialog.prototype.addHotkeyToTitle = function ( element, hotkey ) {
// Separated with a space as in {@see OO.ui.Tool.updateTitle}
element.setTitle( element.getTitle() + ' ' + new ve.ui.Trigger( hotkey ).getMessage() );
};
/**
* Handles key down events.
*
* @protected
* @param {jQuery.Event} e Key down event
*/
ve.ui.MWTransclusionDialog.prototype.onKeyDown = function ( e ) {
const hotkey = new ve.ui.Trigger( e ).toString(),
trigger = this.hotkeyTriggers[ hotkey ];
if ( trigger && ( !trigger.validTypes || trigger.validTypes.test( e.target.nodeName ) ) ) {
trigger.handler( e );
e.preventDefault();
e.stopPropagation();
}
};
/**
* @inheritdoc
*/
ve.ui.MWTransclusionDialog.prototype.getPageFromPart = function ( part ) {
const page = ve.ui.MWTransclusionDialog.super.prototype.getPageFromPart.call( this, part );
if ( !page && part instanceof ve.dm.MWTransclusionContentModel ) {
return new ve.ui.MWTransclusionContentPage( part, part.getId(), { $overlay: this.$overlay, isReadOnly: this.isReadOnly() } );
}
return page;
};
/**
* Automatically expand or collapse the sidebar according to default logic.
*
* @protected
*/
ve.ui.MWTransclusionDialog.prototype.autoExpandSidebar = function () {
let expandSidebar;
const isSmallScreen = this.constructor.static.isSmallScreen();
const showOtherActions = isSmallScreen ||
// Check for unknown actions, show the toolbar if any are available.
this.actions.getOthers().some( ( action ) => action.action !== 'mode' );
this.actions.forEach( { actions: [ 'mode' ] }, ( action ) => {
action.toggle( isSmallScreen );
} );
this.$otherActions.toggleClass( 'oo-ui-element-hidden', !showOtherActions );
if ( isSmallScreen && this.transclusionModel.isEmpty() ) {
expandSidebar = false;
} else if ( isSmallScreen &&
// eslint-disable-next-line no-jquery/no-class-state
this.$content.hasClass( 've-ui-mwTransclusionDialog-small-screen' )
) {
// We did this already. If the sidebar is visible or not is now the user's decision.
return;
} else {
expandSidebar = !isSmallScreen;
}
this.$content.toggleClass( 've-ui-mwTransclusionDialog-small-screen', isSmallScreen );
this.toggleSidebar( expandSidebar );
};
/**
* Set if the sidebar is visible (which means the dialog is expanded), or collapsed.
*
* @param {boolean} expandSidebar
* @private
*/
ve.ui.MWTransclusionDialog.prototype.toggleSidebar = function ( expandSidebar ) {
if ( this.isSidebarExpanded === expandSidebar ) {
return;
}
this.isSidebarExpanded = expandSidebar;
this.$content
.toggleClass( 've-ui-mwTransclusionDialog-collapsed', !expandSidebar )
.toggleClass( 've-ui-mwTransclusionDialog-expanded', expandSidebar );
this.bookletLayout.toggleOutline( expandSidebar );
this.updateTitle();
this.updateModeActionState();
// HACK blur any active input so that its dropdown will be hidden and won't end
// up being mispositioned
this.$content.find( 'input:focus' ).trigger( 'blur' );
if ( this.loaded && this.constructor.static.isSmallScreen() ) {
// Updates the page sizes when the menu is toggled using the button. This needs
// to happen after the animation when the panel is visible.
setTimeout( () => {
this.bookletLayout.stackLayout.getItems().forEach( ( page ) => {
if ( page instanceof ve.ui.MWParameterPage ) {
page.updateSize();
}
} );
}, OO.ui.theme.getDialogTransitionDuration() );
// Reapply selection and scrolling when switching between panes.
const selectedPage = this.bookletLayout.getCurrentPage();
if ( selectedPage ) {
const name = selectedPage.getName();
// Align whichever panel is becoming visible, after animation completes.
// TODO: Should hook onto an animation promise—but is this possible when pure CSS?
setTimeout( () => {
if ( expandSidebar ) {
this.sidebar.setSelectionByPageName( name );
} else {
selectedPage.scrollElementIntoView( { alignToTop: true, padding: { top: 20 } } );
if ( !OO.ui.isMobile() ) {
selectedPage.focus();
}
}
}, OO.ui.theme.getDialogTransitionDuration() );
}
}
};
/**
* @inheritdoc
*/
ve.ui.MWTransclusionDialog.prototype.updateTitle = function () {
if ( !this.transclusionModel.isSingleTemplate() ) {
this.title.setLabel( ve.msg( 'visualeditor-dialog-transclusion-title-edit-transclusion' ) );
} else {
// Parent method
ve.ui.MWTransclusionDialog.super.prototype.updateTitle.call( this );
}
};
/**
* Update the state of the 'mode' action
*
* @private
*/
ve.ui.MWTransclusionDialog.prototype.updateModeActionState = function () {
const isExpanded = this.isSidebarExpanded,
label = ve.msg( isExpanded ?
'visualeditor-dialog-transclusion-collapse-options' :
'visualeditor-dialog-transclusion-expand-options' );
this.actions.forEach( { actions: [ 'mode' ] }, ( action ) => {
action.setLabel( label );
action.$button.attr( 'aria-expanded', isExpanded ? 1 : 0 );
} );
// The button is only visible on very narrow screens, {@see autoExpandSidebar}.
// It's always needed, except in the initial placeholder state.
const isInitialState = !isExpanded && this.transclusionModel.isEmpty(),
canCollapse = !isInitialState;
this.actions.setAbilities( { mode: canCollapse } );
};
/**
* Add a part to the transclusion.
*
* @param {ve.dm.MWTransclusionPartModel} part Part to add
*/
ve.ui.MWTransclusionDialog.prototype.addPart = function ( part ) {
const parts = this.transclusionModel.getParts(),
partId = this.bookletLayout.getTopLevelPartIdForSelection(),
selectedPart = this.transclusionModel.getPartFromId( partId );
// Insert after selected part, or at the end if nothing is selected
const index = selectedPart ? parts.indexOf( selectedPart ) + 1 : parts.length;
// Add the part, and if dialog is loaded switch to part page
const promise = this.transclusionModel.addPart( part, index );
if ( this.loaded && !this.preventReselection ) {
promise.done( this.bookletLayout.focusPart.bind( this.bookletLayout, part.getId() ) );
}
};
/**
* Show a confirm prompt before closing the dialog
*
* @param {string} prompt Prompt
* @return {jQuery.Promise} Close promise
*/
ve.ui.MWTransclusionDialog.prototype.closeConfirm = function ( prompt ) {
return OO.ui.confirm( prompt, {
actions: [
{
action: 'reject',
label: ve.msg( 'visualeditor-dialog-transclusion-confirmation-reject' ),
flags: 'safe'
},
{
action: 'accept',
label: ve.msg( 'visualeditor-dialog-transclusion-confirmation-discard' ),
// TODO: Destructive actions don't get focus by default, but maybe should here?
flags: 'destructive'
}
]
} );
};
Update VE core submodule to master (f2277ea) New changes: 56de6f5 Localisation updates from https://translatewiki.net. f8bda64 Widgetise demo menu 6ac48d8 Localisation updates from https://translatewiki.net. 365e131 builderloader: Omit value for boolean "disabled" attribute per HTML5 706e4b3 Prevent double counting of DM nodes in getNodeAndOffset b141a7d Update OOjs UI to v0.1.0-pre (d2451ac748) c5b3921 Localisation updates from https://translatewiki.net. 1606983 Update reference to ConfirmationDialog to use MessageDialog Deletions: * Styles for ve.ui.MWBetaWelcomeDialog - not needed anymore because OO.ui.MessageDialog provides them * Styles for ve.ui.MWGalleryInspector - not needed anymore because ve.ui.MWExtensionInspector provides part of them and the rest are being replaced by programatic sizing Modifications: * ve.ui.MWLinkTargetInputWidget - Added support for validation and href getter * Split message between tool and dialog title for ve.ui.MWEditModeTool and ve.ui.MWWikitextSwitchConfirmDialog General changes: * Updated inheritance. * Added manager param to constructors of dialogs and inspectors. * Updated use of show/hide with toggle. * Added meaningful descriptions of dialog and inspector classes. * Configured dialog and inspector sizes statically. * Configured dialog action buttons statically. * Interfaced with OO.ui.ActionSet to control action buttons. * Moved applyChanges code into getActionProcess methods. * Always using .next in setup/ready process getters and .first in hold/teardown process getters. Change-Id: Ia74732e6e32c0808eee021f0a26225b9e6c3f971
2014-07-14 21:32:49 +00:00
/**
* @inheritdoc
*/
ve.ui.MWTransclusionDialog.prototype.getActionProcess = function ( action ) {
const willLoseProgress = this.getMode() === 'insert' ?
// A new template with no parameters is not considered valuable.
this.transclusionModel.containsValuableData() :
// The user has changed a parameter, and is not on the template search page.
( this.altered && !this.transclusionModel.isEmpty() );
switch ( action ) {
case 'back':
return new OO.ui.Process( () => {
if ( willLoseProgress ) {
this.closeConfirm( ve.msg( 'visualeditor-dialog-transclusion-back-confirmation-prompt' ) ).then( ( confirmed ) => {
if ( confirmed ) {
this.resetDialog();
}
} );
} else {
this.resetDialog();
}
} );
case 'mode':
return new OO.ui.Process( () => {
this.toggleSidebar( !this.isSidebarExpanded );
} );
case '':
// close action
if ( willLoseProgress ) {
return new OO.ui.Process( () => {
this.closeConfirm( ve.msg( 'visualeditor-dialog-transclusion-close-confirmation-prompt' ) ).then( ( confirmed ) => {
if ( confirmed ) {
this.close();
}
} );
} );
}
}
return ve.ui.MWTransclusionDialog.super.prototype.getActionProcess.call( this, action );
Update VE core submodule to master (f2277ea) New changes: 56de6f5 Localisation updates from https://translatewiki.net. f8bda64 Widgetise demo menu 6ac48d8 Localisation updates from https://translatewiki.net. 365e131 builderloader: Omit value for boolean "disabled" attribute per HTML5 706e4b3 Prevent double counting of DM nodes in getNodeAndOffset b141a7d Update OOjs UI to v0.1.0-pre (d2451ac748) c5b3921 Localisation updates from https://translatewiki.net. 1606983 Update reference to ConfirmationDialog to use MessageDialog Deletions: * Styles for ve.ui.MWBetaWelcomeDialog - not needed anymore because OO.ui.MessageDialog provides them * Styles for ve.ui.MWGalleryInspector - not needed anymore because ve.ui.MWExtensionInspector provides part of them and the rest are being replaced by programatic sizing Modifications: * ve.ui.MWLinkTargetInputWidget - Added support for validation and href getter * Split message between tool and dialog title for ve.ui.MWEditModeTool and ve.ui.MWWikitextSwitchConfirmDialog General changes: * Updated inheritance. * Added manager param to constructors of dialogs and inspectors. * Updated use of show/hide with toggle. * Added meaningful descriptions of dialog and inspector classes. * Configured dialog and inspector sizes statically. * Configured dialog action buttons statically. * Interfaced with OO.ui.ActionSet to control action buttons. * Moved applyChanges code into getActionProcess methods. * Always using .next in setup/ready process getters and .first in hold/teardown process getters. Change-Id: Ia74732e6e32c0808eee021f0a26225b9e6c3f971
2014-07-14 21:32:49 +00:00
};
/**
* Update the widgets in the dialog's action bar.
*
* @private
*/
ve.ui.MWTransclusionDialog.prototype.updateActionSet = function () {
const backButton = this.actions.get( { flags: [ 'back' ] } ).pop(),
saveButton = this.actions.get( { actions: [ 'done' ] } ).pop();
if ( saveButton && this.getMode() === 'edit' ) {
saveButton.setLabel( ve.msg( 'visualeditor-dialog-transclusion-action-save' ) );
}
const closeButton = this.actions.get( { flags: [ 'close' ] } ).pop(),
canGoBack = this.getMode() === 'insert' && this.canGoBack && !this.transclusionModel.isEmpty();
closeButton.toggle( !canGoBack );
backButton.toggle( canGoBack );
};
/**
* Revert the dialog back to its initial state.
*
* @private
*/
ve.ui.MWTransclusionDialog.prototype.resetDialog = function () {
this.transclusionModel.reset();
this.bookletLayout.clearPages();
const placeholderPage = new ve.dm.MWTemplatePlaceholderModel( this.transclusionModel );
this.transclusionModel.addPart( placeholderPage )
.done( () => {
this.bookletLayout.focusPart( placeholderPage.getId() );
this.autoExpandSidebar();
} );
};
/**
* @inheritdoc
*/
ve.ui.MWTransclusionDialog.prototype.initialize = function () {
// Parent method
ve.ui.MWTransclusionDialog.super.prototype.initialize.call( this );
this.setupHotkeyTriggers();
// multipart message gets attached in onReplacePart()
this.multipartMessage = new OO.ui.MessageWidget( {
label: mw.message( 'visualeditor-dialog-transclusion-multipart-message' ).parseDom(),
classes: [ 've-ui-mwTransclusionDialog-multipart-message' ]
} );
ve.targetLinksToNewWindow( this.multipartMessage.$element[ 0 ] );
const helpPopup = new ve.ui.MWFloatingHelpElement( {
label: mw.message( 'visualeditor-dialog-transclusion-help-title' ).text(),
title: mw.message( 'visualeditor-dialog-transclusion-help-title' ).text(),
$message: new OO.ui.FieldsetLayout( {
items: [
new OO.ui.LabelWidget( {
label: mw.message( 'visualeditor-dialog-transclusion-help-message' ).text()
} ),
this.getMessageButton( 'visualeditor-dialog-transclusion-help-page-help', 'helpNotice' ),
this.getMessageButton( 'visualeditor-dialog-transclusion-help-page-shortcuts', 'keyboard' )
],
classes: [ 've-ui-mwTransclusionDialog-floatingHelpElement-fieldsetLayout' ]
} ).$element
} );
helpPopup.$element.addClass( 've-ui-mwTransclusionDialog-floatingHelpElement' );
helpPopup.$element.appendTo( this.$body );
// Events
this.getManager().connect( this, { resize: ve.debounce( this.onWindowResize.bind( this ) ) } );
this.bookletLayout.getOutlineControls().connect( this, {
addTemplate: 'addTemplatePlaceholder',
addWikitext: 'addWikitext',
move: 'onOutlineControlsMove',
remove: 'onOutlineControlsRemove'
} );
};
/**
* @inheritdoc
*/
ve.ui.MWTransclusionDialog.prototype.getSetupProcess = function ( data ) {
return ve.ui.MWTransclusionDialog.super.prototype.getSetupProcess.call( this, data )
.next( () => {
this.bookletLayout.getOutlineControls().toggle( !this.transclusionModel.isSingleTemplate() );
this.$element.toggleClass(
've-ui-mwTransclusionDialog-single-transclusion',
this.transclusionModel.isSingleTemplate()
);
this.updateModeActionState();
this.autoExpandSidebar();
if ( !this.transclusionModel.isSingleTemplate() ) {
this.sidebar.hideAllUnusedParameters();
}
// We can do this only after the widget is visible on screen
this.sidebar.initializeAllStickyHeaderHeights();
} );
};
/**
* @private
*/
ve.ui.MWTransclusionDialog.prototype.onWindowResize = function () {
if ( this.transclusionModel ) {
this.autoExpandSidebar();
this.bookletLayout.getPagesOrdered().forEach( ( page ) => {
if ( page instanceof ve.ui.MWParameterPage ) {
page.updateSize();
}
} );
}
};
/**
* @inheritdoc
*/
ve.ui.MWTransclusionDialog.prototype.getSizeProperties = function () {
return ve.extendObject(
{ height: '90%' },
ve.ui.MWTransclusionDialog.super.prototype.getSizeProperties.call( this )
);
};
/**
* Converts a message link into an OO.ui.ButtonWidget with an icon.
*
* @private
* @param {string} message i18n message key
* @param {string} icon icon name
* @return {OO.ui.ButtonWidget}
*/
ve.ui.MWTemplateDialog.prototype.getMessageButton = function ( message, icon ) {
// Messages that can be used here:
// * visualeditor-dialog-transclusion-help-page-help
// * visualeditor-dialog-transclusion-help-page-shortcuts
const $link = mw.message( message ).parseDom(),
button = new OO.ui.ButtonWidget( {
label: $link.text(),
href: $link.attr( 'href' ),
target: '_blank',
flags: 'progressive',
icon: icon,
framed: false
} );
button.$button.attr( 'role', 'link' );
return button;
};
/* Registration */
ve.ui.windowFactory.register( ve.ui.MWTransclusionDialog );