Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- await (async function(oneLine) {
- function findColumn(columns, fn) {
- const i = columns.findIndex(x => fn(x.name));
- if (i >= 0) {
- return columns.splice(i, 1)[0];
- }
- return null;
- }
- const oth = {
- api: true,
- map: false,
- location: false,
- notes: true,
- customBit: false
- };
- oneLine = oneLine ?? false;
- // ================
- // (0) Check page
- // ================
- const pageTitle = window.location.href.match(/^https:\/\/wiki.guildwars2.com\/wiki\/(.*?)(?:#.*)?$/)[1];
- if (!pageTitle) {
- throw new Error(`Couldn't get GW2W's window.location.href: ${window.location.href}`);
- }
- const $achievementbox = $('h2 ~ table.achievementbox')
- if ($achievementbox.length === 0) {
- throw new Error("Couldn't find .achievementbox");
- }
- const $table = $('h2:has("#Walkthrough") ~ table');
- if ($table.length === 0) {
- throw new Error("Couldn't find walkthrough table");
- }
- // ====================
- // (1) Fetch wikitext
- // ====================
- const pageText = await fetch(`https://wiki.guildwars2.com/index.php?title=${pageTitle}&action=edit`).then(x => x.text());
- //const wikitext = new DOMParser().parseFromString(pageText, 'text/html').getElementById('wpTextbox1').value;
- const wikitext = new DOMParser().parseFromString(pageText, 'text/html').getElementsByTagName('textarea')[0].value;
- if (!wikitext) {
- throw new Error("Couldn't fetch wikitext");
- }
- // ====================
- // (2) Parse wikitext
- // ====================
- const table = wikitext.match(/== *Walkthrough *==\n(?:.*\n)*\{\| \{\{STDT.*\n((?:.*\n)+)\|\}(?:.*\n?)+/im)[1];
- const rr = table.match(/(?:.*?\n)+?(\|-(?:.*\n?)+)/m)[1];
- const rows = rr.match(/(?<=\|-.*\n)(?:(?:!|\|).*\n?)+?(?:[^!|].*\n?)*(?:(?=\|-)|$)/gm)
- .map(x => x.split(/\|\||\n\|/).flat().map(x => x.replace(/^(?:!|\|) *(.*?) *$/g, '$1').trim()));
- const rowClasses = rr.match(/(?<=\|-).*/gm).map(x => x.trim().match(/class="(.*?)"/)?.[1] ?? '');
- const columnNames = table.match(/^! *.*(?:(?=\n!)|(?=\n\|-))$/gm)
- .map(x => x.split(/!!|\|\|/)).flat().map(x => x.replace(/^! *(.*?) *$/g, '$1').replace(/^.*?\|(.*)$/, '$1').trim())
- .map((x, i) => ({ i, name: x }));
- const customColumns = columnNames.slice();
- const indexColumn = findColumn(customColumns, s => (/^(#|number|step|)$/gi).test(s));
- const mapColumn = findColumn(customColumns, s => (/^map$/gi).test(s));
- const locationColumn = findColumn(customColumns, s => (/^(location|image)$/gi).test(s));
- let hintColumn = findColumn(customColumns, s => (/\bhints?\b/gi).test(s));
- let notesColumn = findColumn(customColumns, s => (/^(notes?|directions?|acquisition)$/gi).test(s)) ?? findColumn(customColumns, s => (/^(description|hints)$/gi).test(s));
- if (!notesColumn) {
- notesColumn = hintColumn;
- hintColumn = null;
- }
- const landmarkColumn = findColumn(customColumns, s => (/(^closest landmark$)|(\bpoi\b)|(\bpoint of interest\b)|(\bwaypoint\b)|(\b\{\{map icon\|waypoint}}\b)/gi).test(s));
- let objectivesColumn = customColumns.splice(0, 1)[0];
- if (!objectivesColumn) {
- objectivesColumn = hintColumn;
- hintColumn = null;
- }
- oth.map = !!mapColumn;
- oth.location = !!locationColumn;
- oth.notes = !!notesColumn;
- const oRows = rows.map(row => {
- const o = {};
- let col = 1; let k = 0;
- if (objectivesColumn) o.objective = row[objectivesColumn.i];
- if (indexColumn) o.index = row[indexColumn.i];
- if (notesColumn) o.notes = row[notesColumn.i];
- if (mapColumn) o.map = row[mapColumn.i] === '' || /^(<!--.*-->)|( *([\x{2013}\x{2014}])|(—)|(–) *)$/.test(row[mapColumn.i]) ? null : row[mapColumn.i];
- if (locationColumn) o.location = row[locationColumn.i] === '' || /^(<!--.*-->)|( *([\x{2013}\x{2014}])|(—)|(–) *)$/.test(row[locationColumn.i]) ? null : row[locationColumn.i];
- if (landmarkColumn) {
- o[`col${col}`] = row[landmarkColumn.i];
- col++;
- k++;
- }
- if (hintColumn) {
- o[`col${col}`] = row[hintColumn.i];
- col++;
- k++;
- }
- // Custom columns
- if (customColumns.length > 3 - k) {
- throw new Error(`The table has too many custum columns (${customColumns.length + k}): ${[landmarkColumn, ...customColumns].filter(x => !!x).map(x => `"${x}"`).join(', ')}`);
- }
- for (let i = 0; i < customColumns.length; ++i) {
- o[`col${i+1+k}`] = row[customColumns[i].i] === '' || /^<!--.*-->$/.test(row[customColumns[i].i]) ? '' : row[customColumns[i].i];
- }
- return o;
- });
- // ========================================================
- // (3) Check if the objectives list matches the API order
- // ========================================================
- const achievementId = $achievementbox.find('tr[data-id]').first().attr('data-id');
- const wikiObjectives = $achievementbox.find('table:has(tbody > tr:contains("Objectives:")) > tbody > tr:not(.expandable):not(.collapse-reverse):not(:contains("Objectives:")) li').toArray().map(x => x.innerText);
- const apiAchievement = await fetch(`https://api.guildwars2.com/v2/achievements/${achievementId}`).then(x => x.json());
- if (apiAchievement && apiAchievement.bits) {
- apiAchievement.bits = apiAchievement.bits.map((x, i) => ({ i, ...x }));
- let b = true;
- let i = 0;
- while (b && i < wikiObjectives.length) {
- b &&= wikiObjectives[i] === apiAchievement.bits[i].text;
- i++;
- }
- if (!b) {
- console.warn(`BITS[${i-1}]: "${wikiObjectives[i-1]}" !== "${apiAchievement.bits[i-1].text}"`);
- oth.customBit = true;
- }
- } else {
- oth.api = false;
- }
- // ==========================================
- // (4) Fix the objectives bits if necessary
- // ==========================================
- let bitsManualReview = false;
- if (oth.customBit) {
- const bits = wikiObjectives.map((s, i) => {
- const o = apiAchievement.bits.filter(x => x.text === s);
- if (!o) throw new Error(`BITS: Couldn't find objective: "${s}"`);
- bitsManualReview ||= o.length > 1;
- return o.map(x => x.i);
- });
- for (let i = 0; i < oRows.length; ++i) {
- oRows[i].customBit = bits[i].join(',');
- }
- }
- // =============================
- // (5) Create Objectives table
- // =============================
- const N = oneLine ? '' : '\n';
- // {{Objectives table header}}
- let col = 1;
- let out = '{{Objectives table header';
- if (!oth.api) out += '|api=false';
- if (oth.map) out += '|map=true';
- if (oth.location) out += '|location=true';
- if (!oth.notes) out += '|notes=false';
- if (landmarkColumn) out += `|col${col++}=Closest landmark`;
- if (hintColumn) out += `|col${col++}=Hint`;
- for (let i = 0; i < customColumns.length; ++i, ++col) {
- out += `|col${col}=${customColumns[i].name}`;
- }
- if (oth.customBit) out += '|custom bit=true';
- out += '}}\n';
- // {{Objectives table row}}
- for (let i = 0; i < oRows.length; ++i) {
- out += `{{Objectives table row|${oRows[i].objective ?? wikiObjectives[i]}${N}` +
- (oth.notes ? `| ${oRows[i].notes}${N}` : '') +
- (
- (oth.map ? `| map = ${oRows[i].map?.replace(/^\[\[File:(.*?)(?:\|.*px)?]]$/, '$1') ?? 'none'} ` : '') +
- (oth.location ? `| location = ${oRows[i].location?.replace(/^\[\[File:(.*?)(?:\|.*px)?]]$/, '$1') ?? 'none'} ` : '') +
- (oRows[i].col1 !== undefined ? `| col1 = ${oRows[i].col1} ` : '') +
- (oRows[i].col2 !== undefined ? `| col2 = ${oRows[i].col2} ` : '') +
- (oRows[i].col3 !== undefined ? `| col3 = ${oRows[i].col3} `: '') +
- (oth.customBit ? `| custom bit=${oRows[i].customBit} ` : '') +
- (rowClasses[i] ? `| class=${rowClasses[i]}` : '')
- ).trim() + `${N}` +
- '}}\n';
- }
- out += '|}';
- if (oneLine) {
- out = out.replaceAll('| map = ', '|map=').replaceAll('| location = ', '|location=').replace(/\| col([1-3]) = /g, '|col$1=').replaceAll('| custom bit = ', '|custom bit=').replaceAll('| class = ', '|class=').replaceAll('| ', '|');
- }
- console.log(out);
- if (oth.customBit) {
- console.warn(`BITS: id=${achievementId}`);
- }
- if (bitsManualReview) {
- console.warn('BITS: Some objectives have multiple bits. Manual review required!');
- }
- return {
- oth,
- rows,
- rowClasses,
- columnNames,
- oRows,
- wikiObjectives,
- apiAchievement,
- };
- })()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement