From e0e1b75c09a2cdb7c156c01f8b6856465e9c9916 Mon Sep 17 00:00:00 2001 From: Nardog Date: Tue, 19 Nov 2024 22:52:23 +0900 Subject: [PATCH] Support batch requested moves (#1888) * Update morebits.js * Update morebits.js * Update twinklexfd.js * Update twinklexfd.js * Update twinklexfd.js * Update twinklexfd.js * Update morebits.js * Update twinklexfd.js * always default to the non talk page --------- Co-authored-by: NovemLinguae --- modules/twinklexfd.js | 46 ++++++++++++++++------ morebits.js | 89 +++++++++++++++++++++++++++---------------- 2 files changed, 91 insertions(+), 44 deletions(-) diff --git a/modules/twinklexfd.js b/modules/twinklexfd.js index d571fe9fe..373fc398f 100644 --- a/modules/twinklexfd.js +++ b/modules/twinklexfd.js @@ -671,7 +671,7 @@ Twinkle.xfd.callback.change_category = function twinklexfdCallbackChangeCategory old_area.parentNode.replaceChild(work_area, old_area); break; - case 'rm': + case 'rm': { work_area = new Morebits.quickForm.element({ type: 'field', label: 'Requested moves', @@ -687,7 +687,8 @@ Twinkle.xfd.callback.change_category = function twinklexfdCallbackChangeCategory tooltip: 'Use this option when you are unable to perform this uncontroversial move yourself because of a technical reason (e.g. a page already exists at the new title, or the page is protected)', checked: false, event: function() { - form.newname.required = this.checked; + $('input[name="newname"]', form).prop('required', this.checked); + $('input[type="button"][value="more"]', form)[0].sublist.inputs[1].required = this.checked; }, subgroup: { type: 'checkbox', @@ -705,16 +706,32 @@ Twinkle.xfd.callback.change_category = function twinklexfdCallbackChangeCategory ] }); work_area.append({ - type: 'input', - name: 'newname', - label: 'New title:', - tooltip: 'Required for technical requests. Otherwise, if unsure of the appropriate title, you may leave it blank.' + type: 'dyninput', + inputs: [ + { + label: 'From:', + name: 'currentname', + required: true + }, + { + label: 'To:', + name: 'newname', + tooltip: 'Required for technical requests. Otherwise, if unsure of the appropriate title, you may leave it blank.' + } + ], + min: 1 }); appendReasonBox(); work_area = work_area.render(); old_area.parentNode.replaceChild(work_area, old_area); + + const currentNonTalkPage = Morebits.pageNameNorm + .replace(/^Talk:/, '') + .replace(/^([A-Za-z]*) talk:/, '$1:'); + form.currentname.value = currentNonTalkPage; break; + } default: work_area = new Morebits.quickForm.element({ @@ -766,12 +783,17 @@ Twinkle.xfd.callbacks = { // U+00A0 NO-BREAK SPACE; U+2013 EN RULE } if (venue === 'rm') { - // even if invoked from talk page, propose the subject page for move - var pageName = new mw.Title(Morebits.pageNameNorm).getSubjectPage().toText(); - var rmtrDiscuss = params['rmtr-discuss'] ? '|discuss=no' : ''; - var rmtr = '{{subst:RMassist|1=' + pageName + '|2=' + params.newname + rmtrDiscuss + '|reason=' + params.reason + '}}'; - var requestedMove = '{{subst:Requested move|current1=' + pageName + '|new1=' + params.newname + '|reason=' + params.reason + '}}'; - return params.rmtr ? rmtr : requestedMove; + if (params.rmtr) { + var rmtrDiscuss = params['rmtr-discuss'] ? '|discuss=no' : ''; + return params.currentname + .map((currentname, i) => `{{subst:RMassist|1=${currentname}|2=${params.newname[i]}${rmtrDiscuss}|reason=${params.reason}}}`) + .join('\n'); + } + return `{{subst:Requested move${ + params.currentname + .map((currentname, i) => `|current${i + 1}=${currentname}|new${i + 1}=${params.newname[i]}`) + .join('') + }|reason=${params.reason}}}`; } var text = '{{subst:' + venue + '2'; diff --git a/morebits.js b/morebits.js index d129b7f81..006662e8e 100644 --- a/morebits.js +++ b/morebits.js @@ -353,7 +353,7 @@ Morebits.quickForm.prototype.append = function QuickFormAppend(data) { * - `number`: A number input box. * - Attributes: Everything the text `input` has, as well as: min, max, step, list * - `dyninput`: A set of text boxes with "Remove" buttons and an "Add" button. - * - Attributes: name, label, min, max, sublabel, value, size, maxlength, event + * - Attributes: name, label, min, max, inputs, sublabel, value, size, maxlength, event * - `hidden`: An invisible form field. * - Attributes: name, value * - `header`: A level 5 header. @@ -371,7 +371,7 @@ Morebits.quickForm.prototype.append = function QuickFormAppend(data) { * There is some difference on how types handle the `label` attribute: * - `div`, `select`, `field`, `checkbox`/`radio`, `input`, `textarea`, `header`, and `dyninput` can accept an array of items, * and the label item(s) can be `Element`s. - * - `option`, `optgroup`, `_dyninput_element`, `submit`, and `button` accept only a single string. + * - `option`, `optgroup`, `_dyninput_cell`, `submit`, and `button` accept only a single string. * * @memberof Morebits.quickForm * @class @@ -731,14 +731,17 @@ Morebits.quickForm.element.prototype.compute = function QuickFormElementCompute( var moreButton = more[1]; var sublist = { - type: '_dyninput_element', - label: data.sublabel || data.label, - name: data.name, - value: data.value, - size: data.size, + type: '_dyninput_row', remove: false, maxlength: data.maxlength, - event: data.event + event: data.event, + inputs: data.inputs || [{ + // compatibility + label: data.sublabel || data.label, + name: data.name, + value: data.value, + size: data.size + }] }; for (i = 0; i < min; ++i) { @@ -754,31 +757,13 @@ Morebits.quickForm.element.prototype.compute = function QuickFormElementCompute( moreButton.max = max - min; moreButton.counter = 0; break; - case '_dyninput_element': // Private, similar to normal input + case '_dyninput_row': // Private node = document.createElement('div'); - if (data.label) { - label = node.appendChild(document.createElement('label')); - label.appendChild(document.createTextNode(data.label)); - label.setAttribute('for', id); - label.style.marginRight = '3px'; - } - - subnode = node.appendChild(document.createElement('input')); - if (data.value) { - subnode.setAttribute('value', data.value); - } - subnode.setAttribute('name', data.name); - subnode.setAttribute('type', 'text'); - if (data.size) { - subnode.setAttribute('size', data.size); - } - if (data.maxlength) { - subnode.setAttribute('maxlength', data.maxlength); - } - if (data.event) { - subnode.addEventListener('keyup', data.event, false); - } + data.inputs.forEach(function(subdata) { + var cell = new Morebits.quickForm.element($.extend(subdata, { type: '_dyninput_cell' })); + node.appendChild(cell.render()); + }); if (data.remove) { var remove = this.compute({ type: 'button', @@ -801,6 +786,41 @@ Morebits.quickForm.element.prototype.compute = function QuickFormElementCompute( removeButton.morebutton = data.morebutton; } break; + case '_dyninput_cell': // Private, similar to normal input + node = document.createElement('span'); + + if (data.label) { + label = node.appendChild(document.createElement('label')); + label.appendChild(document.createTextNode(data.label)); + label.setAttribute('for', id + '_input'); + label.style.marginRight = '3px'; + } + + subnode = node.appendChild(document.createElement('input')); + subnode.setAttribute('id', id + '_input'); + if (data.value) { + subnode.setAttribute('value', data.value); + } + subnode.setAttribute('name', data.name); + subnode.setAttribute('type', 'text'); + subnode.setAttribute('data-dyninput', 'data-dyninput'); + if (data.size) { + subnode.setAttribute('size', data.size); + } + if (data.maxlength) { + subnode.setAttribute('maxlength', data.maxlength); + } + if (data.required) { + subnode.setAttribute('required', 'required'); + } + if (data.disabled) { + subnode.setAttribute('required', 'disabled'); + } + if (data.event) { + subnode.addEventListener('keyup', data.event, false); + } + node.style.marginRight = '3px'; + break; case 'hidden': node = document.createElement('input'); node.setAttribute('type', 'hidden'); @@ -980,7 +1000,12 @@ Morebits.quickForm.getInputData = function(form) { break; case 'text': // falls through case 'textarea': - result[fieldNameNorm] = field.value.trim(); + if (field.dataset.dyninput) { + result[fieldNameNorm] = result[fieldNameNorm] || []; + result[fieldNameNorm].push(field.value.trim()); + } else { + result[fieldNameNorm] = field.value.trim(); + } break; default: // could be select-one, date, number, email, etc if (field.value) {