4 Commits

Author SHA1 Message Date
dab3d14b7e non prog 2026-04-02 16:51:12 -05:00
58f55cba47 prog 2026-03-19 22:35:27 -05:00
7641641ccd test 2026-03-19 14:27:41 -05:00
507caba410 refactor 2026-03-19 14:09:28 -05:00
27 changed files with 1168 additions and 1366 deletions

View File

@@ -1,25 +0,0 @@
name: CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v2.3.1
- name: Install and Build
run: |
npm install
npm run compile
- name: Deploy
uses: JamesIves/github-pages-deploy-action@4.1.0
with:
branch: gh-pages
folder: dist

226
dist/index.html vendored
View File

@@ -1,226 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<!--
Copyright (c) 2021 Dominic Masters
This software is released under the MIT License.
https://opensource.org/licenses/MIT
-->
<head>
<title>domsPlace, Personal Website of Technical Lead Dominic Masters</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Personal website of Technical Lead, Dominic Masters, eCommerce Senior Full-Stack Developer using modern web technologies.">
<style type="text/css">/* Elements */
* {
box-sizing: border-box;
}
html,body {
margin: 0;
padding: 0;
font-size: 16px;
line-height: 1.6;
background: #000;
color: #DDD;
font-family: Arial, Helvetica, sans-serif;
}
a {
color: #56C0F1;
text-decoration: underline;
}
a:visited,a:active { color: #D84DAC; }
a:hover { text-decoration: none; }
/* Objects */
.o-break {
padding: 3rem 0;
}
/* Components */
.c-header {
text-align: center;
padding: 4rem 1rem;
font-weight: bold;
max-width: 800px;
margin: auto;
}
.c-header__title {
font-size: 1.5rem;
}
.c-header__title-link {
color: inherit;
text-decoration: none;
}
.c-header__title-link:visited,.c-header__title-link:active {
color: inherit;
}
.c-header__subtitle {
font-style: italic;
}
.c-main {
max-width: 800px;
margin: auto;
padding: 0 1em;
}
.c-footer {
text-align: center;
padding: 12rem 1rem 10rem;
font-weight: bold;
max-width: 800px;
margin: auto;
}
.c-footer__link + .c-footer__link {
margin-left: 0.5rem;
}
</style>
</head>
<body>
<header class="c-header">
<p class="c-header__title">
<a href="/" class="c-header__title-link">
domsPlace, Personal website of Dominic Masters.
</a>
</p>
<p class="c-header__subtitle">
Wow, I can't believe it's not React!
</p>
</header>
<main class="c-main">
<section>
<h1>Dominic Masters</h1>
<h2>Developer, Nerd, Occasionally Funny.</h2>
<p>
Australian-American nerd with a passion for all things computing, coffee and
video games. Currently residing in Los Angeles, California.
</p>
<p>
Programming since before the internet was cool.
</p>
</section>
<div class="o-break"></div>
<section>
<h2>Programming Technical Lead</h2>
<p>
I am a programmer born and bred. Developing since I was old enough to
type on a keyboard and continue to refine my skills more and more
every day. Backed by 14 years of experience and countless lines of code
committed.
</p>
<p>
Background with more anagrams than a bureaucrat, here are some of the
highlights only;
<ul>
<li>eCommerce ( Shopify, Magento, WooCommerce, neto )</li>
<li>Traditional Web ( HTML, CSS, JS )</li>
<li>Modern Web ( ES6, TS, GraphQL )</li>
<li>Server Side ( NodeJS, PHP, .NET )</li>
<li>Client Side ( C/C++, Java )</li>
<li>Database Side ( SQL, NoSQL )</li>
<li>Frameworks ( React, Web Components )</li>
<li>Antiques ( Lua, jQuery, SOAPXML )</li>
<li>Flashy ( OpenGL, Vulkan, Unity )</li>
<li>Boring ( Google Data Studio )</li>
<li>Servers ( AWS, CloudFlare, Heroku, Azure )</li>
<li>Every "API"</li>
<li>Ask me for a comprehensive list.</li>
</ul>
</p>
</section>
<div class="o-break"></div>
<section>
<h2>Shopify Expert</h2>
<p>
I'm currently working full-time as a Technical Lead working with the
Shopify Plus platform. I have been working with the platform
every day for over 4 years, and enjoy working with it immensely.
</p>
<p>
Working with Liquid, REST and GraphQL App Development and general
Shopify consulting. I have had the privilage of working with countless
Shopify Plus clients.
</p>
<p>
Despite Shopify's reputation, I have been able to make it do tricks
thought impossible. I find working with Shopify's unique quirks very
rewarding, and continuously find ways to surprise everyone, including
myself.
</p>
</section>
<div class="o-break"></div>
<section>
<h2>Personal</h2>
<p>
I spend most of my spare time considering programming concepts, and
take a very pragmatic approach to planning software development. I
enjoy unpacking what programming itself means and entails to come up
with new paradigms to use in my job.
</p>
<p>
I also have an appreciation for the raw power of technology and making
something beautiful and intricate out of the minimal amount of tools.
Hence this website appearing so basic.
</p>
<p>
I also enjoy video games and antique electronics. I still own four old
CRT Monitors that I love and use as my primary video game displays.
</p>
</section>
<div class="o-break"></div>
<section>
<h2>Social</h2>
<p>
I don't do social media often, however you can find me below.
For business enquiries contact I suggest LinkedIn.
</p>
<p><a href="//www.linkedin.com/in/dominic-masters-86732678/">LinkedIn</a></p>
<p><a href="//github.com/YourWishes">GitHub</a></p>
</section>
</main>
<footer class="c-footer">
<p>2012 ~ 2021 - Dominic Masters</p>
<p>
<a href="/" class="c-footer__link">Home</a>
<a href="/privacy" class="c-footer__link">Privacy Policy</a>
</p>
</footer>
</body>
</html>

295
dist/privacy.html vendored
View File

@@ -1,295 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<!--
Copyright (c) 2021 Dominic Masters
This software is released under the MIT License.
https://opensource.org/licenses/MIT
-->
<head>
<title>domsPlace, Personal Website of Technical Lead Dominic Masters</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Personal website of Technical Lead, Dominic Masters, eCommerce Senior Full-Stack Developer using modern web technologies.">
<style type="text/css">/* Elements */
* {
box-sizing: border-box;
}
html,body {
margin: 0;
padding: 0;
font-size: 16px;
line-height: 1.6;
background: #000;
color: #DDD;
font-family: Arial, Helvetica, sans-serif;
}
a {
color: #56C0F1;
text-decoration: underline;
}
a:visited,a:active { color: #D84DAC; }
a:hover { text-decoration: none; }
/* Objects */
.o-break {
padding: 3rem 0;
}
/* Components */
.c-header {
text-align: center;
padding: 4rem 1rem;
font-weight: bold;
max-width: 800px;
margin: auto;
}
.c-header__title {
font-size: 1.5rem;
}
.c-header__title-link {
color: inherit;
text-decoration: none;
}
.c-header__title-link:visited,.c-header__title-link:active {
color: inherit;
}
.c-header__subtitle {
font-style: italic;
}
.c-main {
max-width: 800px;
margin: auto;
padding: 0 1em;
}
.c-footer {
text-align: center;
padding: 12rem 1rem 10rem;
font-weight: bold;
max-width: 800px;
margin: auto;
}
.c-footer__link + .c-footer__link {
margin-left: 0.5rem;
}
</style>
</head>
<body>
<header class="c-header">
<p class="c-header__title">
<a href="/" class="c-header__title-link">
domsPlace, Personal website of Dominic Masters.
</a>
</p>
<p class="c-header__subtitle">
Wow, I can't believe it's not React!
</p>
</header>
<main class="c-main">
<h1>Privacy Policy</h1>
<p>Effective date: June 27, 2018</p>
<p>
domsPlace ("us", "we", or "our") operates
the <a href="//domsplace.com">https://domsplace.com</a> website (the
"Service").
</p>
<p>
This page informs you of our policies regarding the collection, use, and
disclosure of personal data when you use our Service and the choices you
have associated with that data.
</p>
<p>
We use your data to provide and improve the Service. By using the
Service, you agree to the collection and use of information in
accordance with this policy. Unless otherwise defined in this Privacy
Policy, terms used in this Privacy Policy have the same meanings as in
our Terms and Conditions, accessible from <a href="//domsplace.com">
https://domsplace.com
</a>
</p>
<div class="o-break"></div>
<h2>Information Collection And Use</h2>
<p>
We collect several different types of information for various purposes
to provide and improve our Service to you.
</p>
<h3>Types of Data Collected</h3>
<h4>Personal Data</h4>
<p>
While using our Service, we may ask you to provide us with certain
personally identifiable information that can be used to contact or
identify you ("Personal Data"). Personally identifiable information may
include, but is not limited to:
</p>
<ul>
<li>Email address</li>
<li>First name and last name</li>
<li>Phone number</li>
<li>Cookies and Usage Data</li>
</ul>
<h4>Usage Data</h4>
<p>
We may also collect information how the Service is accessed and used =
("Usage Data"). This Usage Data may include information such as your
computer's Internet Protocol address (e.g. IP address), browser type,
browser version, the pages of our Service that you visit, the time and
date of your visit, the time spent on those pages, unique device
identifiers and other diagnostic data.
</p>
<h4>Tracking & Cookies Data</h4>
<p>
We use cookies and similar tracking technologies to track the activity
on our Service and hold certain information.
</p>
<p>
Cookies are files with small amount of data which may include an
anonymous unique identifier. Cookies are sent to your browser from a
website and stored on your device. Tracking technologies also used are
beacons, tags, and scripts to collect and track information and to
improve and analyze our Service.
</p>
<p>
You can instruct your browser to refuse all cookies or to indicate when
a cookie is being sent. However, if you do not accept cookies, you may
not be able to use some portions of our Service.
</p>
<p>Examples of Cookies we use:</p>
<ul>
<li><strong>Session Cookies.</strong> We use Session Cookies to operate our Service.</li>
<li><strong>Preference Cookies.</strong> We use Preference Cookies to remember your preferences and various settings.</li>
<li><strong>Security Cookies.</strong> We use Security Cookies for security purposes.</li>
</ul>
<div class="o-break"></div>
<h2>Use of Data</h2>
<p>domsPlace uses the collected data for various purposes:</p>
<ul>
<li>To provide and maintain the Service</li>
<li>To notify you about changes to our Service</li>
<li>To allow you to participate in interactive features of our Service when you choose to do so</li>
<li>To provide customer care and support</li>
<li>To provide analysis or valuable information so that we can improve the Service</li>
<li>To monitor the usage of the Service</li>
<li>To detect, prevent and address technical issues</li>
</ul>
<div class="o-break"></div>
<h2>Transfer Of Data</h2>
<p>Your information, including Personal Data, may be transferred to — and maintained on — computers located outside of your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from your jurisdiction.</p>
<p>If you are located outside Australia and choose to provide information to us, please note that we transfer the data, including Personal Data, to Australia and process it there.</p>
<p>Your consent to this Privacy Policy followed by your submission of such information represents your agreement to that transfer.</p>
<p>domsPlace will take all steps reasonably necessary to ensure that your data is treated securely and in accordance with this Privacy Policy and no transfer of your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of your data and other personal information.</p>
<div class="o-break"></div>
<h2>Disclosure Of Data</h2>
<h3>Legal Requirements</h3>
<p>domsPlace may disclose your Personal Data in the good faith belief that such action is necessary to:</p>
<ul>
<li>To comply with a legal obligation</li>
<li>To protect and defend the rights or property of domsPlace</li>
<li>To prevent or investigate possible wrongdoing in connection with the Service</li>
<li>To protect the personal safety of users of the Service or the public</li>
<li>To protect against legal liability</li>
</ul>
<div class="o-break"></div>
<h2>Security Of Data</h2>
<p>The security of your data is important to us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While we strive to use commercially acceptable means to protect your Personal Data, we cannot guarantee its absolute security.</p>
<div class="o-break"></div>
<h2>Service Providers</h2>
<p>We may employ third party companies and individuals to facilitate our Service ("Service Providers"), to provide the Service on our behalf, to perform Service-related services or to assist us in analyzing how our Service is used.</p>
<p>These third parties have access to your Personal Data only to perform these tasks on our behalf and are obligated not to disclose or use it for any other purpose.</p>
<h3>Analytics</h3>
<p>We may use third-party Service Providers to monitor and analyze the use of our Service.</p>
<ul>
<li>
<p><strong>Google Analytics</strong></p>
<p>Google Analytics is a web analytics service offered by Google that tracks and reports website traffic. Google uses the data collected to track and monitor the use of our Service. This data is shared with other Google services. Google may use the collected data to contextualize and personalize the ads of its own advertising network.</p>
<p>You can opt-out of having made your activity on the Service available to Google Analytics by installing the Google Analytics opt-out browser add-on. The add-on prevents the Google Analytics JavaScript (ga.js, analytics.js, and dc.js) from sharing information with Google Analytics about visits activity.</p>
<p>For more information on the privacy practices of Google, please visit the Google Privacy & Terms web page: <a href="https://policies.google.com/privacy?hl=en" target="_blank" rel="noopener">https://policies.google.com/privacy?hl=en</a>
</p>
</li>
</ul>
<div class="o-break"></div>
<h2>Links To Other Sites</h2>
<p>Our Service may contain links to other sites that are not operated by us. If you click on a third party link, you will be directed to that third party's site. We strongly advise you to review the Privacy Policy of every site you visit.</p>
<p>We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.</p>
<div class="o-break"></div>
<h2>Children's Privacy</h2>
<p>Our Service does not address anyone under the age of 18 ("Children").</p>
<p>We do not knowingly collect personally identifiable information from anyone under the age of 18. If you are a parent or guardian and you are aware that your Children has provided us with Personal Data, please contact us. If we become aware that we have collected Personal Data from children without verification of parental consent, we take steps to remove that information from our servers.</p>
<div class="o-break"></div>
<h2>Changes To This Privacy Policy</h2>
<p>We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page.</p>
<p>We will let you know via email and/or a prominent notice on our Service, prior to the change becoming effective and update the "effective date" at the top of this Privacy Policy.</p>
<p>You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.</p>
</main>
<footer class="c-footer">
<p>2012 ~ 2021 - Dominic Masters</p>
<p>
<a href="/" class="c-footer__link">Home</a>
<a href="/privacy" class="c-footer__link">Privacy Policy</a>
</p>
</footer>
</body>
</html>

View File

@@ -4,13 +4,16 @@
"main": "index.js", "main": "index.js",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"start": "ts-node ./src/serve", "start:dev": "nodemon --exec ts-node ./src/index.ts"
"compile": "ts-node ./src/compile" },
"devDependencies": {
"@types/express": "^5.0.6",
"@types/node": "^25.5.0",
"nodemon": "^3.1.14",
"ts-node": "^10.9.2",
"typescript": "^5.9.3"
}, },
"dependencies": { "dependencies": {
"@types/express": "^4.17.11", "express": "^5.2.1"
"express": "^4.17.3",
"ts-node": "^9.1.1",
"typescript": "^4.2.3"
} }
} }

View File

@@ -1,3 +0,0 @@
import { pageCompileAll } from "./compiler";
pageCompileAll();

View File

@@ -1,75 +0,0 @@
// Copyright (c) 2021 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
import * as fs from 'fs';
import * as path from 'path';
// Path Definitions
export const PATH_HTML = path.join(__dirname, 'html');
export const PATH_CSS = path.join(__dirname, 'css');
export const PATH_OUT = path.join(__dirname, '..', 'dist');
// Load Methods
const loadFile = (dir:string, ext:string) => {
const bits = dir.split('/').join('\\').split('\\');
const fileName = bits.pop();
const fullDir = path.join(__dirname, ...bits, `${fileName}.${ext}`);
if(!fs.existsSync(fullDir)) return '';
return fs.readFileSync(fullDir, 'utf-8');
}
const loadHtml = (fn:string) => loadFile(fn, 'html');
const loadCss = (fn:string) => loadFile(fn, 'css');
const pageCompile = (page:string) => {
// Load Layout
const htmlLayout = loadHtml('partials/layout/layout');
const cssLayout = loadCss('partials/layout/layout');
// Load Page
const htmlPage = loadHtml(`pages/${page}/${page}`);
const cssPage = loadCss(`pages/${page}/${page}`);
// Inject styles
let compiled = htmlLayout.replace('{{styles}}', [
cssLayout,
cssPage
].join('\n'));
// Inject content
compiled = compiled.replace('{{main}}', htmlPage);
// Inject variables
Object.entries({
'year': new Date().getFullYear()
}).forEach(entry => {
const [ key, value ] = entry;
const variable = `{{${key}}}`;
while(compiled.includes(variable)) {
compiled = compiled.replace(variable, `${value}`);
}
});
return compiled;
}
const pageGenerate = (out:string, page:string) => {
const compiled = pageCompile(page);
console.log(page, '->', out);
if(!fs.existsSync(PATH_OUT)) fs.mkdirSync(PATH_OUT);
fs.writeFileSync(path.join(PATH_OUT, `${out}.html`), compiled);
}
export const pageCompileAll = () => {
// Read pages
Object.entries({
'index': 'home',
'privacy': 'privacy'
}).forEach(entry => {
const [ out, page ] = entry;
pageGenerate(out, page);
});
}

11
src/index.ts Normal file
View File

@@ -0,0 +1,11 @@
import express from 'express';
import router from './routes';
const app = express();
const port = process.env.PORT || 3000;
app.use(router);
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});

9
src/locale.ts Normal file
View File

@@ -0,0 +1,9 @@
export const LANGUAGES = <const>{
'en': 'English'
};
export type LocaleLanguage = keyof typeof LANGUAGES;
export type LocaleString = {
[K in LocaleLanguage]:string;
};

28
src/page.ts Normal file
View File

@@ -0,0 +1,28 @@
import { Request, Response } from 'express';
import { SectionData, sectionRender } from "./section";
import TemplateDefault from "./templates/default";
import { templateRender } from './template';
import { LocaleString } from './locale';
export type Page = {
title?:LocaleString;
sections:SectionData<any>[];
}
export const pageRoute = (page:Page) => {
return async (req:Request, res:Response) => {
console.log(`Got Request. Header Agent: ${req.headers['user-agent']}`);
try {
const content = await templateRender({
page,
template: TemplateDefault,
request: req,
language: 'en'
});
res.status(200).send(content);
} catch (error) {
res.status(500).send(`Internal Server Error`);
}
}
}

View File

@@ -1,106 +0,0 @@
<section>
<h1>Dominic Masters</h1>
<h2>Developer, Nerd, Occasionally Funny.</h2>
<p>
Australian-American nerd with a passion for all things computing, coffee and
video games. Currently residing in Peoria, Illinois.
</p>
<p>
Programming since before the internet was cool.
</p>
</section>
<div class="o-break"></div>
<section>
<h2>Programming Technical Lead</h2>
<p>
I am a programmer born and bred. Developing since I was old enough to
type on a keyboard and continue to refine my skills more and more
every day. Backed by 16 years of experience and countless lines of code
committed.
</p>
<p>
Background with more anagrams than a bureaucrat, here are some of the
highlights only;
<ul>
<li>eCommerce ( Shopify, Magento, WooCommerce, neto )</li>
<li>Traditional Web ( HTML, CSS, JS )</li>
<li>Modern Web ( ES6, TS, GraphQL )</li>
<li>Server Side ( NodeJS, PHP, .NET )</li>
<li>Client Side ( C/C++, Java )</li>
<li>Database Side ( SQL, NoSQL )</li>
<li>Frameworks ( React, Web Components )</li>
<li>Antiques ( Lua, jQuery, SOAPXML )</li>
<li>Flashy ( OpenGL, Vulkan, Unity )</li>
<li>Boring ( Google Data Studio )</li>
<li>Servers ( AWS, CloudFlare, Heroku, Azure )</li>
<li>Every "API"</li>
<li>Ask me for a comprehensive list.</li>
</ul>
</p>
</section>
<div class="o-break"></div>
<section>
<h2>Shopify Expert</h2>
<p>
I'm currently working full-time as a Technical Lead working with the
Shopify Plus platform. I have been working with the platform
every day for over 8 years, and enjoy working with it immensely.
</p>
<p>
Working with Liquid, REST and GraphQL App Development and general
Shopify consulting. I have had the privilage of working with countless
Shopify Plus clients.
</p>
<p>
Despite Shopify's reputation, I have been able to make it do tricks
thought impossible. I find working with Shopify's unique quirks very
rewarding, and continuously find ways to surprise everyone, including
myself.
</p>
</section>
<div class="o-break"></div>
<section>
<h2>Personal</h2>
<p>
I spend most of my spare time considering programming concepts, and
take a very pragmatic approach to planning software development. I
enjoy unpacking what programming itself means and entails to come up
with new paradigms to use in my job.
</p>
<p>
I also have an appreciation for the raw power of technology and making
something beautiful and intricate out of the minimal amount of tools.
Hence this website appearing so basic.
</p>
<p>
I also enjoy video games and antique electronics. I still own four old
CRT Monitors that I love and use as my primary video game displays.
</p>
</section>
<div class="o-break"></div>
<section>
<h2>Social</h2>
<p>
I don't do social media often, however you can find me below.
For business enquiries contact I suggest LinkedIn.
</p>
<p><a href="//www.linkedin.com/in/dominic-masters-86732678/">LinkedIn</a></p>
<p><a href="//git.wish.moe/YourWishes">GitHub</a></p>
</section>

View File

@@ -1,175 +0,0 @@
<h1>Privacy Policy</h1>
<p>Effective date: June 27, 2018</p>
<p>
domsPlace ("us", "we", or "our") operates
the <a href="//domsplace.com">https://domsplace.com</a> website (the
"Service").
</p>
<p>
This page informs you of our policies regarding the collection, use, and
disclosure of personal data when you use our Service and the choices you
have associated with that data.
</p>
<p>
We use your data to provide and improve the Service. By using the
Service, you agree to the collection and use of information in
accordance with this policy. Unless otherwise defined in this Privacy
Policy, terms used in this Privacy Policy have the same meanings as in
our Terms and Conditions, accessible from <a href="//domsplace.com">
https://domsplace.com
</a>
</p>
<div class="o-break"></div>
<h2>Information Collection And Use</h2>
<p>
We collect several different types of information for various purposes
to provide and improve our Service to you.
</p>
<h3>Types of Data Collected</h3>
<h4>Personal Data</h4>
<p>
While using our Service, we may ask you to provide us with certain
personally identifiable information that can be used to contact or
identify you ("Personal Data"). Personally identifiable information may
include, but is not limited to:
</p>
<ul>
<li>Email address</li>
<li>First name and last name</li>
<li>Phone number</li>
<li>Cookies and Usage Data</li>
</ul>
<h4>Usage Data</h4>
<p>
We may also collect information how the Service is accessed and used =
("Usage Data"). This Usage Data may include information such as your
computer's Internet Protocol address (e.g. IP address), browser type,
browser version, the pages of our Service that you visit, the time and
date of your visit, the time spent on those pages, unique device
identifiers and other diagnostic data.
</p>
<h4>Tracking & Cookies Data</h4>
<p>
We use cookies and similar tracking technologies to track the activity
on our Service and hold certain information.
</p>
<p>
Cookies are files with small amount of data which may include an
anonymous unique identifier. Cookies are sent to your browser from a
website and stored on your device. Tracking technologies also used are
beacons, tags, and scripts to collect and track information and to
improve and analyze our Service.
</p>
<p>
You can instruct your browser to refuse all cookies or to indicate when
a cookie is being sent. However, if you do not accept cookies, you may
not be able to use some portions of our Service.
</p>
<p>Examples of Cookies we use:</p>
<ul>
<li><strong>Session Cookies.</strong> We use Session Cookies to operate our Service.</li>
<li><strong>Preference Cookies.</strong> We use Preference Cookies to remember your preferences and various settings.</li>
<li><strong>Security Cookies.</strong> We use Security Cookies for security purposes.</li>
</ul>
<div class="o-break"></div>
<h2>Use of Data</h2>
<p>domsPlace uses the collected data for various purposes:</p>
<ul>
<li>To provide and maintain the Service</li>
<li>To notify you about changes to our Service</li>
<li>To allow you to participate in interactive features of our Service when you choose to do so</li>
<li>To provide customer care and support</li>
<li>To provide analysis or valuable information so that we can improve the Service</li>
<li>To monitor the usage of the Service</li>
<li>To detect, prevent and address technical issues</li>
</ul>
<div class="o-break"></div>
<h2>Transfer Of Data</h2>
<p>Your information, including Personal Data, may be transferred to — and maintained on — computers located outside of your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from your jurisdiction.</p>
<p>If you are located outside Australia and choose to provide information to us, please note that we transfer the data, including Personal Data, to Australia and process it there.</p>
<p>Your consent to this Privacy Policy followed by your submission of such information represents your agreement to that transfer.</p>
<p>domsPlace will take all steps reasonably necessary to ensure that your data is treated securely and in accordance with this Privacy Policy and no transfer of your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of your data and other personal information.</p>
<div class="o-break"></div>
<h2>Disclosure Of Data</h2>
<h3>Legal Requirements</h3>
<p>domsPlace may disclose your Personal Data in the good faith belief that such action is necessary to:</p>
<ul>
<li>To comply with a legal obligation</li>
<li>To protect and defend the rights or property of domsPlace</li>
<li>To prevent or investigate possible wrongdoing in connection with the Service</li>
<li>To protect the personal safety of users of the Service or the public</li>
<li>To protect against legal liability</li>
</ul>
<div class="o-break"></div>
<h2>Security Of Data</h2>
<p>The security of your data is important to us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While we strive to use commercially acceptable means to protect your Personal Data, we cannot guarantee its absolute security.</p>
<div class="o-break"></div>
<h2>Service Providers</h2>
<p>We may employ third party companies and individuals to facilitate our Service ("Service Providers"), to provide the Service on our behalf, to perform Service-related services or to assist us in analyzing how our Service is used.</p>
<p>These third parties have access to your Personal Data only to perform these tasks on our behalf and are obligated not to disclose or use it for any other purpose.</p>
<h3>Analytics</h3>
<p>We may use third-party Service Providers to monitor and analyze the use of our Service.</p>
<ul>
<li>
<p><strong>Google Analytics</strong></p>
<p>Google Analytics is a web analytics service offered by Google that tracks and reports website traffic. Google uses the data collected to track and monitor the use of our Service. This data is shared with other Google services. Google may use the collected data to contextualize and personalize the ads of its own advertising network.</p>
<p>You can opt-out of having made your activity on the Service available to Google Analytics by installing the Google Analytics opt-out browser add-on. The add-on prevents the Google Analytics JavaScript (ga.js, analytics.js, and dc.js) from sharing information with Google Analytics about visits activity.</p>
<p>For more information on the privacy practices of Google, please visit the Google Privacy & Terms web page: <a href="https://policies.google.com/privacy?hl=en" target="_blank" rel="noopener">https://policies.google.com/privacy?hl=en</a>
</p>
</li>
</ul>
<div class="o-break"></div>
<h2>Links To Other Sites</h2>
<p>Our Service may contain links to other sites that are not operated by us. If you click on a third party link, you will be directed to that third party's site. We strongly advise you to review the Privacy Policy of every site you visit.</p>
<p>We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.</p>
<div class="o-break"></div>
<h2>Children's Privacy</h2>
<p>Our Service does not address anyone under the age of 18 ("Children").</p>
<p>We do not knowingly collect personally identifiable information from anyone under the age of 18. If you are a parent or guardian and you are aware that your Children has provided us with Personal Data, please contact us. If we become aware that we have collected Personal Data from children without verification of parental consent, we take steps to remove that information from our servers.</p>
<div class="o-break"></div>
<h2>Changes To This Privacy Policy</h2>
<p>We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page.</p>
<p>We will let you know via email and/or a prominent notice on our Service, prior to the change becoming effective and update the "effective date" at the top of this Privacy Policy.</p>
<p>You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.</p>

View File

@@ -1,74 +0,0 @@
/* Elements */
* {
box-sizing: border-box;
}
html,body {
margin: 0;
padding: 0;
font-size: 16px;
line-height: 1.6;
background: #000;
color: #DDD;
font-family: Arial, Helvetica, sans-serif;
}
a {
color: #56C0F1;
text-decoration: underline;
}
a:visited,a:active { color: #D84DAC; }
a:hover { text-decoration: none; }
/* Objects */
.o-break {
padding: 3rem 0;
}
/* Components */
.c-header {
text-align: center;
padding: 4rem 1rem;
font-weight: bold;
max-width: 800px;
margin: auto;
}
.c-header__title {
font-size: 1.5rem;
}
.c-header__title-link {
color: inherit;
text-decoration: none;
}
.c-header__title-link:visited,.c-header__title-link:active {
color: inherit;
}
.c-header__subtitle {
font-style: italic;
}
.c-main {
max-width: 800px;
margin: auto;
padding: 0 1em;
}
.c-footer {
text-align: center;
padding: 12rem 1rem 10rem;
font-weight: bold;
max-width: 800px;
margin: auto;
}
.c-footer__link + .c-footer__link {
margin-left: 0.5rem;
}

View File

@@ -1,47 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<!--
Copyright (c) 2021 Dominic Masters
This software is released under the MIT License.
https://opensource.org/licenses/MIT
-->
<head>
<title>domsPlace, Personal Website of Technical Lead Dominic Masters</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Personal website of Technical Lead, Dominic Masters, eCommerce Senior Full-Stack Developer using modern web technologies.">
<style type="text/css">{{styles}}</style>
</head>
<body>
<header class="c-header">
<p class="c-header__title">
<a href="/" class="c-header__title-link">
domsPlace, Personal website of Dominic Masters.
</a>
</p>
<p class="c-header__subtitle">
Wow, I can't believe it's not React!
</p>
</header>
<main class="c-main">
{{main}}
</main>
<footer class="c-footer">
<p>2012 ~ {{year}} - Dominic Masters</p>
<p>
<a href="/" class="c-footer__link">Home</a>
<a href="/privacy" class="c-footer__link">Privacy Policy</a>
</p>
</footer>
</body>
</html>

16
src/routes/blog/index.ts Normal file
View File

@@ -0,0 +1,16 @@
import { Router } from "express";
import { Page, pageRoute } from "../../page";
import SnesDigitalAudioMod from "./snes-digital-audio-mod";
const ThisPage:Page = {
sections: [
]
};
const router = Router();
router.get('/', pageRoute(ThisPage));
router.get('/snes-digital-audio-mod', SnesDigitalAudioMod);
export default router;

View File

@@ -0,0 +1,147 @@
import { Router } from "express";
import { Page, pageRoute } from "../../../page";
/*
# Super Famicom / SNES Digital Audio Mod
Recently I have been revisiting some of my favorite retro game consoles, mostly
due to reorganising my loungeroom. Probably one I wish I spent more time playing
was the Super Nintendo, and I wanted to address some of the problems with my Japanese Super Famicom (SFC).
Primarily the problems stemmed from less than ideal quality, this is due to the
SNES's well known [terrible image softening](https://www.chrismcovell.com/gotRGB/snesblur.html)
and that my SFC was pretty yellowed.
Second issue was my audio. I have some somewhat decent SCART cables I use but
the static caused by interference from the analogue audio was definitely not
ideal. I wanted to bypass the SNES's analogue audio, and hear the digital audio
from the console. To achieve this I needed to perform a digital audio mod.
Finally, I wanted to challenge my soldering skills a bit more, and so I decided
to tackle these issues all at the same time.
## The plan
To address the image quality issue, I wanted to get a [1chip SFC](https://consolemods.org/wiki/SNES:SNES_Model_Differences#Comparisons).
These 1chip systems are named after their motherboard, which were the names
used on later revision motherboards that had noticeably improved image quality.
These later revisions integrate the video circuitry into a single chip, reducing
signal noise and resulting in a noticeably sharper image while retaining RGB output
Second, I had been aware of Digital Audio Mods for the SNES for a while, but
they all typically involved cutting the case of the system to accommodate a full
TOSLINK connector, which I wanted to avoid doing where possible, and keep the
original case intact.
Finally, I had been made aware that [The Retro Channel](https://www.chrismcovell.com/gotRGB/snesblur.html)
had a no-cut SNES digital audio mod, that replaced the RF module of the SNES, and
did not require cutting.
## New Super Famicom
There are really only three ways to get a 1chip SNES;
1) Run the motherboard lottery, where you purchase a SNES, then have to open it and check if it is a
1chip variant or not.
2) Purchase a SNES/SFC Jr. and mod it for RGB support.
3) Purchase a 1chip SNES/SFC from a reseller.
I decided to go with option 3, really wanted to keep the original SFC and didn't
want to spend a fortune trying to find a 1chip myself. I ended up purchasing a
1chip SFC from an eBay reseller for around $100 USD, not too bad considering they
can go for significantly more.
Originally I had planned to also do a full recap of the system, to extend its
life. This is definitely a moment where I realised that buying a 1chip from a
reseller was maybe not the best idea.
Upon opening the system I found it had been recapped already, but the quality of
the work left a lot to be desired. The recap solder points were very messy with
way too much solder remaining on the board. The legs of the caps were also left
rather long and get close to interfering with the RF Shield. I have not yet
recapped the system but I definitely plan to do so in the near future.
Other than the iffy recap job however, the system worked fine and the image
quality compared to my previous SFC was significantly improved.
## Digital Audio Mod
The SNES typically outputs line level audio through the AV port in stereo. This
is fine but the Digital Signal Processor (DSP) chip in the SNES is capable of
producing much higher quality audio, and several games use the full [32 KHz sample rate](https://www.alpha-ii.com/Info/snes-spdif.html)
that the system is capable of, but the audio the analogue output provides is
[significantly more limited](https://www.youtube.com/watch?v=6J7Sea0KniU&t=98s).
By the time I decide to purchase my 1chip, the no cut mods had sold out
unfortunately, which delayed me initially. After a few weeks however The Retro
Channel had created a new version, the [No-Cut Digital Audio Mod v2](https://lectronz.com/products/super-nintendo-digital-audio-no-cut-mod-v2)
The v1 version of the mod took over the RF connector and turned it into a
digital coaxial output, meaning that from the outside the console looked
completely stock. The new v2 version removed the entire RF module and replaced
it with a small PCB that doubled as a 3.5mm coaxial and mini-TOSLINK output,
meaning that the console looked slightly different but still required no cutting
of the case, which is nice.
I do wish I could have purchased one of the original v1 mods, but the v2 was
available and functionally is the same, so I purchased it.
## Installation
Installation was pretty straight forward, mostly following [The Retro Channel's video](https://www.youtube.com/watch?v=OXpKuyHBA48)
I was able to tackle it in an afternoon. The kit comes with all the parts you
need and it took me around an hour to install, taking my time and testing after
each solder to ensure no shorts or bad connections.
The only difficult part was soldering the three wires to the DSP chip, as they
are very close and keeping the legs apart was a bit tricky, but with patience I
got the soldering done without any issues.
## Results
From the outside it is clear the Super Famicom has been modded, but the mod is
otherwise clean and there's no damage to the case, so it looks good. I attached
a mini-TOSLINK to full size TOSLINK adapter, which hides the smaller 3.5mm size
of the connector and keeps it looking cleaner.
As for the audio, it's fantastic. This is by far the best sounding SNES audio I
have ever heard. I was worried my Sony STR-DN1040 would not like the SNES digital audio signal,
since pauses in the audio count as the digital audio stopping, but it handles it
fine and I've heard no stutters or pauses.
Finally I would be remiss if I did not mention the downsides. Really there is
only two. The obvious is the cost; the 1chip itself is expensive for a SNES and
the mod was also not cheap, then the time it took for me to install the mod was
not insignificant.
The second drawback is that the mod only provides digital audio on the audio
generated by the SNES's internal DSP chip. This is rare but the SNES could allow
games to perform their own audio processing, bypassing the SNES DSP chip entirely and
therefore not outputting through the mod. The only notable instances of this are
the Super Gameboy, which used a custom chip to emulate the Gameboy's audio on
the Super Gameboy Cartridge itself, bypassing the SNES DSP, and any games that
make use of the custom MSU-1 chip.
## Surround Sound
I want to do a full post on this in the future, but the SNES supported Dolby Pro Logic
surround sound in some games. I have yet to find a comprehensive list but definitely
Star Ocean supports it, and uses it very effectively. Over the digital audio the
surround is very clear and has a wide soundstage, it's extremely impressive for a
16-bit console.
## Conclusion
This was an expensive and time consuming mod, but it is about as close to the
perfect SNES as one can get. The only other mods I am aware of that could improve it are;
a better RGB bypass mod, similar to what the N64 RGB mods use, or a pure digital
video mod, similar to the [RetroGEM](https://www.pixelfx.co/product-page/n64-hdmi)
mods, but I am not aware of any for the SNES currently.
*/
const BlogPage:Page = {
sections: [
]
};
const router = Router();
router.get('/', pageRoute(BlogPage));
export default router;

31
src/routes/index.ts Normal file
View File

@@ -0,0 +1,31 @@
import { Router } from 'express';
import { Page, pageRoute } from '../page';
import BlogRoute from './blog';
const HomePage:Page = {
sections:[
{
type: 'hero',
properties: {
title: 'Dominic Masters\nSoftware Developer and Tinkerer.',
subtitle: `I develop all manner of things, and tinker with tech new and old.`,
buttonLeft: {
text: `View the blog`,
url: `/blog`
},
buttonRight: {
text: `About me`,
url: `/about`
}
}
}
]
};
const router = Router();
router.get('/', pageRoute(HomePage));
router.get('/blog', BlogRoute);
export default router;

57
src/section.ts Normal file
View File

@@ -0,0 +1,57 @@
import { Request } from 'express';
import HeroSection from './sections/hero';
import { Template } from './template';
import { LocaleLanguage } from './locale';
import { Page } from './page';
const SECTION_TYPES = <const>{
'hero': HeroSection
}
export type Section<P> = {
properties:P;
validate:(properties:P) => P;
};
export type SectionType = keyof typeof SECTION_TYPES;
export type SectionTypeFor<T extends SectionType> = (
typeof SECTION_TYPES[T]
);
export type SectionProperties<T extends SectionType> = (
SectionTypeFor<T>['properties']
);
export type SectionData<T extends SectionType> = {
type:T;
properties:SectionProperties<T>;
}
export type SectionRenderer<T extends SectionType> = (p:{
properties:SectionProperties<T>;
template:Template;
language:LocaleLanguage;
request:Request;
page:Page;
}) => Promise<string>;
export const sectionRender = async <T extends SectionType>(p:{
request:Request,
section:SectionData<T>;
template:Template;
language:LocaleLanguage;
page:Page;
}):Promise<string> => {
if(!p.template.sections[p.section.type]) {
console.warn(`No section renderer found for section type "${p.section.type}" in template "${p.template.name}".`);
return '';
}
const renderer = p.template.sections[p.section.type] as SectionRenderer<T>;
const properties = p.section.properties;
return await renderer({
...p,
properties: p.section.properties
});
}

29
src/sections/hero.ts Normal file
View File

@@ -0,0 +1,29 @@
import { Section } from "../section";
type HeroProperties = {
title:string|null;
subtitle:string|null;
buttonLeft?:{
text:string;
url:string;
};
buttonRight?:{
text:string;
url:string;
};
};
const HERO:Section<HeroProperties> = {
properties: {
title: '',
subtitle: '',
},
validate: props => {
if(!props.title) throw new Error('Hero section must have a title.');
return props;
}
};
export default HERO;

View File

@@ -1,20 +0,0 @@
// Copyright (c) 2021 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
import * as express from 'express';
import { pageCompileAll, PATH_OUT } from './compiler';
const app = express()
const port = 80;
app.use((req, res, next) => {
pageCompileAll();
next();
})
app.use(express.static(PATH_OUT));
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})

27
src/template.ts Normal file
View File

@@ -0,0 +1,27 @@
import { Request } from "express";
import { Page } from "./page";
import { LocaleLanguage } from "./locale";
import { Section, SectionRenderer, SectionType, SectionTypeFor } from "./section";
export type TemplateSections = {
[key in SectionType]?:SectionRenderer<key>;
};
export type Template = {
name:string;
sections:TemplateSections;
render:(p:{
page:Page;
request:Request;
language:LocaleLanguage;
}) => Promise<string>;
}
export const templateRender = (p:{
page:Page,
template:Template,
request:Request,
language:LocaleLanguage
}):Promise<string> => {
return p.template.render(p);
}

View File

@@ -0,0 +1,16 @@
import { SectionRenderer } from "../../section";
const DefaultTemplateHeroSection:SectionRenderer<'hero'> = async ({
properties,
template,
language,
request
}) => {
return [
'<div>',
`<h1>${properties.title}</h1>`,
'</div>'
].join('\n');
}
export default DefaultTemplateHeroSection;

View File

@@ -0,0 +1,42 @@
import { sectionRender } from "../../section";
import { Template } from "../../template";
import DefaultTemplateHeroSection from "./hero";
const TEMPLATE_DEFAULT:Template = {
name: 'default',
sections: {
'hero': DefaultTemplateHeroSection
},
render: async ({ language, page, request }) => {
return [
`<!DOCTYPE html>`,
`<html>`,
`<head>`,
`<meta charset="UTF-8" />`,
`<title>${page.title ? page.title[language] : 'Untitled Page'}</title>`,
`<style type="text/css">`,
`body { font-family: Arial, sans-serif; margin: 0; padding: 0; }`,
`h1 { color: #333; }`,
`</style>`,
`</head>`,
`<body>`,
`header`,
...(await Promise.all(page.sections.map(async section => {
return await sectionRender({
language,
page,
request,
template: TEMPLATE_DEFAULT,
section
});
}))),
`footer`,
`</body>`,
`</html>`
].join('\n');
}
};
export default TEMPLATE_DEFAULT;

16
src/templates/psp/hero.ts Normal file
View File

@@ -0,0 +1,16 @@
import { SectionRenderer } from "../../section";
const PSPTemplateHeroSection:SectionRenderer<'hero'> = async ({
properties,
template,
language,
request
}) => {
return [
'<div>',
`<h1>${properties.title}</h1>`,
'</div>'
].join('\n');
}
export default PSPTemplateHeroSection;

View File

@@ -0,0 +1,42 @@
import { sectionRender } from "../../section";
import { Template } from "../../template";
import PSPTemplateHeroSection from "./hero";
const TEMPLATE_PSP:Template = {
name: 'psp',
sections: {
'hero': PSPTemplateHeroSection
},
render: async ({ language, page, request }) => {
return [
`<!DOCTYPE html>`,
`<html>`,
`<head>`,
`<meta charset="UTF-8" />`,
`<title>${page.title ? page.title[language] : 'Untitled Page'}</title>`,
`<style type="text/css">`,
`body { font-family: Arial, sans-serif; margin: 0; padding: 0; }`,
`h1 { color: #333; }`,
`</style>`,
`</head>`,
`<body>`,
`header`,
...(await Promise.all(page.sections.map(async section => {
return await sectionRender({
language,
page,
request,
template: TEMPLATE_PSP,
section
});
}))),
`footer`,
`</body>`,
`</html>`
].join('\n');
}
};
export default TEMPLATE_PSP;

14
tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

988
yarn.lock

File diff suppressed because it is too large Load Diff