import CdError from './CdError';
import DivLabelWidget from './DivLabelWidget';
import cd from './cd';
import { createCopyTextField, tweakUserOoUiClass } from './utils-oojs';
import { wrapHtml } from './utils-window';
/**
* Class used to create a "Copy link" dialog.
*
* @augments external:OO.ui.MessageDialog
*/
class CopyLinkDialog extends OO.ui.MessageDialog {
static name = 'copyLinkDialog';
static actions = [
{
label: cd.s('cld-close'),
action: 'close',
},
];
/**
* Create a "Copy link" dialog.
*
* @param {import('./Comment').default|import('./Section').default} object
* @param {'comment' | 'section'} type
* @param {object} content
*/
constructor(object, type, content) {
super({
classes: ['cd-dialog-copyLink'],
});
this.object = object;
this.type = type;
this.content = content;
this.readyDeferred = $.Deferred();
}
/**
* OOUI native method that initializes window contents.
*
* @param {...*} [args]
* @see https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.MessageDialog.html#initialize
* @see https://www.mediawiki.org/wiki/OOUI/Windows#Window_lifecycle
* @ignore
*/
initialize(...args) {
super.initialize(...args);
// By default, the whole message is wrapped in a <label> element. We don't want that behavior
// and revert it.
this.message.$element.remove();
this.message = new DivLabelWidget({ classes: ['oo-ui-messageDialog-message'] });
this.text.$element.append(this.message.$element);
if (this.type === 'comment') {
this.anchorOptionWidget = new OO.ui.ButtonOptionWidget({
data: 'anchor',
label: cd.s('cld-select-anchor'),
selected: true,
});
this.diffOptionWidget = new OO.ui.ButtonOptionWidget({
data: 'diff',
label: cd.s('cld-select-diff'),
disabled: true,
title: cd.s('loading-ellipsis'),
classes: ['cd-dialog-copyLink-diffButton'],
});
this.buttonSelectWidget = (new OO.ui.ButtonSelectWidget({
items: [this.anchorOptionWidget, this.diffOptionWidget],
classes: ['cd-dialog-copyLink-linkTypeSelect'],
})).on('choose', (item) => {
const panel = item === this.anchorOptionWidget ? this.anchorPanel : this.diffPanel;
this.stackLayout.setItem(panel);
this.updateSize();
});
}
this.anchorPanel = new OO.ui.PanelLayout({
$content: this.createAnchorPanelContent(),
padded: false,
expanded: false,
scrollable: true,
});
this.stackLayout = new OO.ui.StackLayout({
items: [this.anchorPanel],
expanded: false,
});
if (this.type === 'comment') {
this.createDiffPanel();
}
}
/**
* OOUI native method that returns a "setup" process which is used to set up a window for use in a
* particular context, based on the `data` argument.
*
* @param {object} [data] Dialog opening data
* @returns {external:OO.ui.Process}
* @see https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.Dialog.html#getSetupProcess
* @see https://www.mediawiki.org/wiki/OOUI/Windows#Window_lifecycle
* @ignore
*/
getSetupProcess(data) {
return super.getSetupProcess(data).next(() => {
this.title.setLabel(
this.type === 'comment' ? cd.s('cld-title-comment') : cd.s('cld-title-section')
);
this.message.setLabel(
$.cdMerge(
this.buttonSelectWidget?.$element,
this.stackLayout.$element,
)
);
this.size = this.type === 'comment' ? 'larger' : 'large';
this.stackLayout.setItem(this.anchorPanel);
});
}
/**
* OOUI native method that returns a "ready" process which is used to ready a window for use in a
* particular context, based on the `data` argument.
*
* @param {object} data Window opening data
* @returns {external:OO.ui.Process}
* @see https://doc.wikimedia.org/oojs-ui/master/js/OO.ui.ProcessDialog.html#getReadyProcess
* @see https://www.mediawiki.org/wiki/OOUI/Windows#Window_lifecycle
* @ignore
*/
getReadyProcess(data) {
return super.getReadyProcess(data).next(() => {
this.readyDeferred.resolve();
});
}
/**
* Callback for copying text.
*
* @param {boolean} successful
* @param {external:OO.ui.CopyTextLayout} field
* @protected
*/
async copyCallback(successful, field) {
if (successful) {
mw.notify(this.content.copyMessages.success);
} else {
mw.notify(this.content.copyMessages.fail, { type: 'error' });
}
// Make external tools that react to text selection quiet
field.textInput.selectRange(0);
this.close();
}
/**
* Create the "Diff" panel in the dialog.
*
* @protected
*/
async createDiffPanel() {
let errorText;
try {
Object.assign(this.content, {
diffStandard: await this.object.getDiffLink('standard'),
diffShort: await this.object.getDiffLink('short'),
diffWikilink: await this.object.getDiffLink('wikilink'),
$diffView: await this.object.generateDiffView(),
});
await mw.loader.using(['mediawiki.diff', 'mediawiki.diff.styles']);
this.diffPanel = new OO.ui.PanelLayout({
$content: this.createDiffPanelContent(),
padded: false,
expanded: false,
scrollable: true,
});
this.stackLayout.addItems([this.diffPanel]);
this.readyDeferred.then(() => {
mw.hook('wikipage.content').fire(this.content.$diffView);
});
} catch (e) {
if (e instanceof CdError) {
const { type } = e.data;
errorText = type === 'network' ?
cd.s('cld-diff-error-network') :
cd.s('cld-diff-error');
} else {
errorText = cd.s('cld-diff-error-unknown');
console.warn(e);
}
}
this.diffOptionWidget.setDisabled(errorText);
this.diffOptionWidget.setTitle(errorText || '');
}
/**
* Create the content of the "Anchor" panel in the dialog.
*
* @returns {external:jQuery}
* @protected
*/
createAnchorPanelContent() {
// Doesn't apply to DT IDs.
let helpOnlyCd;
let helpNotOnlyCd;
if (this.type === 'comment' && this.content.fragment === this.object.id) {
helpOnlyCd = cd.s('cld-help-onlycd');
helpNotOnlyCd = wrapHtml(cd.sParse('cld-help-notonlycd'));
}
const copyCallback = this.copyCallback.bind(this);
const wikilinkField = createCopyTextField({
value: this.content.wikilink,
disabled: !this.content.wikilink,
label: cd.s('cld-wikilink'),
copyCallback,
help: helpOnlyCd,
});
const currentPageWikilinkField = createCopyTextField({
value: this.content.currentPageWikilink,
label: cd.s('cld-currentpagewikilink'),
copyCallback,
help: helpNotOnlyCd,
});
const permanentWikilinkField = createCopyTextField({
value: this.content.permanentWikilink,
label: cd.s('cld-permanentwikilink'),
copyCallback,
help: helpOnlyCd,
});
const linkField = createCopyTextField({
value: this.content.link,
label: cd.s('cld-link'),
copyCallback,
help: helpOnlyCd,
});
const permanentLinkField = createCopyTextField({
value: this.content.permanentLink,
label: cd.s('cld-permanentlink'),
copyCallback,
help: helpOnlyCd,
});
let jsCall;
let jsBreakpoint;
let jsBreakpointTimestamp;
if (cd.g.debug) {
jsCall = createCopyTextField({
value: this.content.jsCall,
label: 'JS call',
copyCallback,
});
jsBreakpoint = createCopyTextField({
value: this.content.jsBreakpoint,
label: 'JS conditional breakpoint',
copyCallback,
});
if (this.type === 'comment') {
jsBreakpointTimestamp = createCopyTextField({
value: this.content.jsBreakpointTimestamp,
label: 'JS conditional breakpoint (timestamp)',
copyCallback,
});
}
}
return $.cdMerge(
wikilinkField.$element,
currentPageWikilinkField.$element,
permanentWikilinkField.$element,
linkField.$element,
permanentLinkField.$element,
jsCall?.$element,
jsBreakpoint?.$element,
jsBreakpointTimestamp?.$element,
);
}
/**
* Create the content of the "Diff" panel in the dialog.
*
* @returns {external:jQuery}
* @protected
*/
createDiffPanelContent() {
const copyCallback = this.copyCallback.bind(this);
const standardField = createCopyTextField({
value: this.content.diffStandard,
disabled: !this.content.diffStandard,
label: cd.s('cld-diff'),
copyCallback,
});
const shortField = createCopyTextField({
value: this.content.diffShort,
disabled: !this.content.diffShort,
label: cd.s('cld-shortdiff'),
copyCallback,
});
const wikilinkField = createCopyTextField({
value: this.content.diffWikilink,
disabled: !this.content.diffWikilink,
label: cd.s('cld-diffwikilink'),
copyCallback,
});
return $.cdMerge(
standardField.$element,
shortField.$element,
wikilinkField.$element,
this.content.$diffView,
);
}
}
tweakUserOoUiClass(CopyLinkDialog);
export default CopyLinkDialog;