30 Commits

Author SHA1 Message Date
58773fc193 release: v3.0.0
Some checks failed
Code Analysis / SonarQube (push) Failing after 17s
Node.js CI / Lint and Test (push) Successful in 40s
2025-07-08 16:46:48 -07:00
5d0660c003 feat!: rename variables to avoid collision (#5)
Some checks failed
Code Analysis / SonarQube (push) Failing after 16s
Node.js CI / Lint and Test (push) Has been cancelled
### Explanation

_No response_

### Issue

_No response_

### Attestations

- [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [x] I have pinned the dependencies to a specific patch version.

### Style

- [x] I have run the linter and resolved any errors.
- [x] My pull request uses an appropriate title, matching the conventional commit standards.
- [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

Major - My pull request introduces a breaking change.

Reviewed-on: #5
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-07-08 16:46:34 -07:00
ab663e9f4b feat: add full social list to footer (#4)
Some checks failed
Code Analysis / SonarQube (push) Failing after 16s
Node.js CI / Lint and Test (push) Successful in 39s
### Explanation

_No response_

### Issue

_No response_

### Attestations

- [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [x] I have pinned the dependencies to a specific patch version.

### Style

- [x] I have run the linter and resolved any errors.
- [x] My pull request uses an appropriate title, matching the conventional commit standards.
- [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

_No response_

Reviewed-on: #4
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-07-08 16:32:40 -07:00
2bd410631b release: v2.1.0
Some checks failed
Code Analysis / SonarQube (push) Failing after 16s
Node.js CI / Lint and Test (push) Successful in 41s
2025-07-04 14:30:54 -07:00
25ec9f8e2a feat: add id to programmatically remove custom styling (#3)
Some checks failed
Code Analysis / SonarQube (push) Failing after 15s
Node.js CI / Lint and Test (push) Has been cancelled
### Explanation

_No response_

### Issue

_No response_

### Attestations

- [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [ ] I have pinned the dependencies to a specific patch version.

### Style

- [x] I have run the linter and resolved any errors.
- [x] My pull request uses an appropriate title, matching the conventional commit standards.
- [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

_No response_

Reviewed-on: #3
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-07-04 14:30:25 -07:00
1f450156c8 2.0.0
Some checks failed
Code Analysis / SonarQube (push) Failing after 18s
Node.js CI / Lint and Test (push) Successful in 43s
2025-06-27 14:22:09 -07:00
5e9d0dd11e feat: copyright should link to homepage 2025-06-27 14:21:56 -07:00
4f05dbafbb feat: remove border radius from CTA avatar
Some checks failed
Code Analysis / SonarQube (push) Failing after 16s
Node.js CI / Lint and Test (push) Successful in 43s
2025-06-12 15:23:04 -07:00
934de19c1e release: v1.8.0
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m8s
Code Analysis / SonarQube (push) Failing after 1m21s
2025-05-31 17:39:55 -07:00
4a76b22119 feat: replace audio button with donate badge
Some checks failed
Code Analysis / SonarQube (push) Has been cancelled
Node.js CI / Lint and Test (push) Has been cancelled
2025-05-31 17:39:34 -07:00
542aef157a release: v1.7.0
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m18s
Code Analysis / SonarQube (push) Failing after 1m26s
2025-05-30 21:05:18 -07:00
2e72c34c2d feat: include google ads snippet 2025-05-30 21:04:38 -07:00
c9ae8804eb feat: remove video overlay
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m13s
Code Analysis / SonarQube (push) Failing after 1m24s
2025-05-08 16:55:33 -07:00
a5f17bb2eb feat: use logo in modalx
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m18s
Code Analysis / SonarQube (push) Failing after 1m28s
2025-05-08 16:54:00 -07:00
06e58752b9 release: v1.6.0
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m13s
Code Analysis / SonarQube (push) Failing after 1m22s
2025-04-04 15:06:21 -07:00
db4dcc3090 feat: track urls and page titles 2025-04-04 15:06:04 -07:00
53aa95c9c2 fix: tweak the colours a bit
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m12s
Code Analysis / SonarQube (push) Failing after 1m22s
2025-03-31 15:42:25 -07:00
93a2339c92 release: v1.5.0
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m14s
Code Analysis / SonarQube (push) Failing after 1m25s
2025-03-27 14:38:28 -07:00
3e3213c554 feat: add dark mode and theme selector
Some checks failed
Code Analysis / SonarQube (push) Has been cancelled
Node.js CI / Lint and Test (push) Has been cancelled
2025-03-27 14:38:10 -07:00
2e7d5d26e4 fix: lkint
All checks were successful
Node.js CI / Lint and Test (push) Successful in 1m9s
Code Analysis / SonarQube (push) Successful in 1m17s
2025-03-11 12:08:14 -07:00
52a7b5b812 release: v1.4.0
Some checks failed
Node.js CI / Lint and Test (push) Failing after 48s
Code Analysis / SonarQube (push) Has been cancelled
2025-03-11 12:07:15 -07:00
6be8026024 feat: add blinkies
Some checks failed
Node.js CI / Lint and Test (push) Has been cancelled
Code Analysis / SonarQube (push) Has been cancelled
2025-03-11 12:06:59 -07:00
c10089c351 release: v1.3.0
All checks were successful
Node.js CI / Lint and Test (push) Successful in 58s
Code Analysis / SonarQube (push) Successful in 1m6s
2025-03-01 00:23:11 -08:00
5b8b597d7d feat: change to pink theme
All checks were successful
Node.js CI / Lint and Test (push) Successful in 59s
Code Analysis / SonarQube (push) Successful in 1m8s
2025-03-01 00:19:48 -08:00
dd7d4042ea chore: sonar workflow
All checks were successful
Node.js CI / Lint and Test (push) Successful in 59s
Code Analysis / SonarQube (push) Successful in 1m10s
2025-02-26 13:26:47 -08:00
bf87dc345f release: v1.2.1
All checks were successful
Node.js CI / Lint and Test (push) Successful in 46s
2025-02-16 23:17:35 -08:00
017261c80b feat: only show cta once per week
Some checks failed
Node.js CI / Lint and Test (push) Has been cancelled
2025-02-16 23:17:18 -08:00
8f76c3b9f1 release: v1.2.0
All checks were successful
Node.js CI / Lint and Test (push) Successful in 40s
2025-02-16 17:10:00 -08:00
b5701520fa feat: remove hubspot and create our own dialog CTA (#2)
Some checks failed
Node.js CI / Lint and Test (push) Has been cancelled
### Explanation

_No response_

### Issue

_No response_

### Attestations

- [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [ ] I have pinned the dependencies to a specific patch version.

### Style

- [x] I have run the linter and resolved any errors.
- [x] My pull request uses an appropriate title, matching the conventional commit standards.
- [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

Minor - My pull request introduces a new non-breaking feature.

Reviewed-on: #2
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-02-16 17:09:50 -08:00
4e05e999b5 feat: setup actions (#1)
All checks were successful
Node.js CI / Lint and Test (push) Successful in 33s
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-01-23 02:03:38 -08:00
7 changed files with 550 additions and 163 deletions

38
.gitea/workflows/ci.yml Normal file
View File

@ -0,0 +1,38 @@
name: Node.js CI
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
lint:
name: Lint and Test
steps:
- name: Checkout Source Files
uses: actions/checkout@v4
- name: Use Node.js v22
uses: actions/setup-node@v4
with:
node-version: 22
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 9
- name: Install Dependencies
run: pnpm install
- name: Lint Source Files
run: pnpm run lint
- name: Verify Build
run: pnpm run build
- name: Run Tests
run: pnpm run test

View File

@ -0,0 +1,34 @@
name: Code Analysis
on:
push:
branches:
- main
jobs:
sonar:
name: SonarQube
steps:
- name: Checkout Source Files
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: SonarCube Scan
uses: SonarSource/sonarqube-scan-action@v4
timeout-minutes: 10
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: "https://quality.nhcarrigan.com"
with:
args: >
-Dsonar.sources=.
-Dsonar.projectKey=website-headers
- name: SonarQube Quality Gate check
uses: sonarsource/sonarqube-quality-gate-action@v1
with:
pollingTimeoutSec: 600
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: "https://quality.nhcarrigan.com"

13
.vscode/settings.json vendored
View File

@ -2,5 +2,16 @@
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit"
}, },
"eslint.validate": ["typescript"] "eslint.validate": [
"typescript"
],
"sonarlint.connectedMode.project": {
"connectionId": "nhcarrigan",
"projectKey": "nhcarrigan_website-headers"
},
"cSpell.words": [
"Adsense",
"nhcarrigan",
"noreferrer"
]
} }

View File

@ -12,7 +12,8 @@ export default [
}, },
{ {
rules: { rules: {
"no-console": "off" "no-console": "off",
"max-lines": "off"
} }
} }
]; ];

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{ {
"name": "website-headers", "name": "website-headers",
"version": "1.1.0", "version": "3.0.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
@ -11,7 +11,7 @@
"build:watch": "tsc --watch", "build:watch": "tsc --watch",
"lint": "eslint src --max-warnings 0", "lint": "eslint src --max-warnings 0",
"start": "node --watch prod/develop.js", "start": "node --watch prod/develop.js",
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 0",
"scan": "SONAR_TOKEN='op://Environment Variables - Development/SonarCloud/website-headers' op run -- sonar-scanner -Dsonar.organization=nhcarrigan -Dsonar.projectKey=nhcarrigan_website-headers -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io" "scan": "SONAR_TOKEN='op://Environment Variables - Development/SonarCloud/website-headers' op run -- sonar-scanner -Dsonar.organization=nhcarrigan -Dsonar.projectKey=nhcarrigan_website-headers -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io"
}, },
"keywords": [], "keywords": [],

View File

@ -5,10 +5,10 @@
*/ */
// #region Version // #region Version
const version = "{{ version }}"; const nhcarriganHeadersVersion = "{{ version }}";
console.log(` console.log(`
======================================== ========================================
Loading NHCarrigan library v${version}. Loading NHCarrigan library v${nhcarriganHeadersVersion}.
Copyright (c) ${new Date().getFullYear(). Copyright (c) ${new Date().getFullYear().
toString()} NHCarrigan toString()} NHCarrigan
Changelog: https://codeberg.org/nhcarrigan/website-headers/releases Changelog: https://codeberg.org/nhcarrigan/website-headers/releases
@ -19,12 +19,18 @@ Questions? Contact us at https://docs.nhcarrigan.com/about/contact
// #endregion // #endregion
// #region Query Selectors // #region Query Selectors
const head = document.querySelector("head"); const nhcarriganHeadersHead = document.querySelector("head");
const body = document.querySelector("body"); const nhcarriganHeadersBody = document.querySelector("body");
const title = document.querySelector("title"); const nhcarriganHeadersTitle = document.querySelector("title");
const description = document.querySelector(`meta[name="description"]`); const nhcarriganHeadersDescription = document.querySelector(
`meta[name="description"]`,
);
const { href: url, hostname } = window.location; const {
href: nhcarriganHeadersUrl,
hostname: nhcarriganHeadersHostname,
pathname: nhcarriganHeadersPathname,
} = window.location;
// #endregion // #endregion
@ -34,86 +40,104 @@ const { href: url, hostname } = window.location;
* The title and description are set by each website. This should * The title and description are set by each website. This should
* only load things like open graph data and favicons. * only load things like open graph data and favicons.
*/ */
const openGraphTitle = document.createElement("meta"); const nhcarriganHeadersOpenGraphTitle = document.createElement("meta");
openGraphTitle.setAttribute("property", "og:title"); nhcarriganHeadersOpenGraphTitle.setAttribute("property", "og:title");
openGraphTitle.setAttribute("content", title?.innerText ?? "NHCarrigan"); nhcarriganHeadersOpenGraphTitle.setAttribute(
const openGraphDescription = document.createElement("meta");
openGraphDescription.setAttribute("property", "og:description");
openGraphDescription.setAttribute(
"content", "content",
description?.getAttribute("content") nhcarriganHeadersTitle?.innerText ?? "NHCarrigan",
// eslint-disable-next-line stylistic/max-len
?? "We are a software engineering and community management consulting firm.",
); );
const openGraphImage = document.createElement("meta"); const nhcarriganHeadersOpenGraphDescription = document.createElement("meta");
openGraphImage.setAttribute("property", "og:image"); nhcarriganHeadersOpenGraphDescription.setAttribute(
openGraphImage.setAttribute( "property",
"og:description",
);
nhcarriganHeadersOpenGraphDescription.setAttribute(
"content",
nhcarriganHeadersDescription?.getAttribute("content")
?? "We are a software engineering and community management consulting firm.",
);
const nhcarriganHeadersOpenGraphImage = document.createElement("meta");
nhcarriganHeadersOpenGraphImage.setAttribute("property", "og:image");
nhcarriganHeadersOpenGraphImage.setAttribute(
"content", "content",
"https://cdn.nhcarrigan.com/og-image.png", "https://cdn.nhcarrigan.com/og-image.png",
); );
const openGraphUrl = document.createElement("meta"); const nhcarriganHeadersOpenGraphUrl = document.createElement("meta");
openGraphUrl.setAttribute("property", "og:url"); nhcarriganHeadersOpenGraphUrl.setAttribute("property", "og:url");
openGraphUrl.setAttribute("content", url); nhcarriganHeadersOpenGraphUrl.setAttribute("content", nhcarriganHeadersUrl);
const openGraphType = document.createElement("meta"); const nhcarriganHeadersOpenGraphType = document.createElement("meta");
openGraphType.setAttribute("property", "og:type"); nhcarriganHeadersOpenGraphType.setAttribute("property", "og:type");
openGraphType.setAttribute("content", "website"); nhcarriganHeadersOpenGraphType.setAttribute("content", "website");
const twitterCard = document.createElement("meta"); const nhcarriganHeadersTwitterCard = document.createElement("meta");
twitterCard.setAttribute("name", "twitter:card"); nhcarriganHeadersTwitterCard.setAttribute("name", "twitter:card");
twitterCard.setAttribute("content", "summary_large_image"); nhcarriganHeadersTwitterCard.setAttribute("content", "summary_large_image");
const twitterDomain = document.createElement("meta"); const nhcarriganHeadersTwitterDomain = document.createElement("meta");
twitterDomain.setAttribute("name", "twitter:domain"); nhcarriganHeadersTwitterDomain.setAttribute("name", "twitter:domain");
twitterDomain.setAttribute("content", hostname); nhcarriganHeadersTwitterDomain.setAttribute(
const twitterUrl = document.createElement("meta");
twitterUrl.setAttribute("name", "twitter:url");
twitterUrl.setAttribute("content", url);
const twitterTitle = document.createElement("meta");
twitterTitle.setAttribute("name", "twitter:title");
twitterTitle.setAttribute("content", title?.innerText ?? "NHCarrigan");
const twitterDescription = document.createElement("meta");
twitterDescription.setAttribute("name", "twitter:description");
twitterDescription.setAttribute(
"content", "content",
description?.getAttribute("content") nhcarriganHeadersHostname,
// eslint-disable-next-line stylistic/max-len );
?? "We are a software engineering and community management consulting firm.", const nhcarriganHeadersTwitterUrl = document.createElement("meta");
nhcarriganHeadersTwitterUrl.setAttribute("name", "twitter:url");
nhcarriganHeadersTwitterUrl.setAttribute("content", nhcarriganHeadersUrl);
const nhcarriganHeadersTwitterTitle = document.createElement("meta");
nhcarriganHeadersTwitterTitle.setAttribute("name", "twitter:title");
nhcarriganHeadersTwitterTitle.setAttribute(
"content",
nhcarriganHeadersTitle?.innerText ?? "NHCarrigan",
);
const nhcarriganHeadersTwitterDescription = document.createElement("meta");
nhcarriganHeadersTwitterDescription.setAttribute("name", "twitter:description");
nhcarriganHeadersTwitterDescription.setAttribute(
"content",
nhcarriganHeadersDescription?.getAttribute("content")
?? "We are a software engineering and community management consulting firm.",
);
const nhcarriganHeadersTwitterImage = document.createElement("meta");
nhcarriganHeadersTwitterImage.setAttribute("name", "twitter:image");
nhcarriganHeadersTwitterImage.setAttribute(
"content",
"https://cdn.nhcarrigan.com/og-image.png",
); );
const twitterImage = document.createElement("meta");
twitterImage.setAttribute("name", "twitter:image");
twitterImage.setAttribute("content", "https://cdn.nhcarrigan.com/og-image.png");
// #endregion // #endregion
// #region Favicon // #region Favicon
const favicon = document.createElement("link"); const nhcarriganHeadersFavicon = document.createElement("link");
favicon.rel = "icon"; nhcarriganHeadersFavicon.rel = "icon";
favicon.type = "image/x-icon"; nhcarriganHeadersFavicon.type = "image/x-icon";
favicon.href = "https://cdn.nhcarrigan.com/favicon/favicon.ico"; nhcarriganHeadersFavicon.href
const appleTouchIcon = document.createElement("link"); = "https://cdn.nhcarrigan.com/favicon/favicon.ico";
appleTouchIcon.rel = "apple-touch-icon"; const nhcarriganHeadersAppleTouchIcon = document.createElement("link");
appleTouchIcon.href = "https://cdn.nhcarrigan.com/favicon/apple-touch-icon.png"; nhcarriganHeadersAppleTouchIcon.rel = "apple-touch-icon";
const smallIcon = document.createElement("link"); nhcarriganHeadersAppleTouchIcon.href
smallIcon.rel = "icon"; = "https://cdn.nhcarrigan.com/favicon/apple-touch-icon.png";
smallIcon.href = "https://cdn.nhcarrigan.com/favicon/favicon-16x16.png"; const nhcarriganHeadersSmallIcon = document.createElement("link");
const largeIcon = document.createElement("link"); nhcarriganHeadersSmallIcon.rel = "icon";
largeIcon.rel = "icon"; nhcarriganHeadersSmallIcon.href
largeIcon.href = "https://cdn.nhcarrigan.com/favicon/favicon-32x32.png"; = "https://cdn.nhcarrigan.com/favicon/favicon-16x16.png";
const nhcarriganHeadersLargeIcon = document.createElement("link");
nhcarriganHeadersLargeIcon.rel = "icon";
nhcarriganHeadersLargeIcon.href
= "https://cdn.nhcarrigan.com/favicon/favicon-32x32.png";
// #endregion // #endregion
// #region Styles // #region Styles
const styles = document.createElement("style"); const nhcarriganHeadersStyles = document.createElement("style");
styles.innerHTML = ` nhcarriganHeadersStyles.id = "nhcarrigan-global-styles";
nhcarriganHeadersStyles.innerHTML = `
@font-face { @font-face {
font-family: 'OpenDyslexic'; font-family: 'OpenDyslexic';
src: url('https://cdn.nhcarrigan.com/fonts/OpenDyslexicMono-Regular.otf') format('opentype'); src: url('https://cdn.nhcarrigan.com/fonts/OpenDyslexicMono-Regular.otf') format('opentype');
} }
:root { :root {
--foreground: #04624f; --foreground: #2a0a18;
--background: #abfcecdd; --background: #ffb6c1bb;
} }
* { * {
@ -152,20 +176,40 @@ main {
width: 95%; width: 95%;
max-width: 1080px; max-width: 1080px;
margin: auto; margin: auto;
margin-bottom: 100px; margin-bottom: 85px;
padding: 10px; padding: 10px;
} }
footer { footer {
width: 100%; width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
color: var(--foreground); color: var(--foreground);
background-color: var(--background); background-color: var(--background);
position: fixed; position: fixed;
bottom: 0; bottom: 0;
height: 75px; height: 75px;
padding-left: 100px; padding: 0 10px;
}
#footer-inner-container {
display: flex;
align-items: center;
justify-content: space-between;
height: 75px;
}
#footer-badge-container {
display: grid;
grid-template-columns: repeat(8, 1fr);
align-items: center;
justify-content: space-around;
}
#show-socials-button, #theme-select-button {
background: none;
border: none;
cursor: url('https://cdn.nhcarrigan.com/cursors/pointer.cur'), pointer;
color: var(--foreground);
font-size: 1rem;
font-family: 'OpenDyslexic', monospace;
}
#show-socials-button > i, #theme-select-button > i {
font-size: 1.5rem;
} }
a { a {
color: unset; color: unset;
@ -175,18 +219,51 @@ a {
display: flex; display: flex;
align-items: center; align-items: center;
} }
#audio-theme-button { #social-list {
background: none; position: absolute;
border: none; bottom: 75px;
cursor: url('https://cdn.nhcarrigan.com/cursors/pointer.cur'), pointer; left: 0;
right: 0;
width: 100vw;
max-width: 400px;
padding: 10px;
background-color: var(--background);
color: var(--foreground); color: var(--foreground);
border-radius: 10px;
border: 1px solid var(--foreground);
display: none;
z-index: 1000;
} }
@media screen and (max-width: 600px) { .social-list-item {
padding: 10px;
}
.social-list-item > a {
display: flex;
align-items: center;
justify-content: space-between;
text-decoration: none;
}
.social-list-divider {
border: 0.5px solid var(--foreground);
}
.social-list-item:hover {
background-color: var(--foreground);
color: var(--background);
}
.is-dark {
--foreground: #ffb6c1;
--background: #2a0a18bb;
}
@media screen and (max-width: 625px) {
#tree-nation-offset-website { #tree-nation-offset-website {
display: none; display: none;
} }
footer { footer, #footer-inner-container {
padding-right: 100px; height: 50px;
justify-content: space-around;
}
main {
margin-bottom: 60px;
} }
} }
`; `;
@ -195,44 +272,86 @@ a {
// #region Components // #region Components
const footer = document.createElement("footer"); const nhcarriganHeadersFooter = document.createElement("footer");
footer.innerHTML = ` nhcarriganHeadersFooter.innerHTML = `
<p>&copy; Naomi Carrigan</p> <div id="footer-inner-container">
<a href="https://chat.nhcarrigan.com" target="_blank" rel="noreferrer"> <p>&copy; <a href="https://nhcarrigan.com" target="_blank">Naomi Carrigan</a></p>
<i class="fa-solid fa-comments"></i> <button id="show-socials-button" type="button">
</a> <i class="fa-solid fa-share-nodes"></i> Connect with Us
<button id="audio-theme-button" type="button">
<i class="fa-solid fa-play"></i>
</button> </button>
<button id="theme-select-button" type="button">
<i id="theme-select-icon" class="fa-solid fa-moon"></i> Toggle Theme
</button>
<a href="https://buy.stripe.com/cN24iTfqu1j6b3afZ2" target="_blank" rel="noreferrer">
<img src="https://cdn.nhcarrigan.com/donate.png" alt="Donate" style="width: 70px; height: 70px;">
</a>
<div id="tree-nation-offset-website"></div> <div id="tree-nation-offset-website"></div>
</div>
<div id="social-list">
<div class="social-list-item">
<a href="https://chat.nhcarrigan.com" target="_blank" rel="noreferrer">
<i class="fa-brands fa-discord"></i><span>Join our Discord~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://forum.nhcarrigan.com" target="_blank" rel="noreferrer">
<i class="fa-brands fa-discourse"></i><span>Sign up for our forum~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://git.nhcarrigan.com" target="_blank" rel="noreferrer">
<i class="fa-brands fa-git-alt"></i><span>Check out our source code~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://bsky.app/profile/nhcarrigan.com" target="_blank" rel="noreferrer">
<i class="fa-brands fa-bluesky"></i><span>Follow us on Bluesky~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://www.linkedin.com/company/nhcarrigan" target="_blank" rel="noreferrer">
<i class="fa-brands fa-linkedin"></i><span>Connect with us on LinkedIn~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://www.reddit.com/r/nhcarrigan/" target="_blank" rel="noreferrer">
<i class="fa-brands fa-reddit"></i><span>Join our subreddit~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://www.youtube.com/@naomilgbt" target="_blank" rel="noreferrer">
<i class="fa-brands fa-youtube"></i><span>Subscribe to our YouTube~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://twitch.tv/naomilgbt" target="_blank" rel="noreferrer">
<i class="fa-brands fa-twitch"></i><span>Subscribe to our Twitch~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://x.com/nhcarrigan1" target="_blank" rel="noreferrer">
<i class="fa-brands fa-twitter"></i><span>We are even on Twitter~!</span>
</a>
</div>
</div>
`; `;
const videoOverlay = document.createElement("video");
videoOverlay.autoplay = true;
videoOverlay.loop = true;
videoOverlay.muted = true;
videoOverlay.playsInline = true;
videoOverlay.src = "https://cdn.nhcarrigan.com/overlay.webm";
videoOverlay.style.pointerEvents = "none";
videoOverlay.style.position = "fixed";
videoOverlay.style.top = "0";
videoOverlay.style.left = "0";
videoOverlay.style.opacity = "0.25";
videoOverlay.style.width = "100vw";
videoOverlay.style.height = "100vh";
videoOverlay.style.objectFit = "cover";
// #endregion
// #region Scripts // #region Scripts
const treeNation = document.createElement("script"); const nhcarriganHeadersTreeNation = document.createElement("script");
treeNation.src nhcarriganHeadersTreeNation.src
= "https://widgets.tree-nation.com/js/widgets/v1/widgets.min.js?v=1.0"; = "https://widgets.tree-nation.com/js/widgets/v1/widgets.min.js?v=1.0";
const treeNationBottom = document.createElement("script"); const nhcarriganHeadersTreeNationBottom = document.createElement("script");
treeNationBottom.defer = true; nhcarriganHeadersTreeNationBottom.defer = true;
treeNationBottom.async = true; nhcarriganHeadersTreeNationBottom.async = true;
treeNationBottom.innerHTML = ` nhcarriganHeadersTreeNationBottom.innerHTML = `
const interval = setInterval(() => { const interval = setInterval(() => {
const tree = document.querySelector("#tree-nation-offset-website"); const tree = document.querySelector("#tree-nation-offset-website");
if (!tree) { if (!tree) {
@ -247,74 +366,230 @@ const interval = setInterval(() => {
clearInterval(interval); clearInterval(interval);
}, 1000); }, 1000);
`; `;
const fontAwesome = document.createElement("script"); const nhcarriganHeadersFontAwesome = document.createElement("script");
fontAwesome.src = "https://kit.fontawesome.com/f949111719.js"; nhcarriganHeadersFontAwesome.src = "https://kit.fontawesome.com/f949111719.js";
const hubspot = document.createElement("script"); const nhcarriganHeadersAnalytics = document.createElement("script");
hubspot.src = "https://js.hs-scripts.com/47086586.js"; nhcarriganHeadersAnalytics.defer = true;
hubspot.async = true; nhcarriganHeadersAnalytics.setAttribute("domain", "nhcarrigan.com");
hubspot.defer = true; nhcarriganHeadersAnalytics.src
hubspot.id = "hs-script-loader";
const analytics = document.createElement("script");
analytics.defer = true;
analytics.setAttribute("domain", "nhcarrigan.com");
analytics.src
// eslint-disable-next-line stylistic/max-len // eslint-disable-next-line stylistic/max-len
= "https://analytics.nhcarrigan.com/js/script.file-downloads.hash.outbound-links.pageview-props.revenue.tagged-events.js"; = "https://analytics.nhcarrigan.com/js/script.file-downloads.hash.outbound-links.pageview-props.revenue.tagged-events.js";
const analytics2 = document.createElement("script"); nhcarriganHeadersAnalytics.setAttribute(
analytics2.innerHTML = ` "event-domain",
nhcarriganHeadersHostname,
);
nhcarriganHeadersAnalytics.setAttribute("data-domain", "nhcarrigan.com");
nhcarriganHeadersAnalytics.setAttribute(
"event-page",
nhcarriganHeadersTitle?.innerText ?? "Unknown Page",
);
nhcarriganHeadersAnalytics.setAttribute(
"event-path",
nhcarriganHeadersPathname,
);
const nhcarriganHeadersAnalytics2 = document.createElement("script");
nhcarriganHeadersAnalytics2.innerHTML = `
window.plausible = window.plausible ?? window.plausible = window.plausible ??
function() { function() {
(window.plausible.q = window.plausible.q ?? []).push(arguments) (window.plausible.q = window.plausible.q ?? []).push(arguments)
} }
`; `;
const nhcarriganHeadersGoogleAdsense = document.createElement("script");
nhcarriganHeadersGoogleAdsense.async = true;
nhcarriganHeadersGoogleAdsense.src
// eslint-disable-next-line stylistic/max-len -- big boi string
= "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3569924701890974";
nhcarriganHeadersGoogleAdsense.setAttribute("crossorigin", "anonymous");
// #endregion // #endregion
// #region Inject Elements // #region Inject Elements
head?.appendChild(openGraphTitle); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersOpenGraphTitle);
head?.appendChild(openGraphDescription); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersOpenGraphDescription);
head?.appendChild(openGraphImage); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersOpenGraphImage);
head?.appendChild(openGraphUrl); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersOpenGraphUrl);
head?.appendChild(openGraphType); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersOpenGraphType);
head?.appendChild(twitterCard); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterCard);
head?.appendChild(twitterDomain); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterDomain);
head?.appendChild(twitterUrl); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterUrl);
head?.appendChild(twitterTitle); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterTitle);
head?.appendChild(twitterDescription); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterDescription);
head?.appendChild(twitterImage); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterImage);
head?.appendChild(favicon); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersFavicon);
head?.appendChild(appleTouchIcon); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersAppleTouchIcon);
head?.appendChild(smallIcon); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersSmallIcon);
head?.appendChild(largeIcon); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersLargeIcon);
head?.appendChild(styles); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersStyles);
head?.appendChild(treeNation); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTreeNation);
head?.appendChild(fontAwesome); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersFontAwesome);
head?.appendChild(hubspot); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersAnalytics);
head?.appendChild(analytics); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersAnalytics2);
head?.appendChild(analytics2); nhcarriganHeadersHead?.appendChild(nhcarriganHeadersGoogleAdsense);
body?.appendChild(footer); nhcarriganHeadersBody?.appendChild(nhcarriganHeadersFooter);
body?.appendChild(videoOverlay); nhcarriganHeadersBody?.appendChild(nhcarriganHeadersTreeNationBottom);
body?.appendChild(treeNationBottom);
// #endregion // #endregion
// #region Audio // #region Theme
const playButton = document.querySelector("#audio-theme-button"); const nhcarriganHeadersThemeButton = document.querySelector(
const audio = new Audio("https://cdn.nhcarrigan.com/theme.mp3"); "#theme-select-button",
let playing = false; );
playButton?.addEventListener("click", () => { const nhcarriganHeadersThemeIcon = document.querySelector("#theme-select-icon");
if (playing) { if (localStorage.getItem("theme") === "dark") {
audio.pause(); nhcarriganHeadersThemeIcon?.classList.remove("fa-moon");
playing = false; nhcarriganHeadersThemeIcon?.classList.add("fa-sun");
playButton.innerHTML = "<i class=\"fa-solid fa-play\"></i>"; document.querySelector("html")?.classList.add("is-dark");
} else { }
void audio.play(); const nhcarriganHeadersToggleTheme = (): void => {
playing = true; const nhcarriganHeadersCurrentTheme = localStorage.getItem("theme");
playButton.innerHTML = "<i class=\"fa-solid fa-pause\"></i>"; if (nhcarriganHeadersCurrentTheme === "dark") {
localStorage.setItem("theme", "light");
nhcarriganHeadersThemeIcon?.classList.remove("fa-sun");
nhcarriganHeadersThemeIcon?.classList.add("fa-moon");
document.querySelector("html")?.classList.remove("is-dark");
return;
} }
}); localStorage.setItem("theme", "dark");
nhcarriganHeadersThemeIcon?.classList.remove("fa-moon");
nhcarriganHeadersThemeIcon?.classList.add("fa-sun");
document.querySelector("html")?.classList.add("is-dark");
};
nhcarriganHeadersThemeButton?.addEventListener(
"click",
nhcarriganHeadersToggleTheme,
);
const nhcarriganHeadersPrefersDark = window.matchMedia(
"(prefers-color-scheme: dark)",
);
if (
nhcarriganHeadersPrefersDark.matches
&& localStorage.getItem("theme") !== null
) {
localStorage.setItem("theme", "dark");
nhcarriganHeadersThemeIcon?.classList.remove("fa-moon");
nhcarriganHeadersThemeIcon?.classList.add("fa-sun");
document.querySelector("html")?.classList.add("is-dark");
}
// #endregion
// #region Social Toggle
const nhcarriganHeadersShowSocialsButton
= document.querySelector<HTMLButtonElement>("#show-socials-button");
const nhcarriganHeadersSocialList
= document.querySelector<HTMLDivElement>("#social-list");
const nhcarriganHeadersToggleSocials = (): void => {
if (!nhcarriganHeadersSocialList) {
throw new Error("Social list element not found.");
}
if (nhcarriganHeadersSocialList.style.display === "block") {
nhcarriganHeadersSocialList.style.display = "none";
nhcarriganHeadersShowSocialsButton?.setAttribute("aria-expanded", "false");
nhcarriganHeadersShowSocialsButton?.setAttribute(
"aria-label",
"Show Socials",
);
return;
}
nhcarriganHeadersSocialList.style.display = "block";
nhcarriganHeadersShowSocialsButton?.setAttribute("aria-expanded", "true");
nhcarriganHeadersShowSocialsButton?.setAttribute(
"aria-label",
"Hide Socials",
);
};
nhcarriganHeadersShowSocialsButton?.addEventListener(
"click",
nhcarriganHeadersToggleSocials,
);
// #region CTA
const nhcarriganHeadersCta = document.createElement("dialog");
nhcarriganHeadersCta.style.position = "fixed";
nhcarriganHeadersCta.style.top = "50%";
nhcarriganHeadersCta.style.left = "50%";
nhcarriganHeadersCta.style.transform = "translate(-50%, -50%)";
nhcarriganHeadersCta.style.padding = "10px";
nhcarriganHeadersCta.style.borderRadius = "10px";
nhcarriganHeadersCta.style.backgroundColor = "var(--background)";
nhcarriganHeadersCta.style.color = "var(--foreground)";
nhcarriganHeadersCta.style.textAlign = "center";
nhcarriganHeadersCta.style.width = "95%";
nhcarriganHeadersCta.style.maxWidth = "400px";
nhcarriganHeadersCta.id = "community-cta";
nhcarriganHeadersCta.innerHTML = `
<h1 autofocus>Hello~!</h1>
<div style="display: flex; justify-content: space-around; margin-bottom: 10px;">
<img src="https://cdn.nhcarrigan.com/logo.png" alt="NHCarrigan Logo" style="width: 100px; height: 100px;">
<p>
Consider joining our community so you can keep up to date on all of our latest activities!
</p>
</div>
<a href="https://chat.nhcarrigan.com" target="_blank" rel="noreferrer" style="padding: 10px; background: var(--foreground); color: var(--background); outline: none">Okay, take me there~!</a>
`;
const nhcarriganHeadersModalBg = document.createElement("div");
nhcarriganHeadersModalBg.style.zIndex = "4999";
nhcarriganHeadersModalBg.style.position = "fixed";
nhcarriganHeadersModalBg.style.top = "0";
nhcarriganHeadersModalBg.style.left = "0";
nhcarriganHeadersModalBg.style.width = "100vw";
nhcarriganHeadersModalBg.style.height = "100vh";
nhcarriganHeadersModalBg.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
nhcarriganHeadersModalBg.style.display = "none";
nhcarriganHeadersModalBg.id = "modal-bg";
const nhcarriganHeadersCloseModal = (): void => {
nhcarriganHeadersCta.close();
nhcarriganHeadersModalBg.style.display = "none";
};
const nhcarriganHeadersHandleModalClick = (event: MouseEvent): void => {
event.stopPropagation();
if (event.target === nhcarriganHeadersCta) {
nhcarriganHeadersCloseModal();
}
};
const nhcarriganHeadersShowModal = (): void => {
const nhcarriganHeadersLastShown = Number.parseInt(
localStorage.getItem("naomi-community-cta") ?? "0",
10,
);
const nhcarriganHeadersLastShownDate = new Date(nhcarriganHeadersLastShown);
const nhcarriganHeadersDiff
= Date.now() - nhcarriganHeadersLastShownDate.getTime();
console.table({
diff: nhcarriganHeadersDiff,
lastShown: nhcarriganHeadersLastShown,
lastShownDate: nhcarriganHeadersLastShownDate,
});
// We only want to show this once a week.
if (nhcarriganHeadersDiff < 1000 * 60 * 60 * 24 * 7) {
return;
}
nhcarriganHeadersCta.showModal();
nhcarriganHeadersModalBg.style.display = "block";
nhcarriganHeadersModalBg.addEventListener(
"click",
nhcarriganHeadersCloseModal,
);
const nhcarriganHeadersCloseButton
= nhcarriganHeadersCta.querySelector("button");
nhcarriganHeadersCloseButton?.addEventListener(
"click",
nhcarriganHeadersCloseModal,
);
nhcarriganHeadersCta.addEventListener(
"click",
nhcarriganHeadersHandleModalClick,
);
localStorage.setItem("naomi-community-cta", Date.now().toString());
};
nhcarriganHeadersBody?.appendChild(nhcarriganHeadersCta);
nhcarriganHeadersBody?.appendChild(nhcarriganHeadersModalBg);
nhcarriganHeadersShowModal();