User:Chlod/Scripts/Deputy.js: Difference between revisions

Content deleted Content added
[57af6cc] Merge remote-tracking branch 'origin/main'
v0.3.0; https://github.com/ChlodAlejandro/deputy/releases/tag/v0.3.0
Line 726:
titles: normalizeTitle(page).getPrefixedText()
})), { rvprop: 'ids|content', rvslots: 'main', rvlimit: '1' }), extraOptions)).then((data) => {
const fallbackText = extraOptions.fallbacktext;
if (data.query.pages[0].revisions == null) {
returnif null;(fallbackText) {
return Object.assign(fallbackText, {
page: data.query.pages[0]
});
}
else {
return null;
}
}
return Object.assign(data.query.pages[0].revisions[0].slots.main.content, {
Line 770 ⟶ 778:
*
* @param section The section to edit
* @param n If the section heading appears multiple times in the page and n is
* provided, this function extracts the nth occurrence of that section heading.
*/
getSectionWikitext(section, n = 1) {
return __awaiter(this, void 0, void 0, function* () {
if (typeof section === 'number') {
Line 785 ⟶ 795:
let capturing = false;
let captureLevel = 0;
let currentN = 1;
const sectionLines = [];
for (let i = 0; i < wikitextLines.length; i++) {
const line = wikitextLines[i];
const headerCheck = /^(=={1,5})\s*(.+?)\s*=={1,5}$/.exec(line);
if (!capturing &&
headerCheck != null &&
headerCheck[2] === section) {
sectionLines.pushif (linecurrentN < n); {
capturing = true currentN++;
captureLevel = headerCheck[1].length;}
else {
sectionLines.push(line);
capturing = true;
captureLevel = headerCheck[1].length;
}
}
else if (capturing) {
if (headerCheck != null && headerCheck[1].length <= captureLevel) {
capturing = false;
break;
}
else {
Line 817 ⟶ 836:
function sectionHeadingName(element) {
var _a, _b;
try {
return (_b = (_a = element.querySelector('.mw-headline')) === null || _a === void 0 ? void 0 : _a.innerText) !== null && _b !== void 0 ? _b : element.innerText;
return (_b = (_a = element.querySelector('.mw-headline')) === null || _a === void 0 ? void 0 : _a.innerText) !== null && _b !== void 0 ? _b : element.innerText;
}
catch (e) {
console.error('Error getting section name', e, element);
throw e;
}
}
 
Line 903 ⟶ 928:
getCaseName() {
return DeputyCase.getCaseName(this.title);
}
}
 
/**
* @param element The element to get the name of
* @return the ID of the section heading.
*/
function sectionHeadingId(element) {
try {
return element.querySelector('.mw-headline')
.getAttribute('id');
}
catch (e) {
console.error('Error getting section heading ID', e, element);
throw e;
}
}
Line 950 ⟶ 990:
title = yield getPageTitle(pageId);
}
// Fix for old data (moved from section name to IDs as of c5251642)
return new DeputyCasePage(pageId, title, document, parsoid, cachedInfo.lastActive, cachedInfo.lastActiveSections);
const oldSections = cachedInfo.lastActiveSections.some((v) => v.indexOf(' ') !== -1);
if (oldSections) {
cachedInfo.lastActiveSections =
cachedInfo.lastActiveSections.map((v) => v.replace(/ /g, '_'));
}
const casePage = new DeputyCasePage(pageId, title, document, parsoid, cachedInfo.lastActive, cachedInfo.lastActiveSections);
if (oldSections) {
// Save to fix the data in storage
yield casePage.saveToCache();
}
return casePage;
}
else {
Line 983 ⟶ 1,034:
* Find a contribution survey heading by section name.
*
* @param sectionNamesectionIdentifier The section nameidentifier to look for, usually the section
* name unless `useId` is set to true.
* @param useId Whether to use the section name instead of the ID
* @return The <h*> element of the heading.
*/
findContributionSurveyHeading(sectionNamesectionIdentifier, useId = false) {
// No need to perform .mw-headline existence check here, already
// done by `findContributionSurveyHeadings`
return this.findContributionSurveyHeadings()
.find((v) => sectionHeadingName(v)useId === sectionName);?
sectionHeadingId(v) === sectionIdentifier :
sectionHeadingName(v) === sectionIdentifier);
}
/**
Line 1,087 ⟶ 1,142:
* and for one-click continuation of past active sessions.
*
* @param sectionId The ID of the section to add.
*/
addActiveSection(sectionsectionId) {
return __awaiter(this, void 0, void 0, function* () {
const lastActiveSection = this.lastActiveSections.indexOf(sectionsectionId);
if (lastActiveSection === -1) {
this.lastActiveSections.push(sectionsectionId);
yield this.saveToCache();
}
Line 1,102 ⟶ 1,157:
* for this section.
*
* @param sectionId ID of the section to remove
*/
removeActiveSection(sectionsectionId) {
return __awaiter(this, void 0, void 0, function* () {
const lastActiveSection = this.lastActiveSections.indexOf(sectionsectionId);
if (lastActiveSection !== -1) {
this.lastActiveSections.splice(lastActiveSection, 1);
Line 1,200 ⟶ 1,255:
h_1("a", { onClick: () => __awaiter(this, void 0, void 0, function* () {
if (casePage && casePage.lastActiveSections.length > 0) {
const headingNameheadingId = sectionHeadingNamesectionHeadingId(heading);
if (casePagewindow.lastActiveSectionsdeputy.indexOfconfig.cci.openOldOnContinue.get(headingName) === -1) {
yieldif (casePage.addActiveSectionlastActiveSections.indexOf(headingNameheadingId); === -1) {
yield casePage.addActiveSection(headingId);
}
yield window.deputy.session.DeputyRootSession.continueSession(casePage);
}
else {
yield window.deputy.session.DeputyRootSession.continueSession(casePage, [headingId]);
}
yield window.deputy.session.DeputyRootSession.continueSession(casePage);
}
else {
Line 1,502 ⟶ 1,562:
}
}
 
var ContributionSurveyRowSort;
(function (ContributionSurveyRowSort) {
// Chronological
ContributionSurveyRowSort[ContributionSurveyRowSort["Date"] = 0] = "Date";
// Reverse chronological
ContributionSurveyRowSort[ContributionSurveyRowSort["DateReverse"] = 1] = "DateReverse";
// New size - old size
ContributionSurveyRowSort[ContributionSurveyRowSort["Bytes"] = 2] = "Bytes";
})(ContributionSurveyRowSort || (ContributionSurveyRowSort = {}));
 
var ContributionSurveyRowStatus;
Line 1,565 ⟶ 1,635:
}
return ContributionSurveyRowStatus.Unknown;
}
/**
* Guesses the sort order for a given set of revisions.
*
* @param diffs The diffs to guess from.
* @return The sort order
*/
static guessSortOrder(diffs) {
let last = null;
let dateScore = 1;
let dateReverseScore = 1;
let byteScore = 1;
for (const diff of diffs) {
if (last == null) {
last = diff;
}
else {
const diffTimestamp = new Date(diff.timestamp).getTime();
const lastTimestamp = new Date(last.timestamp).getTime();
dateScore = (dateScore + (diffTimestamp > lastTimestamp ? 1 : 0)) / 2;
dateReverseScore = (dateReverseScore + (diffTimestamp < lastTimestamp ? 1 : 0)) / 2;
byteScore = (byteScore + (diff.diffsize < last.diffsize ? 1 : 0)) / 2;
last = diff;
}
}
// Multiply by weights to remove ties
dateScore *= 1.1;
dateReverseScore *= 1.05;
switch (Math.max(dateScore, dateReverseScore, byteScore)) {
case byteScore:
return ContributionSurveyRowSort.Bytes;
case dateScore:
return ContributionSurveyRowSort.Date;
case dateReverseScore:
return ContributionSurveyRowSort.DateReverse;
}
}
/**
* Gets the sorter function which will sort a set of diffs based on a given
* sort order.
*
* @param sort
* @param mode The sort mode to use. If `array`, the returned function sorts an
* array of revisions. If `key`, the returned function sorts entries with the first
* entry element (`entry[0]`) being a revision. If `value`, the returned function
* sorts values with the second entry element (`entry[1]`) being a revision.
* @return The sorted array
*/
static getSorterFunction(sort, mode = 'array') {
return (_a, _b) => {
let a, b;
switch (mode) {
case 'array':
a = _a;
b = _b;
break;
case 'key':
a = _a[0];
b = _b[0];
break;
case 'value':
a = _a[1];
b = _b[1];
break;
}
switch (sort) {
case ContributionSurveyRowSort.Date:
return new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime();
case ContributionSurveyRowSort.DateReverse:
return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
case ContributionSurveyRowSort.Bytes:
return b.diffsize - a.diffsize;
}
};
}
/**
Line 1,624 ⟶ 1,768:
amenableparser: true
});
const sortOrder = ContributionSurveyRow.guessSortOrder(revisionData.values());
// Sort from most bytes to least.
return this.diffs = new Map([...revisionData.entries()].sort(ContributionSurveyRow.getSorterFunction(asortOrder, b'value') => b[1].diffsize - a[1].diffsize));
});
}
Line 2,606 ⟶ 2,751:
result += `${this.row.extras}`;
}
const unfinishedDiffs = (_d = (_c = (_b = this.revisions) === null || _b === void 0 ? void 0 : _b.filter((v) => !v.completed)) === null || _c === void 0 ? void 0 : _c.sort((a, b) => ContributionSurveyRow.getSorterFunction(bthis.revision.diffsize - sortOrder)(a.revision, b.diffsizerevision))) !== null && _d !== void 0 ? _d : ||[];
(b.revision.revid - a.revision.revid))) !== null && _d !== void 0 ? _d : [];
if (unfinishedDiffs.length > 0) {
result += unfinishedDiffs.map((v) => {
Line 2,675 ⟶ 2,819:
}
return result;
}
/**
* @return The hash used for autosave keys
*/
get autosaveHash() {
return `CASE--${this.row.casePage.title.getPrefixedDb()}+PAGE--${this.row.title.getPrefixedDb()}`;
}
/**
Line 2,683 ⟶ 2,833:
try {
const diffs = yield this.row.getDiffs();
this.sortOrder = ContributionSurveyRow.guessSortOrder(diffs.values());
this.wasFinished = this.row.completed;
if (this.row.completed) {
Line 2,711 ⟶ 2,862:
}
});
}
/**
* @return The hash used for autosave keys
*/
get autosaveHash() {
return `CASE--${this.row.casePage.title.getPrefixedDb()}+PAGE--${this.row.title.getPrefixedDb()}`;
}
/**
Line 2,891 ⟶ 3,036:
// Identify largest diff
const largestDiff = diffs.get(Array.from(diffs.values())
.sort(ContributionSurveyRow.getSorterFunction(a, b) => bContributionSurveyRowSort.diffsize - a.diffsizeBytes))[0]
.revid);
parts.push(
Line 3,042 ⟶ 3,187:
*/
sendStatusResponse(event) {
var _a, _b, _c, _d, _e;
const rev = (_a = this.revisions) === null || _a === void 0 ? void 0 : _a.find((r) => r.revision.revid === event.data.revision);
if (event.data.page === this.row.title.getPrefixedText() ||
if (event.data.page === this.row.title.getPrefixedText() &&
// `this(event.data.revisions`page may!== be undefinedthis.row.title.getPrefixedText() If|| so,!!rev)) don't reply.{
(_a = this.revisions) === null || _a === void 0 ? void 0 : _a.some((r) => r.revision.revid === event.data.revision))) {
window.deputy.comms.reply(event.data, {
type: 'pageStatusResponse',
Line 3,054 ⟶ 3,198:
status: this.status,
enabledStatuses: this.statusDropdown.getEnabledOptions(),
revisionStatus: event.data.revisionrev ? (_b = this.revisions.find((r) => r.revision.revid === event.data.revision)) === null || _b === void 0 ? void 0 : _brev.completed : undefined,
nextRevision: (_e_d = (_d_c = (_c_b = this.revisions) === null || _c_b === void 0 ? void 0 : _c_b.find((revision) => !revision.completed)) === null || _d_c === void 0 ? void 0 : _d_c.revision.revid) !== null && _e_d !== void 0 ? _e_d : null
});
}
Line 3,266 ⟶ 3,410:
* @param page The page to check for
* @param sectionName The section name to get the ID of
* @param n The `n`th occurrence of a section with the same name
*/
function getSectionId (page, sectionName, n = 1) {
return __awaiter(this, void 0, void 0, function* () {
const parseRequest = yield MwApi.action.get({
Line 3,277 ⟶ 3,422:
throw new Error('Error finding section ID: ' + parseRequest.error.info);
}
constlet indexSection = parseRequest.parse.sections;
let currentN .find((section) => section.line === sectionName)1;
for (const section of parseRequest.parse.sections) {
if (section.line === sectionName) {
if (currentN < n) {
currentN++;
}
else {
indexSection = section;
break;
}
}
}
if (indexSection) {
return isNaN(+indexSection.index) ? null : +indexSection.index;
Line 3,313 ⟶ 3,469:
}
 
var version = "0.3.0";
/**
var gitAbbrevHash = "05c9346";
* Deputy's current version, exported as a string.
var gitBranch = "HEAD";
*
var gitDate = "Thu, 24 Nov 2022 09:28:03 +0800";
* Why is this in its own file? Multiple modules can be run standalone (without
var gitVersion = "0.3.0+g05c9346";
* Deputy), but they are still part of Deputy and hence use the same
* `decorateEditSummary` function. However, Deputy (core) may not be available
* at the moment, leading to a reference error if `window.deputy.version` were
* to be used.
*
* This ensures that the version is available, even if the core is not loaded.
* It also keeps standalone versions lightweight to avoid too much additional code.
*
* This file is automatically modified by npm when running `npm version ...`. Avoid
* modifying it manually.
*/
var deputyVersion = /* v */ '0.2.2' /* v */;
 
/**
Line 3,337 ⟶ 3,482:
*/
function decorateEditSummary (editSummary) {
return `${editSummary} ([[User:Chlod/Scripts/Deputy|Deputy]] v${deputyVersionversion})`;
}
 
Line 3,422 ⟶ 3,567:
}
return new InternalDeputyMessageWidget(config);
}
 
/**
* Returns the last item of an array.
*
* @param array The array to get the last element from
* @return The last element of the array
*/
function last(array) {
return array[array.length - 1];
}
 
/**
* Checks the n of a given element, that is to say the `n`th occurrence of a section
* with this exact heading name in the entire page.
*
* This is purely string- and element-based, with no additional metadata or parsing
* information required.
*
* This function detects the `n` using the following conditions:
* - If the heading ID does not have an n suffix, the n is always 1.
* - If the heading ID does have an n suffix, and the detected heading name does not end
* with a number, the n is always the last number on the ID.
* - If the heading ID and heading name both end with a number,
* - The n is 1 if the ID has an equal number of ending number patterns (sequences of "_n",
* e.g. "_20_30_40" has three) with the heading name.
* - Otherwise, the n is the last number on the ID if the ID than the heading name.
*
* @param heading The heading to check
* @param headingName The name of the heading to check
* @return The n, a number
*/
function sectionHeadingN (heading, headingName) {
try {
const headingNameEndPattern = /(?:\s|_)*(\d+)/g;
const headingIdEndPattern = /_(\d+)/g;
const headingId = sectionHeadingId(heading);
const headingIdMatches = headingId.match(headingIdEndPattern);
const headingNameMatches = headingName.match(headingNameEndPattern);
if (headingIdMatches == null) {
return 1;
}
else if (headingNameMatches == null) {
// Last number of the ID
return +(headingIdEndPattern.exec(last(headingIdMatches))[1]);
}
else if (headingIdMatches.length === headingNameMatches.length) {
return 1;
}
else {
// Last number of the ID
return +(headingIdEndPattern.exec(last(headingIdMatches))[1]);
}
}
catch (e) {
console.error('Error getting section number', e, heading);
throw e;
}
}
 
Line 3,440 ⟶ 3,643:
this.casePage = casePage;
this.heading = heading;
this.headingName = sectionHeadingName(this.heading);
this.sectionElements = casePage.getContributionSurveySection(heading);
}
Line 3,594 ⟶ 3,796:
return mw.msg('deputy.content.reformat');
}
}
/**
* @return the name of the section heading.
*/
get headingName() {
return sectionHeadingName(this.heading);
}
/**
* @return the `n` of the section heading, if applicable.
*/
get headingN() {
return sectionHeadingN(this.heading, this.headingName);
}
/**
Line 3,604 ⟶ 3,818:
return __awaiter(this, void 0, void 0, function* () {
const collapsible = (_b = (_a = this.sectionElements.find((v) => v.querySelector('.mw-collapsible'))) === null || _a === void 0 ? void 0 : _a.querySelector('.mw-collapsible')) !== null && _b !== void 0 ? _b : null;
const sectionWikitext = yield this.casePage.wikitext.getSectionWikitext(this.headingName, this.headingN);
return (_c = this._section) !== null && _c !== void 0 ? _c : (this._section = new ContributionSurveySection(this.casePage, this.headingName, collapsible != null, collapsible === null || collapsible === void 0 ? void 0 : collapsible.querySelector('th > div').innerText, wikitext !== null && wikitext !== void 0 ? wikitext : sectionWikitext, wikitext ? wikitext.revid : sectionWikitext.revid));
});
}
Line 3,809 ⟶ 4,023:
this.setDisabled(true);
saveContainer.classList.add('active');
const sectionId = yield getSectionId(this.casePage.title, this.headingName, this.headingN);
yield this.save(sectionId).then((result) => __awaiter(this, void 0, void 0, function* () {
var _a;
if (result) {
mw.notify(mw.msg('deputy.session.section.saved'));
Line 3,818 ⟶ 4,033:
const sectionElements = this.casePage.getContributionSurveySection(this.heading);
sectionElements.forEach((el) => removeElement(el));
// Clear out section elements and re-append new ones to the DOM.
this.sectionElements = [];
const// oldHeadingHeading =is thispreserved to avoid messing with IDs.heading;
const heading = this.heading;
for (const child of Array.from(element.children)) {
oldHeadingif (!this.insertAdjacentElementcasePage.isContributionSurveyHeading('beforebegin', child);) {
((_a = last(this.sectionElements.push(child);) !== null && _a !== void 0 ? _a : heading)
if (this .casePage.isContributionSurveyHeadinginsertAdjacentElement('afterend', child)) {;
this.heading = sectionElements.push(child);
this.headingName =
sectionHeadingName(child);
}
}
this.originalList = element;
if (!this._section.closed) {
this._section = null;
yield this.getSection(Object.assign(wikitext, { revid }));
yield this.prepare();
oldHeadingheading.insertAdjacentElement('afterend', this.render());
// Run this asynchronously.
setTimeout(this.loadData.bind(this), 0);
}
removeElement(oldHeading);
}
}), (error) => {
Line 3,980 ⟶ 4,195:
return __awaiter(this, void 0, void 0, function* () {
yield mw.loader.using(['oojs-ui-core', 'oojs-ui.styles.icons-content'], () => {
const firstHeading = casePage.findContributionSurveyHeadingsfindFirstContributionSurveyHeading()[0];
if (firstHeading) {
const stopButton = new OO.ui.ButtonWidget({
Line 4,022 ⟶ 4,237:
* Shows the interface for continuing a previous session. This includes
* the `[continue CCI session]` notice at the top of each CCI page section heading
* and a single message box showing when the page was last worked on on top of the
* first CCI heading found.
*
Line 4,032 ⟶ 4,247:
DeputyRootSession.initEntryInterface(),
mw.loader.using(['oojs-ui-core', 'oojs-ui.styles.icons-content'], () => {
const firstHeadinglastActiveSection = casePageDeputyRootSession.findContributionSurveyHeadingsfindFirstLastActiveSection(casePage)[0];
ifconst firstSection = casePage.findFirstContributionSurveyHeading(firstHeading) {;
// Insert element directly into widget (not as text, or else event
// handlers will be destroyed).
const continueButton = new OO.ui.ButtonWidget({
label: mw.msg('deputy.session.continue.button'),
flags: ['primary', 'progressive']
});
const messageBox = DeputyMessageWidget({
classes: [
'deputy', 'dp-cs-session-notice', 'dp-cs-session-lastActive'
],
type: 'notice',
icon: 'history',
title: mw.msg('deputy.session.continue.head', new Date().toLocaleString(mw.config.get('wgUserLanguage'), { dateStyle: 'long', timeStyle: 'medium' })),
message: mw.msg('deputy.session.continue.help',lastActiveSection casePage.lastActiveSections[0]),?
actions:'deputy.session.continue.help' [continueButton],:
closable:'deputy.session.continue.help.fromStart', truelastActiveSection ?
} sectionHeadingName(lastActiveSection); :
const sessionStartListener = () => __awaiter(this, void casePage.lastActiveSections[0, void 0, function* () {]
removeElement .replace(unwrapWidget/_/g, ' '), sectionHeadingName(messageBoxfirstSection));,
actions: yield this.initTabActiveInterface();[continueButton],
});closable: true
continueButton.on('click', (}) => {;
const sessionStartListener = () => __awaiter(this, void 0, void 0, function* removeElement(unwrapWidget(messageBox)); {
removeElement(unwrapWidget(messageBox));
yield this.initTabActiveInterface();
});
continueButton.on('click', () => {
removeElement(unwrapWidget(messageBox));
if (lastActiveSection) {
DeputyRootSession.continueSession(casePage);
window.deputy.comms.removeEventListener('sessionStarted', sessionStartListener);}
});else {
firstHeading DeputyRootSession.insertAdjacentElementcontinueSession('beforebegin'casePage, unwrapWidget(messageBox));[
window.deputy.comms.addEventListener('sessionStarted', sessionStartListener, { once: true } sectionHeadingId(firstSection);
} ]);
}
window.deputy.comms.removeEventListener('sessionStarted', sessionStartListener);
});
firstSection.insertAdjacentElement('beforebegin', unwrapWidget(messageBox));
window.deputy.comms.addEventListener('sessionStarted', sessionStartListener, { once: true });
})
]);
Line 4,078 ⟶ 4,304:
const casePage = _casePage !== null && _casePage !== void 0 ? _casePage : yield DeputyCasePage.build();
return mw.loader.using(['oojs-ui-core', 'oojs-ui.styles.icons-content'], () => {
const firstHeading = casePage.findContributionSurveyHeadingsfindFirstContributionSurveyHeading()[0];
if (firstHeading) {
const messageBox = DeputyMessageWidget({
Line 4,097 ⟶ 4,323:
});
});
}
/**
* Finds the first last active section that exists on the page.
* If a last active section that still exists on the page could not be found,
* `null` is returned.
*
* @param casePage The case page to use
* @return The last active session's heading element.
*/
static findFirstLastActiveSection(casePage) {
const csHeadings = casePage.findContributionSurveyHeadings();
for (const lastActiveSection of casePage.lastActiveSections) {
for (const heading of csHeadings) {
if (sectionHeadingId(heading) === lastActiveSection) {
return heading;
}
}
}
return null;
}
/**
Line 4,106 ⟶ 4,351:
static startSession(section, _casePage) {
return __awaiter(this, void 0, void 0, function* () {
const sectionNamessectionIds = (Array.isArray(section) ? section : [section]).map((_section) => sectionHeadingNamesectionHeadingId(_section));
// Save session to storage
const casePage = _casePage !== null && _casePage !== void 0 ? _casePage : yield DeputyCasePage.build();
const session = yield this.setSession({
casePageId: casePage.pageId,
caseSections: sectionNamessectionIds
});
const rootSession = window.deputy.session.rootSession =
Line 4,123 ⟶ 4,368:
*
* @param casePage The case page to continue with
* @param sectionIds The section IDs to load on startup. If not provided, this will be
* taken from the cache. If provided, this overrides the cache, discarding any
* sections cached previously.
*/
static continueSession(casePage, sectionIds) {
return __awaiter(this, void 0, void 0, function* () {
// Save session to storage
if (sectionIds) {
casePage.lastActiveSections = sectionIds;
}
const session = yield this.setSession({
casePageId: casePage.pageId,
Line 4,198 ⟶ 4,449:
const activeSectionPromises = [];
for (const heading of this.casePage.findContributionSurveyHeadings()) {
const headingNameheadingId = sectionHeadingNamesectionHeadingId(heading);
if (this.session.caseSections.indexOf(headingNameheadingId) !== -1) {
activeSectionPromises.push(this.activateSection(this.casePage, heading)
.then(v => v ? headingNameheadingId : null));
}
else {
Line 4,308 ⟶ 4,559:
return false;
}
const sectionNamesectionId = sectionHeadingNamesectionHeadingId(heading);
this.sections.push(el);
const lastActiveSession = this.session.caseSections.indexOf(sectionNamesectionId);
if (lastActiveSession === -1) {
this.session.caseSections.push(sectionNamesectionId);
yield DeputyRootSession.setSession(this.session);
}
yield casePage.addActiveSection(sectionNamesectionId);
heading.insertAdjacentElement('afterend', el.render());
yield el.loadData();
Line 4,337 ⟶ 4,588:
const heading = e0 instanceof DeputyContributionSurveySection ?
e0.heading : e1;
const sectionNamesectionId = sectionHeadingNamesectionHeadingId(heading);
const sectionListIndex = this.sections.indexOf(el);
if (el != null && sectionListIndex !== -1) {
this.sections.splice(sectionListIndex, 1);
}
const lastActiveSessionlastActiveSection = this.session.caseSections.indexOf(sectionNamesectionId);
if (lastActiveSessionlastActiveSection !== -1) {
this.session.caseSections.splice(lastActiveSessionlastActiveSection, 1);
// If no sections remain, clear the session.
if (this.session.caseSections.length === 0) {
Line 4,353 ⟶ 4,604:
else {
yield DeputyRootSession.setSession(this.session);
yield casePage.removeActiveSection(sectionNamesectionId);
this.addSectionOverlay(casePage, heading);
}
Line 4,528 ⟶ 4,779:
this.supportedProjects = cachedSupported.projects;
}
const sites = yield fetch('https://copyvios`${(yield window.deputy.getWikiConfig()).cci.toolforgeearwigRoot.orgget()}/api.json?action=sites&version=1'`)
.then((r) => r.json());
this.supportedLanguages = [];
Line 4,572 ⟶ 4,823:
}
const { project, language } = this.guessProject(options.project, options.language);
return `https://copyvios${(yield window.toolforgedeputy.org/getWikiConfig()).cci.earwigRoot.get()}?action=search&lang=${language}&project=${project}&${typeof target === 'number' ?
'oldid=' + target :
'title=' + target.getPrefixedText()}&use_engine=${((_a = options.useEngine) !== null && _a !== void 0 ? _a : true) ? 1 : 0}&use_links=${((_b = options.useLinks) !== null && _b !== void 0 ? _b : true) ? 1 : 0}&turnitin=${((_c = options.turnItIn) !== null && _c !== void 0 ? _c : false) ? 1 : 0}`;
Line 5,346 ⟶ 5,597:
// TODO: If no token, start OAuth flow and make login API request
throw new Error('Unimplemented method.');
});
}
/**
* Returns a fully-formed HTTP URL from a given endpoint. This uses the wiki's
* set Dispatch endpoint and a given target (such as `/v1/revisions`) to get
* the full URL.
*
* @param endpoint The endpoint to get
*/
getEndpoint(endpoint) {
return __awaiter(this, void 0, void 0, function* () {
return `${(yield window.deputy.getWikiConfig()).core.dispatchRoot.get()
.href
.replace(/\/+$/, '')}/${endpoint.replace(/^\/+/, '')}`;
});
}
Line 5,358 ⟶ 5,623:
getExpandedRevisionData(revisions) {
return __awaiter(this, void 0, void 0, function* () {
return Requester.fetch(yield this.getEndpoint(`https://zoomiebot.toolforge.org/bot/api/deputy/v1/revisions/${mw.config.get('wgWikiID')} `), {
method: 'POST',
headers: {
Line 9,912 ⟶ 10,177:
// ParsoidDocument:end
var _default = ParsoidDocument_module.default = ParsoidDocument;
 
/**
* Returns the last item of an array.
*
* @param array The array to get the last element from
* @return The last element of the array
*/
function last(array) {
return array[array.length - 1];
}
 
/**
Line 11,459 ⟶ 11,714:
}
}),
signingBehavior: new Setting(generateEnumConfigurationProperties(ContributionSurveyRowSigningBehavior, ContributionSurveyRowSigningBehavior.Always)),
openOldOnContinue: new Setting({
defaultValue: false,
displayOptions: {
type: 'checkbox'
}
})
};
this.ante = {
Line 11,503 ⟶ 11,764:
this.deserialize(serializedData);
}
if (mw.storage.get(`mw-${UserConfiguration.optionKey}-lastVersion`) !== deputyVersionversion) ;
mw.storage.set(`mw-${UserConfiguration.optionKey}-lastVersion`, deputyVersionversion);
}
/**
Line 11,541 ⟶ 11,802:
UserConfiguration.optionKey = 'userjs-deputy';
 
var deputySettingsStyles = ".deputy-setting {margin-bottom: 1em;}.deputy-setting > .oo-ui-fieldLayout-align-top .oo-ui-fieldLayout-header .oo-ui-labelElement-label {font-weight: bold;}.dp-mb {margin-bottom: 1em;}.deputy-about {display: flex;}.deputy-about > :first-child {flex: 0;}.deputy-about > :first-child > img {height: 5em;width: auto;}.ltr .deputy-about > :first-child {margin-right: 1em;}.rtl .deputy-about > :first-child {margin-left: 1em;}.deputy-about > :nth-child(2) {flex: 1;}.deputy-about > :nth-child(2) > :first-child > * {display: inline;}.deputy-about > :nth-child(2) > :first-child > :first-child {font-weight: bold;font-size: 2em;}.deputy-about > :nth-child(2) > :not(:first-child > :nth-child(2) {color: gray;vertical-align: bottom;margin-topleft: 0.5em4em;}.ltr .deputy-about > :nth-child(2) > :lastnot(:first-child) {margin-top: 0.5em;}.ltr .deputy-about + div > :not(:last-child) {margin-right: 0.5em;}.rtl .deputy-about >+ :nth-child(2) > :last-childdiv > :not(:last-child) {margin-left: 0.5em;}.ltr .deputy-about >+ :nth-child(2) > :last-childdiv {text-align: right;}.rtl .deputy-about >+ :nth-child(2) > :last-childdiv {text-align: left;}";
 
/* eslint-disable mediawiki/msg-doc */
Line 11,853 ⟶ 12,114:
 
var deputySettingsEnglish = {
"deputy.about.version": "v$1 ($2)",
"deputy.about": "About",
"deputy.about.homepage": "Homepage",
Line 11,860 ⟶ 12,122:
"deputy.about.license": "Deputy is licensed under the <a href=\"$1\">Apache License 2.0</a>. The source code for Deputy is available on <a href=\"$2\">GitHub</a>, and is free for everyone to view and suggest changes.",
"deputy.about.thirdParty": "Deputy is bundled with third party libraries to make development easier. All libraries have been vetted for user security and license compatibility. For more information, see the <a href=\"$1\">\"Licensing\"</a> section on Deputy's README.",
"deputy.about.buildInfo": "Deputy v$1 ($2), committed $3.",
"deputy.about.footer": "Made with love, coffee, and the tears of copyright editors.",
"deputy.settings.portlet": "Deputy preferences",
Line 11,903 ⟶ 12,166:
"deputy.setting.user.cci.signingBehavior.lastOnly": "Only sign the last row modified (prevents assessor recognition)",
"deputy.setting.user.cci.signingBehavior.never": "Never sign rows (prevents assessor recognition)",
"deputy.setting.user.cci.openOldOnContinue.name": "Open old versions on continue",
"deputy.setting.user.cci.openOldOnContinue.description": "If enabled, all previously-open sections of a given case page will also be opened alongside the section where the \"continue CCI session\" link was clicked.",
"deputy.setting.user.ante": "ANTE",
"deputy.setting.user.ante.enableAutoMerge.name": "Merge automatically on run",
Line 11,935 ⟶ 12,200:
"deputy.setting.user.ia.onBatchSubmit.nothing": "Do nothing",
"deputy.setting.user.ia.onBatchSubmit.reload": "Reload the noticeboard page",
"deputy.setting.wiki.core": "Core",
"deputy.setting.wiki.core.dispatchRoot.name": "Deputy Dispatch root URL",
"deputy.setting.wiki.core.dispatchRoot.description": "The URL to a Deputy Dispatch instance that can handle this wiki. Deputy Dispatch is a webserver responsible for centralizing and optimizing data used by Deputy, and can be used to reduce load on wikis. More information can be found at https://github.com/ChlodAlejandro/deputy-dispatch.",
"deputy.setting.wiki.cci": "CCI",
"deputy.setting.wiki.cci.enabled.name": "Enable contributor copyright investigations assistant",
Line 11,944 ⟶ 12,212:
"deputy.setting.wiki.cci.collapseBottom.name": "Collapsible wikitext (bottom)",
"deputy.setting.wiki.cci.collapseBottom.description": "Placed at the end of a section when closing a contributor survey section. On the English Wikipedia, this is {{collapse bottom}}. Other wikis may have an equivalent template.",
"deputy.setting.wiki.cci.earwigRoot.name": "Earwig's Copyvio Detector root URL",
"deputy.setting.wiki.cci.earwigRoot.description": "The URL to an instance of Earwig's Copyvio Detector that can handle this wiki. The official copyvio detector (copyvios.toolforge.org) can only handle Wikimedia wikis — you may change this behavior by specifying a custom instance that can process this wiki here.",
"deputy.setting.wiki.ante": "ANTE",
"deputy.setting.wiki.ante.enabled.name": "Enable the Attribution Notice Template Editor",
Line 11,956 ⟶ 12,226:
"deputy.setting.wiki.ia.preload.name": "Preload",
"deputy.setting.wiki.ia.preload.description": "Defines the page content to preload the page with if a given subpage does not exist yet. This should be an existing page on-wiki. Leave blank to avoid using a preload entirely.",
"deputy.setting.wiki.ia.listingWikitext.name": "Listing wikitext",
"deputy.setting.wiki.ia.listingWikitext.description": "Defines the wikitext that will be used when adding listings to a noticeboard page. You may use \"$1\" to denote the page being reported, and \"$2\" for user comments (which shouldn't contain the signature).",
"deputy.setting.wiki.ia.listingWikitextMatch.name": "Regular expression for listings",
Line 11,988 ⟶ 12,258:
h_1("img", { src: ConfigurationGroupTabPanel.logoUrl, alt: "Deputy logo" })),
h_1("div", { style: "flex: 1" },
h_1("div", null, mw.msg('deputy.name')),
h_1("div", null, mw.msg('deputy.description')),
h_1("div", null,
h_1("adiv", {null, href: "https://wmw.wiki/5k$q", target: "_blank" }, unwrapWidgetmsg(new OO'deputy.ui.ButtonWidget({name')),
h_1("div", label:null, mw.msg('deputy.about.homepageversion', version, gitAbbrevHash))),
h_1("div", null, flags: [mw.msg('progressivedeputy.description'])))),
h_1("div", })))null,
h_1("a", { href: "https://githubw.com/ChlodAlejandrowiki/deputy5k$q", target: "_blank" }, unwrapWidget(new OO.ui.ButtonWidget({
label: mw.msg('deputy.about.openSourcehomepage'),
flags: ['progressive']
}))),
h_1("a", { href: "https://wgithub.wikicom/ChlodAlejandro/5k$pdeputy", target: "_blank" }, unwrapWidget(new OO.ui.ButtonWidget({
label: mw.msg('deputy.about.contactopenSource'),
flags: ['progressive']
})))))),
h_1("a", { href: "https://w.wiki/5k$p", target: "_blank" }, unwrapWidget(new OO.ui.ButtonWidget({
label: mw.msg('deputy.about.contact'),
flags: ['progressive']
})))),
h_1("p", { dangerouslySetInnerHTML: mw.msg('deputy.about.credit') }),
h_1("p", { dangerouslySetInnerHTML: mw.msg('deputy.about.license', 'https://www.apache.org/licenses/LICENSE-2.0', 'https://github.com/ChlodAlejandro/deputy') }),
h_1("p", { dangerouslySetInnerHTML: mw.msg('deputy.about.thirdParty', 'https://github.com/ChlodAlejandro/deputy#licensing') }),
h_1("p", { style: { fontSize: '0.9em', color: 'darkgray' }, dangerouslySetInnerHTML: mw.msg('deputy.about.buildInfo', gitVersion, gitBranch, new Date(gitDate).toLocaleString()) }),
h_1("p", { style: { fontSize: '0.9em', color: 'darkgray' }, dangerouslySetInnerHTML: mw.msg('deputy.about.footer') })));
}
Line 12,176 ⟶ 12,449:
 
/**
* @param config The current configuration (actively loaded, not the one being viewed)
* @return An HTML element consisting of an OOUI MessageWidget
*/
function WikiConfigurationEditIntro(config) {
const r = 'deputy-' + Math.random().toString().slice(2);
const current = config.onConfigurationPage();
const messageBox = new OO.ui.MessageWidget({
classes: [
'deputy', 'dp-mb'
],
type: 'info',
label: new OO.ui.HtmlSnippet((h_1("span", null,
h_1("b", null, mw.msg('deputy.settings.wikiEditIntro.title')),
h_1("br", null),
current ?
mw.msg('deputy.settings.wikiEditIntro.current') :
h_1("span", { dangerouslySetInnerHTML: mw.message('deputy.settings.wikiEditIntro.other', config.sourcePage.getPrefixedText()).parse() }),
h_1("br", null),
h_1("span", { id: r }))).innerHTML)
});
let buttons;
if (current) {
Line 12,214 ⟶ 12,472:
flags: ['progressive', 'primary'],
label: mw.msg('deputy.settings.wikiEditIntro.edit.otherCurrent'),
disabled: !config.editable,
title: config.editable ?
undefined : mw.msg('deputy.settings.wikiEditIntro.edit.protected')
Line 12,233 ⟶ 12,491:
buttons = [editCurrent, editOther];
}
const messageBox = DeputyMessageWidget({
classes: [
'deputy', 'dp-mb'
],
type: 'info',
title: mw.msg('deputy.settings.wikiEditIntro.title'),
message: current ?
mw.msg('deputy.settings.wikiEditIntro.current') :
h_1("span", { dangerouslySetInnerHTML: mw.message('deputy.settings.wikiEditIntro.other', config.sourcePage.getPrefixedText()).parse() }),
actions: buttons
});
const box = unwrapWidget(messageBox);
swapElements(box.querySelector(`#${r}`), h_1("span", null, buttons.map(b => unwrapWidget(b))));
box.classList.add('deputy', 'deputy-wikiConfig-intro');
return box;
Line 12,342 ⟶ 12,610:
defaultValue: WikiConfiguration.configVersion,
displayOptions: { hidden: true },
alwaysSave: true
}),
dispatchRoot: new Setting({
serialize: (v) => v.href,
deserialize: (v) => new URL(v),
defaultValue: new URL('https://zoomiebot.toolforge.org/bot/api/deputy/'),
displayOptions: { type: 'text' },
alwaysSave: true
})
Line 12,351 ⟶ 12,626:
}),
rootPage: new Setting({
serialize: (v) => v === null || v === void 0 ? void 0 : v.getPrefixedText(),
deserialize: (v) => new mw.Title(v),
defaultValue: null,
Line 12,363 ⟶ 12,638:
defaultValue: collapseBottom,
displayOptions: { type: 'code' }
}),
earwigRoot: new Setting({
serialize: (v) => v.href,
deserialize: (v) => new URL(v),
defaultValue: new URL('https://copyvios.toolforge.org/'),
displayOptions: { type: 'text' },
alwaysSave: true
})
};
Line 12,377 ⟶ 12,659:
}),
rootPage: new Setting({
serialize: (v) => v === null || v === void 0 ? void 0 : v.getPrefixedText(),
deserialize: (v) => new mw.Title(v),
defaultValue: null,
Line 12,387 ⟶ 12,669:
}),
preload: new Setting({
serialize: (v) => { var _a, _b; return ((_b = (_a = v === null || v === void 0 ? void 0 : v.trim()) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) === 0 ? null : v.trim(); },
defaultValue: null,
displayOptions: { type: 'page' }
Line 12,430 ⟶ 12,712:
responses: new Setting(Object.assign(Object.assign({}, Setting.basicSerializers), { defaultValue: null, displayOptions: { type: 'unimplemented' } }))
};
this.all = { core: this.core, cci: this.cci, ante: this.ante, ia: this.ia };
/**
* Set to true when this configuration is outdated based on latest data. Usually adds banners
Line 12,475 ⟶ 12,757:
}
if (configPage) {
return new WikiConfiguration(new mw.Title(configPage.title.title, configPage.title.namespace), JSON.parse(configPage.wt), configPage.editable);
}
else {
Line 12,492 ⟶ 12,774:
const content = yield getPageContent(sourcePage, {
prop: 'revisions|info',
intestactions: 'edit',
fallbacktext: '{}'
});
return {
Line 12,968 ⟶ 13,251:
"deputy.session.continue.head": "You last worked on this page on $1.",
"deputy.session.continue.help": "Continue working on \"$1\" and pick up where you left off.",
"deputy.session.continue.help.fromStart": "The section \"$1\" might have been archived already. Not to worry, you can being working on \"$2\".",
"deputy.session.tabActive.head": "You are working on this case page from another tab.",
"deputy.session.tabActive.help": "Deputy can only run on one case page and tab at a time. Navigate to the other tab to continue working.",
Line 15,071 ⟶ 15,355:
* @type {string}
*/
this.version = deputyVersionversion;
/**
* The current page as an mw.Title.