From d1a7f0a8c18b91ddf67e70e4fb27ef85a9c8fcef Mon Sep 17 00:00:00 2001 From: FineComputer14451 Date: Wed, 22 Jul 2026 16:27:27 -0400 Subject: [PATCH] feat: ship modular Charity Recon tool as single canonical app Make tools/index.html a thin shell that loads ES modules (OSINT + checklists), share state_data.json for all jurisdictions including DC and Puerto Rico, and redirect the old standalone apps so GitHub Pages serves one working entry point. Quarantine unused Firebase/PWA assets under tools/_legacy. --- README.md | 26 +- STATE_RESOURCES.md | 3 +- tools/README.md | 44 + tools/_legacy/README.md | 25 + .../firestore.indexes.json | 0 .../animal_checklist_app/firestore.rules | 0 .../animal_checklist_app/manifest.json | 0 .../animal_checklist_app/service-worker.js | 0 .../animal_checklist_app/storage.rules | 0 .../animal_checklist_app/styles.css | 0 tools/{ => _legacy}/firestore.rules | 0 tools/{ => _legacy}/functions/index.js | 0 tools/{ => _legacy}/functions/package.json | 0 tools/{ => _legacy}/osint_app/manifest.json | 0 .../{ => _legacy}/osint_app/service-worker.js | 0 tools/animal_checklist_app/index.html | 858 +-------------- tools/index.html | 996 +----------------- tools/js/app.js | 133 +++ tools/js/checklist.js | 412 ++++++++ tools/js/osint-links.js | 221 ++++ tools/js/osint-links.test.mjs | 55 + tools/js/osint.js | 183 ++++ tools/js/util.js | 138 +++ tools/manifest.json | 16 + tools/osint_app/README.md | 33 +- tools/osint_app/index.html | 588 +---------- tools/osint_app/state_data.json | 119 --- tools/state_data.json | 832 +++++++++++++++ 28 files changed, 2126 insertions(+), 2556 deletions(-) create mode 100644 tools/README.md create mode 100644 tools/_legacy/README.md rename tools/{ => _legacy}/animal_checklist_app/firestore.indexes.json (100%) rename tools/{ => _legacy}/animal_checklist_app/firestore.rules (100%) rename tools/{ => _legacy}/animal_checklist_app/manifest.json (100%) rename tools/{ => _legacy}/animal_checklist_app/service-worker.js (100%) rename tools/{ => _legacy}/animal_checklist_app/storage.rules (100%) rename tools/{ => _legacy}/animal_checklist_app/styles.css (100%) rename tools/{ => _legacy}/firestore.rules (100%) rename tools/{ => _legacy}/functions/index.js (100%) rename tools/{ => _legacy}/functions/package.json (100%) rename tools/{ => _legacy}/osint_app/manifest.json (100%) rename tools/{ => _legacy}/osint_app/service-worker.js (100%) create mode 100644 tools/js/app.js create mode 100644 tools/js/checklist.js create mode 100644 tools/js/osint-links.js create mode 100644 tools/js/osint-links.test.mjs create mode 100644 tools/js/osint.js create mode 100644 tools/js/util.js create mode 100644 tools/manifest.json delete mode 100644 tools/osint_app/state_data.json create mode 100644 tools/state_data.json diff --git a/README.md b/README.md index 70005f1..6924b09 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,19 @@ *Your essential companion for promoting transparency, accountability, and excellence in the animal welfare community.* -**Last Verified**: 2026-03-15 -**Recent Update**: Puerto Rico (U.S. Territory) resources added — now covers all 50 states, DC, **and U.S. Territories**. +**Last Verified**: 2026-07-22 +**Recent Update**: Single canonical web tool at `tools/index.html` (OSINT + checklists). Shared `state_data.json` covers all 50 states, DC, and Puerto Rico. Welcome to Charity Recon! This guide and integrated web tool empower animal rescue organizations, donors, and the public to find and verify legitimate charities, report fraudulent activity, understand key compliance requirements, and track assessment findings. --- ## Quick Start Guide for Donors -In a hurry? Here are three essential steps to quickly vet an organization using the tool: - 1. **Launch the Tool**: Open the [Charity Recon Tool](https://elitegreyit67.github.io/Charity_Recon/tools/osint_app/index.html). +In a hurry? Here are essential steps to quickly vet an organization using the tool: + 1. **Launch the Tool**: Open the [Charity Recon Tool](https://elitegreyit67.github.io/Charity_Recon/tools/index.html). 2. **Generate Links**: On the "OSINT Tool" tab, enter the organization's name and click "Generate". 3. **Verify Status**: Use the generated links in the "Federal Financial & Legal" section to check their 501(c)(3) status on the IRS website and review financials on ProPublica. - 4. **Start a Checklist**: Switch to the "Compliance Checklists" tab within the same tool. Click "Start New Checklist," enter the organization's name, and use the checklist items to assess their transparency based on your findings. Checklists are saved securely to your account. + 4. **Start a Checklist**: Switch to the "Compliance Checklists" tab in the same tool. Click "Start New Checklist," enter the organization's name, and assess transparency based on your findings. Checklists are saved in **this browser only** (local storage)—not to a cloud account. --- @@ -42,11 +42,13 @@ In a hurry? Here are three essential steps to quickly vet an organization using --- ## The Charity Recon Tool -This project features a single, powerful web application that integrates two key functionalities: +This project features a **single integrated web application** with two tabs: -- **[Launch the Charity Recon Tool](https://elitegreyit67.github.io/Charity_Recon/tools/osint_app/index.html)** - - **OSINT Dashboard Tab**: Provides a centralized interface to quickly generate direct search links to a wide array of public records (federal/state registrations, financials), news archives, review sites, and online platforms relevant to investigating nonprofit animal rescue organizations. - - **Compliance Checklists Tab**: An interactive, Firebase-powered tool to help donors, volunteers, and adopters assess an organization's transparency and compliance. Create, save, edit, and securely share checklists in the cloud based on a standardized template or your own custom items. +- **[Launch the Charity Recon Tool](https://elitegreyit67.github.io/Charity_Recon/tools/index.html)** + - **OSINT Tool tab** (`#osint`): Generates direct search links to public records (federal and state/territory registrations, financials), news, reputation sites, personnel, domain intelligence, and court-record starting points. + - **Compliance Checklists tab** (`#checklist`): Interactive assessments of transparency and compliance. Create, edit, and save checklists **locally in your browser** (no account required). Data stays on this device unless you export/copy it yourself. + +**Note:** Older URLs under `tools/osint_app/` and `tools/animal_checklist_app/` redirect to this integrated tool so bookmarks keep working. --- @@ -74,7 +76,7 @@ This guide and the integrated tool are designed for everyone in the animal welfa ### For Donors & Supporters Your goal is to ensure your contributions make a real impact. -- **Start Research**: Use the "OSINT Tool" tab in the [Charity Recon Tool](https://elitegreyit67.github.io/Charity_Recon/tools/osint_app/index.html) to perform initial checks (501c3 status, financials, state registration). +- **Start Research**: Use the "OSINT Tool" tab in the [Charity Recon Tool](https://elitegreyit67.github.io/Charity_Recon/tools/index.html) to perform initial checks (501c3 status, financials, state registration). - **Identify Red Flags**: Review the [Spotting Red Flags](#spotting-red-flags-a-deeper-look) section to learn what warrants a closer look during your research. - **Assess Systematically**: Switch to the "Compliance Checklists" tab. Use the checklist to systematically evaluate the organization's transparency and practices based on your OSINT findings. - **Report Concerns**: If you suspect fraud after your investigation, use the links in the [Resource Hub](#resource-hub) (e.g., FTC, State Attorney General) to file a report. @@ -92,7 +94,7 @@ You are entrusting an organization to provide you with a healthy, well-cared-for ### For Rescue Founders & Operators You are dedicated to running a compliant, ethical, and effective organization. - **Understand Obligations**: Thoroughly review the [Key Legal & Compliance Requirements](#key-legal--compliance-requirements) section. This is critical for legal operation. -- **Self-Assess**: Use the "Compliance Checklists" tab in the [Charity Recon Tool](https://elitegreyit67.github.io/Charity_Recon/tools/index.html) as a self-audit tool. Ensure your organization meets transparency and compliance standards. Save and update your checklist as policies evolve. +- **Self-Assess**: Use the "Compliance Checklists" tab in the [Charity Recon Tool](https://elitegreyit67.github.io/Charity_Recon/tools/index.html#checklist) as a self-audit tool. Ensure your organization meets transparency and compliance standards. Save and update your checklist as policies evolve (browser local storage). - **Build Trust**: Use the checklist results to identify areas for improvement in transparency. Proactively publish information like your Form 990, board list, and clear operational policies. - **Register Correctly**: Ensure you are registered with the IRS, your Secretary of State, and your state's charity office for fundraising. Use the OSINT tool's links to quickly access relevant state agency websites. @@ -151,7 +153,7 @@ Animal rescues must comply with a web of federal, state, and local laws. Failure --- ## Resource Hub -Use these resources to conduct your research. The OSINT tab of the [Charity Recon Tool](https://elitegreyit67.github.io/Charity_Recon/tools/osint_app/index.html) provides direct search links for many of these. +Use these resources to conduct your research. The OSINT tab of the [Charity Recon Tool](https://elitegreyit67.github.io/Charity_Recon/tools/index.html) provides direct search links for many of these. ### National Resources (U.S.) **Charity Databases & Watchdogs**: diff --git a/STATE_RESOURCES.md b/STATE_RESOURCES.md index 8fd686f..bd67001 100644 --- a/STATE_RESOURCES.md +++ b/STATE_RESOURCES.md @@ -1,7 +1,7 @@ # State-Specific Resources for Animal Rescue Organizations -*Last Updated: October 26, 2025* +*Last Updated: July 22, 2026* *Government agency information, websites, and contact details can change. To ensure the accuracy of this valuable resource, consider periodically verifying the links and information provided below.* @@ -53,6 +53,7 @@ Use the expandable section below for direct links to your state’s: - [Oklahoma](#oklahoma) - [Oregon](#oregon) - [Pennsylvania](#pennsylvania) +- [Puerto Rico](#puerto-rico) - [Rhode Island](#rhode-island) - [South Carolina](#south-carolina) - [South Dakota](#south-dakota) diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..153c5b0 --- /dev/null +++ b/tools/README.md @@ -0,0 +1,44 @@ +# Charity Recon Tools + +## Canonical app + +| File | Role | +|------|------| +| [`index.html`](./index.html) | UI shell (markup + styles only) | +| [`js/app.js`](./js/app.js) | Boot, routing (`#osint` / `#checklist`) | +| [`js/osint-links.js`](./js/osint-links.js) | **Pure** OSINT link builder (no DOM) | +| [`js/osint.js`](./js/osint.js) | OSINT tab UI | +| [`js/checklist.js`](./js/checklist.js) | Checklists (localStorage) | +| [`js/util.js`](./js/util.js) | Theme, toast, escapeHtml, modals | +| [`state_data.json`](./state_data.json) | 50 states + DC + Puerto Rico link data | +| [`manifest.json`](./manifest.json) | PWA manifest | + +| Tab | Hash | +|-----|------| +| OSINT Tool | `#osint` | +| Compliance Checklists | `#checklist` | + +Checklists use browser **local storage** only (key `charity_recon_checklists`). No account. + +## Legacy redirects + +| Path | Redirects to | +|------|----------------| +| [`osint_app/`](./osint_app/) | `index.html#osint` | +| [`animal_checklist_app/`](./animal_checklist_app/) | `index.html#checklist` | + +Unused Firebase / PWA leftovers: [`_legacy/`](./_legacy/). + +## Local development + +```bash +# from repo root (ES modules require HTTP) +python3 -m http.server 8080 +# open http://localhost:8080/tools/index.html +``` + +## Tests + +```bash +node tools/js/osint-links.test.mjs +``` diff --git a/tools/_legacy/README.md b/tools/_legacy/README.md new file mode 100644 index 0000000..494703b --- /dev/null +++ b/tools/_legacy/README.md @@ -0,0 +1,25 @@ +# Legacy (unused) Charity Recon assets + +These files are **not used** by the current integrated app (`tools/index.html` + `tools/js/`). + +They are retained only as historical reference from an earlier Firebase-backed checklist design and old standalone PWA shells. + +| Path | Former role | +|------|-------------| +| `functions/` | Cloud Functions to sync `memberIds` on checklist docs | +| `firestore.rules` | Artifact-path Firestore rules | +| `animal_checklist_app/` | Standalone checklist PWA rules, styles, service worker | +| `osint_app/` | Old OSINT PWA service worker + manifest | + +Do **not** re-enable without an intentional product decision to restore cloud accounts. + +Active app layout: + +``` +tools/index.html +tools/js/{app,util,osint,osint-links,checklist}.js +tools/state_data.json +tools/manifest.json +tools/osint_app/index.html → redirect #osint +tools/animal_checklist_app/index.html → redirect #checklist +``` diff --git a/tools/animal_checklist_app/firestore.indexes.json b/tools/_legacy/animal_checklist_app/firestore.indexes.json similarity index 100% rename from tools/animal_checklist_app/firestore.indexes.json rename to tools/_legacy/animal_checklist_app/firestore.indexes.json diff --git a/tools/animal_checklist_app/firestore.rules b/tools/_legacy/animal_checklist_app/firestore.rules similarity index 100% rename from tools/animal_checklist_app/firestore.rules rename to tools/_legacy/animal_checklist_app/firestore.rules diff --git a/tools/animal_checklist_app/manifest.json b/tools/_legacy/animal_checklist_app/manifest.json similarity index 100% rename from tools/animal_checklist_app/manifest.json rename to tools/_legacy/animal_checklist_app/manifest.json diff --git a/tools/animal_checklist_app/service-worker.js b/tools/_legacy/animal_checklist_app/service-worker.js similarity index 100% rename from tools/animal_checklist_app/service-worker.js rename to tools/_legacy/animal_checklist_app/service-worker.js diff --git a/tools/animal_checklist_app/storage.rules b/tools/_legacy/animal_checklist_app/storage.rules similarity index 100% rename from tools/animal_checklist_app/storage.rules rename to tools/_legacy/animal_checklist_app/storage.rules diff --git a/tools/animal_checklist_app/styles.css b/tools/_legacy/animal_checklist_app/styles.css similarity index 100% rename from tools/animal_checklist_app/styles.css rename to tools/_legacy/animal_checklist_app/styles.css diff --git a/tools/firestore.rules b/tools/_legacy/firestore.rules similarity index 100% rename from tools/firestore.rules rename to tools/_legacy/firestore.rules diff --git a/tools/functions/index.js b/tools/_legacy/functions/index.js similarity index 100% rename from tools/functions/index.js rename to tools/_legacy/functions/index.js diff --git a/tools/functions/package.json b/tools/_legacy/functions/package.json similarity index 100% rename from tools/functions/package.json rename to tools/_legacy/functions/package.json diff --git a/tools/osint_app/manifest.json b/tools/_legacy/osint_app/manifest.json similarity index 100% rename from tools/osint_app/manifest.json rename to tools/_legacy/osint_app/manifest.json diff --git a/tools/osint_app/service-worker.js b/tools/_legacy/osint_app/service-worker.js similarity index 100% rename from tools/osint_app/service-worker.js rename to tools/_legacy/osint_app/service-worker.js diff --git a/tools/animal_checklist_app/index.html b/tools/animal_checklist_app/index.html index 3fe34be..f5b45ce 100644 --- a/tools/animal_checklist_app/index.html +++ b/tools/animal_checklist_app/index.html @@ -3,849 +3,27 @@ - Animal Rescue Compliance Checklist - - - - - - - + Redirecting to Charity Recon Tool… + + + - - -
- -
- Local Mode - -
- -
- -
-
-

Rescue Compliance

-

Your locally saved checklists for ensuring animal welfare standards.

-
- - - -
- - -
- -
-
- -
-

External Resources

-
-
- -

My Checklists

- -
-
- - -
+ +
+

Checklists live in the integrated tool

+

+ The standalone compliance checklist app has been folded into the main Charity Recon Tool. + Checklists are stored in this browser via local storage (same device/browser only). +

+

+ Continue to Compliance Checklists +

- - - - - - - diff --git a/tools/index.html b/tools/index.html index e4d1bdd..30705b0 100644 --- a/tools/index.html +++ b/tools/index.html @@ -155,8 +155,8 @@

Investigation Details

- +
@@ -282,997 +282,7 @@
- - - + diff --git a/tools/js/app.js b/tools/js/app.js new file mode 100644 index 0000000..7d992c1 --- /dev/null +++ b/tools/js/app.js @@ -0,0 +1,133 @@ +/** + * Charity Recon app shell: routing, init, wiring. + */ +import { cacheElements, initTheme, toggleTheme, closeModal, refreshIcons, showToast } from './util.js'; +import { validateStateData } from './osint-links.js'; +import { createOsintController } from './osint.js'; +import { createChecklistController } from './checklist.js'; + +const ELEMENT_IDS = [ + 'theme-toggle', 'search-form', 'generate-button', 'clear-button', 'org-name', 'person-name', + 'org-ein', 'location', 'domain-name', 'social-handle', 'state-select', 'results-container', + 'placeholder', 'error-message', 'results-title', 'accordion-container', 'toast-container', + 'skeleton-container', 'tab-osint', 'tab-checklist', 'osint-view', 'checklist-view', + 'checklist-list-view', 'new-checklist-btn', + 'checklist-search-input', 'no-checklists-message', 'saved-checklists-container', + 'checklist-editor-view', 'back-to-list-btn', 'checklist-title', 'checklist-org-name-input', + 'checklist-org-name-error', 'checklist-items-container', 'add-category-btn', + 'checklist-summary-notes', 'delete-checklist-btn', 'save-checklist-btn', + 'save-button-text', 'save-spinner', 'status-message', 'confirmation-modal', 'modal-title', + 'modal-message', 'modal-confirm-btn', + 'start-checklist-from-osint-btn', +]; + +const VIEW_TAB = { + 'osint-view': 'tab-osint', + 'checklist-view': 'tab-checklist', +}; + +const VIEW_HASH = { + 'osint-view': '#osint', + 'checklist-view': '#checklist', +}; + +let stateData = { states: {}, stateResources: {}, stateAnimalWelfareResources: {} }; + +export async function initApp() { + const elements = cacheElements(ELEMENT_IDS); + + initTheme(elements.themeToggle); + elements.themeToggle?.addEventListener('click', () => toggleTheme(elements.themeToggle)); + + const osint = createOsintController({ + elements, + getStateData: () => stateData, + toastContainer: elements.toastContainer, + }); + + const checklist = createChecklistController({ + elements, + onSwitchToList: () => {}, + }); + + function switchView(viewId) { + document.querySelectorAll('.view-content').forEach((el) => el.classList.add('hidden')); + document.getElementById(viewId)?.classList.remove('hidden'); + + document.querySelectorAll('.nav-tab').forEach((tab) => tab.classList.remove('active')); + const tabId = VIEW_TAB[viewId]; + if (tabId) document.getElementById(tabId)?.classList.add('active'); + + const hash = VIEW_HASH[viewId] || '#osint'; + if (location.hash !== hash) { + history.replaceState(null, '', hash); + } + } + + function applyRouteFromLocation() { + const hash = (location.hash || '').toLowerCase(); + const viewParam = (new URLSearchParams(location.search).get('view') || '').toLowerCase(); + if (hash === '#checklist' || viewParam === 'checklist') { + switchView('checklist-view'); + } else { + switchView('osint-view'); + } + } + + elements.tabOsint?.addEventListener('click', () => switchView('osint-view')); + elements.tabChecklist?.addEventListener('click', () => switchView('checklist-view')); + window.addEventListener('hashchange', applyRouteFromLocation); + + elements.startChecklistFromOsintBtn?.addEventListener('click', () => { + const orgName = elements.orgName?.value.trim() || ''; + if (!orgName) { + showToast(elements.toastContainer, 'Please enter an organization name first.', 'error'); + return; + } + switchView('checklist-view'); + checklist.renderForm(null, orgName); + }); + + // Modal dismiss + document.querySelectorAll('.modal').forEach((modal) => { + modal.addEventListener('click', (e) => { + if (e.target === modal) closeModal(modal); + }); + modal.querySelectorAll('[data-dismiss="modal"]').forEach((btn) => { + btn.addEventListener('click', () => closeModal(modal)); + }); + }); + + osint.bind(); + checklist.bind(); + + await loadStateData(elements, osint); + checklist.refreshList(); + applyRouteFromLocation(); + refreshIcons(); +} + +async function loadStateData(elements, osint) { + try { + // Resolve from this module URL so it works under any base path (e.g. GitHub Pages). + const stateUrl = new URL('../state_data.json', import.meta.url); + const response = await fetch(stateUrl, { cache: 'no-cache' }); + if (!response.ok) throw new Error(`HTTP ${response.status} loading ${stateUrl.pathname}`); + const raw = await response.json(); + const validated = validateStateData(raw); + if (!validated.ok) throw new Error(validated.error); + stateData = validated.data; + osint.populateStates(); + } catch (error) { + console.error('[app] state data load failed:', error); + if (elements.errorMessage) { + elements.errorMessage.textContent = + 'Error: Could not load state resources. Ensure tools/state_data.json is deployed and served over HTTP.'; + elements.errorMessage.classList.remove('hidden'); + } + } +} + +document.addEventListener('DOMContentLoaded', () => { + initApp().catch((err) => console.error('[app] init failed:', err)); +}); diff --git a/tools/js/checklist.js b/tools/js/checklist.js new file mode 100644 index 0000000..305c5d2 --- /dev/null +++ b/tools/js/checklist.js @@ -0,0 +1,412 @@ +/** + * Compliance checklist: localStorage model + UI. + */ +import { escapeHtml, showStatus, refreshIcons, openModal, closeModal } from './util.js'; + +export const STORAGE_KEY = 'charity_recon_checklists'; +const LEGACY_STORAGE_KEY = 'charity_recon_checklists_standalone'; + +export function getChecklistMasterTemplate() { + return JSON.parse(JSON.stringify([ + { + id: 'cat_fed_state', + name: 'Federal & State Compliance', + isCustom: false, + items: [ + { id: 'fed_irs_status', text: 'IRS 501(c)(3) status verified?', link: 'https://apps.irs.gov/app/eos/', linkText: 'IRS Search', isCustom: false, checked: false, notes: '' }, + { id: 'fed_form_990', text: 'IRS Form 990 accessible & reviewed?', link: 'https://projects.propublica.org/nonprofits/search?q=', linkText: 'ProPublica Search', isCustom: false, checked: false, notes: '' }, + { id: 'rep_bbb_rating', text: 'Better Business Bureau (BBB) rating checked?', link: 'https://www.bbb.org/search?find_text=', linkText: 'BBB Search', isCustom: false, checked: false, notes: '' }, + { id: 'fed_aphis_license', text: 'APHIS license status verified (if applicable)?', link: 'https://aphis.my.site.com/PublicSearchTool/s/', linkText: 'APHIS Search', isCustom: false, checked: false, notes: '' }, + { id: 'state_charity_reg', text: 'Registered with State Charity Officials?', link: 'https://www.nasconet.org/resources/state-government/', linkText: 'NASCO Directory', isCustom: false, checked: false, notes: '' }, + { id: 'state_local_licenses', text: 'Necessary state/local animal shelter/rescue licenses obtained?', isCustom: false, checked: false, notes: '' }, + ], + }, + { + id: 'cat_operational_trans', + name: 'Operational Transparency', + isCustom: false, + items: [ + { id: 'op_legal_dba_name', text: 'Legal name and DBA (Doing Business As) are clearly stated?', isCustom: false, checked: false, notes: '' }, + { id: 'op_contact_info', text: 'Verifiable contact information (phone/email) is available and responsive?', isCustom: false, checked: false, notes: '' }, + { id: 'op_website_mission', text: 'Website clearly states mission, programs, and animal welfare practices?', isCustom: false, checked: false, notes: '' }, + { id: 'op_board_list', text: 'A list of the Board of Directors is publicly available?', isCustom: false, checked: false, notes: '' }, + { id: 'op_adoption_process', text: 'Adoption process is clearly documented and transparent?', isCustom: false, checked: false, notes: '' }, + { id: 'op_sourcing_policy', text: 'Animal sourcing policy (where animals come from) is clear?', isCustom: false, checked: false, notes: '' }, + { id: 'op_medical_records', text: 'Comprehensive medical records (vaccinations, spay/neuter) are provided with adoptions?', isCustom: false, checked: false, notes: '' }, + ], + }, + { + id: 'cat_reputation_acc', + name: 'Reputation & Accountability', + isCustom: false, + items: [ + { id: 'rep_watchdog_sites', text: 'Charity watchdog sites (e.g., Charity Navigator, GuideStar) reviewed?', link: 'https://www.charitynavigator.org/', linkText: 'Charity Navigator', isCustom: false, checked: false, notes: '' }, + { id: 'rep_news_archives', text: 'News archives and search engines checked for significant negative press or legal issues?', isCustom: false, checked: false, notes: '' }, + ], + }, + ])); +} + +export function migrateLegacyChecklistStorage() { + try { + const legacyRaw = localStorage.getItem(LEGACY_STORAGE_KEY); + if (!legacyRaw) return; + const legacy = JSON.parse(legacyRaw); + if (!Array.isArray(legacy) || legacy.length === 0) return; + const existingRaw = localStorage.getItem(STORAGE_KEY); + const existing = existingRaw ? JSON.parse(existingRaw) : []; + const byId = new Map((Array.isArray(existing) ? existing : []).map((c) => [c.id, c])); + for (const item of legacy) { + if (item?.id && !byId.has(item.id)) byId.set(item.id, item); + } + localStorage.setItem(STORAGE_KEY, JSON.stringify([...byId.values()])); + localStorage.removeItem(LEGACY_STORAGE_KEY); + } catch (err) { + console.warn('[checklist] migration skipped:', err); + } +} + +export function loadChecklists() { + migrateLegacyChecklistStorage(); + const raw = localStorage.getItem(STORAGE_KEY); + return raw ? JSON.parse(raw) : []; +} + +export function saveChecklists(checklists) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(checklists)); +} + +export function createChecklistController({ elements, onSwitchToList }) { + let current = null; + let saveTimeout = null; + let confirmAction = null; + + function showSubView(subView) { + elements.checklistListView?.classList.toggle('hidden', subView !== 'list'); + elements.checklistEditorView?.classList.toggle('hidden', subView !== 'editor'); + } + + function setSaveButtonState(isSaving) { + if (!elements.saveChecklistBtn || !elements.saveButtonText || !elements.saveSpinner) return; + elements.saveButtonText.textContent = isSaving ? 'Saving...' : 'Save'; + elements.saveSpinner.classList.toggle('hidden', !isSaving); + elements.saveChecklistBtn.disabled = isSaving; + } + + function renderItemHTML(item, categoryId) { + const toolButtonHtml = item.link + ? `${escapeHtml(item.linkText || 'Open Tool')}` + : ''; + return ` +
+
+ + +
+
+ ${toolButtonHtml} + +
+
`; + } + + function renderCategoryHTML(category) { + return ` +
+
+

${escapeHtml(category.name)}

+ +
+
+
+ ${(category.items || []).map((item) => renderItemHTML(item, category.id)).join('')} + +
+
+
`; + } + + function updateDynamicToolLinks() { + if (!elements.checklistOrgNameInput || !elements.checklistItemsContainer) return; + const orgName = elements.checklistOrgNameInput.value.trim(); + if (!orgName) return; + const encodedOrgName = encodeURIComponent(orgName); + elements.checklistItemsContainer.querySelectorAll('a.contextual-tool-button[data-base-url]').forEach((link) => { + link.href = `${link.dataset.baseUrl}${encodedOrgName}`; + }); + } + + function buildUI(data) { + if (!elements.checklistItemsContainer || !elements.checklistSummaryNotes) return; + elements.checklistItemsContainer.innerHTML = ''; + (data.categories || []).forEach((category) => { + const wrap = document.createElement('div'); + wrap.innerHTML = renderCategoryHTML(category); + elements.checklistItemsContainer.appendChild(wrap.firstElementChild); + }); + elements.checklistSummaryNotes.value = data.summary || ''; + updateDynamicToolLinks(); + refreshIcons(); + } + + function filterAndRenderListView() { + if (!elements.checklistSearchInput || !elements.savedChecklistsContainer || !elements.noChecklistsMessage) return; + const searchTerm = elements.checklistSearchInput.value.toLowerCase(); + const cards = elements.savedChecklistsContainer.querySelectorAll('[data-id]'); + let visibleCount = 0; + cards.forEach((card) => { + const title = card.querySelector('h3')?.textContent.toLowerCase() || ''; + const isVisible = title.includes(searchTerm); + card.style.display = isVisible ? '' : 'none'; + if (isVisible) visibleCount++; + }); + const hasAny = elements.savedChecklistsContainer.children.length > 0; + elements.noChecklistsMessage.classList.toggle('hidden', !( !hasAny || visibleCount === 0 )); + } + + function renderListView(checklists) { + const container = elements.savedChecklistsContainer; + if (!container) return; + container.innerHTML = ''; + elements.noChecklistsMessage?.classList.toggle('hidden', checklists.length > 0); + const sorted = [...checklists].sort((a, b) => new Date(b.lastUpdated) - new Date(a.lastUpdated)); + sorted.forEach((list) => { + const card = document.createElement('div'); + card.className = 'p-5 bg-white dark:bg-gray-800 rounded-lg shadow-sm hover:shadow-md transition-shadow cursor-pointer border border-transparent hover:border-indigo-500/50'; + card.dataset.id = list.id; + card.addEventListener('click', () => renderForm(list.id)); + const lastUpdated = list.lastUpdated ? new Date(list.lastUpdated).toLocaleString() : 'N/A'; + card.innerHTML = ` +
+

${escapeHtml(list.orgName || 'Untitled Checklist')}

+ Local +
+

Last updated: ${escapeHtml(lastUpdated)}

`; + container.appendChild(card); + }); + filterAndRenderListView(); + } + + function refreshList() { + try { + const checklists = loadChecklists(); + showSubView('list'); + renderListView(checklists); + } catch (error) { + console.error(error); + if (elements.noChecklistsMessage) { + elements.noChecklistsMessage.innerHTML = `

Error Loading Checklists

`; + refreshIcons(); + } + renderListView([]); + } + } + + function renderForm(checklistId = null, prefilledOrgName = '') { + if (elements.checklistItemsContainer) elements.checklistItemsContainer.innerHTML = ''; + current = null; + + if (checklistId) { + const data = loadChecklists().find((c) => c.id === checklistId); + if (!data) { + showStatus(elements.statusMessage, 'Checklist not found.', 'error'); + showSubView('list'); + return; + } + current = data; + buildUI(data); + if (elements.checklistOrgNameInput) elements.checklistOrgNameInput.value = data.orgName || ''; + if (elements.checklistTitle) elements.checklistTitle.textContent = data.orgName || 'New Checklist'; + elements.deleteChecklistBtn?.classList.remove('hidden'); + } else { + current = { + id: null, + orgName: prefilledOrgName, + categories: getChecklistMasterTemplate(), + summary: '', + lastUpdated: new Date().toISOString(), + }; + buildUI(current); + if (elements.checklistOrgNameInput) elements.checklistOrgNameInput.value = prefilledOrgName || ''; + if (elements.checklistTitle) elements.checklistTitle.textContent = prefilledOrgName || 'New Checklist'; + elements.deleteChecklistBtn?.classList.add('hidden'); + } + showSubView('editor'); + } + + async function handleSave(force = false) { + clearTimeout(saveTimeout); + const saveAction = async () => { + if (!current) return; + const orgName = elements.checklistOrgNameInput.value.trim(); + if (!orgName) { + elements.checklistOrgNameError?.classList.remove('hidden'); + return; + } + elements.checklistOrgNameError?.classList.add('hidden'); + current.orgName = orgName; + current.summary = elements.checklistSummaryNotes.value; + current.lastUpdated = new Date().toISOString(); + setSaveButtonState(true); + try { + let checklists = loadChecklists(); + if (current.id) { + const index = checklists.findIndex((c) => c.id === current.id); + if (index !== -1) checklists[index] = current; + else checklists.push(current); + } else { + current.id = `checklist_${Date.now()}`; + checklists.push(current); + elements.deleteChecklistBtn?.classList.remove('hidden'); + } + saveChecklists(checklists); + showStatus(elements.statusMessage, 'Checklist saved!', 'success'); + } catch (error) { + console.error(error); + showStatus(elements.statusMessage, `Error saving: ${error.message}`, 'error', null); + } finally { + setSaveButtonState(false); + } + }; + if (force) await saveAction(); + else saveTimeout = setTimeout(saveAction, 1500); + } + + function openConfirmation(title, message, onConfirm) { + if (!elements.confirmationModal || !elements.modalTitle || !elements.modalMessage) return; + elements.modalTitle.textContent = title; + elements.modalMessage.textContent = message; + confirmAction = onConfirm; + openModal(elements.confirmationModal); + } + + function handleDelete() { + if (!current?.id) return; + openConfirmation( + 'Delete Checklist?', + `Are you sure you want to permanently delete the "${current.orgName || 'Untitled Checklist'}" checklist? This cannot be undone.`, + () => { + try { + const checklists = loadChecklists().filter((c) => c.id !== current.id); + saveChecklists(checklists); + current = null; + showSubView('list'); + refreshList(); + } catch (error) { + showStatus(elements.statusMessage, `Error deleting: ${error.message}`, 'error'); + } + } + ); + } + + function addItem(categoryId) { + if (!current?.categories) return; + const category = current.categories.find((c) => c.id === categoryId); + if (!category) return; + const newItemText = prompt('Enter text for the new checklist item:', 'New Item'); + if (newItemText === null || newItemText.trim() === '') return; + if (!category.items) category.items = []; + category.items.push({ + id: `item_${Date.now()}_${Math.random().toString(16).slice(2)}`, + text: newItemText.trim(), + isCustom: true, + checked: false, + notes: '', + }); + buildUI(current); + handleSave(true); + } + + function addCategory() { + if (!current) return; + const newCategoryName = prompt('Enter name for the new category:', 'New Category'); + if (newCategoryName === null || newCategoryName.trim() === '') return; + if (!current.categories) current.categories = []; + current.categories.push({ + id: `cat_${Date.now()}_${Math.random().toString(16).slice(2)}`, + name: newCategoryName.trim(), + isCustom: true, + items: [], + }); + buildUI(current); + handleSave(true); + } + + function onContainerClick(e) { + const target = e.target.closest('[data-action]'); + if (!target) return; + const action = target.dataset.action; + const categoryEl = target.closest('.category-block'); + const categoryId = categoryEl?.dataset.categoryId; + + if (action === 'toggle-category' && categoryEl) { + const content = categoryEl.querySelector('.category-content'); + const icon = categoryEl.querySelector('.category-toggle-icon'); + const isOpen = content?.classList.contains('open'); + content?.classList.toggle('open'); + icon?.classList.toggle('rotate-180', !isOpen); + categoryEl.classList.toggle('expanded', !isOpen); + } else if (action === 'add-item' && categoryId) { + addItem(categoryId); + } + } + + function onContainerInput(e) { + const target = e.target.closest('[data-action="update-note"]'); + if (!target || !current) return; + const itemEl = target.closest('.checklist-item'); + const category = current.categories?.find((c) => c.id === itemEl?.dataset.categoryId); + const item = category?.items?.find((i) => i.id === itemEl?.dataset.itemId); + if (item) { + item.notes = target.value; + handleSave(false); + } + } + + function onContainerChange(e) { + const target = e.target.closest('[data-action="check-item"]'); + if (!target || !current) return; + const itemEl = target.closest('.checklist-item'); + const category = current.categories?.find((c) => c.id === itemEl?.dataset.categoryId); + const item = category?.items?.find((i) => i.id === itemEl?.dataset.itemId); + if (item) { + item.checked = target.checked; + handleSave(true); + } + } + + function bind() { + elements.newChecklistBtn?.addEventListener('click', () => renderForm(null)); + elements.backToListBtn?.addEventListener('click', () => { + showSubView('list'); + refreshList(); + onSwitchToList?.(); + }); + elements.saveChecklistBtn?.addEventListener('click', () => handleSave(true)); + elements.deleteChecklistBtn?.addEventListener('click', handleDelete); + elements.checklistSearchInput?.addEventListener('input', filterAndRenderListView); + elements.checklistOrgNameInput?.addEventListener('input', () => { + if (elements.checklistTitle) { + elements.checklistTitle.textContent = elements.checklistOrgNameInput.value.trim() || 'New Checklist'; + } + updateDynamicToolLinks(); + handleSave(false); + }); + elements.checklistSummaryNotes?.addEventListener('input', () => handleSave(false)); + elements.addCategoryBtn?.addEventListener('click', addCategory); + elements.checklistItemsContainer?.addEventListener('click', onContainerClick); + elements.checklistItemsContainer?.addEventListener('input', onContainerInput); + elements.checklistItemsContainer?.addEventListener('change', onContainerChange); + elements.modalConfirmBtn?.addEventListener('click', () => { + confirmAction?.(); + if (elements.confirmationModal) closeModal(elements.confirmationModal); + }); + } + + return { + bind, + refreshList, + renderForm, + showSubView, + }; +} diff --git a/tools/js/osint-links.js b/tools/js/osint-links.js new file mode 100644 index 0000000..de72b9f --- /dev/null +++ b/tools/js/osint-links.js @@ -0,0 +1,221 @@ +/** + * Pure OSINT link section builder. No DOM. Safe for unit tests. + */ + +export function normalizeInputs(inputs = {}) { + const orgName = (inputs.orgName || '').trim(); + const personName = (inputs.personName || '').trim(); + const orgEin = (inputs.orgEin || '').trim(); + const location = (inputs.location || '').trim(); + const domainName = (inputs.domainName || '').trim(); + const socialHandle = (inputs.socialHandle || '').trim(); + const selectedState = (inputs.selectedState || '').trim(); + + return { + orgName, + personName, + orgEin, + location, + selectedState, + encodedOrgName: encodeURIComponent(orgName), + plusEncodedOrgName: orgName.replace(/\s/g, '+'), + encodedPersonName: encodeURIComponent(personName), + encodedLocation: encodeURIComponent(location), + einWithoutHyphen: orgEin.replace(/-/g, ''), + locationQuery: location ? ` "${encodeURIComponent(location)}"` : '', + domain: domainName.replace(/^(https?:\/\/)?(www\.)?/, '').replace(/\/$/, ''), + handle: socialHandle.replace(/^@/, ''), + }; +} + +/** + * @param {object} inputs - raw form values + * @param {{ states?: object, stateResources?: object, stateAnimalWelfareResources?: object }} stateData + * @returns {Array<{id:string,title:string,icon:string,isOpen:boolean,links:Array<{name:string,url:string}>}>} + */ +export function buildLinkSections(inputs, stateData = {}) { + const ctx = normalizeInputs(inputs); + const { + orgName, personName, orgEin, location, selectedState, + encodedOrgName, plusEncodedOrgName, encodedPersonName, encodedLocation, + einWithoutHyphen, locationQuery, domain, handle, + } = ctx; + + const states = stateData.states || {}; + const stateResources = stateData.stateResources || {}; + const stateAnimalWelfareResources = stateData.stateAnimalWelfareResources || {}; + const stateLabel = selectedState && states[selectedState] ? states[selectedState] : selectedState; + + const sections = []; + + if (selectedState && stateAnimalWelfareResources[selectedState]?.length) { + sections.push({ + id: 'state-animal', + title: `${stateLabel} Animal Welfare`, + icon: 'shield-plus', + isOpen: true, + links: stateAnimalWelfareResources[selectedState].map((r) => ({ name: r.n, url: r.u })), + }); + } + + if (selectedState && stateResources[selectedState]?.length) { + sections.push({ + id: 'state', + title: `${stateLabel} State-Level`, + icon: 'building-2', + isOpen: true, + links: stateResources[selectedState].map((r) => ({ name: r.n, url: r.u })), + }); + } + + sections.push({ + id: 'financial', + title: 'Federal Financial & Legal', + icon: 'landmark', + isOpen: true, + links: [ + { name: 'IRS Tax Exempt Org Search', url: 'https://apps.irs.gov/app/eos/' }, + { name: 'USDA APHIS Public Search Tool', url: 'https://aphis.my.site.com/PublicSearchTool/s/' }, + { + name: 'ProPublica Nonprofit Explorer', + url: orgEin + ? `https://projects.propublica.org/nonprofits/organizations/${einWithoutHyphen}` + : `https://projects.propublica.org/nonprofits/search?q=${plusEncodedOrgName}`, + }, + { name: 'Candid (GuideStar)', url: `https://candid.org/search?q=${orgEin || encodedOrgName}` }, + { name: 'OpenCorporates', url: `https://opencorporates.com/companies?q=${encodedOrgName}` }, + ], + }); + + sections.push({ + id: 'news', + title: 'News & Public Perception', + icon: 'newspaper', + isOpen: false, + links: [ + { name: 'Google News', url: `https://www.google.com/search?q=%22${encodedOrgName}%22${locationQuery}&tbm=nws` }, + { name: 'Google Reviews Search', url: `https://www.google.com/search?q=%22${encodedOrgName}%22${locationQuery}+reviews` }, + { name: 'Reddit Search', url: `https://www.reddit.com/search/?q=%22${encodedOrgName}%22${locationQuery}` }, + { name: 'Charity Navigator', url: `https://www.charitynavigator.org/search?q=${orgEin || encodedOrgName}` }, + { name: 'CharityWatch', url: `https://www.charitywatch.org/search?q=${encodedOrgName}` }, + { name: 'GreatNonprofits Reviews', url: `https://greatnonprofits.org/search?q=${encodedOrgName}` }, + { + name: 'Better Business Bureau', + url: `https://www.bbb.org/search?find_country=USA&find_text=${encodedOrgName}${location ? '&find_loc=' + encodedLocation : ''}`, + }, + ], + }); + + if (personName) { + sections.push({ + id: 'personnel', + title: `Personnel: ${personName}`, + icon: 'user-check', + isOpen: true, + links: [ + { + name: `Google News: "${personName}" & "${orgName}"`, + url: `https://www.google.com/search?q=%22${encodedPersonName}%22+%22${encodedOrgName}%22&tbm=nws`, + }, + { + name: `LinkedIn: "${personName}" & "${orgName}"`, + url: `https://www.linkedin.com/search/results/all/?keywords=%22${encodedPersonName}%22%20%22${encodedOrgName}%22`, + }, + { name: `Google Scholar: ${personName}`, url: `https://scholar.google.com/scholar?q=${encodedPersonName}` }, + ], + }); + } + + const onlineLinks = [ + { name: 'Wikipedia', url: `https://en.wikipedia.org/w/index.php?search=${encodedOrgName}` }, + { name: 'Facebook', url: `https://www.facebook.com/search/top/?q=${encodedOrgName}${locationQuery}` }, + { name: 'Instagram tag search', url: `https://www.instagram.com/explore/tags/${orgName.replace(/\s+/g, '').toLowerCase()}/` }, + { name: 'X (Twitter) Search', url: `https://twitter.com/search?q=${encodedOrgName}&src=typed_query` }, + { name: 'TikTok Search', url: `https://www.tiktok.com/search?q=${encodedOrgName}` }, + { name: 'LinkedIn Org Search', url: `https://www.linkedin.com/search/results/all/?keywords=${encodedOrgName}` }, + { name: 'YouTube', url: `https://www.youtube.com/results?search_query=${encodedOrgName}` }, + ]; + if (domain) { + onlineLinks.push( + { name: 'Internet Archive (Wayback)', url: `https://web.archive.org/web/*/${domain}` }, + { name: 'Whois Lookup', url: `https://whois.domaintools.com/${domain}` }, + { name: 'SSL Labs Test', url: `https://www.ssllabs.com/ssltest/analyze.html?d=${domain}` }, + { name: 'ViewDNS IP History', url: `https://viewdns.info/iphistory/?domain=${domain}` }, + ); + } + if (handle) { + onlineLinks.push( + { name: `X Handle (@${handle})`, url: `https://twitter.com/${handle}` }, + { name: `Facebook /${handle}`, url: `https://www.facebook.com/${handle}` }, + { name: `Instagram @${handle}`, url: `https://www.instagram.com/${handle}` }, + ); + } + sections.push({ id: 'online', title: 'Online Presence', icon: 'globe-2', links: onlineLinks, isOpen: false }); + + if (handle) { + sections.push({ + id: 'social-deep', + title: 'Social Media Deep Dive', + icon: 'users', + isOpen: false, + links: [ + { name: `Check Usernames (@${handle})`, url: `https://instantusername.com/#/?query=${handle}` }, + { name: 'Google Reverse Image Search', url: 'https://images.google.com/' }, + { name: 'TinEye Reverse Image Search', url: 'https://tineye.com/' }, + ], + }); + } + + const legalLinks = [ + { name: 'PACER (Federal Courts)', url: 'https://pacer.uscourts.gov/' }, + { name: `CourtListener: "${orgName}"`, url: `https://www.courtlistener.com/?q=%22${encodedOrgName}%22` }, + { name: 'Google Litigation Search', url: `https://www.google.com/search?q=%22${encodedOrgName}%22+litigation+OR+lawsuit` }, + ]; + if (personName) { + legalLinks.push({ + name: `Court records: "${personName}" & "${orgName}"`, + url: `https://www.google.com/search?q=%22${encodedPersonName}%22+%22${encodedOrgName}%22+court+records`, + }); + } + if (location) { + legalLinks.push({ + name: `Local court records (${location})`, + url: `https://www.google.com/search?q=${plusEncodedOrgName}+court+records+${encodedLocation.replace(/%20/g, '+')}`, + }); + } + sections.push({ id: 'legal', title: 'Legal & Court Records', icon: 'gavel', isOpen: false, links: legalLinks }); + + if (domain) { + sections.push({ + id: 'advanced', + title: 'Advanced & Technical', + icon: 'shield-check', + isOpen: false, + links: [ + { name: 'DNS Dumpster', url: 'https://dnsdumpster.com/' }, + { name: 'crt.sh Certificate Search', url: `https://crt.sh/?q=${domain}` }, + { name: 'BuiltWith Technology Profile', url: `https://builtwith.com/${domain}` }, + { name: 'SpyOnWeb Analytics Search', url: `https://spyonweb.com/${domain}` }, + { name: 'Google Filetype Search (PDF)', url: `https://www.google.com/search?q=site%3A${domain}+filetype%3Apdf` }, + { name: 'Have I Been Pwned', url: 'https://haveibeenpwned.com/' }, + ], + }); + } + + return sections; +} + +/** @returns {{ok:true,data:object}|{ok:false,error:string}} */ +export function validateStateData(data) { + if (!data || typeof data !== 'object') return { ok: false, error: 'State data is not an object' }; + if (!data.states || typeof data.states !== 'object' || !Object.keys(data.states).length) { + return { ok: false, error: 'state_data.json missing non-empty "states"' }; + } + if (!data.stateResources || typeof data.stateResources !== 'object') { + return { ok: false, error: 'state_data.json missing "stateResources"' }; + } + if (!data.stateAnimalWelfareResources || typeof data.stateAnimalWelfareResources !== 'object') { + return { ok: false, error: 'state_data.json missing "stateAnimalWelfareResources"' }; + } + return { ok: true, data }; +} diff --git a/tools/js/osint-links.test.mjs b/tools/js/osint-links.test.mjs new file mode 100644 index 0000000..3bd1800 --- /dev/null +++ b/tools/js/osint-links.test.mjs @@ -0,0 +1,55 @@ +import { readFileSync } from 'fs'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; +import { buildLinkSections, normalizeInputs, validateStateData } from './osint-links.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const stateData = JSON.parse(readFileSync(join(__dirname, '../state_data.json'), 'utf8')); + +let failed = 0; +function assert(name, cond, detail = '') { + if (!cond) { + console.error('FAIL', name, detail); + failed++; + } else { + console.log('PASS', name, detail); + } +} + +const v = validateStateData(stateData); +assert('validateStateData ok', v.ok); + +const min = buildLinkSections({ orgName: 'Happy Paws' }, stateData); +assert('min has financial', min.some((s) => s.id === 'financial')); +assert('min section count', min.length >= 4, String(min.map((s) => s.id))); + +const withEin = buildLinkSections({ orgName: 'X', orgEin: '12-3456789' }, stateData); +const pp = withEin.find((s) => s.id === 'financial').links.find((l) => l.name.includes('ProPublica')); +assert('EIN path', pp.url.endsWith('/organizations/123456789'), pp.url); + +const pr = buildLinkSections({ orgName: 'PR Org', selectedState: 'PR' }, stateData); +assert('PR state', pr.some((s) => s.id === 'state') && pr.some((s) => s.id === 'state-animal')); + +const full = buildLinkSections({ + orgName: 'Acme', + personName: 'Jane', + orgEin: '98-7654321', + location: 'Austin, TX', + domainName: 'https://www.acme.org/', + socialHandle: '@acme', + selectedState: 'TX', +}, stateData); +const ids = full.map((s) => s.id); +assert('full sections', ['personnel', 'advanced', 'social-deep', 'legal'].every((id) => ids.includes(id)), ids.join(',')); + +const ctx = normalizeInputs({ domainName: 'https://www.Example.COM/path/', socialHandle: '@Foo' }); +assert('domain normalize', ctx.domain === 'Example.COM/path' || ctx.domain === 'Example.COM/path'.replace(/\/$/, ''), ctx.domain); +assert('handle normalize', ctx.handle === 'Foo', ctx.handle); + +assert('reject empty', !validateStateData({}).ok); + +if (failed) { + console.error(`\n${failed} failed`); + process.exit(1); +} +console.log('\nAll osint-links tests passed'); diff --git a/tools/js/osint.js b/tools/js/osint.js new file mode 100644 index 0000000..98a8b17 --- /dev/null +++ b/tools/js/osint.js @@ -0,0 +1,183 @@ +/** + * OSINT tab UI: form submit, results accordion, state select. + */ +import { buildLinkSections } from './osint-links.js'; +import { escapeHtml, copyToClipboard, showToast, refreshIcons } from './util.js'; + +export function createOsintController({ elements, getStateData, toastContainer }) { + function createLinkHTML(text, href) { + const isValidUrl = href && (href.startsWith('http://') || href.startsWith('https://')); + const safeText = escapeHtml(text); + const safeHref = escapeHtml(href || ''); + const linkAttributes = isValidUrl + ? `href="${safeHref}" target="_blank" rel="noopener noreferrer"` + : `href="#" aria-disabled="true" title="Invalid URL provided"`; + const linkColor = isValidUrl ? 'text-indigo-500 hover:text-indigo-600' : 'text-gray-400 cursor-not-allowed'; + const copyButtonDisabled = !isValidUrl ? 'disabled' : ''; + + return ` + `; + } + + function createAccordionSectionHTML(id, title, icon, links, isOpen = false) { + if (!links || links.length === 0) return ''; + const linksHtml = links.map((link) => createLinkHTML(link.name, link.url)).join(''); + const allLinksText = escapeHtml(links.map((l) => `${l.name}: ${l.url}`).join('\n')); + const safeTitle = escapeHtml(title); + const safeId = escapeHtml(id); + const safeIcon = escapeHtml(icon); + + return ` +
+

+ + + + +

+
+
${linksHtml}
+
+
`; + } + + function resetGenerateButton() { + if (!elements.generateButton) return; + elements.generateButton.disabled = false; + elements.generateButton.innerHTML = `Generate`; + refreshIcons(); + } + + function renderResults(orgName, sections) { + elements.resultsTitle.textContent = `Investigation for: "${orgName}"`; + elements.accordionContainer.innerHTML = sections + .map((s) => createAccordionSectionHTML(s.id, s.title, s.icon, s.links, s.isOpen)) + .join(''); + elements.placeholder.classList.add('hidden-fade'); + elements.resultsContainer.classList.remove('hidden-fade'); + elements.resultsContainer.classList.add('visible-fade'); + resetGenerateButton(); + refreshIcons(); + } + + function gatherInputs() { + return { + orgName: elements.orgName?.value.trim() || '', + personName: elements.personName?.value.trim() || '', + orgEin: elements.orgEin?.value.trim() || '', + location: elements.location?.value.trim() || '', + domainName: elements.domainName?.value.trim() || '', + socialHandle: elements.socialHandle?.value.trim() || '', + selectedState: elements.stateSelect?.value || '', + }; + } + + function handleSubmit(e) { + e.preventDefault(); + elements.generateButton.disabled = true; + elements.generateButton.innerHTML = `Generating...`; + refreshIcons(); + elements.placeholder.classList.add('hidden'); + elements.skeletonContainer.classList.remove('hidden'); + + // Sync work — no artificial delay + const inputs = gatherInputs(); + if (!inputs.orgName) { + elements.errorMessage.textContent = 'Organization name is required.'; + elements.errorMessage.classList.remove('hidden'); + resetGenerateButton(); + elements.skeletonContainer.classList.add('hidden'); + elements.placeholder.classList.remove('hidden'); + return; + } + elements.errorMessage.classList.add('hidden'); + const sections = buildLinkSections(inputs, getStateData()); + renderResults(inputs.orgName, sections); + elements.skeletonContainer.classList.add('hidden'); + } + + function clearResults() { + elements.searchForm?.reset(); + elements.resultsContainer.classList.remove('visible-fade'); + elements.resultsContainer.classList.add('hidden-fade'); + setTimeout(() => { + elements.accordionContainer.innerHTML = ''; + elements.resultsTitle.textContent = ''; + elements.placeholder.classList.remove('hidden-fade'); + elements.placeholder.classList.remove('hidden'); + }, 300); + } + + function handleAccordionClick(e) { + const accordionButton = e.target.closest('button.accordion-header'); + const copyButton = e.target.closest('.copy-link-btn, .copy-all-btn'); + + if (accordionButton && !copyButton) { + const targetId = accordionButton.dataset.target; + if (!targetId) return; + const content = document.getElementById(targetId); + const chevron = accordionButton.querySelector('.chevron-icon'); + const isExpanded = accordionButton.getAttribute('aria-expanded') === 'true'; + accordionButton.setAttribute('aria-expanded', String(!isExpanded)); + content?.classList.toggle('open'); + chevron?.classList.toggle('rotate-180'); + return; + } + + if (copyButton) { + e.stopPropagation(); + const textToCopy = copyButton.dataset.link || copyButton.dataset.links; + if (textToCopy) copyToClipboard(textToCopy, copyButton, toastContainer); + return; + } + + const link = e.target.closest('a'); + if (link) link.classList.add('visited-link'); + } + + function populateStates() { + const states = getStateData().states || {}; + if (!elements.stateSelect) return; + const fragment = document.createDocumentFragment(); + const sorted = Object.entries(states).sort(([, a], [, b]) => a.localeCompare(b)); + for (const [abbr, name] of sorted) { + const option = document.createElement('option'); + option.value = abbr; + option.textContent = name; + fragment.appendChild(option); + } + elements.stateSelect.innerHTML = ''; + elements.stateSelect.appendChild(fragment); + } + + function bind() { + elements.searchForm?.addEventListener('submit', handleSubmit); + elements.clearButton?.addEventListener('click', clearResults); + elements.accordionContainer?.addEventListener('click', handleAccordionClick); + } + + return { + bind, + populateStates, + gatherInputs, + clearResults, + }; +} diff --git a/tools/js/util.js b/tools/js/util.js new file mode 100644 index 0000000..d432863 --- /dev/null +++ b/tools/js/util.js @@ -0,0 +1,138 @@ +/** + * Shared DOM / browser utilities for Charity Recon. + */ + +export function escapeHtml(value) { + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +/** Convert kebab-case id to camelCase element key. */ +export function idToKey(id) { + return id.replace(/-(\w)/g, (_, c) => c.toUpperCase()); +} + +export function cacheElements(ids) { + const elements = {}; + for (const id of ids) { + elements[idToKey(id)] = document.getElementById(id); + } + return elements; +} + +export function initTheme(themeToggleEl) { + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + const savedTheme = localStorage.getItem('theme'); + if (savedTheme === 'dark' || (!savedTheme && prefersDark)) { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } + updateThemeIcons(themeToggleEl); +} + +export function toggleTheme(themeToggleEl) { + document.documentElement.classList.toggle('dark'); + localStorage.setItem( + 'theme', + document.documentElement.classList.contains('dark') ? 'dark' : 'light' + ); + updateThemeIcons(themeToggleEl); +} + +export function updateThemeIcons(themeToggleEl) { + if (!themeToggleEl) return; + const isDark = document.documentElement.classList.contains('dark'); + const sunIcon = themeToggleEl.querySelector('[data-lucide="sun"]'); + const moonIcon = themeToggleEl.querySelector('[data-lucide="moon"]'); + if (sunIcon) sunIcon.style.display = isDark ? 'none' : 'block'; + if (moonIcon) moonIcon.style.display = isDark ? 'block' : 'none'; +} + +export function showToast(container, message, type = 'success') { + if (!container) return; + const toast = document.createElement('div'); + const icon = type === 'success' ? 'check-circle' : 'alert-circle'; + toast.className = `toast ${type}`; + toast.innerHTML = `${escapeHtml(message)}`; + container.appendChild(toast); + if (window.lucide) lucide.createIcons(); + setTimeout(() => toast.classList.add('show'), 10); + setTimeout(() => { + toast.classList.remove('show'); + toast.addEventListener('transitionend', () => toast.remove()); + }, 3000); +} + +export function copyToClipboard(text, button, toastContainer) { + const done = () => { + const isCopyAll = button.classList.contains('copy-all-btn'); + showToast(toastContainer, isCopyAll ? 'All links copied!' : 'Link copied!'); + const original = button.innerHTML; + button.innerHTML = ``; + if (window.lucide) lucide.createIcons(); + setTimeout(() => { + button.innerHTML = original; + if (window.lucide) lucide.createIcons(); + }, 1500); + }; + + if (navigator.clipboard?.writeText) { + navigator.clipboard.writeText(text).then(done).catch(() => { + showToast(toastContainer, 'Failed to copy!', 'error'); + }); + return; + } + // Fallback + const ta = document.createElement('textarea'); + ta.value = text; + document.body.appendChild(ta); + ta.select(); + try { + document.execCommand('copy'); + done(); + } catch { + showToast(toastContainer, 'Failed to copy!', 'error'); + } + document.body.removeChild(ta); +} + +export function showStatus(element, message, type = 'success', duration = 3000) { + if (!element) return; + const colors = { success: 'text-green-600', error: 'text-red-600', info: 'text-gray-600' }; + const darkColors = { + success: 'dark:text-green-400', + error: 'dark:text-red-400', + info: 'dark:text-gray-400', + }; + element.textContent = message; + element.className = `mt-4 text-center font-medium ${colors[type] || colors.info} ${darkColors[type] || darkColors.info}`; + if (element._statusTimeout) clearTimeout(element._statusTimeout); + if (duration !== null) { + element._statusTimeout = setTimeout(() => { + if (element.textContent === message) { + element.textContent = ''; + element.className = 'mt-4 text-center font-medium'; + } + delete element._statusTimeout; + }, duration); + } else { + delete element._statusTimeout; + } +} + +export function closeModal(modalElement) { + if (modalElement) modalElement.style.display = 'none'; +} + +export function openModal(modalElement) { + if (modalElement) modalElement.style.display = 'block'; +} + +export function refreshIcons() { + if (window.lucide) lucide.createIcons(); +} diff --git a/tools/manifest.json b/tools/manifest.json new file mode 100644 index 0000000..c82bb6f --- /dev/null +++ b/tools/manifest.json @@ -0,0 +1,16 @@ +{ + "name": "Charity Recon Tool", + "short_name": "Charity Recon", + "start_url": "./index.html", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#4f46e5", + "description": "Integrated OSINT and compliance checklist tool for investigating nonprofit animal rescue organizations.", + "icons": [ + { + "src": "osint_app/icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/tools/osint_app/README.md b/tools/osint_app/README.md index 3c32bbf..d187208 100644 --- a/tools/osint_app/README.md +++ b/tools/osint_app/README.md @@ -1,29 +1,12 @@ -## Charity Recon: OSINT Tool for Animal Rescues +# Charity Recon OSINT (legacy path) -Charity Recon is a free, powerful, and easy-to-use web application designed to streamline the open-source intelligence (OSINT) gathering process for investigating nonprofit animal rescue organizations. It provides a centralized dashboard to quickly generate direct search links to a wide array of public records and online platforms. +This folder is a **redirect** to the canonical integrated app: -This tool is built for journalists, researchers, potential donors, and members of the public who want to perform effective due diligence and gain a comprehensive understanding of a nonprofit's operations, financial health, and public reputation. +**[Charity Recon Tool](../index.html)** (`tools/index.html` + `tools/js/`) -## Key Features -- **All-in-One Dashboard**: Combines dozens of search tools into a single, intuitive interface. -- **Multi-Faceted Investigation**: - - **Financial & Legal**: Instantly check federal 501(c)(3) status, search state-level charity registries, and access financial documents from sources like the IRS and ProPublica. - - **Reputation & News**: Scan Google News, public review sites, and community forums like Reddit to gauge public perception. - - **Key Personnel**: Investigate the individuals behind an organization through targeted searches. - - **Online Presence**: Aggregate social media profiles, website history via the Internet Archive, and domain registration details. -- **Efficient Workflow**: - - **Location-Specific Searches**: Refine your investigation by adding a city or location. - - **EIN-Precision**: Use an Employer Identification Number (EIN) for direct, accurate lookups. - - **One-Click Copy**: Easily copy individual links or entire categories of links for your notes and reports. -- **Modern & User-Friendly**: Features a clean, responsive design with a dark mode theme and intuitive controls. +| Old URL | New URL | +|---------|---------| +| `tools/osint_app/index.html` | `tools/index.html#osint` | +| `tools/animal_checklist_app/index.html` | `tools/index.html#checklist` | -## How to Use -1. **Go to the Web App**: Open the index.html file in any modern web browser. -2. **Enter Details**: Fill in the organization's name (required). For more precise results, add any optional details you have, such as a key person's name, EIN, location, domain, or state. -3. **Generate Links**: Click the "Generate" button. -4. **Investigate**: The dashboard will populate with collapsible sections. Click on any link to open the corresponding search in a new tab. Use the copy buttons to save links for your research. - -## Disclaimer -This tool is an aggregator of publicly available information. All data should be critically evaluated and verified from multiple sources. The existence of a link does not imply endorsement or wrongdoing. - -*This project is open-source and created for educational and research purposes.* \ No newline at end of file +Source modules live under [`../js/`](../js/). Shared jurisdiction data: [`../state_data.json`](../state_data.json). diff --git a/tools/osint_app/index.html b/tools/osint_app/index.html index ba11b5a..107405d 100644 --- a/tools/osint_app/index.html +++ b/tools/osint_app/index.html @@ -1,573 +1,29 @@ - + - + - Charity Recon | OSINT Tool for Animal Rescues - - - - - - - - - - -
-
-
-
-
-

Charity Recon: OSINT Tool

-
-
- -
- -
-

Investigate with Intelligence.

-

A free, open-source intelligence (OSINT) tool designed to streamline due diligence on nonprofit animal rescue organizations.

- -
- -
-
-
- -

Financial & Legal

-

Quickly access federal and state registration, tax-exempt status, and financial filings from official sources.

-
-
- -

Reputation & News

-

Scan Google News, public reviews, and community discussions to gauge public perception and find critical reports.

-
-
- -

Online Presence

-

Aggregate an organization's social media profiles, website history, and domain information in one place.

-
-
-
- -
-
-

OSINT Dashboard

-

Enter an organization's details to generate a dashboard of direct links for your investigation.

-
-
- -
-
-

-
-
-
- -

Your investigation results will appear here.

-

Fill out the form to get started.

-
- -
-
-
- -
-

Disclaimer: This tool aggregates links to public information. Always verify data from multiple sources.

-

Created as an open-source project. View on GitHub.

-
-
-
- - + Redirecting to Charity Recon Tool… + + + + + +
+

Charity Recon has one integrated tool now

+

+ The standalone OSINT app has been folded into the main Charity Recon Tool + (OSINT + Compliance Checklists). +

+

+ Continue to the Charity Recon Tool +

+
diff --git a/tools/osint_app/state_data.json b/tools/osint_app/state_data.json deleted file mode 100644 index 066b309..0000000 --- a/tools/osint_app/state_data.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "states": { - "AL": "Alabama", "AK": "Alaska", "AZ": "Arizona", "AR": "Arkansas", "CA": "California", - "CO": "Colorado", "CT": "Connecticut", "DE": "Delaware", "DC": "District of Columbia", - "FL": "Florida", "GA": "Georgia", "HI": "Hawaii", "ID": "Idaho", "IL": "Illinois", - "IN": "Indiana", "IA": "Iowa", "KS": "Kansas", "KY": "Kentucky", "LA": "Louisiana", - "ME": "Maine", "MD": "Maryland", "MA": "Massachusetts", "MI": "Michigan", "MN": "Minnesota", - "MS": "Mississippi", "MO": "Missouri", "MT": "Montana", "NE": "Nebraska", "NV": "Nevada", - "NH": "New Hampshire", "NJ": "New Jersey", "NM": "New Mexico", "NY": "New York", - "NC": "North Carolina", "ND": "North Dakota", "OH": "Ohio", "OK": "Oklahoma", "OR": "Oregon", - "PA": "Pennsylvania", "RI": "Rhode Island", "SC": "South Carolina", "SD": "South Dakota", - "TN": "Tennessee", "TX": "Texas", "UT": "Utah", "VT": "Vermont", "VA": "Virginia", - "WA": "Washington", "WV": "West Virginia", "WI": "Wisconsin", "WY": "Wyoming" - }, - "stateResources": { - "AL": [{ "n": "AG Charity Search", "u": "https://www.alabamaag.gov/consumer-protection/charitable-organizations/" }, { "n": "SOS Business Search", "u": "https://www.sos.alabama.gov/government-records/business-entity-records" }], - "AK": [{ "n": "Dept. of Law Charity Search", "u": "https://www.law.alaska.gov/department/civil/consumer/charities_list.html" }, { "n": "Dept. of Commerce Business Search", "u": "https://www.commerce.alaska.gov/cbp/main/search/entities" }], - "AZ": [{ "n": "AG Charity Search", "u": "https://www.azag.gov/charities" }, { "n": "Corporation Commission Business Search", "u": "https://ecorp.azcc.gov/entitysearch/index" }], - "AR": [{ "n": "SOS Charity Search", "u": "https://www.sos.arkansas.gov/corps/charity/index.php" }, { "n": "SOS Business Search", "u": "https://www.sos.arkansas.gov/corps/search_all.php" }], - "CA": [{ "n": "DOJ Charity Search", "u": "https://rct.doj.ca.gov/Verification/Web/Search.aspx?facility=Y" }, { "n": "SOS Business Search", "u": "https://bizfileonline.sos.ca.gov/search/business" }], - "CO": [{ "n": "SOS Charity Search", "u": "https://www.sos.state.co.us/pubs/charities/charitableHome.html" }, { "n": "SOS Business Search", "u": "https://www.sos.state.co.us/biz/BusinessEntityCriteria.do" }], - "CT": [{ "n": "DCP License Lookup", "u": "https://www.elicense.ct.gov/lookup/licenselookup.aspx" }, { "n": "SOS Business Search", "u": "https://service.ct.gov/business/s/onlinebusinesssearch" }], - "DE": [{ "n": "Division of Corporations Search", "u": "https://icis.corp.delaware.gov/Ecorp/EntitySearch/NameSearch.aspx" }], - "FL": [{ "n": "DACS Charity Search", "u": "https://csapp.fdacs.gov/cspublicapp/giftgiversquery/giftgiversquery.aspx" }, { "n": "SunBiz Business Search", "u": "http://dos.myflorida.com/sunbiz/search/" }], - "GA": [{ "n": "SOS Charity Search", "u": "https://sos.ga.gov/charities-division-georgia-secretary-states-office" }, { "n": "SOS Business Search", "u": "https://ecorp.sos.ga.gov/BusinessSearch" }], - "HI": [{ "n": "AG Charity Search", "u": "https://charity.ehawaii.gov/charity/new-search.html" }, { "n": "DCCA Business Search", "u": "https://hbe.ehawaii.gov/documents/search.html" }], - "ID": [{ "n": "SOS Business Search", "u": "https://sosbiz.idaho.gov/search/business" }], - "IL": [{ "n": "AG Charity Search", "u": "https://charitable.illinoisattorneygeneral.gov/search" }, { "n": "SOS Business Search", "u": "https://apps.ilsos.gov/corporatellc/" }], - "IN": [{ "n": "INBiz Business Search", "u": "https://inbiz.in.gov/BOS/Home/Search" }], - "IA": [{ "n": "SOS Business Search", "u": "https://sos.iowa.gov/search/business/search.aspx" }], - "KS": [{ "n": "AG Charity Search", "u": "https://ag.ks.gov/offices/charitable-gaming" }, { "n": "SOS Business Search", "u": "https://www.kansas.gov/businesscenter/" }], - "KY": [{ "n": "AG Charity Search", "u": "https://ag.ky.gov/Resources/Consumer-Resources/charity/Pages/default.aspx" }, { "n": "SOS Business Search", "u": "https://web.sos.ky.gov/ftsearch/" }], - "LA": [{ "n": "SOS Charity/Business Search", "u": "https://coraweb.sos.la.gov/CommercialSearch/CommercialSearch.aspx" }], - "ME": [{ "n": "Dept. of Professional & Financial Regulation Search", "u": "https://www.maine.gov/pfr/consumer/licensee-search" }, { "n": "SOS Business Search", "u": "https://apps3.web.maine.gov/nei-sos-icrs/ICRS?MainPage=x" }], - "MD": [{ "n": "SOS Charity Search", "u": "https://sos.maryland.gov/Charity/pages/default.aspx" }, { "n": "Dept. of Assessments and Taxation Business Search", "u": "https://egov.maryland.gov/businessexpress/entitysearch" }], - "MA": [{ "n": "AG Charity Search", "u": "https://www.mass.gov/info-details/search-public-charities-filings" }, { "n": "SOS Business Search", "u": "https://corp.sec.state.ma.us/corpweb/corpsearch/corpsearch.aspx" }], - "MI": [{ "n": "AG Charity Search", "u": "https://www.ag.state.mi.us/charitabletrust/frmDisclaimer.aspx" }, { "n": "LARA Business Search", "u": "https://cofs.lara.state.mi.us/SearchApi/Search/Search" }], - "MN": [{ "n": "AG Charity Search", "u": "https://www.ag.state.mn.us/Charity/Search/" }, { "n": "SOS Business Search", "u": "https://mblsportal.sos.state.mn.us/Business/Search" }], - "MS": [{ "n": "SOS Charity Search", "u": "https://charities.sos.ms.gov/online/portal/ch/page/charities-search/portal.aspx" }, { "n": "SOS Business Search", "u": "https://corp.sos.ms.gov/corp/portal/c/page/corpbusinessidsearch/portal.aspx" }], - "MO": [{ "n": "AG Charity Search", "u": "https://ago.mo.gov/get-help/programs-services-from-a-z/charity/" }, { "n": "SOS Business Search", "u": "https://bsd.sos.mo.gov/BusinessEntity/BESearch.aspx?SearchType=0" }], - "MT": [{ "n": "SOS Business Search", "u": "https://biz.sosmt.gov/search/business" }], - "NE": [{ "n": "SOS Business Search", "u": "https://www.nebraska.gov/sos/corp/corpsearch.cgi" }], - "NV": [{ "n": "SOS Charity/Business Search", "u": "https://www.nvsos.gov/sos/businesses" }], - "NH": [{ "n": "AG Registered Charities", "u": "https://www.doj.nh.gov/charitable-trusts/registered-charities.htm" }, { "n": "SOS Business Search", "u": "https://quickstart.sos.nh.gov/online/BusinessInquire" }], - "NJ": [{ "n": "Division of Consumer Affairs Charity Search", "u": "https://charportal.dca.njoag.gov/Charity-Registration/CHR-Public-Search-Page/" }, { "n": "Dept. of Treasury Business Search", "u": "https://www.njportal.com/dor/businessnamesearch/" }], - "NM": [{ "n": "AG Charity Search", "u": "https://nmag.gov/charities/" }, { "n": "SOS Business Search", "u": "https://portal.sos.state.nm.us/BFS/online/CorporationBusinessSearch" }], - "NY": [{ "n": "AG Charity Search", "u": "https://www.charitiesnys.com/RegistrySearch/search_charities.jsp" }, { "n": "Dept. of State Business Search", "u": "https://apps.dos.ny.gov/publicInquiry/" }], - "NC": [{ "n": "SOS Charity Search", "u": "https://www.sosnc.gov/online_services/search/by_title/search_charities" }, { "n": "SOS Business Search", "u": "https://www.sosnc.gov/online_services/search/by_title/search_Business_Registration" }], - "ND": [{ "n": "SOS Charity/Business Search", "u": "https://firststop.sos.nd.gov/search" }], - "OH": [{ "n": "AG Charity Search", "u": "https://charitableregistration.ohioattorneygeneral.gov/" }, { "n": "SOS Business Search", "u": "https://businesssearch.ohiosos.gov/" }], - "OK": [{ "n": "SOS Charity/Business Search", "u": "https://www.sos.ok.gov/corp/corpInquiryFind.aspx" }], - "OR": [{ "n": "DOJ Charity Search", "u": "https://www.doj.state.or.us/charitable-activities/" }, { "n": "SOS Business Search", "u": "https://secure.sos.state.or.us/cbrmanager/" }], - "PA": [{ "n": "Dept. of State Charity Search", "u": "https://file.dos.pa.gov/" }, { "n": "Dept. of State Business Search", "u": "https://www.corporations.pa.gov/search/bussearch" }], - "RI": [{ "n": "Dept. of Business Regulation Search", "u": "https://dbr.ri.gov/public-records-request" }, { "n": "SOS Business Search", "u": "https://business.sos.ri.gov/CorpWeb/CorpSearch/CorpSearch.aspx" }], - "SC": [{ "n": "SOS Charity Search", "u": "https://search.scsos.com/charities" }, { "n": "SOS Business Search", "u": "https://businessfilings.sc.gov/BusinessFiling/Entity/Search" }], - "SD": [{ "n": "SOS Business Search", "u": "https://sosenterprise.sd.gov/BusinessServices/Business/FilingSearch.aspx" }], - "TN": [{ "n": "SOS Charity Search", "u": "http://sos.tn.gov/charities/services/charitable-entity-search" }, { "n": "SOS Business Search", "u": "https://sos.tn.gov/search" }], - "TX": [{ "n": "SOS Business Search", "u": "https://direct.sos.state.tx.us/corp/sosda/index.shtml" }], - "UT": [{ "n": "Division of Consumer Protection Charity Search", "u": "https://dcp.utah.gov/for-businesses/charities/" }, { "n": "Dept. of Commerce Business Search", "u": "https://secure.utah.gov/bes/" }], - "VT": [{ "n": "SOS Business Search", "u": "https://bizfilings.vermont.gov/online/BusinessInquire/" }], - "VA": [{ "n": "DACS Charity Search", "u": "https://cos.vdacs.virginia.gov/cgi-bin/char_search.cgi" }, { "n": "State Corporation Commission Business Search", "u": "https://cis.scc.virginia.gov/EntitySearch/Index" }], - "WA": [{ "n": "SOS Charity Search", "u": "https://www.sos.wa.gov/corporations-charities/nonprofits-charities/charitable-organizations" }, { "n": "SOS Business Search", "u": "https://www.sos.wa.gov/corporations-charities/" }], - "WV": [{ "n": "SOS Charity/Business Search", "u": "https://apps.wv.gov/SOS/BusinessEntitySearch/" }], - "WI": [{ "n": "DFI Charity Search", "u": "https://wdfi.org/apps/CharitySearch/Search.aspx" }, { "n": "DFI Business Search", "u": "https://www.wdfi.org/apps/CorpSearch/Search.aspx" }], - "WY": [{ "n": "SOS Business Search", "u": "https://wyobiz.wyo.gov/business/filingsearch.aspx" }] - }, - "stateAnimalWelfareResources": { - "AL": [{ "n": "Dept. of Agriculture and Industries - Animal Industries Division", "u": "https://agi.alabama.gov/animalindustries/" }], - "AK": [{ "n": "Dept. of Environmental Conservation - Office of the State Veterinarian", "u": "https://dec.alaska.gov/eh/vet/" }], - "AZ": [{ "n": "Dept. of Agriculture - Animal Services Division", "u": "https://agriculture.az.gov/animals" }], - "AR": [{ "n": "Dept. of Agriculture - Livestock and Poultry Commission", "u": "https://www.agriculture.arkansas.gov/livestock-poultry/" }], - "CA": [{ "n": "Dept. of Food and Agriculture - Animal Health and Food Safety Services", "u": "https://www.cdfa.ca.gov/ahfss/" }], - "CO": [{ "n": "Dept. of Agriculture - Animal Health Division", "u": "https://ag.colorado.gov/animals" }], - "CT": [{ "n": "Dept. of Agriculture - State Animal Control", "u": "https://portal.ct.gov/doag/regulatory/regulatory/animal-control-division" }], - "DE": [{ "n": "Dept. of Agriculture - Poultry and Animal Health", "u": "https://agriculture.delaware.gov/poultry-animal-health/" }], - "FL": [{ "n": "Dept. of Agriculture and Consumer Services - Division of Animal Industry", "u": "https://www.fdacs.gov/Divisions-Offices/Animal-Industry" }], - "GA": [{ "n": "Dept. of Agriculture - Animal Industry Division", "u": "https://agr.georgia.gov/animals" }], - "HI": [{ "n": "Dept. of Agriculture - Animal Industry Division", "u": "https://hdoa.hawaii.gov/ai/" }], - "ID": [{ "n": "State Dept. of Agriculture - Division of Animal Industries", "u": "https://agri.idaho.gov/main/animals/" }], - "IL": [{ "n": "Dept. of Agriculture - Bureau of Animal Health and Welfare", "u": "https://agr.illinois.gov/animals/animalhealth.html" }], - "IN": [{ "n": "State Board of Animal Health", "u": "https://www.in.gov/boah/" }], - "IA": [{ "n": "Dept. of Agriculture and Land Stewardship - Animal Industry Bureau", "u": "https://iowaagriculture.gov/animal-industry-bureau" }], - "KS": [{ "n": "Dept. of Agriculture - Division of Animal Health", "u": "https://agriculture.ks.gov/divisions-programs/division-of-animal-health" }], - "KY": [{ "n": "Dept. of Agriculture - Office of the State Veterinarian", "u": "https://www.kyagr.com/statevet/" }], - "LA": [{ "n": "Dept. of Agriculture and Forestry - Animal Health & Food Safety", "u": "https://www.ldaf.state.la.us/about/organization/ahfs" }], - "ME": [{ "n": "Dept. of Agriculture, Conservation and Forestry - Division of Animal and Plant Health", "u": "https://www.maine.gov/dacf/ahw/animal_health/" }], - "MD": [{ "n": "Dept. of Agriculture - Animal Health", "u": "https://mda.maryland.gov/animalhealth/Pages/default.aspx" }], - "MA": [{ "n": "Dept. of Agricultural Resources - Division of Animal Health", "u": "https://www.mass.gov/info-details/division-of-animal-health" }], - "MI": [{ "n": "Dept. of Agriculture & Rural Development - Animal Industry Division", "u": "https://www.michigan.gov/mdard/animals" }], - "MN": [{ "n": "Board of Animal Health", "u": "https://www.bah.state.mn.us/" }], - "MS": [{ "n": "Board of Animal Health", "u": "https://www.mbah.ms.gov/" }], - "MO": [{ "n": "Dept. of Agriculture - Animal Health Division", "u": "https://agriculture.mo.gov/animals/" }], - "MT": [{ "n": "Dept. of Livestock - Animal Health & Food Safety Division", "u": "https://liv.mt.gov/" }], - "NE": [{ "n": "Dept. of Agriculture - Animal Health", "u": "https://nda.nebraska.gov/animal" }], - "NV": [{ "n": "Dept. of Agriculture - Division of Animal Industry", "u": "https://agri.nv.gov/animals/animal_home/" }], - "NH": [{ "n": "Dept. of Agriculture, Markets & Food - Division of Animal Industry", "u": "https://www.agriculture.nh.gov/divisions/animal-industry/index.htm" }], - "NJ": [{ "n": "Dept. of Agriculture - Division of Animal Health", "u": "https://www.nj.gov/agriculture/divisions/ah/" }], - "NM": [{ "n": "Livestock Board", "u": "https://www.nmlbonline.com/" }], - "NY": [{ "n": "Dept. of Agriculture and Markets - Division of Animal Industry", "u": "https://agriculture.ny.gov/animals" }], - "NC": [{ "n": "Dept. of Agriculture & Consumer Services - Veterinary Division", "u": "https://www.ncagr.gov/vet/" }], - "ND": [{ "n": "Board of Animal Health", "u": "https://www.ndda.nd.gov/divisions/animal-health/north-dakota-state-board-animal-health" }], - "OH": [{ "n": "Dept. of Agriculture - Division of Animal Health", "u": "https://agri.ohio.gov/divisions/animal-health" }], - "OK": [{ "n": "Dept. of Agriculture, Food and Forestry - Animal Industry Services", "u": "https://ag.ok.gov/divisions/animal-industry-state-veterinarian/" }], - "OR": [{ "n": "Dept. of Agriculture - Animal Health and Identification", "u": "https://www.oregon.gov/oda/animal-health-feeds-livestock-id/pages/default.aspx" }], - "PA": [{ "n": "Dept. of Agriculture - Bureau of Animal Health and Diagnostic Services", "u": "https://www.agriculture.pa.gov/animals/health/Pages/default.aspx" }], - "RI": [{ "n": "Dept. of Environmental Management - Division of Agriculture and Forest Environment", "u": "https://dem.ri.gov/natural-resources-bureau/agriculture-and-forest-environment/agriculture/animal-health" }], - "SC": [{ "n": "Clemson University - Livestock Poultry Health", "u": "https://www.clemson.edu/public/lph/" }], - "SD": [{ "n": "Animal Industry Board", "u": "https://aib.sd.gov/" }], - "TN": [{ "n": "Dept. of Agriculture - Animal Health", "u": "https://www.tn.gov/agriculture/animals.html" }], - "TX": [{ "n": "Animal Health Commission", "u": "https://www.tahc.texas.gov/" }], - "UT": [{ "n": "Dept. of Agriculture and Food - Division of Animal Industry", "u": "https://ag.utah.gov/animal-industry/" }], - "VT": [{ "n": "Agency of Agriculture, Food and Markets - Animal Health", "u": "https://agriculture.vermont.gov/animal-health" }], - "VA": [{ "n": "Dept. of Agriculture and Consumer Services - Animal and Food Industry Services", "u": "https://www.vdacs.virginia.gov/animals.shtml" }], - "WA": [{ "n": "State Dept. of Agriculture - Animal Health Program", "u": "https://agr.wa.gov/departments/animals-livestock-and-pets/animal-health" }], - "WV": [{ "n": "Dept. of Agriculture - Animal Health", "u": "https://agriculture.wv.gov/divisions/animal-health/" }], - "WI": [{ "n": "Dept. of Agriculture, Trade and Consumer Protection - Division of Animal Health", "u": "https://datcp.wi.gov/Pages/Programs_Services/Programs.aspx" }], - "WY": [{ "n": "Livestock Board", "u": "https://wlsb.state.wy.us/" }] - } -} diff --git a/tools/state_data.json b/tools/state_data.json new file mode 100644 index 0000000..46ad6c7 --- /dev/null +++ b/tools/state_data.json @@ -0,0 +1,832 @@ +{ + "states": { + "AL": "Alabama", + "AK": "Alaska", + "AZ": "Arizona", + "AR": "Arkansas", + "CA": "California", + "CO": "Colorado", + "CT": "Connecticut", + "DE": "Delaware", + "DC": "District of Columbia", + "FL": "Florida", + "GA": "Georgia", + "HI": "Hawaii", + "ID": "Idaho", + "IL": "Illinois", + "IN": "Indiana", + "IA": "Iowa", + "KS": "Kansas", + "KY": "Kentucky", + "LA": "Louisiana", + "ME": "Maine", + "MD": "Maryland", + "MA": "Massachusetts", + "MI": "Michigan", + "MN": "Minnesota", + "MS": "Mississippi", + "MO": "Missouri", + "MT": "Montana", + "NE": "Nebraska", + "NV": "Nevada", + "NH": "New Hampshire", + "NJ": "New Jersey", + "NM": "New Mexico", + "NY": "New York", + "NC": "North Carolina", + "ND": "North Dakota", + "OH": "Ohio", + "OK": "Oklahoma", + "OR": "Oregon", + "PA": "Pennsylvania", + "PR": "Puerto Rico", + "RI": "Rhode Island", + "SC": "South Carolina", + "SD": "South Dakota", + "TN": "Tennessee", + "TX": "Texas", + "UT": "Utah", + "VT": "Vermont", + "VA": "Virginia", + "WA": "Washington", + "WV": "West Virginia", + "WI": "Wisconsin", + "WY": "Wyoming" + }, + "stateResources": { + "AK": [ + { + "n": "Dept. of Law Charity Search", + "u": "https://www.law.alaska.gov/department/civil/consumer/charities_list.html" + }, + { + "n": "Dept. of Commerce Business Search", + "u": "https://www.commerce.alaska.gov/cbp/main/search/entities" + } + ], + "AL": [ + { + "n": "AG Charity Search", + "u": "https://www.alabamaag.gov/consumer-protection/charitable-organizations/" + }, + { + "n": "SOS Business Search", + "u": "https://www.sos.alabama.gov/government-records/business-entity-records" + } + ], + "AR": [ + { + "n": "SOS Charity Search", + "u": "https://www.sos.arkansas.gov/corps/charity/index.php" + }, + { + "n": "SOS Business Search", + "u": "https://www.sos.arkansas.gov/corps/search_all.php" + } + ], + "AZ": [ + { + "n": "AG Charity Search", + "u": "https://www.azag.gov/charities" + }, + { + "n": "Corporation Commission Business Search", + "u": "https://ecorp.azcc.gov/entitysearch/index" + } + ], + "CA": [ + { + "n": "DOJ Charity Search", + "u": "https://rct.doj.ca.gov/Verification/Web/Search.aspx?facility=Y" + }, + { + "n": "SOS Business Search", + "u": "https://bizfileonline.sos.ca.gov/search/business" + } + ], + "CO": [ + { + "n": "SOS Charity Search", + "u": "https://www.sos.state.co.us/pubs/charities/charitableHome.html" + }, + { + "n": "SOS Business Search", + "u": "https://www.sos.state.co.us/biz/BusinessEntityCriteria.do" + } + ], + "CT": [ + { + "n": "DCP License Lookup", + "u": "https://www.elicense.ct.gov/lookup/licenselookup.aspx" + }, + { + "n": "SOS Business Search", + "u": "https://service.ct.gov/business/s/onlinebusinesssearch" + } + ], + "DC": [ + { + "n": "OAG / Nonprofits & Consumer Protection", + "u": "https://oag.dc.gov/consumer-protection" + }, + { + "n": "DLCP Business Entity Search", + "u": "https://corponline.dlcp.dc.gov/BizEntitySearch.aspx" + } + ], + "DE": [ + { + "n": "Division of Corporations Search", + "u": "https://icis.corp.delaware.gov/Ecorp/EntitySearch/NameSearch.aspx" + } + ], + "FL": [ + { + "n": "DACS Charity Search", + "u": "https://csapp.fdacs.gov/cspublicapp/giftgiversquery/giftgiversquery.aspx" + }, + { + "n": "SunBiz Business Search", + "u": "http://dos.myflorida.com/sunbiz/search/" + } + ], + "GA": [ + { + "n": "SOS Charity Search", + "u": "https://sos.ga.gov/charities-division-georgia-secretary-states-office" + }, + { + "n": "SOS Business Search", + "u": "https://ecorp.sos.ga.gov/BusinessSearch" + } + ], + "HI": [ + { + "n": "AG Charity Search", + "u": "https://charity.ehawaii.gov/charity/new-search.html" + }, + { + "n": "DCCA Business Search", + "u": "https://hbe.ehawaii.gov/documents/search.html" + } + ], + "IA": [ + { + "n": "SOS Business Search", + "u": "https://sos.iowa.gov/search/business/search.aspx" + } + ], + "ID": [ + { + "n": "SOS Business Search", + "u": "https://sosbiz.idaho.gov/search/business" + } + ], + "IL": [ + { + "n": "AG Charity Search", + "u": "https://charitable.illinoisattorneygeneral.gov/search" + }, + { + "n": "SOS Business Search", + "u": "https://apps.ilsos.gov/corporatellc/" + } + ], + "IN": [ + { + "n": "INBiz Business Search", + "u": "https://inbiz.in.gov/BOS/Home/Search" + } + ], + "KS": [ + { + "n": "AG Charity Search", + "u": "https://ag.ks.gov/offices/charitable-gaming" + }, + { + "n": "SOS Business Search", + "u": "https://www.kansas.gov/businesscenter/" + } + ], + "KY": [ + { + "n": "AG Charity Search", + "u": "https://ag.ky.gov/Resources/Consumer-Resources/charity/Pages/default.aspx" + }, + { + "n": "SOS Business Search", + "u": "https://web.sos.ky.gov/ftsearch/" + } + ], + "LA": [ + { + "n": "SOS Charity/Business Search", + "u": "https://coraweb.sos.la.gov/CommercialSearch/CommercialSearch.aspx" + } + ], + "MA": [ + { + "n": "AG Charity Search", + "u": "https://www.mass.gov/info-details/search-public-charities-filings" + }, + { + "n": "SOS Business Search", + "u": "https://corp.sec.state.ma.us/corpweb/corpsearch/corpsearch.aspx" + } + ], + "MD": [ + { + "n": "SOS Charity Search", + "u": "https://sos.maryland.gov/Charity/pages/default.aspx" + }, + { + "n": "Dept. of Assessments and Taxation Business Search", + "u": "https://egov.maryland.gov/businessexpress/entitysearch" + } + ], + "ME": [ + { + "n": "Dept. of Professional & Financial Regulation Search", + "u": "https://www.maine.gov/pfr/consumer/licensee-search" + }, + { + "n": "SOS Business Search", + "u": "https://apps3.web.maine.gov/nei-sos-icrs/ICRS?MainPage=x" + } + ], + "MI": [ + { + "n": "AG Charity Search", + "u": "https://www.ag.state.mi.us/charitabletrust/frmDisclaimer.aspx" + }, + { + "n": "LARA Business Search", + "u": "https://cofs.lara.state.mi.us/SearchApi/Search/Search" + } + ], + "MN": [ + { + "n": "AG Charity Search", + "u": "https://www.ag.state.mn.us/Charity/Search/" + }, + { + "n": "SOS Business Search", + "u": "https://mblsportal.sos.state.mn.us/Business/Search" + } + ], + "MO": [ + { + "n": "AG Charity Search", + "u": "https://ago.mo.gov/get-help/programs-services-from-a-z/charity/" + }, + { + "n": "SOS Business Search", + "u": "https://bsd.sos.mo.gov/BusinessEntity/BESearch.aspx?SearchType=0" + } + ], + "MS": [ + { + "n": "SOS Charity Search", + "u": "https://charities.sos.ms.gov/online/portal/ch/page/charities-search/portal.aspx" + }, + { + "n": "SOS Business Search", + "u": "https://corp.sos.ms.gov/corp/portal/c/page/corpbusinessidsearch/portal.aspx" + } + ], + "MT": [ + { + "n": "SOS Business Search", + "u": "https://biz.sosmt.gov/search/business" + } + ], + "NC": [ + { + "n": "SOS Charity Search", + "u": "https://www.sosnc.gov/online_services/search/by_title/search_charities" + }, + { + "n": "SOS Business Search", + "u": "https://www.sosnc.gov/online_services/search/by_title/search_Business_Registration" + } + ], + "ND": [ + { + "n": "SOS Charity/Business Search", + "u": "https://firststop.sos.nd.gov/search" + } + ], + "NE": [ + { + "n": "SOS Business Search", + "u": "https://www.nebraska.gov/sos/corp/corpsearch.cgi" + } + ], + "NH": [ + { + "n": "AG Registered Charities", + "u": "https://www.doj.nh.gov/charitable-trusts/registered-charities.htm" + }, + { + "n": "SOS Business Search", + "u": "https://quickstart.sos.nh.gov/online/BusinessInquire" + } + ], + "NJ": [ + { + "n": "Division of Consumer Affairs Charity Search", + "u": "https://charportal.dca.njoag.gov/Charity-Registration/CHR-Public-Search-Page/" + }, + { + "n": "Dept. of Treasury Business Search", + "u": "https://www.njportal.com/dor/businessnamesearch/" + } + ], + "NM": [ + { + "n": "AG Charity Search", + "u": "https://nmag.gov/charities/" + }, + { + "n": "SOS Business Search", + "u": "https://portal.sos.state.nm.us/BFS/online/CorporationBusinessSearch" + } + ], + "NV": [ + { + "n": "SOS Charity/Business Search", + "u": "https://www.nvsos.gov/sos/businesses" + } + ], + "NY": [ + { + "n": "AG Charity Search", + "u": "https://www.charitiesnys.com/RegistrySearch/search_charities.jsp" + }, + { + "n": "Dept. of State Business Search", + "u": "https://apps.dos.ny.gov/publicInquiry/" + } + ], + "OH": [ + { + "n": "AG Charity Search", + "u": "https://charitableregistration.ohioattorneygeneral.gov/" + }, + { + "n": "SOS Business Search", + "u": "https://businesssearch.ohiosos.gov/" + } + ], + "OK": [ + { + "n": "SOS Charity/Business Search", + "u": "https://www.sos.ok.gov/corp/corpInquiryFind.aspx" + } + ], + "OR": [ + { + "n": "DOJ Charity Search", + "u": "https://www.doj.state.or.us/charitable-activities/" + }, + { + "n": "SOS Business Search", + "u": "https://secure.sos.state.or.us/cbrmanager/" + } + ], + "PA": [ + { + "n": "Dept. of State Charity Search", + "u": "https://file.dos.pa.gov/" + }, + { + "n": "Dept. of State Business Search", + "u": "https://www.corporations.pa.gov/search/bussearch" + } + ], + "PR": [ + { + "n": "Dept. of State Entity Search", + "u": "https://rcp.estado.pr.gov/en/search" + }, + { + "n": "DACO Consumer Protection", + "u": "https://www.daco.pr.gov/" + } + ], + "RI": [ + { + "n": "Dept. of Business Regulation Search", + "u": "https://dbr.ri.gov/public-records-request" + }, + { + "n": "SOS Business Search", + "u": "https://business.sos.ri.gov/CorpWeb/CorpSearch/CorpSearch.aspx" + } + ], + "SC": [ + { + "n": "SOS Charity Search", + "u": "https://search.scsos.com/charities" + }, + { + "n": "SOS Business Search", + "u": "https://businessfilings.sc.gov/BusinessFiling/Entity/Search" + } + ], + "SD": [ + { + "n": "SOS Business Search", + "u": "https://sosenterprise.sd.gov/BusinessServices/Business/FilingSearch.aspx" + } + ], + "TN": [ + { + "n": "SOS Charity Search", + "u": "http://sos.tn.gov/charities/services/charitable-entity-search" + }, + { + "n": "SOS Business Search", + "u": "https://sos.tn.gov/search" + } + ], + "TX": [ + { + "n": "SOS Business Search", + "u": "https://direct.sos.state.tx.us/corp/sosda/index.shtml" + } + ], + "UT": [ + { + "n": "Division of Consumer Protection Charity Search", + "u": "https://dcp.utah.gov/for-businesses/charities/" + }, + { + "n": "Dept. of Commerce Business Search", + "u": "https://secure.utah.gov/bes/" + } + ], + "VA": [ + { + "n": "DACS Charity Search", + "u": "https://cos.vdacs.virginia.gov/cgi-bin/char_search.cgi" + }, + { + "n": "State Corporation Commission Business Search", + "u": "https://cis.scc.virginia.gov/EntitySearch/Index" + } + ], + "VT": [ + { + "n": "SOS Business Search", + "u": "https://bizfilings.vermont.gov/online/BusinessInquire/" + } + ], + "WA": [ + { + "n": "SOS Charity Search", + "u": "https://www.sos.wa.gov/corporations-charities/nonprofits-charities/charitable-organizations" + }, + { + "n": "SOS Business Search", + "u": "https://www.sos.wa.gov/corporations-charities/" + } + ], + "WI": [ + { + "n": "DFI Charity Search", + "u": "https://wdfi.org/apps/CharitySearch/Search.aspx" + }, + { + "n": "DFI Business Search", + "u": "https://www.wdfi.org/apps/CorpSearch/Search.aspx" + } + ], + "WV": [ + { + "n": "SOS Charity/Business Search", + "u": "https://apps.wv.gov/SOS/BusinessEntitySearch/" + } + ], + "WY": [ + { + "n": "SOS Business Search", + "u": "https://wyobiz.wyo.gov/business/filingsearch.aspx" + } + ] + }, + "stateAnimalWelfareResources": { + "AK": [ + { + "n": "Dept. of Environmental Conservation - Office of the State Veterinarian", + "u": "https://dec.alaska.gov/eh/vet/" + } + ], + "AL": [ + { + "n": "Dept. of Agriculture and Industries - Animal Industries Division", + "u": "https://agi.alabama.gov/animalindustries/" + } + ], + "AR": [ + { + "n": "Dept. of Agriculture - Livestock and Poultry Commission", + "u": "https://www.agriculture.arkansas.gov/livestock-poultry/" + } + ], + "AZ": [ + { + "n": "Dept. of Agriculture - Animal Services Division", + "u": "https://agriculture.az.gov/animals" + } + ], + "CA": [ + { + "n": "Dept. of Food and Agriculture - Animal Health and Food Safety Services", + "u": "https://www.cdfa.ca.gov/ahfss/" + } + ], + "CO": [ + { + "n": "Dept. of Agriculture - Animal Health Division", + "u": "https://ag.colorado.gov/animals" + } + ], + "CT": [ + { + "n": "Dept. of Agriculture - State Animal Control", + "u": "https://portal.ct.gov/doag/regulatory/regulatory/animal-control-division" + } + ], + "DC": [ + { + "n": "DC Department of Health (animal-related programs)", + "u": "https://dchealth.dc.gov/" + } + ], + "DE": [ + { + "n": "Dept. of Agriculture - Poultry and Animal Health", + "u": "https://agriculture.delaware.gov/poultry-animal-health/" + } + ], + "FL": [ + { + "n": "Dept. of Agriculture and Consumer Services - Division of Animal Industry", + "u": "https://www.fdacs.gov/Divisions-Offices/Animal-Industry" + } + ], + "GA": [ + { + "n": "Dept. of Agriculture - Animal Industry Division", + "u": "https://agr.georgia.gov/animals" + } + ], + "HI": [ + { + "n": "Dept. of Agriculture - Animal Industry Division", + "u": "https://hdoa.hawaii.gov/ai/" + } + ], + "IA": [ + { + "n": "Dept. of Agriculture and Land Stewardship - Animal Industry Bureau", + "u": "https://iowaagriculture.gov/animal-industry-bureau" + } + ], + "ID": [ + { + "n": "State Dept. of Agriculture - Division of Animal Industries", + "u": "https://agri.idaho.gov/main/animals/" + } + ], + "IL": [ + { + "n": "Dept. of Agriculture - Bureau of Animal Health and Welfare", + "u": "https://agr.illinois.gov/animals/animalhealth.html" + } + ], + "IN": [ + { + "n": "State Board of Animal Health", + "u": "https://www.in.gov/boah/" + } + ], + "KS": [ + { + "n": "Dept. of Agriculture - Division of Animal Health", + "u": "https://agriculture.ks.gov/divisions-programs/division-of-animal-health" + } + ], + "KY": [ + { + "n": "Dept. of Agriculture - Office of the State Veterinarian", + "u": "https://www.kyagr.com/statevet/" + } + ], + "LA": [ + { + "n": "Dept. of Agriculture and Forestry - Animal Health & Food Safety", + "u": "https://www.ldaf.state.la.us/about/organization/ahfs" + } + ], + "MA": [ + { + "n": "Dept. of Agricultural Resources - Division of Animal Health", + "u": "https://www.mass.gov/info-details/division-of-animal-health" + } + ], + "MD": [ + { + "n": "Dept. of Agriculture - Animal Health", + "u": "https://mda.maryland.gov/animalhealth/Pages/default.aspx" + } + ], + "ME": [ + { + "n": "Dept. of Agriculture, Conservation and Forestry - Division of Animal and Plant Health", + "u": "https://www.maine.gov/dacf/ahw/animal_health/" + } + ], + "MI": [ + { + "n": "Dept. of Agriculture & Rural Development - Animal Industry Division", + "u": "https://www.michigan.gov/mdard/animals" + } + ], + "MN": [ + { + "n": "Board of Animal Health", + "u": "https://www.bah.state.mn.us/" + } + ], + "MO": [ + { + "n": "Dept. of Agriculture - Animal Health Division", + "u": "https://agriculture.mo.gov/animals/" + } + ], + "MS": [ + { + "n": "Board of Animal Health", + "u": "https://www.mbah.ms.gov/" + } + ], + "MT": [ + { + "n": "Dept. of Livestock - Animal Health & Food Safety Division", + "u": "https://liv.mt.gov/" + } + ], + "NC": [ + { + "n": "Dept. of Agriculture & Consumer Services - Veterinary Division", + "u": "https://www.ncagr.gov/vet/" + } + ], + "ND": [ + { + "n": "Board of Animal Health", + "u": "https://www.ndda.nd.gov/divisions/animal-health/north-dakota-state-board-animal-health" + } + ], + "NE": [ + { + "n": "Dept. of Agriculture - Animal Health", + "u": "https://nda.nebraska.gov/animal" + } + ], + "NH": [ + { + "n": "Dept. of Agriculture, Markets & Food - Division of Animal Industry", + "u": "https://www.agriculture.nh.gov/divisions/animal-industry/index.htm" + } + ], + "NJ": [ + { + "n": "Dept. of Agriculture - Division of Animal Health", + "u": "https://www.nj.gov/agriculture/divisions/ah/" + } + ], + "NM": [ + { + "n": "Livestock Board", + "u": "https://www.nmlbonline.com/" + } + ], + "NV": [ + { + "n": "Dept. of Agriculture - Division of Animal Industry", + "u": "https://agri.nv.gov/animals/animal_home/" + } + ], + "NY": [ + { + "n": "Dept. of Agriculture and Markets - Division of Animal Industry", + "u": "https://agriculture.ny.gov/animals" + } + ], + "OH": [ + { + "n": "Dept. of Agriculture - Division of Animal Health", + "u": "https://agri.ohio.gov/divisions/animal-health" + } + ], + "OK": [ + { + "n": "Dept. of Agriculture, Food and Forestry - Animal Industry Services", + "u": "https://ag.ok.gov/divisions/animal-industry-state-veterinarian/" + } + ], + "OR": [ + { + "n": "Dept. of Agriculture - Animal Health and Identification", + "u": "https://www.oregon.gov/oda/animal-health-feeds-livestock-id/pages/default.aspx" + } + ], + "PA": [ + { + "n": "Dept. of Agriculture - Bureau of Animal Health and Diagnostic Services", + "u": "https://www.agriculture.pa.gov/animals/health/Pages/default.aspx" + } + ], + "PR": [ + { + "n": "Oficina Estatal para el Control de Animales (OECA)", + "u": "https://oeca.salud.pr.gov/" + } + ], + "RI": [ + { + "n": "Dept. of Environmental Management - Division of Agriculture and Forest Environment", + "u": "https://dem.ri.gov/natural-resources-bureau/agriculture-and-forest-environment/agriculture/animal-health" + } + ], + "SC": [ + { + "n": "Clemson University - Livestock Poultry Health", + "u": "https://www.clemson.edu/public/lph/" + } + ], + "SD": [ + { + "n": "Animal Industry Board", + "u": "https://aib.sd.gov/" + } + ], + "TN": [ + { + "n": "Dept. of Agriculture - Animal Health", + "u": "https://www.tn.gov/agriculture/animals.html" + } + ], + "TX": [ + { + "n": "Animal Health Commission", + "u": "https://www.tahc.texas.gov/" + } + ], + "UT": [ + { + "n": "Dept. of Agriculture and Food - Division of Animal Industry", + "u": "https://ag.utah.gov/animal-industry/" + } + ], + "VA": [ + { + "n": "Dept. of Agriculture and Consumer Services - Animal and Food Industry Services", + "u": "https://www.vdacs.virginia.gov/animals.shtml" + } + ], + "VT": [ + { + "n": "Agency of Agriculture, Food and Markets - Animal Health", + "u": "https://agriculture.vermont.gov/animal-health" + } + ], + "WA": [ + { + "n": "State Dept. of Agriculture - Animal Health Program", + "u": "https://agr.wa.gov/departments/animals-livestock-and-pets/animal-health" + } + ], + "WI": [ + { + "n": "Dept. of Agriculture, Trade and Consumer Protection - Division of Animal Health", + "u": "https://datcp.wi.gov/Pages/Programs_Services/Programs.aspx" + } + ], + "WV": [ + { + "n": "Dept. of Agriculture - Animal Health", + "u": "https://agriculture.wv.gov/divisions/animal-health/" + } + ], + "WY": [ + { + "n": "Livestock Board", + "u": "https://wlsb.state.wy.us/" + } + ] + } +}