fix(bundle): mirror source fixes in embedded userscript
This commit is contained in:
@@ -122,76 +122,80 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===== dom.js (embedded) =====
|
// ===== 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() {
|
function currentAttribute() {
|
||||||
const KNOWN = ['strength', 'defense', 'speed', 'dexterity', 'endurance', 'intelligence'];
|
const li = tatFindActiveAttributeLi();
|
||||||
const ATTR_RE = new RegExp('\\b(' + KNOWN.join('|') + ')\\b');
|
if (!li) return null;
|
||||||
const headers = document.querySelectorAll('h1, h2, h3, h4, .title, .gym-title, [class*="gym"]');
|
const attr = tatExtractAttrFromLi(li);
|
||||||
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; }
|
|
||||||
}
|
|
||||||
if (!attr) return null;
|
if (!attr) return null;
|
||||||
let valEl = findValueNear(attrEl);
|
const current = tatExtractValueFromLi(li);
|
||||||
if (!valEl) valEl = findValueElement();
|
|
||||||
if (!valEl) return null;
|
|
||||||
const current = parseNumber(valEl.textContent);
|
|
||||||
if (current == null) return null;
|
if (current == null) return null;
|
||||||
const gym = findGymName() || 'Unknown gym';
|
const gym = tatFindGymName() || 'Unknown gym';
|
||||||
return { attr: attr, current: current, gym: gym };
|
return { attr: attr, current: current, gym: tatEsc(gym) };
|
||||||
}
|
}
|
||||||
function findValueNear(el) {
|
function tatFindActiveAttributeLi() {
|
||||||
const scope = [];
|
// Priority 1: the <li> with the "success" class (just trained).
|
||||||
let cur = el;
|
const lis = document.querySelectorAll('ul[class*="properties"] > li[class*="success"]');
|
||||||
for (let depth = 0; depth < 3 && cur; depth++) {
|
for (const li of lis) {
|
||||||
scope.push(cur);
|
if (tatExtractAttrFromLi(li)) return li;
|
||||||
cur = cur.parentElement;
|
|
||||||
}
|
}
|
||||||
let best = null, bestN = -Infinity;
|
// Priority 2: the <li> corresponding to the .gained message's attribute.
|
||||||
for (const root of scope) {
|
const gained = document.querySelector('[class*="gained"]');
|
||||||
const candidates = root.querySelectorAll('*');
|
if (gained) {
|
||||||
for (const c of candidates) {
|
const text = (gained.textContent || '').toLowerCase();
|
||||||
if (c.children.length > 0) continue;
|
for (const attr of TAT_KNOWN_ATTRS) {
|
||||||
const t = (c.textContent || '').trim();
|
if (text.indexOf(attr) !== -1) {
|
||||||
if (!/^[\d,]+(\.\d+)?$/.test(t)) continue;
|
const li = document.querySelector('ul[class*="properties"] > li[class^="' + attr + '___"]');
|
||||||
const n = parseNumber(t);
|
if (li) return li;
|
||||||
if (n == null || n < 1) continue;
|
|
||||||
if (n > bestN) { best = c; bestN = n; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return best;
|
|
||||||
}
|
}
|
||||||
function findValueElement() {
|
// Priority 3: the first <li> in the properties list.
|
||||||
const candidates = document.querySelectorAll('*');
|
const all = document.querySelectorAll('ul[class*="properties"] > li');
|
||||||
let best = null, bestN = -Infinity;
|
for (const li of all) {
|
||||||
for (const el of candidates) {
|
if (tatExtractAttrFromLi(li)) return li;
|
||||||
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; }
|
|
||||||
}
|
}
|
||||||
return best;
|
return null;
|
||||||
}
|
}
|
||||||
function findGymName() {
|
function tatExtractAttrFromLi(li) {
|
||||||
const panel = document.querySelector('.gym, #gym, [class*="gym-"], [class*="Gym"]');
|
const cls = li.className || '';
|
||||||
const roots = panel ? [panel, document.body] : [document.body];
|
const parts = cls.split(/\s+/);
|
||||||
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 attr of TAT_KNOWN_ATTRS) {
|
||||||
for (const root of roots) {
|
const prefix = attr + '___';
|
||||||
const all = root.querySelectorAll('h1, h2, h3, h4, p, span, div, li');
|
for (const c of parts) {
|
||||||
for (const el of all) {
|
if (c.indexOf(prefix) === 0) return attr;
|
||||||
if (el.children.length > 0) continue;
|
|
||||||
const t = (el.textContent || '').trim();
|
|
||||||
for (const name of known) { if (t.includes(name)) return name; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
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;
|
if (!text) return null;
|
||||||
const cleaned = text.replace(/,/g, '').trim();
|
const cleaned = String(text).replace(/,/g, '').trim();
|
||||||
if (!/^\d+(\.\d+)?$/.test(cleaned)) return null;
|
if (!/^\d+(\.\d+)?$/.test(cleaned)) return null;
|
||||||
const n = parseFloat(cleaned);
|
const n = parseFloat(cleaned);
|
||||||
return Number.isFinite(n) ? Math.floor(n) : null;
|
return Number.isFinite(n) ? Math.floor(n) : null;
|
||||||
@@ -204,25 +208,52 @@
|
|||||||
function handle(text, url) {
|
function handle(text, url) {
|
||||||
const parsed = parseTrainResponse(text, url, opts.currentAttr);
|
const parsed = parseTrainResponse(text, url, opts.currentAttr);
|
||||||
if (!parsed) { if (warnedFor !== url) { warnedFor = url; opts.onParseFail && opts.onParseFail(url); } return; }
|
if (!parsed) { if (warnedFor !== url) { warnedFor = url; opts.onParseFail && opts.onParseFail(url); } return; }
|
||||||
if (parsed.attr && opts.currentAttr && parsed.attr !== opts.currentAttr) return;
|
let delta;
|
||||||
const delta = parsed.newValue - lastValue;
|
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;
|
lastValue = parsed.newValue;
|
||||||
if (delta > 0) opts.onTrain({ attr: parsed.attr || opts.currentAttr, delta: delta, ts: Date.now() });
|
} 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);
|
wrapXhr(handle); wrapFetch(handle);
|
||||||
return { updatePrevValue: function (v) { lastValue = v; } };
|
return { updatePrevValue: function (v) { lastValue = v; } };
|
||||||
}
|
}
|
||||||
function parseTrainResponse(text, url, fallbackAttr) {
|
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 {
|
try {
|
||||||
const j = JSON.parse(text);
|
const j = JSON.parse(text);
|
||||||
if (j && typeof j === 'object' && 'newValue' in j && 'attr' in j) {
|
if (j && typeof j === 'object' && 'newValue' in j && 'attr' in j) {
|
||||||
return { newValue: Number(j.newValue), attr: String(j.attr) };
|
return { newValue: Number(j.newValue), attr: String(j.attr) };
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
const m = text.match(/(\d{1,3}(?:,\d{3})+|\d{4,})/);
|
// Strategy 3: regex fallback (last resort). Don't use the first number
|
||||||
if (m) {
|
// blindly; look specifically for the propertyValue span content, which
|
||||||
const newValue = parseInt(m[1].replace(/,/g, ''), 10);
|
// is the authoritative source.
|
||||||
if (Number.isFinite(newValue) && newValue > 0) return { newValue: newValue, attr: fallbackAttr || null };
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user