Spaces:
Running
Running
Download recallroll/core.js from CyberMax-tools/cybermax: direct link, hf CLI and curl.
- Browser
- Download file 6.33 kB
-
https://huggingface.co/spaces/CyberMax-tools/cybermax/resolve/main/recallroll/core.js
- Command line
-
hf download hf://spaces/CyberMax-tools/cybermax/recallroll/core.js
-
curl -L -o core.js https://huggingface.co/spaces/CyberMax-tools/cybermax/resolve/main/recallroll/core.js
6.33 kB
| /* Recallroll core: pure functions (no DOM, no network) shared by the page and the Node tests. © 2026 CyberMax */ | |
| (function (root) { | |
| 'use strict'; | |
| var TRANS = { A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7, H: 8, J: 1, K: 2, L: 3, M: 4, N: 5, P: 7, R: 9, S: 2, T: 3, U: 4, V: 5, W: 6, X: 7, Y: 8, Z: 9 }; | |
| var WEIGHTS = [8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2]; | |
| function normVin(s) { return String(s || '').toUpperCase().replace(/[\s-]/g, ''); } | |
| // {vin, ok, error, warning}: 17 chars, no I/O/Q; check digit (position 9) is required for North American VINs, | |
| // so a mismatch is a warning (typo likely), not a hard error. | |
| function checkVin(input) { | |
| var vin = normVin(input); | |
| if (!vin) return { vin: vin, ok: false, error: 'Enter a VIN.' }; | |
| if (vin.length !== 17) return { vin: vin, ok: false, error: 'A VIN has 17 characters (this one has ' + vin.length + ').' }; | |
| if (/[IOQ]/.test(vin)) return { vin: vin, ok: false, error: 'VINs never contain the letters I, O or Q (check for 1 and 0).' }; | |
| if (!/^[A-HJ-NPR-Z0-9]{17}$/.test(vin)) return { vin: vin, ok: false, error: 'Only letters and digits are allowed.' }; | |
| var sum = 0; | |
| for (var i = 0; i < 17; i++) { | |
| var c = vin[i]; | |
| var v = /[0-9]/.test(c) ? +c : TRANS[c]; | |
| sum += v * WEIGHTS[i]; | |
| } | |
| var r = sum % 11, want = r === 10 ? 'X' : String(r); | |
| return { vin: vin, ok: true, checkDigit: want, warning: vin[8] === want ? null : 'The check digit (9th character) should be ' + want + ' for this VIN. North American VINs always pass this check, so look for a typo.' }; | |
| } | |
| // Pull VIN-looking tokens out of pasted text (one per line, commas, spreadsheet columns...). Keeps order, dedupes. | |
| function extractVins(text, max) { | |
| var seen = {}, out = []; | |
| String(text || '').toUpperCase().split(/[^A-Z0-9]+/).forEach(function (t) { | |
| if (t.length === 17 && !seen[t]) { seen[t] = 1; out.push(t); } | |
| }); | |
| return max ? out.slice(0, max) : out; | |
| } | |
| function pick(r, k) { var v = (r[k] || '').toString().trim(); return v && v !== 'Not Applicable' ? v : ''; } | |
| // vPIC DecodeVinValues(Batch) result row -> compact vehicle | |
| function vehicleFromVpic(r) { | |
| var code = pick(r, 'ErrorCode'); | |
| return { | |
| vin: pick(r, 'VIN'), year: pick(r, 'ModelYear'), make: pick(r, 'Make'), model: pick(r, 'Model'), trim: pick(r, 'Trim'), | |
| series: pick(r, 'Series'), body: pick(r, 'BodyClass'), type: pick(r, 'VehicleType'), | |
| engine: [pick(r, 'DisplacementL') && (Number(pick(r, 'DisplacementL')).toFixed(1) + ' L'), pick(r, 'EngineCylinders') && (pick(r, 'EngineCylinders') + ' cyl'), pick(r, 'FuelTypePrimary'), pick(r, 'ElectrificationLevel')].filter(Boolean).join(' · '), | |
| drive: pick(r, 'DriveType'), plant: [pick(r, 'PlantCity'), pick(r, 'PlantState'), pick(r, 'PlantCountry')].filter(Boolean).join(', '), | |
| manufacturer: pick(r, 'Manufacturer'), gvwr: pick(r, 'GVWR'), | |
| errorCode: code, errorText: pick(r, 'ErrorText'), | |
| decoded: !!(pick(r, 'Make') && pick(r, 'ModelYear')), | |
| }; | |
| } | |
| function key(s) { return String(s || '').toUpperCase().replace(/[^A-Z0-9]/g, ''); } | |
| // Recall API model names don't always equal vPIC's ("F-150" vs "F-150 SUPERCREW"). Given the models NHTSA lists | |
| // for recalls in that make/year, return the names to query: exact match first, else names starting with ours. | |
| function matchModels(model, recallModels) { | |
| var k = key(model); | |
| if (!k) return []; | |
| var names = (recallModels || []).map(function (m) { return typeof m === 'string' ? m : m.model; }); | |
| var exact = names.filter(function (n) { return key(n) === k; }); | |
| if (exact.length) return exact.slice(0, 1); | |
| var starts = names.filter(function (n) { return key(n).indexOf(k) === 0 || k.indexOf(key(n)) === 0; }); | |
| return starts.slice(0, 4); | |
| } | |
| // api.nhtsa.gov recallsByVehicle rows -> deduped campaigns, newest first | |
| function normalizeRecalls(rows) { | |
| var seen = {}, out = []; | |
| (rows || []).forEach(function (r) { | |
| var id = r.NHTSACampaignNumber; | |
| if (!id || seen[id]) return; | |
| seen[id] = 1; | |
| out.push({ | |
| campaign: id, date: parseNhtsaDate(r.ReportReceivedDate), component: r.Component || '', summary: r.Summary || '', | |
| consequence: r.Consequence || '', remedy: r.Remedy || '', notes: r.Notes || '', manufacturer: r.Manufacturer || '', | |
| doNotDrive: !!r.parkIt, parkOutside: !!r.parkOutSide, ota: !!r.overTheAirUpdate, | |
| model: r.Model || '', year: r.ModelYear || '', | |
| }); | |
| }); | |
| out.sort(function (a, b) { return (b.date || '').localeCompare(a.date || ''); }); | |
| return out; | |
| } | |
| // The Recalls API sends day/month/year: "27/06/2019" -> "2019-06-27"; ISO strings pass through. | |
| function parseNhtsaDate(s) { | |
| s = String(s || ''); | |
| var m = s.match(/^(\d{2})\/(\d{2})\/(\d{4})/); | |
| if (m) return m[3] + '-' + m[2] + '-' + m[1]; | |
| m = s.match(/^(\d{4}-\d{2}-\d{2})/); | |
| return m ? m[1] : ''; | |
| } | |
| function csvCell(v) { | |
| var s = v == null ? '' : String(v); | |
| if (/^[=+\-@]/.test(s)) s = "'" + s; // no formula injection when opened in a spreadsheet | |
| return /[",\n\r]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s; | |
| } | |
| function toCsv(rows, cols) { | |
| return [cols.join(',')].concat(rows.map(function (r) { return cols.map(function (c) { return csvCell(r[c]); }).join(','); })).join('\r\n') + '\r\n'; | |
| } | |
| // filter the snapshot's recent campaigns by free text + make | |
| function filterRecent(list, q, make, flagsOnly) { | |
| q = String(q || '').toLowerCase().trim(); | |
| return (list || []).filter(function (c) { | |
| if (make && c.makes.split(', ').indexOf(make) < 0) return false; | |
| if (flagsOnly && !(c.do_not_drive || c.park_outside)) return false; | |
| if (!q) return true; | |
| return (c.vehicles + ' ' + c.component + ' ' + c.manufacturer + ' ' + c.campaign + ' ' + (c.summary || '')).toLowerCase().indexOf(q) >= 0; | |
| }); | |
| } | |
| var api = { checkVin: checkVin, extractVins: extractVins, vehicleFromVpic: vehicleFromVpic, matchModels: matchModels, normalizeRecalls: normalizeRecalls, parseNhtsaDate: parseNhtsaDate, toCsv: toCsv, csvCell: csvCell, filterRecent: filterRecent, normVin: normVin }; | |
| if (typeof module !== 'undefined' && module.exports) module.exports = api; else root.RR = api; | |
| })(typeof window !== 'undefined' ? window : this); | |