Skip to content

Commit d8ecb96

Browse files
[failing] abstractify attribution notices in CTEPD
1 parent 95c6f97 commit d8ecb96

14 files changed

+300
-72
lines changed

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"rollup-plugin-node-license": "^0.2.1",
5858
"rollup-plugin-sourcemaps": "^0.6.3",
5959
"rollup-plugin-string": "^3.0.0",
60+
"rollup-pluginutils": "^2.8.2",
6061
"serve-handler": "^6.1.3",
6162
"shx": "^0.3.4",
6263
"ts-jest": "^28.0.5",

rollup.config.js

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import typescript from 'rollup-plugin-typescript2';
22
import sourcemaps from 'rollup-plugin-sourcemaps';
33
import { nodeResolve } from '@rollup/plugin-node-resolve';
4-
import { string } from 'rollup-plugin-string';
54
import commonjs from '@rollup/plugin-commonjs';
65
import json from '@rollup/plugin-json';
76
import license from 'rollup-plugin-node-license';
7+
import { createFilter } from 'rollup-pluginutils';
88
import * as fs from 'fs';
99
import * as path from 'path';
1010

1111
const production = process.env.NODE_ENV === 'production';
1212

13+
// UTILS
14+
1315
/**
1416
* Converts standard text to block comments.
1517
*
@@ -43,13 +45,44 @@ function loadBanner( ...bannerPath ) {
4345
return blockCommentIfy( fs.readFileSync( path.join( __dirname, ...bannerPath ) ) );
4446
}
4547

48+
// TRANSFORMS
49+
50+
/**
51+
* Loads CSS files as strings. Facilitates CSS loading through `mw.util`.
52+
*
53+
* @param {Object} options Options for the plugin.
54+
* @param {boolean} options.minify Whether to perform a simple CSS minify.
55+
* @return The plugin
56+
*/
57+
function cssString( options = { minify: true } ) {
58+
const filter = createFilter( 'src/**/*.css' );
59+
60+
return {
61+
name: 'CSS as string',
62+
transform( code, id ) {
63+
if ( filter( id ) ) {
64+
return {
65+
code: `export default ${JSON.stringify(
66+
options.minify ? code.replace( /[\n\t]/g, '' ) : code
67+
)};`,
68+
map: { mappings: '' }
69+
};
70+
}
71+
}
72+
};
73+
}
74+
75+
// OPTIONS
76+
4677
/**
4778
* Get plugins for this Rollup instance.
4879
*
4980
* @return {Plugin[]}
5081
*/
5182
function getPlugins() {
5283
return [
84+
// Appends license information
85+
license(),
5386
// Inserts sourcemaps
5487
!production && sourcemaps(),
5588
// Makes Common.js imports possible
@@ -60,10 +93,8 @@ function getPlugins() {
6093
typescript(),
6194
// Allows JSON imports (i18n files)
6295
json(),
63-
// Allows file imports as standard strings (CSS files)
64-
string( { include: 'src/**/*.css' } ),
65-
// Appends license information
66-
license()
96+
// Transform CSS to standard JS strings
97+
cssString()
6798
].filter( ( v ) => !!v );
6899
}
69100

src/DeputyLanguage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export default class DeputyLanguage {
2727
// of translations), but the user has a 'wgUserLanguage' differing from
2828
// English.
2929
mw.notify(
30-
// No languages to fall back on. Do not translate this string.
30+
// No languages to fall back on. Do not translate this string.
3131
'Deputy: Could not load requested language file.',
3232
{ type: 'error' }
3333
);

src/modules/cte/CopiedTemplateEditor.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import CopiedTemplate from './models/CopiedTemplate';
77
import cteStyles from './css/copied-template-editor.css';
88
import deputyCteEnglish from '../../../i18n/cte/en.json';
99
import DeputyLanguage from '../../DeputyLanguage';
10+
import WikiAttributionNotices from './models/WikiAttributionNotices';
1011

1112
declare global {
1213
interface Window {
@@ -168,8 +169,9 @@ export default class CopiedTemplateEditor {
168169
'mediawiki.widgets',
169170
'mediawiki.widgets.datetime',
170171
'jquery.makeCollapsible'
171-
], () => {
172+
], async () => {
172173
mw.util.addCSS( cteStyles );
174+
await WikiAttributionNotices.init();
173175

174176
if ( !this.dialog ) {
175177
// The following classes are used here:

src/modules/cte/models/CTEParsoidDocument.ts

Lines changed: 49 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import ParsoidDocument from '@chlodalejandro/parsoid';
22
import last from '../../../util/last';
3+
import AttributionNotice from './AttributionNotice';
4+
import WikiAttributionNotices from './WikiAttributionNotices';
35
import CopiedTemplate from './CopiedTemplate';
46

57
/**
68
* An event dispatched when a template inside a `CopiedTemplateEditorDialog` is inserted.
79
*/
810
export class TemplateInsertEvent extends Event {
911

10-
template: CopiedTemplate;
12+
template: AttributionNotice;
1113

1214
/**
1315
* @param template The template that was inserted
1416
* @param eventInitDict
1517
*/
16-
constructor( template: CopiedTemplate, eventInitDict?: EventInit ) {
18+
constructor( template: AttributionNotice, eventInitDict?: EventInit ) {
1719
super( 'templateInsert', eventInitDict );
1820
this.template = template;
1921
}
@@ -64,25 +66,15 @@ export default class CTEParsoidDocument extends ParsoidDocument {
6466
'<html><body><section data-mw-section-id="0"></section></body></html>';
6567

6668
/**
67-
* Aliases of the {{copied}} template. This must be in lowercase and all
68-
* spaces must be replaced with underscores.
69+
* A map of all Parsoid HTML elements and their attribution notices. When notices are
70+
* detected, they are added here. ParsoidTemplateTransclusionNode is not used here
71+
* since they are regenerated every time `findTemplate` is called.
6972
*/
70-
static readonly copiedTemplateAliases = <const>[
71-
'copied',
72-
'copied_from',
73-
'copywithin'
74-
];
75-
76-
/**
77-
* A list of {{copied}} notices in the document.
78-
*
79-
* @type {CopiedTemplate[]}
80-
*/
81-
copiedNotices: CopiedTemplate[];
73+
notices: Map<HTMLElement, AttributionNotice> = new Map();
8274
/**
8375
* The original number of {{copied}} notices in the document.
8476
*/
85-
originalNoticeCount: number;
77+
originalCount: number = null;
8678

8779
/**
8880
* Creates a new CTE-specific ParsoidDocument. This extends from the existing
@@ -100,8 +92,8 @@ export default class CTEParsoidDocument extends ParsoidDocument {
10092
return;
10193
}
10294

103-
this.findCopiedNotices();
104-
this.originalNoticeCount = this.copiedNotices.length;
95+
const notices = this.findCopiedNotices();
96+
this.originalCount = notices.length;
10597
} );
10698
}
10799

@@ -110,52 +102,53 @@ export default class CTEParsoidDocument extends ParsoidDocument {
110102
*/
111103
reset() {
112104
super.reset();
113-
this.originalNoticeCount = undefined;
114-
this.copiedNotices = undefined;
105+
this.originalCount = undefined;
106+
this.notices.clear();
115107
}
116108

117109
/**
118-
* Finds this document's {{copied}} notices.
110+
* Finds all content attribution notices in the talk page. This includes {{copied}},
111+
* {{merged to}}, {{merged from}}, etc.
112+
*
113+
* @return An array of AttributionNotice objects.
119114
*/
120-
findCopiedNotices() {
121-
if ( !this.copiedNotices ) {
122-
this.copiedNotices = [];
123-
}
124-
125-
const newCopiedNotices: CopiedTemplate[] = [];
115+
findNotices(): AttributionNotice[] {
126116
this.buildIndex();
117+
// Used instead of `this.notices.values()` to exclude nodes that are no longer on the DOM.
118+
const notices: AttributionNotice[] = [];
127119

128-
for ( const templateElement of this.findTemplate(
129-
new RegExp(
130-
CTEParsoidDocument.copiedTemplateAliases.map(
131-
( v ) => `(${mw.util.escapeRegExp( v )})`
132-
).join( '|' ),
133-
'gi'
134-
)
135-
) ) {
136-
// This is a copied template.
137-
const existing = this.copiedNotices.find(
138-
( v ) => v.element === templateElement.originalElement
139-
);
140-
if ( existing ) {
141-
// Record exists, reuse that same object (prevents memory leaks).
142-
newCopiedNotices.push( existing );
143-
} else {
144-
// Not yet in the existing array, create a new object.
145-
const notice = new CopiedTemplate(
146-
CTEParsoidTransclusionTemplateNode.upgradeNode( templateElement, this )
147-
);
148-
newCopiedNotices.push(
149-
notice
120+
for (
121+
const node of this.findTemplate( WikiAttributionNotices.templateAliasRegExp )
122+
) {
123+
if ( !this.notices.has( node.originalElement ) ) {
124+
// Notice not yet cached, but this is an attribution notice.
125+
// Now to determine what type.
126+
const type = WikiAttributionNotices.getTemplateNoticeType(
127+
node.getTarget().href
150128
);
151-
notice.addEventListener( 'destroy', () => {
152-
const i = this.copiedNotices.indexOf( notice );
153-
this.copiedNotices.splice( i, 1 );
154-
} );
129+
130+
const noticeInstance = new (
131+
WikiAttributionNotices.attributionNoticeClasses[ type ]
132+
)( CTEParsoidTransclusionTemplateNode.upgradeNode( node, this ) );
133+
this.notices.set( node.originalElement, noticeInstance );
155134
}
135+
136+
notices.push( this.notices.get( node.originalElement ) );
156137
}
157138

158-
this.copiedNotices = newCopiedNotices;
139+
return notices;
140+
}
141+
142+
/**
143+
* Finds this document's {{copied}} notices.
144+
*
145+
* @return An array of all CopiedTemplate objects found
146+
*/
147+
findCopiedNotices(): CopiedTemplate[] {
148+
return this.findNotices().filter(
149+
( notice ) => notice instanceof
150+
WikiAttributionNotices.attributionNoticeClasses.copied
151+
) as CopiedTemplate[];
159152
}
160153

161154
/**
@@ -237,9 +230,7 @@ export default class CTEParsoidDocument extends ParsoidDocument {
237230
// Insert.
238231
element.insertAdjacentElement( position, template );
239232
this.findCopiedNotices();
240-
const templateObject = this.copiedNotices.find(
241-
( v ) => v.element === template
242-
);
233+
const templateObject = this.notices.get( template );
243234
this.dispatchEvent( new TemplateInsertEvent( templateObject ) );
244235
}
245236
}

0 commit comments

Comments
 (0)