Compare commits

..
Author SHA1 Message Date
DeCentN2Madness 96d3c138f6 refactor(migration): remove obsolete mirror extraction tooling
- delete extraction scripts that depended on the removed local mirror
- remove stale extract npm scripts and migration-brief references
- drop the old mirror ignore rule from .gitignore
- update reports and redirect reasons to use live-source wording
2026-07-13 12:49:58 -07:00
DeCentN2Madness 6648d1d7d5 fix(seo): noindex staging builds by default
- add PUBLIC_ALLOW_INDEXING as an explicit production indexing opt-in
- emit robots meta noindex,nofollow unless indexing is enabled
- generate robots.txt from the same indexing flag
- document staging SEO behavior for migration preview hosts
2026-07-13 12:48:14 -07:00
DeCentN2Madness 9c391c8c05 refactor(component): rename contact obfuscation component 2026-07-02 15:14:51 -07:00
DeCentN2Madness 77b83f5d21 feat(page): match live contact page layout 2026-07-02 15:13:52 -07:00
DeCentN2Madness 8c9c581cc0 feat(brand): add and switch to cog favicon 2026-07-02 13:31:04 -07:00
DeCentN2Madness 46090e9159 chore(docs): remove local mirror references, use live site only
Deleted the www.azinstitute4autism.com mirror directory and scrubbed all
references to it from AGENTS.md, MIGRATION_BRIEF.md, www/README.md, and
www/original/README.md. The live public site is now the sole source of
truth with no local mirror fallback.
2026-07-02 13:31:04 -07:00
DeCentN2Madness fd8e12f925 feat(component): add email obfuscation component 2026-07-02 13:31:04 -07:00
48 changed files with 466 additions and 567 deletions
-3
View File
@@ -58,6 +58,3 @@ temp/
.DS_Store
Thumbs.db
Desktop.ini
# Read-only wget mirror used as migration source material
/www.azinstitute4autism.com/
+2 -26
View File
@@ -383,7 +383,6 @@ www/
rtl.css
tools/
extract-site.mjs
crawl-live-site.mjs
audit-links.mjs
generate-redirects.mjs
@@ -856,39 +855,17 @@ src/pages/es/[...slug].astro
Implement routing in a clean, maintainable way.
## Extraction scripts
## Migration utility scripts
Create:
```txt
tools/extract-site.mjs
tools/crawl-live-site.mjs
tools/audit-links.mjs
tools/generate-redirects.mjs
tools/generate-sitemap.mjs
```
### `extract-site.mjs`
This script should:
- scan the available source and generated output
- identify HTML files
- infer URL paths
- identify page type
- identify blog posts
- identify listing pages
- extract title/meta/H1/body/main content
- extract canonical and Open Graph metadata
- extract schema/JSON-LD where present
- convert blog posts to Markdown where feasible
- create page Markdown where useful
- copy/normalize assets
- rewrite local asset references
- create initial reports
The script does not need to be perfect, but it should be useful and documented.
### `crawl-live-site.mjs`
This script should:
@@ -898,7 +875,7 @@ This script should:
- compare discovered URLs against the current site source and generated output
- document missing URLs/assets
- avoid aggressive crawling
- respect the scope of `www.azinstitute4autism.com`
- respect the live-site scope
### `audit-links.mjs`
@@ -1109,7 +1086,6 @@ Add scripts similar to:
"dev": "astro dev",
"build": "astro check && astro build",
"preview": "astro preview",
"extract": "node tools/extract-site.mjs",
"crawl": "node tools/crawl-live-site.mjs",
"audit:links": "node tools/audit-links.mjs",
"generate:sitemap": "node tools/generate-sitemap.mjs",
+1
View File
@@ -1 +1,2 @@
PUBLIC_AIA_API_BASE=https://api.azinstitute4autism.com
PUBLIC_ALLOW_INDEXING=false
+7 -3
View File
@@ -38,19 +38,23 @@ cd www
npm install
```
Copy the example environment file when testing the like/view counter:
Copy the example environment file when testing the like/view counter or staging
SEO behavior:
```sh
cp .env.example .env
```
The default value is:
The default values are:
```txt
PUBLIC_AIA_API_BASE=https://api.azinstitute4autism.com
PUBLIC_ALLOW_INDEXING=false
```
The site still renders when that API is unavailable.
The site still renders when the AIA API is unavailable. Migration and staging
builds emit `noindex,nofollow` by default; set `PUBLIC_ALLOW_INDEXING=true`
only for production builds on the canonical domain.
## View And Build
-2
View File
@@ -10,8 +10,6 @@
"build": "astro build",
"build:sandbox": "NODE_OPTIONS=--require=./tools/localhost-dns.cjs astro build",
"preview": "astro preview",
"extract": "node tools/extract-fallback.mjs",
"extract:full": "node tools/extract-site.mjs",
"extract:blog-footers": "node tools/extract-blog-footers.mjs",
"crawl:live": "node tools/crawl-live-site.mjs",
"audit:links": "node tools/audit-links.mjs",
+28
View File
@@ -0,0 +1,28 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100"
width="420" height="420">
<defs>
<g id="tooth" transform="translate(50,50)" fill="#ff914d">
<path d="m0 50 h1 q8 0 8 -6 q0 -8 8 -10 h-20"/>
<path d="m0 50 h1 q8 0 8 -6 q0 -8 8 -10 h-20" transform="scale(-1,1)"/>
</g>
</defs>
<g id="gear">
<circle cx="50" cy="50" r="32" stroke="#ff914d" stroke-width="10" fill="none"/>
<g id="teeth">
<use xlink:href="#tooth" transform="rotate( -10 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 35 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 80 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 125 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 170 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 215 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 260 50 50 )"/>
<use xlink:href="#tooth" transform="rotate( 305 50 50 )"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

-3
View File
@@ -1,3 +0,0 @@
User-agent: *
Allow: /
Sitemap: https://www.azinstitute4autism.com/sitemap.xml
+8 -9
View File
@@ -1,14 +1,14 @@
# Cleanup Log
- Replaced HubSpot-generated wrappers, inline styles, scripts, analytics, and modules with reusable Astro components.
- Preserved canonical clean URLs while excluding local mirror query-string and AMP duplicates.
- Preserved canonical clean URLs while excluding HubSpot query-string and AMP duplicates.
- Consolidated responsive `hs-fs` image variants to canonical `hubfs` assets where available.
- Excluded mirrored HubSpot CSS and JavaScript from the new public asset package.
- Self-hosted canonical images, SVGs, fonts, and PDFs from the mirror.
- Excluded HubSpot CSS and JavaScript from the new public asset package.
- Self-hosted canonical images, SVGs, fonts, and PDFs where practical.
- Recreated navigation and footer from structured JSON.
- Recreated visible contact and consultation forms as static accessible HTML.
- Used the source palette: AIA blue `#254080`, dark blue, straw yellow, orange, and teal.
- Used the live site's locally mirrored Playfair Display headings, Rubik body copy, and Caveat accent font.
- Used self-hosted Playfair Display headings, Rubik body copy, and Caveat accent font.
- Added multilingual collection and route structure with Arabic RTL support.
- Kept all extracted source content editable as Markdown.
- Replaced the initial generic rounded-card visual system with live-derived
@@ -21,18 +21,17 @@
- Reorganized the homepage source into named `home:` frontmatter blocks so the
content is easier to inspect and edit without changing the rendered layout.
- Replaced the extracted linear team article with a dedicated live-derived team grid.
- Repaired mirror-rewritten PDF, lightbox, CTA, and relative content links.
- Repaired rewritten PDF, lightbox, CTA, and relative content links.
- Limited language-switcher choices to translations that have generated routes.
- Verified and implemented the current production likes/views API contract.
- Recovered 18 live page-banner assets that the wget mirror missed because
HubSpot injected them through malformed inline `background-image` styles.
- Recovered 18 live page-banner assets that HubSpot injected through malformed
inline `background-image` styles.
- Restored the live library banner and consultation form section background.
- Added a mapped-page-imagery audit to prevent missing visual assets from
silently passing the source link audit.
- Removed duplicated article titles, author/date blocks, and featured images
from all 65 blog Markdown bodies; these elements are rendered by the shared
blog-post layout. Updated both extractors and added a blog-content audit to
prevent recurrence.
blog-post layout. Added a blog-content audit to prevent recurrence.
- Converted 19 FAQ-bearing blog posts to MDX and restored a reusable
`FAQAccordion.astro` component that owns the live-style accordion and
matching `FAQPage` JSON-LD.
+28 -12
View File
@@ -3,9 +3,9 @@
## Status
A maintainable Astro migration and substantial fidelity-first pass are
implemented. The live public site, not the wget mirror, was used as the
authority for the shared shell, English and Spanish homepages, service-page
family, team page, library indexes, and blog-post family.
implemented. The live public site was used as the authority for the shared
shell, English and Spanish homepages, service-page family, team page, library
indexes, and blog-post family.
## Migrated Content and Assets
@@ -15,9 +15,9 @@ family, team page, library indexes, and blog-post family.
- 167 self-hosted images, 20 font files, and two PDF downloads.
- Current June and May 2026 English library articles are included.
The raw mirror remains untouched. HubSpot-generated wrappers, analytics,
scripts, CSS, query-language duplicates, AMP variants, pagination, and author
archive variants are not carried into the Astro implementation.
HubSpot-generated wrappers, analytics, scripts, CSS, query-language duplicates,
AMP variants, pagination, and author archive variants are not carried into the
Astro implementation.
## Live-Source Fidelity Pass
@@ -44,6 +44,9 @@ Implemented fidelity work includes:
- Restored the consultation form section's injected background image.
- Added a dedicated live-derived team card grid rather than presenting the
extracted team content as a generic article.
- Rebuilt the contact page around the live two-column content/form section and
full-width Leaflet map while retaining local clickable obfuscated phone
links.
- Rebuilt library indexes and blog-post presentation around the live sidebar,
article list, byline, featured-image, and counter patterns.
- Converted FAQ-bearing posts to MDX and recreated their live-style accordions
@@ -52,7 +55,7 @@ Implemented fidelity work includes:
links, fawn backgrounds, spacing, rounded presentation, and quote-mark SVG.
- Replaced generic oversized cards, rounded controls, and marketing heroes
with live-derived typography, palette, widths, spacing, and compact controls.
- Corrected material live/mirror discrepancies found during the pass,
- Corrected material live-source discrepancies found during the pass,
including current homepage ESA copy, testimonial content, and ABA copy.
## URLs and Redirects
@@ -77,6 +80,12 @@ backend and spam-protection TODO comments. Submission is intentionally
disabled. Appointment and enrollment calls to action route to the static
consultation page rather than retaining the production Jotform backend.
## External Dependencies
- The contact page map intentionally matches the live Leaflet/OpenStreetMap
implementation and loads Leaflet from `unpkg.com` plus map tiles from
`tile.openstreetmap.org`.
## Multilingual
- Spanish has the full live-derived homepage, translated service pages,
@@ -105,6 +114,9 @@ unavailable.
- Canonicals, Open Graph tags, Twitter card tags, semantic titles, and global
organization JSON-LD are emitted by shared layouts.
- Organization JSON-LD intentionally omits raw `telephone` and `email` fields;
visible contact details use the Astro obfuscation component to avoid exposing
plain email and phone targets in rendered HTML.
- Library-index canonicals and current visible page H1s are explicitly set.
- Extracted image alt text is retained where available.
- Semantic landmarks, skip link, labeled forms, keyboard-operable navigation,
@@ -125,11 +137,15 @@ unavailable.
- `npm run generate:sitemap`: passed; generated 97 URLs.
- `npm run generate:redirects`: passed.
- All migration `.mjs` tools and the sandbox DNS helper pass `node --check`.
- The Astro compiler parsed all 33 `.astro` files successfully.
- The Astro compiler parsed all 49 `.astro` files successfully.
- `npm run build`: blocked before compilation because this autonomous sandbox
denies `/etc/hosts`, causing `getaddrinfo EAI_AGAIN localhost`.
- `npm run build:sandbox`: bypasses that DNS lookup and reaches Vite, then the
sandbox rejects esbuild's required child process with `spawn EPERM`.
cannot resolve `localhost`, causing `getaddrinfo EAI_AGAIN localhost`.
- `npm run build:sandbox`: passed; generated 97 static pages.
- Live contact-page structure was inspected in browser at a 1280px desktop
viewport; local generated HTML was checked for the contact layout, Leaflet
assets, map container, and obfuscated clickable phone script. Local browser
rendering was blocked because the Playwright browser cannot reach shell
loopback servers and blocks `file:` URLs in this environment.
- Nix shell verification and `npm audit` retrieval were blocked by sandbox
proxy/cache network resets.
@@ -141,7 +157,7 @@ final production-build proof.
- Perform full-page desktop and mobile visual comparisons for page-specific
layouts beyond the completed homepage, team, service, library, and article
families.
- Editorially review extracted long-form content for remaining mirror artifacts,
- Editorially review extracted long-form content for remaining extraction artifacts,
stale phone/email references, and heading hierarchy.
- Review all source alt text, keyboard behavior, screen-reader output, and
contrast with accessibility tooling.
+3 -3
View File
@@ -1,4 +1,4 @@
from,to,status,reason
"/aba","/aba-therapy",301,"Brief alias to preserved mirror URL"
"/autismevaluations","/autism-evaluations",301,"Brief alias to preserved mirror URL"
"/learnersocialclub","/learner-social-club",301,"Brief alias to preserved mirror URL"
"/aba","/aba-therapy",301,"Brief legacy alias to preserved URL"
"/autismevaluations","/autism-evaluations",301,"Brief legacy alias to preserved URL"
"/learnersocialclub","/learner-social-club",301,"Brief legacy alias to preserved URL"
1 from to status reason
2 /aba /aba-therapy 301 Brief alias to preserved mirror URL Brief legacy alias to preserved URL
3 /autismevaluations /autism-evaluations 301 Brief alias to preserved mirror URL Brief legacy alias to preserved URL
4 /learnersocialclub /learner-social-club 301 Brief alias to preserved mirror URL Brief legacy alias to preserved URL
+2 -2
View File
@@ -18,8 +18,8 @@
## Cleanup
- HubSpot `?hsLang=...` mirror duplicates are excluded from generated routes.
- AMP mirror variants are excluded because Astro pages are responsive and static.
- HubSpot `?hsLang=...` duplicates are excluded from generated routes.
- AMP variants are excluded because Astro pages are responsive and static.
- Blog pagination and author archive variants are not preserved as separate generated pages.
- Brief aliases `/aba`, `/autismevaluations`, and `/learnersocialclub` redirect to preserved source URLs.
+84
View File
@@ -0,0 +1,84 @@
---
import Button from './Button.astro';
import ContactObfuscation from './ContactObfuscation.astro';
interface Props {
title?: string;
}
const { title = 'Contact AIA' } = Astro.props;
---
<form class="contact-form-card" method="post" action="">
<!-- TODO: Wire this form to Netlify Forms, a custom API endpoint, or another backend service. -->
<!-- TODO: Add spam protection before production launch. -->
<h3 class="contact-form-card__title">{title}</h3>
<div class="contact-form-card__fields">
<label>Parent or guardian name<input name="name" autocomplete="name" /></label>
<label>Email<input type="email" name="email" autocomplete="email" /></label>
<label>Phone<input type="tel" name="phone" autocomplete="tel" /></label>
<label>Service of interest<select name="service"><option>ABA Therapy</option><option>Autism Evaluation</option><option>Learner Social Club</option><option>Other</option></select></label>
<label>How can we help?<textarea name="message" rows="5"></textarea></label>
<Button type="submit" disabled aria-describedby="contact-form-note" class="contact-form-card__submit">Submit</Button>
<p id="contact-form-note" class="contact-form-card__note">Online submission is not yet connected. Please call <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />.</p>
</div>
</form>
<style>
.contact-form-card {
background: white;
border-radius: 4px;
box-shadow: 0 4px 4px 0 rgb(81 81 81 / 4%), 0 4px 16px 0 rgb(81 81 81 / 8%);
margin: 0 auto;
max-width: 600px;
overflow: hidden;
}
.contact-form-card__title {
border-bottom: 1px solid #f1f1f1;
margin: 0;
padding: 1.2rem 1.5rem;
text-align: center;
}
.contact-form-card__fields {
display: grid;
gap: 1rem;
padding: 1.5rem;
}
.contact-form-card label {
display: grid;
font-weight: 700;
gap: .4rem;
}
.contact-form-card input,
.contact-form-card select,
.contact-form-card textarea {
background: #fcf9f5;
border: 1px solid var(--color-border);
border-radius: 4px;
color: var(--color-primary);
font: inherit;
padding: .8rem;
width: 100%;
}
.contact-form-card textarea {
resize: vertical;
}
.contact-form-card :global(.contact-form-card__submit) {
justify-self: center;
max-width: 375px;
width: 100%;
}
.contact-form-card__note {
color: var(--color-muted);
font-size: .9rem;
margin: 0;
text-align: center;
}
</style>
@@ -0,0 +1,58 @@
---
interface Props {
email?: string;
phone?: string;
hrefValue?: string;
linkText?: string;
obfuscatedText?: string;
class?: string;
id?: string;
}
const {
email,
phone,
hrefValue,
linkText,
obfuscatedText,
class: className,
id
} = Astro.props;
if ((email ? 1 : 0) + (phone ? 1 : 0) !== 1) {
throw new Error('ContactObfuscation requires exactly one of email or phone.');
}
const contactType = email ? 'email' : 'phone';
const sourceValue = email ?? phone ?? '';
const linkValue = linkText ?? sourceValue;
const fallbackText = obfuscatedText ?? sourceValue;
const protocol = email ? 'mailto' : 'tel';
const telHrefValue = hrefValue ?? sourceValue.replace(/[^\d+]/g, '');
const targetValue = email ? (hrefValue ?? sourceValue) : telHrefValue;
const encode = (value: string) => Array.from(value, (character) => character.charCodeAt(0));
const encodedTarget = encode(targetValue);
const encodedText = encode(linkValue);
const spanId = id ?? `${contactType}-obfuscation-${Math.random().toString(36).slice(2, 10)}`;
---
<span id={spanId} class={className}>{fallbackText}</span><script type="text/javascript" define:vars={{ encodedTarget, encodedText, protocol, spanId }}>
(() => {
const contactSpan = document.getElementById(spanId);
if (!contactSpan) return;
const decode = (encodedValue) => encodedValue
.map((characterCode) => String.fromCharCode(characterCode))
.join('');
const target = decode(encodedTarget);
const text = decode(encodedText);
const link = document.createElement('a');
link.href = `${protocol}:${target}`;
link.textContent = text;
contactSpan.textContent = '';
contactSpan.appendChild(link);
})();
</script>
-32
View File
@@ -1,32 +0,0 @@
---
interface Props {
email: string;
obfuscatedText?: string;
class?: string;
id?: string;
}
const {
email,
obfuscatedText = email,
class: className,
id
} = Astro.props;
const spanId = id ?? `email-obfuscation-${Math.random().toString(36).slice(2, 10)}`;
---
<span id={spanId} class={className}>{obfuscatedText}</span><script type="text/javascript" define:vars={{ email, spanId }}>
(() => {
const emailSpan = document.getElementById(spanId);
if (!emailSpan) return;
const link = document.createElement('a');
link.href = `mailto:${email}`;
link.textContent = email;
emailSpan.textContent = '';
emailSpan.appendChild(link);
})();
</script>
+3 -2
View File
@@ -1,4 +1,5 @@
---
import ContactObfuscation from './ContactObfuscation.astro';
import site from '../data/site.json';
const year = new Date().getFullYear();
const { lang = 'en' } = Astro.props;
@@ -31,11 +32,11 @@ const labels = lang === 'es'
<div class="footer-contact">
<div class="footer-contact-item">
<img src="/assets/images/phone-blue.svg" alt="" aria-hidden="true" width="24" height="24" />
<a href={`tel:${site.phone.replace(/\D/g, '')}`}>{site.phone}</a>
<ContactObfuscation phone={site.phone} hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
</div>
<div class="footer-contact-item">
<img src="/assets/images/envelope-blue.svg" alt="" aria-hidden="true" width="24" height="24" />
<a href={`mailto:${site.email}`}>{site.email}</a>
<ContactObfuscation email={site.email} obfuscatedText="moc.msitua4etutitsniza@ofni" />
</div>
<div class="footer-contact-item">
<img src="/assets/images/location-blue.svg" alt="" aria-hidden="true" width="24" height="24" />
+2 -1
View File
@@ -1,5 +1,6 @@
---
import Button from './Button.astro';
import ContactObfuscation from './ContactObfuscation.astro';
const { title = 'Schedule Your Free Consultation', showBackground = false } = Astro.props;
---
<section class:list={['form-section', { 'form-section-background': showBackground }]} aria-labelledby="form-title">
@@ -14,7 +15,7 @@ const { title = 'Schedule Your Free Consultation', showBackground = false } = As
<label>Service of interest<select name="service"><option>ABA Therapy</option><option>Autism Evaluation</option><option>Learner Social Club</option><option>Other</option></select></label>
<label>How can we help?<textarea name="message" rows="5"></textarea></label>
<Button type="submit" disabled aria-describedby="form-note">Submit</Button>
<p id="form-note" class="form-note">Online submission is not yet connected. Please call (480) 687-7099.</p>
<p id="form-note" class="form-note">Online submission is not yet connected. Please call <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />.</p>
</form>
</div>
</section>
+12 -1
View File
@@ -6,12 +6,23 @@ interface Props {
image?: string;
lang?: string;
type?: 'website' | 'article';
robots?: string;
}
const { title, description = '', canonical, image, lang = 'en', type = 'website' } = Astro.props;
const {
title,
description = '',
canonical,
image,
lang = 'en',
type = 'website',
robots
} = Astro.props;
const defaultRobots = import.meta.env.PUBLIC_ALLOW_INDEXING === 'true' ? 'index,follow' : 'noindex,nofollow';
const absoluteImage = image ? new URL(image, Astro.site ?? Astro.url).href : undefined;
---
<title>{title}</title>
<meta name="description" content={description} />
<meta name="robots" content={robots ?? defaultRobots} />
{canonical && <link rel="canonical" href={canonical} />}
<meta property="og:type" content={type} />
<meta property="og:title" content={title} />
+111
View File
@@ -0,0 +1,111 @@
---
import ContactForm from '../ContactForm.astro';
interface Props {
title: string;
}
const { title } = Astro.props;
---
<section class="contact-page">
<div class="container contact-page__grid">
<div class="contact-page__copy">
<slot />
</div>
<ContactForm title={title} />
</div>
</section>
<section class="contact-map-section" aria-label="Map showing Arizona Institute for Autism in Scottsdale">
<div id="aia-contact-map" class="contact-map"></div>
</section>
<script is:inline src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script is:inline>
(() => {
const mapElement = document.getElementById('aia-contact-map');
if (!mapElement || !window.L) return;
const map = window.L.map(mapElement).setView([33.63, -111.88756], 12);
const tiles = window.L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
maxZoom: 19
});
tiles.on('tileload', (event) => {
if (event.tile && !event.tile.alt) {
event.tile.alt = 'OpenStreetMap tile showing area around Arizona Institute for Autism in Scottsdale, AZ';
}
});
tiles.addTo(map);
window.L.marker([33.617746, -111.88756]).addTo(map).bindPopup(`
<span style="display:block;text-align:center">
<b>Arizona Institute for Autism</b><br>
<img alt="Arizona Institute for Autism" src="/assets/images/aia-logo.svg" width="94">
</span>
`).openPopup();
})();
</script>
<style>
.contact-page {
background: white;
padding-block: 100px;
}
.contact-page__grid {
align-items: start;
display: grid;
gap: clamp(2rem, 5vw, 4rem);
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.contact-page__copy {
color: var(--color-primary);
text-align: left;
}
.contact-page__copy :global(h2) {
margin: 0 0 25px;
}
.contact-page__copy :global(p) {
margin: 0 0 25px;
}
.contact-page__copy :global(.content-button) {
margin-bottom: 40px;
}
.contact-map-section {
background: white;
margin: 0;
padding: 0;
}
.contact-map {
height: 360px;
width: 100%;
}
@media (max-width: 992px) {
.contact-page {
padding-block: 50px;
}
}
@media (max-width: 767px) {
.contact-page__grid {
grid-template-columns: 1fr;
}
.contact-page__copy :global(.content-button) {
display: table;
margin-inline: auto;
}
}
</style>
+2 -2
View File
@@ -1,6 +1,6 @@
---
import Button from '../Button.astro';
import EmailObfuscation from '../EmailObfuscation.astro';
import ContactObfuscation from '../ContactObfuscation.astro';
const groups = [
{
title: 'Board Certified Behavioral Analysts & Psychologists',
@@ -69,7 +69,7 @@ const groups = [
<p class="eyebrow">Join the AIA Team!</p>
<h2>Employment Opportunities</h2>
<p>Apply online to join the AIA team! At the Arizona Institute for Autism (AIA) we are on a mission to improve special education and strengthen communities throughout Arizona.</p>
<p>Our talented and passionate employees are a critical piece to the high-quality ABA therapy we provide along with our commitment to behavioral health, school solutions, and community outreach. For questions regarding open positions, please contact us at <EmailObfuscation email="hr@azinstitute4autism.com" obfuscatedText="hr [at] abaclinicaz [dot] com" class="team-email" />.</p>
<p>Our talented and passionate employees are a critical piece to the high-quality ABA therapy we provide along with our commitment to behavioral health, school solutions, and community outreach. For questions regarding open positions, please contact us at <ContactObfuscation email="hr@azinstitute4autism.com" obfuscatedText="hr [at] abaclinicaz [dot] com" class="team-email" />.</p>
<Button href="/careers">View Open Positions</Button>
</div>
</section>
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Applied Behavior Analysis (ABA) is an evidence-based approach that utilizes the principles of behavior analysis to modify maladaptive behaviors and enhance socially significant behaviors. It is often employed for children with Autism Spectrum Disorder (ASD). ABA aims to teach a variety of skills, such as communication, social interactions, and daily skills, all within a safe, engaging, and fun environment conducive to therapeutic success. It also helps children minimize disruptive behaviors that may impede their learning, while prioritizing the child's individual autonomy.
ABA therapy is highly beneficial for children with an autism diagnosis because it is tailored to each child's unique needs, optimizing outcomes. Skills are taught in a manner that maximizes the child's receptivity; this is achieved by breaking the skills into smaller, achievable targets. Furthermore, skills are generalized by teaching them across different settings and individuals. When a child begins receiving ABA therapy, progress is continuously monitored through ongoing data collection. This data is analyzed to ensure the efficacy of the intervention, and observations are made to guarantee that the child is learning effectively. An essential goal of ABA is not just short-term behavioral modification, but also the generalization and maintenance of learned skills over time. ABA therapy provides tools for managing challenging behaviors such as aggression, self-injury, or disruption. By understanding the function of these behaviors, suitable replacements can be taught, aiding the child's overall improvement.
@@ -21,4 +23,4 @@ Parental participation is a critical component of ABA Therapy. Parents and careg
At the Arizona Institute for Autism, our focus is on the learner. Our services are designed to meet individual needs, working collaboratively with a team of clinicians and parents. This approach aims to improve the quality of life for children with autism and their families.
For more information on ABA therapy services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com). If you are looking for more applied behavioral analysis and ASD diagnosis tips, check out [more articles](../library) from AIAs clinical director, Rula Diab!
For more information on ABA therapy services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more applied behavioral analysis and ASD diagnosis tips, check out [more articles](../library) from AIAs clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Toilet training a child with autism can be a complex process that requires patience, consistency, and tailored strategies. Applied Behavior Analysis (ABA) offers effective techniques to support this journey. Here's a structured approach to facilitate toilet training for children with autism:
## 1. Assess Readiness
@@ -61,4 +63,4 @@ Engage with therapists or specialists experienced in ABA to develop and refine y
By implementing these strategies with patience and consistency, you can support your child with autism in achieving successful toilet training.
For more information about ABA therapy services offered by AIA or to book your free consultation, visit our Arizona Institute for Autism website at [www.azinstitute4autism.com](../index), call [(480) 687-7099](tel:+14806877099), or email info@azinstitute4autism.com. If you are looking for more applied behavioral analysis and ASD diagnosis and treatment tips, check out more [blog posts](../library) from AIAs clinical director, Rula Diab!
For more information about ABA therapy services offered by AIA or to book your free consultation, visit our Arizona Institute for Autism website at [www.azinstitute4autism.com](../index), call <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more applied behavioral analysis and ASD diagnosis and treatment tips, check out more [blog posts](../library) from AIAs clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Hello AIA families,
Happy April, a.k.a World Autism Month!
@@ -45,4 +47,4 @@ Celebrate and support neurodiversity and inclusion with our AIA family by attend
## Additional questions?
If you have questions about AIA's World Autism Community Day of Celebration or are interested in providing a sensory-friendly booth, contact kelly@azinstitute4autism.com.
If you have questions about AIA's World Autism Community Day of Celebration or are interested in providing a sensory-friendly booth, contact <ContactObfuscation email="kelly@azinstitute4autism.com" obfuscatedText="kelly [at] azinstitute4autism [dot] com" />.
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
As a parent of a child diagnosed with [autism spectrum disorder](https://www.autismspeaks.org/what-autism), you may experience challenges when it comes to mealtime. Many children on the spectrum struggle with [sensory and food sensitivities](https://www.eatright.org/health/health-conditions/intellectual-and-developmental-disabilities/nutrition-for-your-child-with-autism-spectrum-disorder-asd), making it difficult to find foods that they prefer. With Thanksgiving quickly approaching, you may be wondering if there are any recipes that your child may enjoy for the holiday. Well good news- you are in the right place! For this months blog, I will be sharing an easy, delicious Thanksgiving recipe that you and your child will both enjoy- Pumpkin Dump Cake! This recipe is a great twist on the classic pumpkin pie.
This recipe is super easy to follow, and the best part is that your child can help you! Being able to help prepare their food often makes it more enticing for them to eat. It also teaches your child [fundamental life skills](https://www.purdueglobal.edu/blog/psychology/cooking-activities-help-children-autism/), such as how to cook, make decisions, and how to follow directions, as well as strengthening their fine motor skills. Giving your child the opportunity to help you with this recipe can make it more enjoyable for the both of you!
@@ -73,4 +75,4 @@ If you have any leftovers, they can be stored for up to 4 days in the fridge or
This recipe is simple, easy, and delicious! We hope that you encourage your child to help you prepare the cake and have a great time while doing it. Enjoy!
For more information on aba therapy services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at (480) 707-2195, or email info@azinstitute4autism.com. If you are looking for more applied behavioral analysis and asd diagnosis tips, check out AIAs clinical director, Rula Diab, monthly [blog posts](../library)!
For more information on aba therapy services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at <ContactObfuscation phone="(480) 707-2195" hrefValue="+14807072195" obfuscatedText="5912-707 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more applied behavioral analysis and asd diagnosis tips, check out AIAs clinical director, Rula Diab, monthly [blog posts](../library)!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
If you are the parent of a child with autism spectrum disorder, you probably know how difficult mealtime can be. Many children with an autism diagnosis tend to have sensory processing disorder, which can make it difficult for them to enjoy certain foods due to the texture, consistency, temperature, etc. Your child may only prefer a small variety of foods, which often leaves their diet lacking the essential nutrients they need. In this blog series, I will be sharing sensory-friendly recipes and tips to hopefully make mealtime easy and fun for your kiddo! These recipes will help broaden your childs horizons when it comes to food and nutrition.
For this blog, I will be sharing an easy recipe that most kids love- Pancakes! This recipe can be modified to your childs liking, as its important to gradually introduce new foods and ingredients. Use ingredients that your child prefers in order to make it more enticing and enjoyable!
@@ -89,4 +91,4 @@ Your kiddo may not want plain pancakes, which is perfectly fine! There are a few
Feel free to vary the recipe however you want and give your kiddo the opportunity to choose which toppings or fillings they want. Mealtime should be fun and enjoyable, so allowing your child to help you or pick their preferred ingredients can make eating more enticing for them. I hope you and your child enjoy this sensory-friendly pancake recipe!
For more information on aba therapy services offered by AIA or to book your free consultation, visit our [contact page](../contact), call us at [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com). If you are looking for more applied behavioral analysis and asd diagnosis tips, check out AIAs clinical director, Rula Diab, monthly blog posts!
For more information on aba therapy services offered by AIA or to book your free consultation, visit our [contact page](../contact), call us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more applied behavioral analysis and asd diagnosis tips, check out AIAs clinical director, Rula Diab, monthly blog posts!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Children may exhibit maladaptive behaviors for various reasons, often driven by underlying functions. To address these behaviors, Applied Behavior clinicians use the [ABC model](https://www.iidc.indiana.edu/irca/articles/observing-behavior-using-a-b-c-data), a structured approach that helps identify patterns and triggers. This model involves observing a child's behavior in their natural environment, focusing on three key elements: the Antecedent (what happens before the behavior), the Behavior itself, and the Consequence (what follows the behavior). By carefully analyzing these factors, clinicians gain insights into why certain behaviors occur.
Once the ABC data is collected, it guides clinicians in developing effective intervention strategies. By understanding the cause-and-effect relationship of behaviors, they can implement targeted consequences that reduce maladaptive behaviors while reinforcing positive alternatives. This process not only helps in decreasing unwanted actions but also encourages healthier coping mechanisms, fostering long-term behavioral improvements.
@@ -35,6 +37,6 @@ BCBA clinicians use [consequence interventions](https://specialconnections.ku.ed
## Looking for more information?
For more information on services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at [(480) 687-7099](tel:+14806877099), or email [hello@azinstitute4autism.com](mailto:hello@azinstitute4autism.com).
For more information on services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="hello@azinstitute4autism.com" obfuscatedText="hello [at] azinstitute4autism [dot] com" />.
If you are looking for tips, check out more monthly [blog posts](../library) from AIA's clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Critical thinking is an important skill our children need to learn. It enables a child to generate the ability for a higher level of conceptual thinking and appropriately solve problems in their everyday life.
Children with Autism may lack the ability to appropriately communicate their feelings or sometimes tend to engage in repetitive ways of rigid thinking and repetitive behaviors. When they are faced with challenges, moments of frustration occur when no clear expectations are provided to our children regarding their daily schedule, and the activities they are required to engage in throughout the day.
@@ -33,4 +35,4 @@ Applied Behavior Analysis is “*the process of systematically applying interven
It is okay for our children to make errors, however, as caregivers and parents, we should create learning opportunities for our children, give them the emotional support they need and model proper responses.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to [hello@azinsitute4autism.com](mailto:hello@azinsitute4autism.com) or contact us directly from our contact page.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to <ContactObfuscation email="hello@azinsitute4autism.com" obfuscatedText="hello [at] azinsitute4autism [dot] com" /> or contact us directly from our contact page.
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Generalization is a crucial aspect of learning, ensuring that skills and behaviors extend beyond the initial learning environment. Without generalization, individuals may struggle to apply what theyve learned to new situations, limiting the effectiveness of their education or therapy.
In behavioral science, generalization refers to the process by which a learned behavior is applied across different stimuli, responses, or settings.
@@ -39,6 +41,6 @@ Understanding the three primary types of generalization—stimulus, response, an
## Looking for more information?
For more information on services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com).
For more information on services offered by AIA, or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />.
If you are looking for more tips, check our [monthly blog posts](../library) from AIAs clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
The Arizona Institute for Autism is thrilled to announce its transformation into an Integrated ABA Model, an approach that brings together clinicians and educators to provide exceptional intervention, learning experience, and individualized support for learners with autism. This model incorporates evidence-based interventions, ensuring a comprehensive approach tailored to each child's abilities, interests, and learning style.
By doing so, we aim to create a more effective and engaging treatment and learning process, addressing the diverse needs of each of our learners. Research has consistently demonstrated that effective ABA therapy can profoundly and positively impact children with autism, leading to long-lasting improvements in various areas of development. The Integrated ABA Model comprehensive nature enhances the potential for long-lasting improvements in various areas of development.
@@ -25,4 +27,4 @@ Our approach focuses on teaching children to apply the skills they acquire durin
At the Arizona Institute for Autism, we are committed to empowering children with autism, maximizing their potential for growth and development, and paving the way for a brighter future filled with endless possibilities.
For more information on services offered by AIA or to book your free consultation, visit [https://www.azinstitute4autism.com](../index), call us at [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com). If you are looking for more tips, check out monthly [blog posts](../library) from AIA's clinical director, Rula Diab!
For more information on services offered by AIA or to book your free consultation, visit [https://www.azinstitute4autism.com](../index), call us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more tips, check out monthly [blog posts](../library) from AIA's clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Children with Autism may have difficulty communicating their needs and wants. Struggling to express their needs and escaping an unpreferred situation can all result in a child feeling frustrated. Not having control over their environment can often lead to aggressive and unengaging behaviors.
Communicating with our kiddos when they are engaging in challenging behaviors can be difficult. It is important to understand that behaviors are just an expression of distress. Understanding the function of your child's aggressive behavior can direct you to determine the proper ways to support your kiddos to communicate those needs appropriately and functionally.
@@ -33,4 +35,4 @@ The best strategy for parents to manage aggressive behavior outburst are the fol
- Use less verbal interaction and use more visuals to help to de-escalate the challenging behavior.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to hello@azinsitute4autism.com or contact us directly at www.azinstitute4autism.com/contactus.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to <ContactObfuscation email="hello@azinsitute4autism.com" obfuscatedText="hello [at] azinsitute4autism [dot] com" /> or contact us directly at www.azinstitute4autism.com/contactus.
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
The start of a new year is often associated with feelings of joy and optimism. It is also a time for parents of children with autism to reflect on their child's progress and set goals for the future. Here are some important factors to keep in mind for New Year 2024 when it comes to Applied Behavioral Analysis (ABA) therapy and holiday time:
- Take the time to review and celebrate the progress your child has made in the past year. Discuss any areas that need further development with your licensed Behavioral Analyst (BCBA) to ensure continued growth.
@@ -25,4 +27,4 @@ The start of a new year is often associated with feelings of joy and optimism. I
The New Year symbolizes growth and development, and that applies to children with autism as well. Take this time to reflect, plan, and prioritize the well-being of both the child receiving therapy and their caregivers.
For more information about ABA therapy services offered by AIA or to book your free consultation, visit our Arizona Institute for Autism website at [www.azinstitute4autism.com](../index), call [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com). If you are looking for more applied behavioral analysis and ASD diagnosis and treatment tips, check out more [blog posts](../library) from AIAs clinical director, Rula Diab!
For more information about ABA therapy services offered by AIA or to book your free consultation, visit our Arizona Institute for Autism website at [www.azinstitute4autism.com](../index), call <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more applied behavioral analysis and ASD diagnosis and treatment tips, check out more [blog posts](../library) from AIAs clinical director, Rula Diab!
@@ -13,8 +13,10 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
The New Year can be challenging for children with Autism and may lead to distress. Different factors affect the ability of children with autism to cope during those times. The inability to deal with changes and disruption in routines and cope with unclear expectations can be stressful. Additionally, sensory overload including loud music, bright lighting, strong smells, etc. may cause children to feel uncomfortable.
To decrease stress as much as possible during the holiday time, it is important to provide your child with visual Support (i.e., visual schedules) for the purpose of preparing your child to manage changes in schedule and minimize disruption and triggers. Additionally, social stories can be great visual support to clarify activities and events during the holidays along with using a reinforcer system (i.e., Token System) to handle behaviors before it happens.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to [hello@azinsitute4autism.com](mailto:hello@azinsitute4autism.com) or contact us directly from our contact page.
For more information on how to manage aggressive behavior and proper communication, connect with us by sending your messages and questions to <ContactObfuscation email="hello@azinsitute4autism.com" obfuscatedText="hello [at] azinsitute4autism [dot] com" /> or contact us directly from our contact page.
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Children can get overwhelmed, frustrated, and sensory overloaded at any time because of different needs, which can result in a child engaging in challenging behaviors. However, the common thing among all children is that they are in need of support with their communication skills. Different strategies can be used to prevent or minimize challenging behavior, including:
- Being proactive, instead of reacting to a childs behavior. Using proactive strategies that have the most significant impact can help in teaching children the skills they need to convey their needs/wants. Proactive strategies include Communication Skills, Coping Skills, Manding Skills, Attending Skills, etc. Waiting until the child is engaging in disruptive behaviors and then intervening is not beneficial, nor teachable for the child.
@@ -23,4 +25,4 @@ Children can get overwhelmed, frustrated, and sensory overloaded at any time bec
- Allow your child to make a choice in the type of the activity or task presented (e.g., “Do you want to read a book about a cat or a book about a dog?”) or the place to engage in the activity (e.g. “Do you like to play with Lego inside or outside”).
For more information on how to reduce behavior management, schedule a consultation with us today at [(480) 687-7099](tel:+14806877099) or reach out via our [contact form](../contact).
For more information on how to reduce behavior management, schedule a consultation with us today at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" /> or reach out via our [contact form](../contact).
@@ -13,10 +13,12 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Children with autism often engage in repetitive behaviors as a way to cope with sensory overload. These behaviors can manifest in various ways including repetitive motor behaviors. Repetitive behaviors can affect children's learning experiences, social interactions, and engagement in daily activities.
As each child is unique and often requires tailored support, it's essential to comprehend each childs needs and preferences also offering alternative and functional ways for them to engage in these behaviors. Such adaptations can help children be more engaged throughout the day, as well as offer them a constructive outlet for emotional expression and self-soothing. The goal is to teach a child alternative behaviors that serve the same purpose but are more adaptable during structured environments. This allows for better focus and participation during activities requiring attention, while still giving them the coping mechanisms they need. Changing their surroundings can also reduce triggers that lead to repetitive behaviors. Therefore, it helps children to regulate their emotions in a socially acceptable manner, while still meeting their behavioral and emotional needs.
## Looking for more information?
For more information on services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at [(480) 687-7099](tel:+14806877099), or email [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com). If you are looking for more tips, check out monthly [blog posts](../library) from AIAs clinical director, Rula Diab!
For more information on services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), contact us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />. If you are looking for more tips, check out monthly [blog posts](../library) from AIAs clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Parenting a child with autism is an immensely rewarding experience, but it also presents unique challenges. To provide the best support, it is crucial to navigate our children's development while comprehending their needs.
In this blog post, we will delve into essential insights and practical tips to help parents better understand and support their child with autism.
@@ -41,6 +43,6 @@ Remember, each child with autism is unique. By educating yourself, seeking suppo
## Looking for more information?
For more information on services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), call us at [(480) 687-7099](tel:+14806877099), or email [hello@azinstitute4autism.com](mailto:hello@azinstitute4autism.com).
For more information on services offered by AIA or to book your free consultation, visit us at [https://www.azinstitute4autism.com](../index), call us at <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, or email <ContactObfuscation email="hello@azinstitute4autism.com" obfuscatedText="hello [at] azinstitute4autism [dot] com" />.
If you are looking for more tips, check out [monthly blog posts](../library) from AIAs clinical director, Rula Diab!
@@ -13,6 +13,8 @@ tags: []
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
La generalización es un aspecto crucial del aprendizaje, ya que garantiza que las habilidades y los comportamientos se extiendan más allá del entorno de aprendizaje inicial. Sin generalización, los individuos pueden tener dificultades para aplicar lo que han aprendido a nuevas situaciones, limitando la efectividad de su educación o terapia.
En la ciencia del comportamiento, la generalización se refiere al proceso por el cual una conducta aprendida se aplica a diferentes estímulos, respuestas o entornos.
@@ -39,6 +41,6 @@ Comprender los tres tipos principales de generalización (estímulo, respuesta y
## ¿Buscas más información?
Para más información sobre los servicios ofrecidos por AIA, o para reservar su consulta gratuita, visítenos en [https://www.azinstitute4autism.com](../../index), contáctenos en [(480) 687-7099](tel:+14806877099), o correo electrónico [info@azinstitute4autism.com](mailto:info@azinstitute4autism.com).
Para más información sobre los servicios ofrecidos por AIA, o para reservar su consulta gratuita, visítenos en [https://www.azinstitute4autism.com](../../index), contáctenos en <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />, o correo electrónico <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />.
Si buscas más consejos, ¡consulta las [publicaciones mensuales de nuestro blog](../../library) de la directora clínica de AIA, Rula Diab!
@@ -7,24 +7,25 @@ lang: "en"
translationKey: "contact"
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
import Button from '../../../components/Button.astro';
## Schedule a Tour
Reach out to one of our expert Client Advocates to see if ABA-integrated therapy is right for your learner.
Schedule Tour
<Button href="/tour">Schedule Tour</Button>
## Reach Out
If you have questions about any of our services and programs, feel free to use the contact form on this page. You may also call or email using the following information.
General Inquiries: 9907-786 (084)
**General Inquiries:** <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
Billing Inquiries: 9240-807 (206)
**Billing Inquiries:** <ContactObfuscation phone="(602) 708-0429" hrefValue="+16027080429" obfuscatedText="9240-807 (206)" />
Email: moc.msitua4etutitsniza@ofni
**Email:** <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="moc.msitua4etutitsniza@ofni" />
Address: 8901 E. Raintree Drive, St #160 Scottsdale, AZ 85260
**Address:** 8901 E. Raintree Drive, St #160 Scottsdale, AZ 85260
Hours: Monday - Friday, 8am - 6pm MST
### Contact AIA
**Hours:** Monday - Friday, 8am - 6pm MST
@@ -7,6 +7,8 @@ lang: "en"
translationKey: "donate-autism-giveback"
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
## Ways to Giveback
Every act of generosity, no matter how big or small, makes a difference at the Arizona Institute for Autism. Whether you donate items directly, support our Amazon iGiveback Wishlist, or contribute to our 501(c)(3) public charity, your support helps improve the lives of children with autism, their families, and the communities around them.
@@ -21,7 +23,7 @@ Visit AIAs Amazon iGiveback Wishlist today, pick an item (or two!), and help
### Host a Fundraiser
Would you like to host a fundraiser to benefit the Arizona Institute for Autism? Please contact us at moc.msitua4etutitsniza@ofni and a member of our team will be in touch to discuss next steps! All potential third party events will be evaluated in terms of their alignment with Arizona Institute for Autism's mission.
Would you like to host a fundraiser to benefit the Arizona Institute for Autism? Please contact us at <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="moc.msitua4etutitsniza@ofni" /> and a member of our team will be in touch to discuss next steps! All potential third party events will be evaluated in terms of their alignment with Arizona Institute for Autism's mission.
## Community 4 Autism
@@ -9,7 +9,7 @@ draft: false
---
import Button from '../../../components/Button.astro';
import EmailObfuscation from '../../../components/EmailObfuscation.astro';
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
## Learner Social Club
@@ -51,6 +51,6 @@ The Learner Social Club currently serves youth learners aged 817 years. Our f
Interested in Trying Out Our Program?
Get a 1-Day Trial Pass by emailing us at <EmailObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />.
Get a 1-Day Trial Pass by emailing us at <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />.
<Button href="https://form.jotform.com/231638510219149" target="_blank" center>Enroll Now</Button>
@@ -7,6 +7,8 @@ lang: "en"
translationKey: "privacy-policy"
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
Last updated: February 19, 2025
## Introduction
@@ -123,10 +125,10 @@ We may update this privacy policy periodically. All changes will be posted on th
If you have any questions or concerns about this privacy policy or wish to exercise your privacy rights, please contact us at:
- Email: info@azinstitute4autism.com
- Email: <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />
- Address: 8901 E. Raintree Drive, Suite #160 Scottsdale Arizona 85260
- Phone: [(480) 687-7099](tel:+14806877099)
- Phone: <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
By using our website and services, you confirm that you have read, understood, and agree to the terms outlined in this privacy policy.
@@ -7,6 +7,8 @@ lang: "en"
translationKey: "schedule-consultation"
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
We're here for you!
A Client Advocate from Arizona Institute for Autism will contact you within one business day upon submission of this form.
@@ -37,13 +39,13 @@ Along with ABA therapy for children and teens diagnosed with autism, our support
![stem sped tech kids](/assets/images/stem-sped-tech-kids_446x.jpg) ![call us to schedule a free consultation](/assets/images/phone-1.svg) Phone
(480) 687-7099
<ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
![email for more information](/assets/images/mail.svg)
Email
info@abaclinicaz.com
<ContactObfuscation email="info@abaclinicaz.com" obfuscatedText="info [at] abaclinicaz [dot] com" />
![located in scottsdale](/assets/images/map-pin.svg)
@@ -7,24 +7,25 @@ lang: "es"
translationKey: "contact"
draft: false
---
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
import Button from '../../../components/Button.astro';
## Programar una visita
Comuníquese con uno de nuestros defensores de clientes expertos para ver si la terapia integrada con ABA es adecuada para su alumno.
Programar visita
<Button href="/tour">Programar visita</Button>
## Comunícate
Si tiene preguntas sobre alguno de nuestros servicios y programas, no dude en utilizar el formulario de contacto de esta página. También puede llamar o enviar un correo electrónico utilizando la siguiente información.
Consultas generales: 9907-786 (084)
**Consultas generales:** <ContactObfuscation phone="(480) 687-7099" hrefValue="+14806877099" obfuscatedText="9907-786 (084)" />
Consultas de facturación: 9240-807 (206)
**Consultas de facturación:** <ContactObfuscation phone="(602) 708-0429" hrefValue="+16027080429" obfuscatedText="9240-807 (206)" />
Correo electrónico: moc.msitua4etutitsniza@ofni
**Correo electrónico:** <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="moc.msitua4etutitsniza@ofni" />
Dirección: 8901 E. Raintree Drive, St #160 Scottsdale, AZ 85260
**Dirección:** 8901 E. Raintree Drive, St #160 Scottsdale, AZ 85260
Horas: Lunes - Viernes, 8am - 6pm MST
### Contactar a AIA
**Horas:** Lunes - Viernes, 8am - 6pm MST
@@ -9,7 +9,7 @@ draft: false
---
import Button from '../../../components/Button.astro';
import EmailObfuscation from '../../../components/EmailObfuscation.astro';
import ContactObfuscation from '../../../components/ContactObfuscation.astro';
## Learner Social Club
@@ -59,6 +59,6 @@ Learner Social Club actualmente atiende a jóvenes de 8 a 17 años. Nuestras opc
¿Le interesa probar nuestro programa?
Obtenga un pase de prueba de 1 día enviándonos un correo electrónico a <EmailObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />.
Obtenga un pase de prueba de 1 día enviándonos un correo electrónico a <ContactObfuscation email="info@azinstitute4autism.com" obfuscatedText="info [at] azinstitute4autism [dot] com" />.
<Button href="https://form.jotform.com/231638510219149" target="_blank">Enroll Now</Button>
+3 -3
View File
@@ -12,8 +12,7 @@ const organizationSchema = {
'@type': 'MedicalOrganization',
name: site.name,
url: site.url,
telephone: site.phone,
email: site.email,
// Contact details are rendered through ContactObfuscation to avoid plain text in HTML.
address: {
'@type': 'PostalAddress',
streetAddress: '8901 E Raintree Dr Ste 160',
@@ -30,8 +29,9 @@ const organizationSchema = {
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" href="/assets/images/aia-logo.svg" />
<link rel="icon" href="/assets/images/favicon.svg" />
<Seo title={title} description={description} canonical={canonical} image={image} lang={lang} type={type} />
<slot name="head" />
<script type="application/ld+json" set:html={JSON.stringify(organizationSchema)} />
</head>
<body>
+5 -1
View File
@@ -5,11 +5,12 @@ import TeamPage from '../components/pages/TeamPage.astro';
import AboutPage from '../components/pages/AboutPage.astro';
import ServicesPage from '../components/pages/ServicesPage.astro';
import CareersPage from '../components/pages/CareersPage.astro';
import ContactPage from '../components/pages/ContactPage.astro';
import FaqPage from '../components/pages/FaqPage.astro';
import PageHero from '../components/PageHero.astro';
import { pageHeroImages } from '../data/page-visuals';
const { entry } = Astro.props;
const showForm = ['client-consultation', 'contact', 'schedule-consultation', 'referrals'].includes(entry.data.slug);
const showForm = ['client-consultation', 'schedule-consultation', 'referrals'].includes(entry.data.slug);
const serviceTitles: Record<string, Record<string, string>> = {
en: {
'aba-therapy': 'Behavioral',
@@ -53,6 +54,7 @@ const isService = Boolean(serviceTitles[entry.data.lang]?.[entry.data.slug]);
const heroImage = pageHeroImages[entry.data.slug];
---
<BaseLayout title={entry.data.title} description={entry.data.description} canonical={entry.data.canonical} image={entry.data.featuredImage || heroImage} lang={entry.data.lang}>
{entry.data.slug === 'contact' && <Fragment slot="head"><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" /></Fragment>}
<PageHero title={bannerTitle} subtitle={entry.data.slug === 'faqs' ? 'FAQs' : undefined} image={heroImage} constrain />
{entry.data.slug === 'team' && entry.data.lang === 'en'
? <TeamPage />
@@ -64,6 +66,8 @@ const heroImage = pageHeroImages[entry.data.slug];
? <CareersPage />
: entry.data.slug === 'faqs' && entry.data.lang === 'en'
? <FaqPage><slot /></FaqPage>
: entry.data.slug === 'contact'
? <ContactPage title={bannerTitle}><slot /></ContactPage>
: <article class:list={['prose', 'source-page', 'container', { 'service-page': isService }]}><slot /></article>}
{showForm && <FormShell title={bannerTitle} showBackground={entry.data.slug === 'client-consultation'} />}
</BaseLayout>
+23
View File
@@ -0,0 +1,23 @@
const allowIndexing = import.meta.env.PUBLIC_ALLOW_INDEXING === 'true';
const body = allowIndexing
? [
'User-agent: *',
'Allow: /',
'Sitemap: https://www.azinstitute4autism.com/sitemap.xml',
''
].join('\n')
: [
'User-agent: *',
'Allow: /',
'# Staging and migration builds emit meta robots noindex,nofollow.',
''
].join('\n');
export function GET() {
return new Response(body, {
headers: {
'Content-Type': 'text/plain; charset=utf-8'
}
});
}
-220
View File
@@ -1,220 +0,0 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const mirror = path.resolve(root, '../www.azinstitute4autism.com');
const content = path.join(root, 'src/content');
const assets = path.join(root, 'public/assets');
const reports = path.join(root, 'reports');
const site = 'https://www.azinstitute4autism.com';
const mkdir = (value) => fs.mkdir(value, { recursive: true });
const quote = (value = '') => JSON.stringify(String(value).replace(/\s+/g, ' ').trim());
const csv = (value = '') => `"${String(value).replaceAll('"', '""')}"`;
const blogPreamble = /^# .+\n\n!\[[^\]]*\]\(\/assets\/images\/rula-diab-avatar\.jpg\)\n\n[^\n]+\n\n[^\n]+\n\n!\[[^\]]*\]\([^)]+\)\n\n/;
async function walk(dir, prefix = '') {
const output = [];
for (const entry of await fs.readdir(dir, { withFileTypes: true })) {
const relative = path.join(prefix, entry.name);
if (entry.isDirectory()) output.push(...await walk(path.join(dir, entry.name), relative));
else output.push(relative);
}
return output;
}
function logical(file) {
return file.replace(/(?:\.html)?\?(?:hsLang=[^.]+|hs_amp=true)\.html$/, '.html');
}
function selectCanonical(files) {
const map = new Map();
for (const file of files) {
if (!file.endsWith('.html') || file.includes('hs_amp=true') || /\/(?:page|author)\//.test(file)) continue;
const target = logical(file);
const key = `${target.startsWith('ar/') ? 'ar' : target.startsWith('es/') ? 'es' : 'en'}:${target}`;
if (!map.has(key) || (!file.includes('?') && map.get(key).includes('?'))) map.set(key, file);
}
return [...map.values()];
}
function decode(value = '') {
return value
.replace(/&#(\d+);/g, (_, code) => String.fromCodePoint(Number(code)))
.replace(/&#x([\da-f]+);/gi, (_, code) => String.fromCodePoint(parseInt(code, 16)))
.replaceAll('&amp;', '&').replaceAll('&quot;', '"').replaceAll('&#39;', "'")
.replaceAll('&lt;', '<').replaceAll('&gt;', '>').replaceAll('&nbsp;', ' ');
}
function text(value = '') {
return decode(value.replace(/<[^>]+>/g, ' ')).replace(/\s+/g, ' ').trim();
}
function normalizeHref(value = '') {
return decode(value)
.replace(/^https?:\/\/www\.azinstitute4autism\.com/, '')
.replace(/(?:\.html)?(?:%3F|\?)(?:hsLang=[^.#]+|hs_amp=true)(?:\.html)?$/, '')
.replace(/\.html$/, '')
.replace(/^index$/, '/');
}
function inline(value = '') {
return text(value.replace(/<a[^>]+href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi, (_, href, label) => {
const cleanLabel = text(label);
return cleanLabel ? `[${cleanLabel}](${normalizeHref(href)})` : '';
}));
}
function meta(html, name, property = false) {
const key = property ? 'property' : 'name';
const pattern = new RegExp(`<meta[^>]+${key}=["']${name}["'][^>]+content=["']([^"']*)`, 'i');
const reverse = new RegExp(`<meta[^>]+content=["']([^"']*)["'][^>]+${key}=["']${name}["']`, 'i');
return decode(html.match(pattern)?.[1] || html.match(reverse)?.[1] || '');
}
function asset(value = '') {
const match = value.match(/(?:hs-fs\/)?hubfs\/([^?#]+)/);
return match ? `/assets/images/${path.basename(decodeURIComponent(match[1])).split('?')[0]}` : undefined;
}
function normalizeTable(value = '') {
return value
.replace(/(<a\b[^>]*\bhref=["'])([^"']+)(["'])/gi,
(_, prefix, href, suffix) => `${prefix}${normalizeHref(href)}${suffix}`)
.replace(/(<img\b[^>]*\bsrc=["'])([^"']+)(["'])/gi,
(_, prefix, src, suffix) => `${prefix}${asset(src) || src}${suffix}`);
}
function markdownFrom(html) {
let body = html.match(/<main[\s\S]*?<\/main>/i)?.[0] ||
html.match(/blog-post__body[\s\S]*?(?=<footer|blog-post__tags|<\/article>)/i)?.[0] ||
'';
const tables = [];
body = body
.replace(/<(script|style|noscript|header|footer|nav|form)\b[\s\S]*?<\/\1>/gi, '')
.replace(/<table[\s\S]*?<\/table>/gi, (match) => {
const token = `__TABLE_${tables.length}__`;
tables.push(normalizeTable(match));
return `\n\n${token}\n\n`;
})
.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, (_, value) => `\n\n> ${inline(value)}\n\n`)
.replace(/<img[^>]+src=["']([^"']+)["'][^>]*alt=["']([^"']*)["'][^>]*>/gi, (_, src, alt) => {
const local = asset(src);
return local ? `\n\n![${text(alt)}](${local})\n\n` : '';
})
.replace(/<h([1-6])[^>]*>([\s\S]*?)<\/h\1>/gi, (_, level, value) => `\n\n${'#'.repeat(Number(level))} ${inline(value)}\n\n`)
.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_, value) => `\n- ${inline(value)}`)
.replace(/<p[^>]*>([\s\S]*?)<\/p>/gi, (_, value) => `\n\n${inline(value)}\n\n`)
.replace(/<br\s*\/?>/gi, '\n')
.replace(/<[^>]+>/g, ' ');
body = decode(body).replace(/[ \t]+/g, ' ').replace(/^\s+$/gm, '').replace(/\n{3,}/g, '\n\n').trim();
return tables.reduce((output, table, index) => output.replaceAll(`__TABLE_${index}__`, table), body);
}
function record(file, html) {
const target = logical(file);
const lang = target.startsWith('ar/') ? 'ar' : target.startsWith('es/') ? 'es' : 'en';
const url = target === 'index.html' ? '/' : `/${target.replace(/\/index\.html$/, '').replace(/\.html$/, '')}`;
const title = text(html.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1] || url);
const h1 = text(html.match(/<h1[^>]*>([\s\S]*?)<\/h1>/i)?.[1] || title);
const published = meta(html, 'article:published_time', true) ||
html.match(/<time[^>]+datetime=["']([^"']+)/i)?.[1] || '';
return {
file, lang, url, title, h1,
description: meta(html, 'description'),
image: asset(meta(html, 'og:image', true)),
date: /^\d{4}-\d{2}-\d{2}/.test(published) ? published.slice(0, 10) : '2024-01-01',
markdown: markdownFrom(html)
};
}
function frontmatter(item, blog) {
const slug = ['/', '/ar', '/es'].includes(item.url) ? 'index' : item.url.split('/').filter(Boolean).at(-1);
const lines = [
'---',
`title: ${quote(item.title)}`,
`description: ${quote(item.description)}`,
`slug: ${quote(slug)}`,
`canonical: ${quote(`${site}${item.url}`)}`,
`lang: ${quote(item.lang)}`,
`translationKey: ${quote(slug)}`,
item.image ? `featuredImage: ${quote(item.image)}` : '',
blog ? `date: "${item.date}"` : '',
blog ? 'author: "rula-diab"' : '',
blog ? 'category: "Library"' : '',
blog ? 'tags: []' : '',
'draft: false',
'---',
'',
(blog ? item.markdown.replace(blogPreamble, '') : item.markdown) || `# ${item.h1}`,
''
].filter((line) => line !== '');
return `${lines.join('\n')}\n`;
}
async function copySourceAssets(files) {
const rows = ['source,target,size,kind'];
const seen = new Set();
await Promise.all(['images', 'fonts', 'downloads'].map((kind) => fs.rm(path.join(assets, kind), { recursive: true, force: true })));
for (const file of files.filter((name) => name.startsWith('hubfs/') || name.startsWith('hs-fs/hubfs/') || name.startsWith('_hcms/googlefonts/'))) {
const font = file.startsWith('_hcms/googlefonts/');
const raw = font ? file.replace('_hcms/googlefonts/', '') : path.basename(file).split('?')[0];
if (!font && !/\.(?:avif|gif|jpe?g|png|svg|webp|pdf)$/i.test(raw)) continue;
const key = `${font}:${raw}`;
if (!raw || seen.has(key)) continue;
seen.add(key);
const kind = font ? 'fonts' : raw.endsWith('.pdf') ? 'downloads' : 'images';
const target = path.join(assets, kind, raw);
await mkdir(path.dirname(target));
await fs.copyFile(path.join(mirror, file), target);
const size = (await fs.stat(target)).size;
rows.push([file, path.relative(root, target), size, kind].map(csv).join(','));
}
await fs.writeFile(path.join(reports, 'asset-inventory.csv'), `${rows.join('\n')}\n`);
}
async function removeMissingFeaturedImages() {
for (const type of ['pages', 'blog']) {
for (const file of await walk(path.join(content, type))) {
const target = path.join(content, type, file);
let source = await fs.readFile(target, 'utf8');
const image = source.match(/^featuredImage:\s*"\/assets\/images\/([^"]+)"/m)?.[1];
if (!image) continue;
if (!await fs.access(path.join(assets, 'images', image)).then(() => true).catch(() => false)) {
source = source.replace(/^featuredImage:.*\n/m, '');
await fs.writeFile(target, source);
}
}
}
}
async function main() {
const files = await walk(mirror);
const selected = selectCanonical(files);
const records = [];
for (const file of selected) records.push(record(file, await fs.readFile(path.join(mirror, file), 'utf8')));
await Promise.all(['pages', 'blog', 'authors'].map((type) => fs.rm(path.join(content, type), { recursive: true, force: true })));
for (const lang of ['en', 'ar', 'es']) {
for (const type of ['pages', 'blog', 'authors']) await mkdir(path.join(content, type, lang));
}
for (const item of records) {
const blog = item.url.includes('/library/') && !item.url.endsWith('/library');
const slug = ['/', '/ar', '/es'].includes(item.url) ? 'index' : item.url.split('/').filter(Boolean).at(-1);
await fs.writeFile(path.join(content, blog ? 'blog' : 'pages', item.lang, `${slug}.md`), frontmatter(item, blog));
}
for (const lang of ['en', 'ar', 'es']) {
await fs.writeFile(path.join(content, 'authors', lang, 'rula-diab.md'), `---\nname: "Rula Diab"\nslug: "rula-diab"\ndescription: "Founder and clinical leader at Arizona Institute for Autism."\navatar: "/assets/images/rula-diab-avatar.jpg"\nlang: "${lang}"\ntranslationKey: "rula-diab"\n---\n`);
}
if (!records.some((item) => item.lang === 'ar' && item.url === '/ar')) {
await fs.writeFile(path.join(content, 'pages/ar/index.md'), `---\ntitle: "معهد أريزونا للتوحد"\ndescription: "صفحة عربية تمهيدية لمعهد أريزونا للتوحد."\nslug: "index"\ncanonical: "https://www.azinstitute4autism.com/ar"\nlang: "ar"\ntranslationKey: "home"\ndraft: false\n---\n\n<!-- TODO: Replace this placeholder with reviewed Arabic content. -->\n\nتتوفر المقالات العربية الحالية في المكتبة. يرجى مراجعة المحتوى العربي قبل النشر.\n`);
}
const rows = ['source_file,url,language,type,title,description,h1'];
for (const item of records) rows.push([item.file, item.url, item.lang, item.url.includes('/library/') ? 'blog' : 'page', item.title, item.description, item.h1].map(csv).join(','));
await mkdir(reports);
await fs.writeFile(path.join(reports, 'url-inventory.csv'), `${rows.join('\n')}\n`);
await copySourceAssets(files);
await removeMissingFeaturedImages();
console.log(`Fallback extraction completed: ${records.length} canonical records.`);
}
await main();
-198
View File
@@ -1,198 +0,0 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import fg from 'fast-glob';
import { load } from 'cheerio';
import TurndownService from 'turndown';
import matter from 'gray-matter';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const mirror = path.resolve(root, '../www.azinstitute4autism.com');
const site = 'https://www.azinstitute4autism.com';
const contentRoot = path.join(root, 'src/content');
const publicRoot = path.join(root, 'public/assets');
const reportsRoot = path.join(root, 'reports');
const turndown = new TurndownService({ headingStyle: 'atx', bulletListMarker: '-' });
turndown.remove(['script', 'style', 'noscript', 'iframe']);
const mkdir = (dir) => fs.mkdir(dir, { recursive: true });
const clean = (value = '') => value.replace(/\s+/g, ' ').trim();
const csv = (value = '') => `"${String(value).replaceAll('"', '""')}"`;
const yaml = (value = '') => JSON.stringify(String(value));
const blogPreamble = /^# .+\n\n!\[[^\]]*\]\(\/assets\/images\/rula-diab-avatar\.jpg\)\n\n[^\n]+\n\n[^\n]+\n\n!\[[^\]]*\]\([^)]+\)\n\n/;
function contentMarkdown(markdown, isBlog) {
return isBlog ? markdown.replace(blogPreamble, '') : markdown;
}
function logicalFile(file) {
return file.replace(/(?:\.html)?\?(?:hsLang=[^.]+|hs_amp=true)\.html$/, '.html');
}
function canonicalFiles(files) {
const selected = new Map();
for (const file of files) {
if (!file.endsWith('.html') || /\/(?:page|author)\//.test(file) || file.includes('hs_amp=true')) continue;
const logical = logicalFile(file);
const lang = logical.startsWith('ar/') ? 'ar' : logical.startsWith('es/') ? 'es' : 'en';
const key = `${lang}:${logical}`;
const current = selected.get(key);
if (!current || (!file.includes('?') && current.includes('?'))) selected.set(key, file);
}
return [...selected.values()];
}
function sourceUrl(file) {
const logical = logicalFile(file);
if (logical === 'index.html') return '/';
return `/${logical.replace(/\/index\.html$/, '').replace(/\.html$/, '')}`;
}
function isBlogFile(file) {
return file.startsWith('library/') || file.includes('/library/');
}
function languageFor(file, $) {
if (file.startsWith('ar/')) return 'ar';
if (file.startsWith('es/')) return 'es';
return $('html').attr('lang')?.split('-')[0] || 'en';
}
function localizeAsset(value = '') {
const decoded = value.replace(/^https?:\/\/[^/]+/, '').replace(/^\.\.\//, '/');
const match = decoded.match(/(?:\/)?(?:hs-fs\/)?hubfs\/([^?#]+)/);
if (!match) return undefined;
return `/assets/images/${path.basename(decodeURIComponent(match[1])).split('?')[0]}`;
}
function extractRecord(file, html) {
const $ = load(html);
const lang = languageFor(file, $);
const url = sourceUrl(file);
const title = clean($('title').first().text()) || clean($('h1').first().text()) || url;
const description = $('meta[name="description"]').attr('content') || '';
const h1 = clean($('h1').first().text());
const image = localizeAsset(
$('meta[property="og:image"]').attr('content') ||
$('.blog-post__body img, main img, .body-container-wrapper img').first().attr('src') ||
''
);
const alt = clean($('.blog-post__body img, main img, .body-container-wrapper img').first().attr('alt') || '');
const dateText = $('meta[property="article:published_time"]').attr('content') ||
$('time').first().attr('datetime') || '2024-01-01';
const date = /^\d{4}-\d{2}-\d{2}/.test(dateText) ? dateText.slice(0, 10) : '2024-01-01';
const selector = isBlogFile(file) ? '.blog-post__body' : 'main, .body-container-wrapper';
const body = $(selector).first().clone();
body.find('header, footer, nav, form, script, style, noscript, .hs_cos_wrapper_type_form').remove();
body.find('*').removeAttr('style').removeAttr('id').removeAttr('data-hs-cos-general-type').removeAttr('data-hs-cos-type');
body.find('a').each((_, element) => {
const href = $(element).attr('href');
if (!href) return;
$(element).attr('href', href
.replace(/\.html(?:%3F|\?)[^"]*$/, '')
.replace(/\.html$/, '')
.replace(/^index$/, '/'));
});
body.find('img').each((_, element) => {
const src = localizeAsset($(element).attr('src'));
if (src) $(element).attr('src', src);
});
const tables = [];
body.find('table').each((_, element) => {
const token = `__TABLE_${tables.length}__`;
tables.push($(element).prop('outerHTML'));
$(element).replaceWith(`\n\n${token}\n\n`);
});
const markdown = contentMarkdown(
tables.reduce((output, table, index) => output.replaceAll(`__TABLE_${index}__`, table),
turndown.turndown(body.html() || '').replace(/\n{3,}/g, '\n\n').trim()),
isBlogFile(file)
);
return { file, lang, url, title, description, h1, image, alt, date, markdown };
}
function frontmatter(record, type) {
const slug = ['/', '/ar', '/es'].includes(record.url) ? 'index' : record.url.split('/').filter(Boolean).at(-1);
const data = {
title: record.title,
description: record.description,
slug,
canonical: `${site}${record.url}`,
lang: record.lang,
translationKey: slug,
draft: false
};
if (record.image) data.featuredImage = record.image;
if (record.alt) data.alt = record.alt;
if (type === 'blog') Object.assign(data, {
date: record.date,
author: 'rula-diab',
category: 'Library',
tags: []
});
return matter.stringify(record.markdown || `# ${record.h1 || record.title}\n`, data);
}
async function copyAssets() {
const assets = await fg(['hubfs/*', 'hs-fs/hubfs/*', '_hcms/googlefonts/**/*'], { cwd: mirror, onlyFiles: true });
const inventory = ['source,target,size,kind'];
const seen = new Set();
await Promise.all(['images', 'fonts', 'downloads'].map((kind) => fs.rm(path.join(publicRoot, kind), { recursive: true, force: true })));
for (const source of assets) {
const original = path.join(mirror, source);
const stat = await fs.stat(original);
const isFont = source.startsWith('_hcms/googlefonts/');
const rawName = isFont ? source.replace('_hcms/googlefonts/', '') : path.basename(source).split('?')[0];
if (!isFont && !/\.(?:avif|gif|jpe?g|png|svg|webp|pdf)$/i.test(rawName)) continue;
if (!rawName || seen.has(`${isFont}:${rawName}`)) continue;
seen.add(`${isFont}:${rawName}`);
const target = path.join(publicRoot, isFont ? 'fonts' : rawName.endsWith('.pdf') ? 'downloads' : 'images', rawName);
await mkdir(path.dirname(target));
await fs.copyFile(original, target);
inventory.push([source, path.relative(root, target), stat.size, isFont ? 'font' : path.extname(rawName).slice(1)].map(csv).join(','));
}
await fs.writeFile(path.join(reportsRoot, 'asset-inventory.csv'), `${inventory.join('\n')}\n`);
}
async function main() {
await Promise.all([
mkdir(contentRoot), mkdir(publicRoot), mkdir(reportsRoot),
...['en', 'ar', 'es'].flatMap((lang) => [
mkdir(path.join(contentRoot, 'pages', lang)),
mkdir(path.join(contentRoot, 'blog', lang)),
mkdir(path.join(contentRoot, 'authors', lang))
])
]);
const files = canonicalFiles(await fg(['**/*.html'], { cwd: mirror, onlyFiles: true }));
const records = [];
for (const file of files) records.push(extractRecord(file, await fs.readFile(path.join(mirror, file), 'utf8')));
const corePages = new Set(records.filter((r) => !isBlogFile(r.file)).map((r) => r.file));
for (const record of records) {
const isBlog = isBlogFile(record.file);
if (!isBlog && !corePages.has(record.file)) continue;
const slug = ['/', '/ar', '/es'].includes(record.url) ? 'index' : record.url.split('/').filter(Boolean).at(-1);
const target = path.join(contentRoot, isBlog ? 'blog' : 'pages', record.lang, `${slug}.md`);
await fs.writeFile(target, frontmatter(record, isBlog ? 'blog' : 'page'));
}
for (const lang of ['en', 'ar', 'es']) {
await fs.writeFile(path.join(contentRoot, 'authors', lang, 'rula-diab.md'), matter.stringify('', {
name: 'Rula Diab',
slug: 'rula-diab',
description: 'Founder and clinical leader at Arizona Institute for Autism.',
avatar: '/assets/images/rula-diab-avatar.jpg',
lang,
translationKey: 'rula-diab'
}));
}
const urlRows = ['source_file,url,language,type,title,description,h1'];
for (const record of records) urlRows.push([
record.file, record.url, record.lang,
isBlogFile(record.file) ? 'blog' : 'page',
record.title, record.description, record.h1
].map(csv).join(','));
await fs.writeFile(path.join(reportsRoot, 'url-inventory.csv'), `${urlRows.join('\n')}\n`);
await copyAssets();
console.log(`Extracted ${records.length} canonical pages and posts.`);
}
await main();
+3 -3
View File
@@ -1,9 +1,9 @@
import fs from 'node:fs/promises';
const redirects = [
{ from: '/aba', to: '/aba-therapy', status: 301, reason: 'Brief alias to preserved mirror URL' },
{ from: '/autismevaluations', to: '/autism-evaluations', status: 301, reason: 'Brief alias to preserved mirror URL' },
{ from: '/learnersocialclub', to: '/learner-social-club', status: 301, reason: 'Brief alias to preserved mirror URL' }
{ from: '/aba', to: '/aba-therapy', status: 301, reason: 'Brief legacy alias to preserved URL' },
{ from: '/autismevaluations', to: '/autism-evaluations', status: 301, reason: 'Brief legacy alias to preserved URL' },
{ from: '/learnersocialclub', to: '/learner-social-club', status: 301, reason: 'Brief legacy alias to preserved URL' }
];
await fs.writeFile(new URL('../src/data/redirects.json', import.meta.url), `${JSON.stringify(redirects, null, 2)}\n`);
await fs.writeFile(new URL('../reports/redirect-map.csv', import.meta.url),