Skip to content

Commit fa06183

Browse files
support new headers (T13555)
Still TODO: Refactor all uses of `ContributionSurveyHeading` to use WikiHeading instead of HTMLHeadingElement. This will avoid unnecessary repeated calls of normalizeWikiHeading().
1 parent 1c5f541 commit fa06183

15 files changed

Lines changed: 354 additions & 221 deletions

src/modules/ia/models/CopyrightProblemsListing.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import decorateEditSummary from '../../../wiki/util/decorateEditSummary';
66
import MwApi from '../../../MwApi';
77
import changeTag from '../../../config/changeTag';
88
import warn from '../../../util/warn';
9+
import normalizeWikiHeading from '../../../wiki/util/normalizeWikiHeading';
910

1011
export interface SerializedCopyrightProblemsListingData {
1112
basic: boolean;
@@ -113,17 +114,24 @@ export default class CopyrightProblemsListing {
113114
el.parentElement.tagName === 'LI' ? el.parentElement.parentElement : el.parentElement
114115
).previousElementSibling;
115116

116-
while ( previousPivot != null && previousPivot.tagName !== 'H4' ) {
117+
let heading;
118+
// Search for a level 4 heading backwards.
119+
while (
120+
previousPivot != null &&
121+
// Set the ceiling to be immediately above for efficiency.
122+
( heading = normalizeWikiHeading( previousPivot, previousPivot.parentElement ) )
123+
?.level !== 4
124+
) {
117125
previousPivot = previousPivot.previousElementSibling;
118126
}
119127

120128
if ( previousPivot == null ) {
121129
return false;
122130
}
123131

124-
if ( previousPivot.querySelector( '.mw-headline' ) != null ) {
125-
// At this point, previousPivot is likely a MediaWiki level 4 heading.
126-
const h4Anchor = previousPivot.querySelector( '.mw-headline a' );
132+
// At this point, previousPivot is likely a MediaWiki level 4 heading.
133+
const h4Anchor = heading.h.querySelector( 'a' );
134+
if ( h4Anchor ) {
127135
listingPage = pagelinkToTitle( h4Anchor as HTMLAnchorElement );
128136

129137
// Identify if the page is a proper listing page (within the root page's

src/modules/ia/models/CopyrightProblemsSession.ts

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import equalTitle from '../../../util/equalTitle';
77
import swapElements from '../../../util/swapElements';
88
import NewCopyrightProblemsListing from '../ui/NewCopyrightProblemsListing';
99
import normalizeTitle from '../../../wiki/util/normalizeTitle';
10+
import normalizeWikiHeading from '../../../wiki/util/normalizeWikiHeading';
1011

1112
/**
1213
* A CopyrightProblemsPage that represents a page that currently exists on a document.
@@ -116,11 +117,14 @@ export default class CopyrightProblemsSession extends CopyrightProblemsPage {
116117
}
117118

118119
/**
119-
*
120+
* Adds a panel containing the "new listing" buttons (single and multiple)
121+
* and the panel container (when filing a multiple-page listing) to the proper
122+
* location: either at the end of the copyright problems section or replacing
123+
* the redlink to the blank copyright problems page.
120124
*/
121125
addNewListingsPanel(): void {
122126
document.querySelectorAll(
123-
'.mw-headline > a, a.external, a.redlink'
127+
'.mw-headline a, .mw-heading a, a.external, a.redlink'
124128
).forEach( ( el ) => {
125129
const href = el.getAttribute( 'href' );
126130
const url = new URL( href, window.location.href );
@@ -133,61 +137,54 @@ export default class CopyrightProblemsSession extends CopyrightProblemsPage {
133137
CopyrightProblemsPage.getCurrentListingPage().getPrefixedText()
134138
)
135139
) {
136-
// Crawl backwards, avoiding common inline elements, to see if this is a standalone
137-
// line within the rendered text.
138-
let currentPivot: Element = el.parentElement;
139-
140-
while (
141-
currentPivot !== null &&
142-
[ 'I', 'B', 'SPAN', 'EM', 'STRONG' ].indexOf( currentPivot.tagName ) !== -1
143-
) {
144-
currentPivot = currentPivot.parentElement;
145-
}
146-
147-
// By this point, current pivot will be a <div>, <p>, or other usable element.
148-
if (
149-
!el.parentElement.classList.contains( 'mw-headline' ) &&
150-
( currentPivot == null ||
151-
currentPivot.children.length > 1 )
152-
) {
153-
return;
154-
} else if ( el.parentElement.classList.contains( 'mw-headline' ) ) {
155-
// "Edit source" button of an existing section heading.
156-
let headingBottom = el.parentElement.parentElement.nextElementSibling;
157-
let pos: InsertPosition = 'beforebegin';
140+
if ( el.classList.contains( 'external' ) || el.classList.contains( 'redlink' ) ) {
141+
// Keep crawling up and find the parent of this element that is directly
142+
// below the parser root or the current section.
143+
let currentPivot = el;
158144
while (
159-
headingBottom != null &&
160-
!/^H[123456]$/.test( headingBottom.tagName )
145+
currentPivot != null &&
146+
!currentPivot.classList.contains( 'mw-parser-output' ) &&
147+
[ 'A', 'I', 'B', 'SPAN', 'EM', 'STRONG' ]
148+
.indexOf( currentPivot.tagName ) !== -1
161149
) {
162-
headingBottom = headingBottom.nextElementSibling;
150+
currentPivot = currentPivot.parentElement;
163151
}
164152

165-
if ( headingBottom == null ) {
166-
headingBottom = el.parentElement.parentElement.parentElement;
167-
pos = 'beforeend';
153+
// We're now at the <p> or <div> or whatever.
154+
// Check if it only has one child (the tree that contains this element)
155+
// and if so, replace the links.
156+
157+
if ( currentPivot.children.length > 1 ) {
158+
return;
168159
}
169160

170-
// Add below today's section header.
171161
mw.loader.using( [
172162
'oojs-ui-core',
173163
'oojs-ui.styles.icons-interactions',
174164
'mediawiki.widgets',
175165
'mediawiki.widgets.TitlesMultiselectWidget'
176166
], () => {
177-
// H4
178-
headingBottom.insertAdjacentElement(
179-
pos,
180-
NewCopyrightProblemsListing()
181-
);
167+
swapElements( currentPivot, NewCopyrightProblemsListing() );
182168
} );
183169
} else {
170+
// This is in a heading. Let's place it after the section heading.
171+
const heading = normalizeWikiHeading( el );
172+
173+
if ( heading.root.classList.contains( 'dp-ia-upgraded' ) ) {
174+
return;
175+
}
176+
heading.root.classList.add( 'dp-ia-upgraded' );
177+
184178
mw.loader.using( [
185179
'oojs-ui-core',
186180
'oojs-ui.styles.icons-interactions',
187181
'mediawiki.widgets',
188182
'mediawiki.widgets.TitlesMultiselectWidget'
189183
], () => {
190-
swapElements( el, NewCopyrightProblemsListing() );
184+
heading.root.insertAdjacentElement(
185+
'afterend',
186+
NewCopyrightProblemsListing()
187+
);
191188
} );
192189
}
193190
}

src/session/DeputyRootSession.ts

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import DeputyCasePage, { ContributionSurveyHeading } from '../wiki/DeputyCasePag
22
import DeputyCCISessionStartLink from '../ui/root/DeputyCCISessionStartLink';
33
import removeElement from '../util/removeElement';
44
import unwrapWidget from '../util/unwrapWidget';
5-
import sectionHeadingName from '../wiki/util/sectionHeadingName';
65
import {
76
DeputyMessageEvent,
87
DeputySessionRequestMessage,
@@ -13,9 +12,9 @@ import DeputyContributionSurveySection from '../ui/root/DeputyContributionSurvey
1312
import { SessionInformation } from './DeputySession';
1413
import { ArrayOrNot } from '../types';
1514
import DeputyMessageWidget from '../ui/shared/DeputyMessageWidget';
16-
import sectionHeadingId from '../wiki/util/sectionHeadingId';
1715
import last from '../util/last';
1816
import findNextSiblingElement from '../util/findNextSiblingElement';
17+
import normalizeWikiHeading from '../wiki/util/normalizeWikiHeading';
1918

2019
/**
2120
* The DeputyRootSession. Instantiated only when:
@@ -43,9 +42,10 @@ export default class DeputyRootSession {
4342

4443
casePage.findContributionSurveyHeadings()
4544
.forEach( ( heading: ContributionSurveyHeading ) => {
46-
const link = DeputyCCISessionStartLink( heading, casePage );
45+
const normalizedHeading = normalizeWikiHeading( heading );
46+
const link = DeputyCCISessionStartLink( normalizedHeading, casePage );
4747
startLink.push( link as HTMLElement );
48-
heading.appendChild( link );
48+
normalizedHeading.root.appendChild( link );
4949
} );
5050

5151
window.deputy.comms.addEventListener( 'sessionStarted', () => {
@@ -68,7 +68,7 @@ export default class DeputyRootSession {
6868
await mw.loader.using(
6969
[ 'oojs-ui-core', 'oojs-ui.styles.icons-content' ],
7070
() => {
71-
const firstHeading = casePage.findFirstContributionSurveyHeading();
71+
const firstHeading = casePage.findFirstContributionSurveyHeadingElement();
7272
if ( firstHeading ) {
7373
const stopButton = new OO.ui.ButtonWidget( {
7474
label: mw.msg( 'deputy.session.otherActive.button' ),
@@ -108,9 +108,9 @@ export default class DeputyRootSession {
108108
window.deputy.session.init();
109109
} );
110110

111-
casePage.normalizeSectionHeading(
111+
normalizeWikiHeading(
112112
firstHeading
113-
).insertAdjacentElement(
113+
).root.insertAdjacentElement(
114114
'beforebegin',
115115
unwrapWidget( messageBox )
116116
);
@@ -136,8 +136,8 @@ export default class DeputyRootSession {
136136
const lastActiveSection =
137137
DeputyRootSession.findFirstLastActiveSection( casePage );
138138
const firstSection =
139-
casePage.normalizeSectionHeading(
140-
casePage.findFirstContributionSurveyHeading()
139+
normalizeWikiHeading(
140+
casePage.findFirstContributionSurveyHeadingElement()
141141
);
142142

143143
// Insert element directly into widget (not as text, or else event
@@ -165,10 +165,10 @@ export default class DeputyRootSession {
165165
'deputy.session.continue.help' :
166166
'deputy.session.continue.help.fromStart',
167167
lastActiveSection ?
168-
sectionHeadingName( lastActiveSection ) :
168+
normalizeWikiHeading( lastActiveSection ).title :
169169
casePage.lastActiveSections[ 0 ]
170170
.replace( /_/g, ' ' ),
171-
sectionHeadingName( firstSection )
171+
firstSection.title
172172
),
173173
actions: [ continueButton ],
174174
closable: true
@@ -183,7 +183,7 @@ export default class DeputyRootSession {
183183
DeputyRootSession.continueSession( casePage );
184184
} else {
185185
DeputyRootSession.continueSession( casePage, [
186-
sectionHeadingId( firstSection )
186+
firstSection.id
187187
] );
188188
}
189189
window.deputy.comms.removeEventListener(
@@ -192,7 +192,7 @@ export default class DeputyRootSession {
192192
);
193193
} );
194194

195-
firstSection.insertAdjacentElement(
195+
firstSection.root.insertAdjacentElement(
196196
'beforebegin',
197197
unwrapWidget( messageBox )
198198
);
@@ -221,7 +221,7 @@ export default class DeputyRootSession {
221221
[ 'oojs-ui-core', 'oojs-ui.styles.icons-content' ],
222222
() => {
223223
const firstHeading =
224-
casePage.findFirstContributionSurveyHeading();
224+
casePage.findFirstContributionSurveyHeadingElement();
225225
if ( firstHeading ) {
226226
const messageBox = DeputyMessageWidget( {
227227
classes: [
@@ -232,9 +232,9 @@ export default class DeputyRootSession {
232232
message: mw.msg( 'deputy.session.tabActive.help' ),
233233
closable: true
234234
} );
235-
casePage.normalizeSectionHeading(
235+
normalizeWikiHeading(
236236
firstHeading
237-
).insertAdjacentElement(
237+
).root.insertAdjacentElement(
238238
'beforebegin',
239239
unwrapWidget( messageBox )
240240
);
@@ -264,7 +264,7 @@ export default class DeputyRootSession {
264264
const csHeadings = casePage.findContributionSurveyHeadings();
265265
for ( const lastActiveSection of casePage.lastActiveSections ) {
266266
for ( const heading of csHeadings ) {
267-
if ( sectionHeadingId( heading ) === lastActiveSection ) {
267+
if ( normalizeWikiHeading( heading ).id === lastActiveSection ) {
268268
return heading;
269269
}
270270
}
@@ -284,7 +284,7 @@ export default class DeputyRootSession {
284284
_casePage?: DeputyCasePage
285285
): Promise<void> {
286286
const sectionIds = ( Array.isArray( section ) ? section : [ section ] ).map(
287-
( _section ) => sectionHeadingId( _section )
287+
( _section ) => normalizeWikiHeading( _section ).id
288288
);
289289
// Save session to storage
290290
const casePage = _casePage ?? await DeputyCasePage.build();
@@ -438,7 +438,7 @@ export default class DeputyRootSession {
438438

439439
const activeSectionPromises = [];
440440
for ( const heading of this.casePage.findContributionSurveyHeadings() ) {
441-
const headingId = sectionHeadingId( heading );
441+
const headingId = normalizeWikiHeading( heading ).id;
442442

443443
if ( this.session.caseSections.indexOf( headingId ) !== -1 ) {
444444
activeSectionPromises.push(
@@ -509,8 +509,8 @@ export default class DeputyRootSession {
509509
* @param heading
510510
*/
511511
addSectionOverlay( casePage: DeputyCasePage, heading: ContributionSurveyHeading ): void {
512-
const normalizedHeading = casePage.normalizeSectionHeading( heading );
513-
const section = casePage.getContributionSurveySection( normalizedHeading );
512+
const normalizedHeading = normalizeWikiHeading( heading ).root;
513+
const section = casePage.getContributionSurveySection( normalizedHeading as HTMLElement );
514514
const list = section.find(
515515
( v ) => v instanceof HTMLElement && v.tagName === 'UL'
516516
) as HTMLUListElement;
@@ -582,7 +582,7 @@ export default class DeputyRootSession {
582582
return false;
583583
}
584584

585-
const sectionId = sectionHeadingId( heading );
585+
const sectionId = normalizeWikiHeading( heading ).id;
586586
this.sections.push( el );
587587
const lastActiveSession = this.session.caseSections.indexOf( sectionId );
588588
if ( lastActiveSession === -1 ) {
@@ -591,11 +591,7 @@ export default class DeputyRootSession {
591591
}
592592
await casePage.addActiveSection( sectionId );
593593

594-
if ( heading.parentElement.classList.contains( 'mw-heading' ) ) {
595-
heading.parentElement.insertAdjacentElement( 'afterend', el.render() );
596-
} else {
597-
heading.insertAdjacentElement( 'afterend', el.render() );
598-
}
594+
normalizeWikiHeading( heading ).root.insertAdjacentElement( 'afterend', el.render() );
599595
await el.loadData();
600596
mw.hook( 'deputy.load.cci.session' ).fire();
601597

@@ -628,9 +624,9 @@ export default class DeputyRootSession {
628624
const casePage = e0 instanceof DeputyContributionSurveySection ?
629625
e0.casePage : e0;
630626
const heading = e0 instanceof DeputyContributionSurveySection ?
631-
e0.heading : e1;
627+
e0.heading : normalizeWikiHeading( e1 );
632628

633-
const sectionId = sectionHeadingId( heading );
629+
const sectionId = heading.id;
634630
const sectionListIndex = this.sections.indexOf( el );
635631
if ( el != null && sectionListIndex !== -1 ) {
636632
this.sections.splice( sectionListIndex, 1 );
@@ -647,7 +643,7 @@ export default class DeputyRootSession {
647643
} else {
648644
await DeputyRootSession.setSession( this.session );
649645
await casePage.removeActiveSection( sectionId );
650-
this.addSectionOverlay( casePage, heading );
646+
this.addSectionOverlay( casePage, heading.h );
651647
}
652648
}
653649
}

src/ui/root/DeputyCCISessionStartLink.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { h } from 'tsx-dom';
2-
import DeputyCasePage, { ContributionSurveyHeading } from '../../wiki/DeputyCasePage';
3-
import sectionHeadingId from '../../wiki/util/sectionHeadingId';
2+
import DeputyCasePage from '../../wiki/DeputyCasePage';
3+
import { WikiHeading } from '../../wiki/util/normalizeWikiHeading';
44

55
/**
66
* The CCI session start link. Starts a CCI session when pressed.
@@ -10,14 +10,14 @@ import sectionHeadingId from '../../wiki/util/sectionHeadingId';
1010
* @return The link element to be displayed
1111
*/
1212
export default function (
13-
heading: ContributionSurveyHeading,
13+
heading: WikiHeading,
1414
casePage?: DeputyCasePage
1515
): JSX.Element {
1616
return <span class="deputy dp-sessionStarter">
1717
<span class="dp-sessionStarter-bracket">[</span>
1818
<a onClick={ async () => {
1919
if ( casePage && casePage.lastActiveSections.length > 0 ) {
20-
const headingId = sectionHeadingId( heading );
20+
const headingId = heading.id;
2121
if ( window.deputy.config.cci.openOldOnContinue.get() ) {
2222
if ( casePage.lastActiveSections.indexOf( headingId ) === -1 ) {
2323
await casePage.addActiveSection( headingId );
@@ -29,7 +29,7 @@ export default function (
2929
);
3030
}
3131
} else {
32-
await window.deputy.session.DeputyRootSession.startSession( heading );
32+
await window.deputy.session.DeputyRootSession.startSession( heading.h );
3333
}
3434
} }>{
3535
mw.message(

0 commit comments

Comments
 (0)