fix(bundle): mirror source fixes in embedded userscript

This commit is contained in:
dev
2026-06-05 08:43:22 -05:00
parent 540416949b
commit 8d89e40b91
+95 -64
View File
@@ -122,76 +122,80 @@
}
// ===== dom.js (embedded) =====
const TAT_KNOWN_ATTRS = ['strength', 'defense', 'speed', 'dexterity', 'endurance', 'intelligence'];
const TAT_KNOWN_GYMS = [
'Total Bastion', 'Frontline Fitness', 'Premier Fitness', 'Average Joes',
"Woody's Workout Club", "Baldr's Gym", 'Sportscience Laboratory',
'Chrome Gym', "Mr. Miyagi's", 'Power House', 'Gym 300', 'Gym 400', 'Gym 500', 'Gym 600',
'Elite Gym', "David's Gym",
];
function currentAttribute() {
const KNOWN = ['strength', 'defense', 'speed', 'dexterity', 'endurance', 'intelligence'];
const ATTR_RE = new RegExp('\\b(' + KNOWN.join('|') + ')\\b');
const headers = document.querySelectorAll('h1, h2, h3, h4, .title, .gym-title, [class*="gym"]');
let attr = null, attrEl = null;
for (const el of headers) {
const t = (el.textContent || '').trim().toLowerCase();
const m = t.match(ATTR_RE);
if (m) { attr = m[1]; attrEl = el; break; }
}
const li = tatFindActiveAttributeLi();
if (!li) return null;
const attr = tatExtractAttrFromLi(li);
if (!attr) return null;
let valEl = findValueNear(attrEl);
if (!valEl) valEl = findValueElement();
if (!valEl) return null;
const current = parseNumber(valEl.textContent);
const current = tatExtractValueFromLi(li);
if (current == null) return null;
const gym = findGymName() || 'Unknown gym';
return { attr: attr, current: current, gym: gym };
const gym = tatFindGymName() || 'Unknown gym';
return { attr: attr, current: current, gym: tatEsc(gym) };
}
function findValueNear(el) {
const scope = [];
let cur = el;
for (let depth = 0; depth < 3 && cur; depth++) {
scope.push(cur);
cur = cur.parentElement;
function tatFindActiveAttributeLi() {
// Priority 1: the <li> with the "success" class (just trained).
const lis = document.querySelectorAll('ul[class*="properties"] > li[class*="success"]');
for (const li of lis) {
if (tatExtractAttrFromLi(li)) return li;
}
let best = null, bestN = -Infinity;
for (const root of scope) {
const candidates = root.querySelectorAll('*');
for (const c of candidates) {
if (c.children.length > 0) continue;
const t = (c.textContent || '').trim();
if (!/^[\d,]+(\.\d+)?$/.test(t)) continue;
const n = parseNumber(t);
if (n == null || n < 1) continue;
if (n > bestN) { best = c; bestN = n; }
// Priority 2: the <li> corresponding to the .gained message's attribute.
const gained = document.querySelector('[class*="gained"]');
if (gained) {
const text = (gained.textContent || '').toLowerCase();
for (const attr of TAT_KNOWN_ATTRS) {
if (text.indexOf(attr) !== -1) {
const li = document.querySelector('ul[class*="properties"] > li[class^="' + attr + '___"]');
if (li) return li;
}
}
}
return best;
}
function findValueElement() {
const candidates = document.querySelectorAll('*');
let best = null, bestN = -Infinity;
for (const el of candidates) {
if (el.children.length > 0) continue;
const t = (el.textContent || '').trim();
if (!/^[\d,]+(\.\d+)?$/.test(t)) continue;
const n = parseNumber(t);
if (n == null || n < 1) continue;
if (n > bestN) { best = el; bestN = n; }
// Priority 3: the first <li> in the properties list.
const all = document.querySelectorAll('ul[class*="properties"] > li');
for (const li of all) {
if (tatExtractAttrFromLi(li)) return li;
}
return best;
return null;
}
function findGymName() {
const panel = document.querySelector('.gym, #gym, [class*="gym-"], [class*="Gym"]');
const roots = panel ? [panel, document.body] : [document.body];
const known = ['Total Bastion', 'Frontline Fitness', 'Gym 300', 'Gym 500', "Baldr's Gym", 'Sportscience Laboratory', 'Premier Fitness', 'Chrome Gym', "Mr. Miyagi's", 'Power House'];
for (const root of roots) {
const all = root.querySelectorAll('h1, h2, h3, h4, p, span, div, li');
for (const el of all) {
if (el.children.length > 0) continue;
const t = (el.textContent || '').trim();
for (const name of known) { if (t.includes(name)) return name; }
function tatExtractAttrFromLi(li) {
const cls = li.className || '';
const parts = cls.split(/\s+/);
for (const attr of TAT_KNOWN_ATTRS) {
const prefix = attr + '___';
for (const c of parts) {
if (c.indexOf(prefix) === 0) return attr;
}
}
return null;
}
function parseNumber(text) {
function tatExtractValueFromLi(li) {
const valueSpan = li.querySelector('[class^="propertyValue"]');
if (!valueSpan) return null;
return tatParseNumber(valueSpan.textContent);
}
function tatFindGymName() {
// Gym names live in aria-labels of <button class="gymButton___HASH">.
const buttons = document.querySelectorAll('button[class*="gymButton"]');
for (const btn of buttons) {
const label = btn.getAttribute('aria-label') || '';
for (const name of TAT_KNOWN_GYMS) {
// aria-label format: "Gym Name. Membership cost - $X. ..."
if (label === name || label.indexOf(name + '.') === 0 || label.indexOf(name + ' ') === 0) {
return name;
}
}
}
return null;
}
function tatParseNumber(text) {
if (!text) return null;
const cleaned = text.replace(/,/g, '').trim();
const cleaned = String(text).replace(/,/g, '').trim();
if (!/^\d+(\.\d+)?$/.test(cleaned)) return null;
const n = parseFloat(cleaned);
return Number.isFinite(n) ? Math.floor(n) : null;
@@ -204,25 +208,52 @@
function handle(text, url) {
const parsed = parseTrainResponse(text, url, opts.currentAttr);
if (!parsed) { if (warnedFor !== url) { warnedFor = url; opts.onParseFail && opts.onParseFail(url); } return; }
if (parsed.attr && opts.currentAttr && parsed.attr !== opts.currentAttr) return;
const delta = parsed.newValue - lastValue;
lastValue = parsed.newValue;
if (delta > 0) opts.onTrain({ attr: parsed.attr || opts.currentAttr, delta: delta, ts: Date.now() });
let delta;
if (typeof parsed.delta === 'number' && parsed.delta > 0) {
delta = parsed.delta;
} else if (typeof parsed.newValue === 'number' && parsed.newValue > 0) {
delta = parsed.newValue - lastValue;
lastValue = parsed.newValue;
} else {
return;
}
if (delta <= 0) return;
const attr = parsed.attr || opts.currentAttr;
if (!attr) return;
opts.onTrain({ attr: attr, delta: delta, ts: Date.now() });
}
wrapXhr(handle); wrapFetch(handle);
return { updatePrevValue: function (v) { lastValue = v; } };
}
function parseTrainResponse(text, url, fallbackAttr) {
// Strategy 1: look for the "gained" message in the response.
// Format: "You gained <number> <attr>" (e.g. "You gained 10,885.76 dexterity").
// Torn sometimes prefixes with other text (e.g. "You gained 10,885.76 dexterity"),
// so we match the number-and-attribute-name pattern directly.
const gainedMatch = text.match(/[Yy]ou\s+gained\s+([\d,]+(?:\.\d+)?)\s+(strength|defense|speed|dexterity|endurance|intelligence)\b/i);
if (gainedMatch) {
const delta = parseFloat(gainedMatch[1].replace(/,/g, ''));
const attr = gainedMatch[2].toLowerCase();
if (Number.isFinite(delta) && delta >= 0) {
return { delta: delta, attr: attr };
}
}
// Strategy 2: JSON with newValue + attr.
try {
const j = JSON.parse(text);
if (j && typeof j === 'object' && 'newValue' in j && 'attr' in j) {
return { newValue: Number(j.newValue), attr: String(j.attr) };
}
} catch {}
const m = text.match(/(\d{1,3}(?:,\d{3})+|\d{4,})/);
if (m) {
const newValue = parseInt(m[1].replace(/,/g, ''), 10);
if (Number.isFinite(newValue) && newValue > 0) return { newValue: newValue, attr: fallbackAttr || null };
// Strategy 3: regex fallback (last resort). Don't use the first number
// blindly; look specifically for the propertyValue span content, which
// is the authoritative source.
const propertyValueMatch = text.match(/class="propertyValue[^"]*"[^>]*>([\d,]+(?:\.\d+)?)</);
if (propertyValueMatch) {
const newValue = parseInt(propertyValueMatch[1].replace(/,/g, ''), 10);
if (Number.isFinite(newValue) && newValue > 0) {
return { newValue: newValue, attr: fallbackAttr || null };
}
}
return null;
}