Major update - simpler file structure, better layouts, moar content
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
out/
|
||||
IDEAS.md
|
||||
TODO.md
|
2
.prettierignore
Normal file
@ -0,0 +1,2 @@
|
||||
*.ejs
|
||||
*.min.js
|
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Douglas Matoso
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
19
README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Mystic Site Builder (2021 Edition)
|
||||
|
||||
Micro static site generator in Node.js
|
||||
|
||||
Based on the ideas in this post: https://medium.com/douglas-matoso-english/build-static-site-generator-nodejs-8969ebe34b22
|
||||
|
||||
## Setup
|
||||
|
||||
```console
|
||||
$ npm i
|
||||
$ npm run build
|
||||
$ npm run serve
|
||||
```
|
||||
|
||||
Go to http://localhost:5000 to see the generated site.
|
||||
|
||||
## How to use
|
||||
|
||||
If you want to use NanoGen to generate your own site, just fork this repository and add your content to the `src` folder.
|
30
app.js
Normal file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
"use strict";
|
||||
|
||||
require("dotenv").config();
|
||||
|
||||
const path = require("path"),
|
||||
build = require("./lib/build"),
|
||||
serve = require("./lib/serve"),
|
||||
watch = require("./lib/watch"),
|
||||
{ log, readJsonIfExists } = require("./lib/utils"),
|
||||
{ version } = require("./package.json"),
|
||||
localConfig =
|
||||
readJsonIfExists(path.resolve(process.cwd(), "site.config.json5")) ||
|
||||
{},
|
||||
siteOpts = require("./lib/loadConfig")(localConfig, "MULE"),
|
||||
config = {
|
||||
...siteOpts,
|
||||
logFunction: log,
|
||||
},
|
||||
yargs = require("yargs")
|
||||
.version(version)
|
||||
.alias("v", "version")
|
||||
.usage("Usage: $0 start|stop")
|
||||
.demandCommand(1),
|
||||
{ argv } = yargs;
|
||||
|
||||
if (argv._[0] === "build") return build(config);
|
||||
else if (argv._[0] === "serve") return serve(config);
|
||||
else if (argv._[0] === "watch") return watch(config);
|
370
lib/build.js
Normal file
@ -0,0 +1,370 @@
|
||||
const { exists } = require("fs-extra/lib/fs");
|
||||
|
||||
module.exports = async (config) => {
|
||||
const { promises: fs } = require("fs"),
|
||||
fse = require("fs-extra"),
|
||||
path = require("path"),
|
||||
ejs = require("ejs"),
|
||||
frontMatter = require("front-matter"),
|
||||
glob = require("glob"),
|
||||
hljs = require("highlight.js"),
|
||||
md = require("markdown-it")({
|
||||
highlight: (str, lang) => {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
try {
|
||||
return hljs.highlight(str, { language: lang }).value;
|
||||
} catch (__) {}
|
||||
}
|
||||
|
||||
return ""; // use external default escaping
|
||||
},
|
||||
html: true,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
xhtmlOut: true,
|
||||
}),
|
||||
emoji = require("markdown-it-emoji"),
|
||||
// { readJsonIfExists } = require("./utils"),
|
||||
{ build, isRebuild, logFunction: log = () => {} } = config || {},
|
||||
{ outputPath, journalsPerPage = 5, srcPath } = build,
|
||||
{ site } = config,
|
||||
copyAssets = async (directory) => {
|
||||
const assets = await fs.readdir(directory);
|
||||
|
||||
assets.forEach(async (asset) => {
|
||||
// we no longer merge scripts and styles, thanks to http/2's parallel file handling
|
||||
if (asset === "_root") {
|
||||
fse.copy(path.join(srcPath, "assets", asset), outputPath);
|
||||
} else {
|
||||
fse.copy(
|
||||
path.join(srcPath, "assets", asset),
|
||||
path.join(outputPath, asset)
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
getReadTime = (text) => {
|
||||
const WPM = 275,
|
||||
fixedString = text.replace(/[^\w\s]+/g, ""),
|
||||
count = fixedString.split(/\s+/).length;
|
||||
|
||||
if (count < WPM) return "less than 1 minute";
|
||||
else return `${Math.ceil(count / WPM)} minutes`;
|
||||
},
|
||||
tagSorter = (a, b) => a.toLowerCase().localeCompare(b.toLowerCase()),
|
||||
parseFile = (file, pagePath, siteData, isSupport) => {
|
||||
const { dir, ext, name } = path.parse(file) || {},
|
||||
hasExt = name.indexOf(".") > -1,
|
||||
destPath = path.join(outputPath, dir),
|
||||
filePath = path.join(pagePath, file),
|
||||
// read page file
|
||||
data = fse.readFileSync(filePath, "utf-8"),
|
||||
// render page
|
||||
{ attributes, body } = frontMatter(data),
|
||||
{ content_type: contentType, tags: originalTags = [] } =
|
||||
attributes,
|
||||
// TODO: Look for tags in posts as well, link to them, and add them to tag pages
|
||||
tags =
|
||||
typeof originalTags === "string"
|
||||
? originalTags.split(/\W+/)
|
||||
: [].concat(originalTags),
|
||||
innerTags = (
|
||||
contentType === "journal"
|
||||
? body.match(/\b#(\w+)/g) || []
|
||||
: []
|
||||
).map((val) => val.replace("#", "")),
|
||||
allTags = [...tags, ...innerTags].sort(tagSorter),
|
||||
updatedBody =
|
||||
contentType === "journal"
|
||||
? allTags.reduce(
|
||||
(acc, tag) =>
|
||||
acc.replace(
|
||||
`#${tag}`,
|
||||
`
|
||||
<a href="/journal/tags/${tag}/index.html">
|
||||
#<span class="p-category category">${tag}</span>
|
||||
</a>`
|
||||
),
|
||||
body
|
||||
)
|
||||
: body;
|
||||
|
||||
return {
|
||||
...config,
|
||||
page: {
|
||||
name,
|
||||
...attributes,
|
||||
body: updatedBody,
|
||||
destPath,
|
||||
filePath,
|
||||
path: path.join(dir, hasExt ? name : `${name}.html`),
|
||||
tags: [...tags, ...innerTags].sort(tagSorter),
|
||||
ext,
|
||||
},
|
||||
site: {
|
||||
...site,
|
||||
pages: isSupport ? siteData : [],
|
||||
},
|
||||
};
|
||||
},
|
||||
parseContent = (page, siteData) => {
|
||||
const {
|
||||
body,
|
||||
content_type: contentType,
|
||||
filePath,
|
||||
// tags,
|
||||
} = page || {},
|
||||
{ ext } = path.parse(filePath) || {},
|
||||
{ pages, tags } = siteData || {};
|
||||
|
||||
let content = body,
|
||||
readTime;
|
||||
|
||||
if (ext === ".md") {
|
||||
if (contentType === "journal" && typeof body === "string") {
|
||||
readTime = getReadTime(body);
|
||||
}
|
||||
content = md.render(body);
|
||||
} else if (ext === ".ejs") {
|
||||
content = ejs.render(
|
||||
body,
|
||||
{ page, site: { ...site, pages, tags } },
|
||||
{ filename: filePath }
|
||||
);
|
||||
}
|
||||
|
||||
return { ...page, content, readTime };
|
||||
},
|
||||
renderFile = async (page, isSupport) => {
|
||||
const {
|
||||
content,
|
||||
destPath,
|
||||
layout,
|
||||
path: pagePath,
|
||||
pages,
|
||||
siteTags,
|
||||
tags,
|
||||
} = page || {};
|
||||
try {
|
||||
const layoutFileName = `${srcPath}/layouts/${
|
||||
layout || "default"
|
||||
}.ejs`,
|
||||
layoutData = await fs.readFile(layoutFileName, "utf-8"),
|
||||
completePage = isSupport
|
||||
? content
|
||||
: ejs.render(layoutData, {
|
||||
content,
|
||||
page,
|
||||
site: {
|
||||
...site,
|
||||
pages,
|
||||
tags:
|
||||
page.content_type === "journal"
|
||||
? siteTags
|
||||
: tags,
|
||||
},
|
||||
filename: layoutFileName,
|
||||
});
|
||||
|
||||
if (!completePage) {
|
||||
console.log("failed!", pagePath, content);
|
||||
return;
|
||||
}
|
||||
|
||||
// create destination directory
|
||||
fse.mkdirsSync(destPath);
|
||||
|
||||
// save the html file
|
||||
fse.writeFileSync(
|
||||
path.join(outputPath, pagePath),
|
||||
completePage
|
||||
);
|
||||
} catch (e) {
|
||||
console.log("failed!", pagePath);
|
||||
console.log("paths", destPath, outputPath);
|
||||
console.error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
md.use(emoji);
|
||||
|
||||
log(`${isRebuild ? "Reb" : "B"}uilding...`);
|
||||
|
||||
// clear destination folder
|
||||
fse.emptyDirSync(outputPath);
|
||||
|
||||
// copy assets folder
|
||||
await copyAssets(path.join(srcPath, "assets"));
|
||||
|
||||
const files = ["pages", "sitePosts"].reduce((acc, pageDir) => {
|
||||
return [
|
||||
...acc,
|
||||
...glob
|
||||
.sync("**/*.@(md|ejs|html)", {
|
||||
cwd: path.join(srcPath, pageDir),
|
||||
})
|
||||
.map((file) =>
|
||||
parseFile(file, path.join(srcPath, pageDir))
|
||||
),
|
||||
];
|
||||
}, []),
|
||||
sortByPubDate = (a, b) => {
|
||||
if (a.date_pub && b.date_pub) {
|
||||
let a_dt = new Date(a.date_pub).getTime(),
|
||||
b_dt = new Date(b.date_pub).getTime();
|
||||
if (a_dt < b_dt) {
|
||||
return 1;
|
||||
}
|
||||
if (b_dt < a_dt) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (a.date_pub) return -1;
|
||||
if (b.date_pub) return 1;
|
||||
return 0;
|
||||
},
|
||||
pages = files.map(({ page }) => ({ ...page })).sort(sortByPubDate),
|
||||
tagCloud = pages.reduce((acc, curr) => {
|
||||
const { tags } = curr;
|
||||
tags.forEach((tag) => {
|
||||
if (acc[tag]) acc[tag]++;
|
||||
else acc[tag] = 1;
|
||||
});
|
||||
return acc;
|
||||
}, {}),
|
||||
tags = Object.keys(tagCloud).sort(tagSorter),
|
||||
yearCloud = pages
|
||||
.filter(({ content_type = "" }) => content_type === "journal")
|
||||
.reduce((acc, curr) => {
|
||||
const { date_pub } = curr;
|
||||
if (date_pub) {
|
||||
const year = new Date(date_pub).getFullYear();
|
||||
if (acc[year]) acc[year]++;
|
||||
else acc[year] = 1;
|
||||
}
|
||||
return acc;
|
||||
}, {}),
|
||||
years = Object.keys(yearCloud).sort().reverse(),
|
||||
pagesWithContent = pages.map((page) =>
|
||||
parseContent(page, { pages, tags })
|
||||
);
|
||||
|
||||
// add data for the whole site to each page as it's rendered
|
||||
pagesWithContent.forEach((page) => {
|
||||
renderFile({ ...page, pages: pagesWithContent, siteTags: tags });
|
||||
});
|
||||
|
||||
/* Journal Stuff - Tags & Years */
|
||||
|
||||
// make page(s) for each tag
|
||||
tags.forEach((tag) => {
|
||||
// check counts
|
||||
let postCount = tagCloud[tag],
|
||||
pageCount = Math.ceil(postCount / journalsPerPage);
|
||||
for (let i = 1; i <= pageCount; i++) {
|
||||
const firstEntryIndex = journalsPerPage * (i - 1),
|
||||
lastEntryIndex = journalsPerPage * i;
|
||||
|
||||
renderFile({
|
||||
content: tag,
|
||||
destPath: path.join(outputPath, "journal", "tags", tag),
|
||||
entriesToList: pagesWithContent
|
||||
.filter(
|
||||
(p) =>
|
||||
p && Array.isArray(p.tags) && p.tags.includes(tag)
|
||||
)
|
||||
.slice(firstEntryIndex, lastEntryIndex),
|
||||
layout: "tag",
|
||||
path: `journal/tags/${tag}/${
|
||||
i === 1 ? "index.html" : `page${i}.html`
|
||||
}`,
|
||||
site: { ...site, pages: pagesWithContent, tags },
|
||||
pageCount,
|
||||
pageNum: i,
|
||||
pages: pagesWithContent,
|
||||
tag,
|
||||
tags,
|
||||
title: `Journal Entries Tagged with #${tag}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// make page(s) for each year
|
||||
years.forEach((year) => {
|
||||
// check counts
|
||||
let postCount = yearCloud[year],
|
||||
pageCount = Math.ceil(postCount / journalsPerPage);
|
||||
for (let i = 1; i <= pageCount; i++) {
|
||||
const firstEntryIndex = journalsPerPage * (i - 1),
|
||||
lastEntryIndex = journalsPerPage * i;
|
||||
|
||||
// TODO: rethink the data passed in here - you're paging solution works (kinda), take it over the finish line!
|
||||
renderFile({
|
||||
content: year,
|
||||
destPath: path.join(outputPath, "journal", year),
|
||||
entriesToList: pagesWithContent
|
||||
.filter(({ content_type = "", date_pub = "" }) => {
|
||||
if (!date_pub || content_type !== "journal")
|
||||
return false;
|
||||
|
||||
const p_dt = new Date(date_pub).getTime(),
|
||||
y1_dt = new Date(
|
||||
`${year}-01-01T00:00:00-0500`
|
||||
).getTime(),
|
||||
y2_dt = new Date(
|
||||
`${year}-12-31T23:59:59-0500`
|
||||
).getTime();
|
||||
return p_dt >= y1_dt && p_dt <= y2_dt;
|
||||
})
|
||||
.slice(firstEntryIndex, lastEntryIndex),
|
||||
layout: "journal-year",
|
||||
path: `journal/${year}/${
|
||||
i === 1 ? "index.html" : `page${i}.html`
|
||||
}`,
|
||||
site: { ...site, pages: pagesWithContent, tags },
|
||||
pageCount,
|
||||
pageNum: i,
|
||||
pages: pagesWithContent,
|
||||
tags,
|
||||
title: `Journal Entries from ${year}`,
|
||||
year,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* Support pages - anything too weird / specific for markdown rendering */
|
||||
|
||||
// collect support pages
|
||||
const support = ["support"].reduce((acc, pageDir) => {
|
||||
return [
|
||||
...acc,
|
||||
...glob
|
||||
.sync("**/*.@(md|ejs|html)", {
|
||||
cwd: path.join(srcPath, pageDir),
|
||||
})
|
||||
.map((file) =>
|
||||
parseFile(
|
||||
file,
|
||||
path.join(srcPath, pageDir),
|
||||
pagesWithContent,
|
||||
true
|
||||
)
|
||||
),
|
||||
];
|
||||
}, []);
|
||||
|
||||
// write each one out
|
||||
support.forEach((fileData) => {
|
||||
const { page } = fileData;
|
||||
if (page?.ext === ".ejs") {
|
||||
const pageAndContent = parseContent(page, {
|
||||
pages: pagesWithContent,
|
||||
tags,
|
||||
});
|
||||
return renderFile({ ...fileData, ...pageAndContent, tags }, true);
|
||||
}
|
||||
return renderFile(fileData, true);
|
||||
});
|
||||
};
|
19
lib/defaults.json5
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
/** TACO Express Default Options **/
|
||||
|
||||
/*
|
||||
The function used to log output (console.log, morgan, etc).
|
||||
Should take one (or more) strings as arguments.
|
||||
*/
|
||||
logFunction: null,
|
||||
|
||||
build: {},
|
||||
site: {},
|
||||
serve: {
|
||||
port: 5000,
|
||||
},
|
||||
}
|
43
lib/loadConfig.js
Normal file
@ -0,0 +1,43 @@
|
||||
module.exports = (opts, envKey) => {
|
||||
const
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
|
||||
json5 = require('json5'),
|
||||
|
||||
{ convertCamelToUpperSnakeCase, readJsonIfExists } = require('./utils'),
|
||||
|
||||
{ cwd, env } = process,
|
||||
|
||||
def = readJsonIfExists(path.resolve(__dirname, 'defaults.json5')),
|
||||
|
||||
// gets value from ENV || options || defaults (in that order)
|
||||
getVal = (envName) => {
|
||||
const snakeEnvName = `${envKey}_${convertCamelToUpperSnakeCase(envName)}`;
|
||||
if (env[snakeEnvName]) return env[snakeEnvName];
|
||||
if (opts[envName]) return opts[envName];
|
||||
return def[envName];
|
||||
},
|
||||
|
||||
// gets array from ENV || options || defaults (in that order)
|
||||
getArray = (envName, optName = '') => {
|
||||
if (optName === '') {
|
||||
optName = envName;
|
||||
envName = convertCamelToUpperSnakeCase(envName);
|
||||
}
|
||||
envName = `${envKey}_${envName}`;
|
||||
if (env[envName]) return env[envName].split(path.delimiter);
|
||||
if (Array.isArray(opts[optName]) && opts[optName].length) return opts[optName];
|
||||
return def[optName];
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
|
||||
...Object.keys(def).reduce((acc, curr) => {
|
||||
if (Array.isArray(def[curr])) acc[curr] = getArray(curr);
|
||||
else acc[curr] = getVal(curr);
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
};
|
26
lib/serve.js
Normal file
@ -0,0 +1,26 @@
|
||||
module.exports = async (config) => {
|
||||
let isReady = false;
|
||||
const
|
||||
http = require('http'),
|
||||
|
||||
address = require('network-address'),
|
||||
handler = require('serve-handler'),
|
||||
|
||||
build = require('./build'),
|
||||
|
||||
{ build: buildOpts, logFunction: log = () => {}, serve: serveOpts } = config || {},
|
||||
{ outputPath, srcPath } = buildOpts || {},
|
||||
{ port = 5000 } = serveOpts || {},
|
||||
|
||||
server = http.createServer((request, response) => {
|
||||
// You pass two more arguments for config and middleware
|
||||
// More details here: https://github.com/vercel/serve-handler#options
|
||||
return handler(request, response, { public: outputPath });
|
||||
});
|
||||
|
||||
await build(config);
|
||||
|
||||
server.listen(port, async () => {
|
||||
log(`Running at http://${address()}:${port} / http://localhost:${port}`);
|
||||
});
|
||||
};
|
57
lib/utils.js
Normal file
@ -0,0 +1,57 @@
|
||||
module.exports = (() => {
|
||||
const
|
||||
chalk = require('chalk'),
|
||||
|
||||
getTime = () => {
|
||||
const
|
||||
now = new Date(),
|
||||
tzo = -now.getTimezoneOffset(),
|
||||
dif = tzo >= 0 ? '+' : '-',
|
||||
|
||||
pad = (num) => {
|
||||
const norm = Math.floor(Math.abs(num));
|
||||
return `${norm < 10 ? '0' : ''}${norm}`;
|
||||
};
|
||||
return [
|
||||
now.getFullYear(),
|
||||
'-',
|
||||
pad(now.getMonth() + 1),
|
||||
'-',
|
||||
pad(now.getDate()),
|
||||
'T',
|
||||
pad(now.getHours()),
|
||||
':',
|
||||
pad(now.getMinutes()),
|
||||
':',
|
||||
pad(now.getSeconds()),
|
||||
dif,
|
||||
pad(tzo / 60),
|
||||
':',
|
||||
pad(tzo % 60)
|
||||
].join('');
|
||||
};
|
||||
|
||||
return {
|
||||
convertCamelToUpperSnakeCase:
|
||||
str => str.replace(/[A-Z]/g, letter => `_${letter}`).toUpperCase(),
|
||||
|
||||
getTime,
|
||||
|
||||
log: (msg) => console.log(`${chalk.grey(`${getTime()}:`)} ${msg}`),
|
||||
|
||||
readJsonIfExists: (filePath) => {
|
||||
const
|
||||
fs = require('fs'),
|
||||
|
||||
json5 = require('json5');
|
||||
|
||||
try {
|
||||
return json5.parse(fs.readFileSync(filePath, {encoding: 'utf8'}));
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') return {};
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
})();
|
58
lib/watch.js
Normal file
@ -0,0 +1,58 @@
|
||||
module.exports = async (config) => {
|
||||
let isReady = false;
|
||||
const
|
||||
http = require('http'),
|
||||
|
||||
chokidar = require('chokidar'),
|
||||
address = require('network-address'),
|
||||
handler = require('serve-handler'),
|
||||
|
||||
build = require('./build'),
|
||||
rebuild = (cfg) => {
|
||||
isReady = false;
|
||||
build({ ...cfg, isRebuild: true });
|
||||
isReady = true;
|
||||
},
|
||||
|
||||
{ build: buildOpts, logFunction: log = () => {}, serve: serveOpts } = config || {},
|
||||
{ outputPath, srcPath } = buildOpts || {},
|
||||
{ port = 5000 } = serveOpts || {},
|
||||
|
||||
watcher = chokidar.watch([srcPath, '*.json'], {
|
||||
ignored: /(^|[\/\\])\../, // ignore dotfiles
|
||||
persistent: true
|
||||
})
|
||||
.on('add', (path) => {
|
||||
if (isReady) {
|
||||
log(`File ${path} has been added`)
|
||||
rebuild(config);
|
||||
}
|
||||
})
|
||||
.on('change', (path) => {
|
||||
if (isReady) {
|
||||
log(`File ${path} has been changed`)
|
||||
rebuild(config);
|
||||
}
|
||||
})
|
||||
.on('ready', () => {
|
||||
isReady = true;
|
||||
})
|
||||
.on('unlink', (path) => {
|
||||
if (isReady) {
|
||||
log(`File ${path} has been removed`)
|
||||
rebuild(config);
|
||||
}
|
||||
}),
|
||||
|
||||
server = http.createServer((request, response) => {
|
||||
// You pass two more arguments for config and middleware
|
||||
// More details here: https://github.com/vercel/serve-handler#options
|
||||
return handler(request, response, { public: outputPath });
|
||||
});
|
||||
|
||||
await build(config);
|
||||
|
||||
server.listen(port, () => {
|
||||
log(`Running at http://${address()}:${port} / http://localhost:${port}`);
|
||||
});
|
||||
};
|
2653
package-lock.json
generated
Normal file
34
package.json
Executable file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "iew-site-builder",
|
||||
"version": "0.9.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "node app.js build",
|
||||
"build:prod": "cross-env NODE_ENV=production node ./lib/build",
|
||||
"serve": "node app.js serve",
|
||||
"watch": "node app.js watch"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2",
|
||||
"chokidar": "^3.4.3",
|
||||
"dotenv": "^16.0.1",
|
||||
"highlight.js": "^11.3.1",
|
||||
"json5": "^2.1.3",
|
||||
"markdown-it": "^13.0.1",
|
||||
"markdown-it-emoji": "^2.0.0",
|
||||
"network-address": "^1.1.2",
|
||||
"serve-handler": "^6.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ejs": "^3.1.5",
|
||||
"front-matter": "^4.0.2",
|
||||
"fs-extra": "^10.0.0",
|
||||
"glob": "^8.0.3",
|
||||
"serve": "^13.0.2",
|
||||
"yargs": "^17.3.1"
|
||||
}
|
||||
}
|
26
site.config.json5
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
site: {
|
||||
title: "It's Eric Woodward (dotcom)",
|
||||
author: {
|
||||
name: "Eric Woodward",
|
||||
email: "redacted@nunyodam.com", // not used
|
||||
photo: "/images/eric-8bit.gif",
|
||||
site: "https://itsericwoodward.com",
|
||||
},
|
||||
|
||||
base_uri: "",
|
||||
robots: "index,follow",
|
||||
language: "en-us",
|
||||
copyright: "Copyright 2014-2022 Eric Woodward, licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.",
|
||||
basePath: "",
|
||||
uri: "https://www.itsericwoodward.com",
|
||||
},
|
||||
build: {
|
||||
journalsPerPage: 5,
|
||||
srcPath: "src",
|
||||
outputPath: "out",
|
||||
},
|
||||
serve: {
|
||||
port: 4997,
|
||||
},
|
||||
}
|
2
src/assets/_root/browserconfig.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig><msapplication><tile><square70x70logo src="/images/favicons/ms-icon-70x70.png"/><square150x150logo src="/images/favicons/ms-icon-150x150.png"/><square310x310logo src="/images/favicons/ms-icon-310x310.png"/><TileColor>#0d1852</TileColor></tile></msapplication></browserconfig>
|
BIN
src/assets/_root/chim/chim-exe.zip
Normal file
348
src/assets/_root/chim/js-dos-api.js
Normal file
@ -0,0 +1,348 @@
|
||||
/*!
|
||||
* jQLite JavaScript Library v1.1.1 (http://code.google.com/p/jqlite/)
|
||||
* Copyright (c) 2010 Brett Fattori (bfattori@gmail.com)
|
||||
* Licensed under the MIT license
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Many thanks to the jQuery team's efforts. Some code is
|
||||
* Copyright (c) 2010, John Resig. See
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* @author Brett Fattori (bfattori@gmail.com)
|
||||
* @author $Author: bfattori $
|
||||
* @version $Revision: 145 $
|
||||
*
|
||||
* Created: 03/29/2010
|
||||
* Modified: $Date: 2010-06-21 11:08:14 -0400 (Mon, 21 Jun 2010) $
|
||||
*/
|
||||
(function(){function B(){return+new Date}var D=function(a,b){if(a===""&&b)return b;var d=a.split(" "),c=d.shift(),e;if(c.charAt(0)=="#"){var g=i.getElementById(c.substring(1));e=g?[g]:[]}else{e=c.charAt(0)!=="."?c.split(".")[0]:"*";var h=c.split("."),j=null;if(e.indexOf("[")!=-1){j=e;e=e.substr(0,e.indexOf("["))}g=function(o){var n=arguments.callee,k;if(!(k=!n.needClass)){k=n.classes;if(o.className.length==0)k=false;else{for(var r=o.className.split(" "),l=k.length,p=0;p<k.length;p++)f.inArray(k[p],
|
||||
r)!=-1&&l--;k=l==0}}if(k=k){if(!(k=!n.needAttribute)){n=n.attributes;k=true;for(r=0;r<n.length;r++){l=n[r].split("=");p=l[0].indexOf("!")!=-1||l[0].indexOf("*")!=-1?l[0].charAt(l[0].length-1)+"=":"=";if(p!="=")l[0]=l[0].substring(0,l[0].length-1);switch(p){case "=":k&=o.getAttribute(l[0])===l[1];break;case "!=":k&=o.getAttribute(l[0])!==l[1];break;case "*=":k&=o.getAttribute(l[0]).indexOf(l[1])!=-1;break;default:k=false}}k=k}k=k}if(k)return o};for(var u=[],s=0;s<b.length;s++)for(var C=b[s].getElementsByTagName(e),
|
||||
v=0;v<C.length;v++)u.push(C[v]);h&&h.shift();e=[];g.classes=h;if(j!=null){var w=j.indexOf("[");s=j.lastIndexOf("]");w=j.substring(w+1,s).split("][")}g.attributes=j!=null?w:null;g.needClass=c.indexOf(".")!=-1&&h.length>0;g.needAttribute=j!=null;for(c=0;c<u.length;c++)g(u[c])&&e.push(u[c])}return D(d.join(" "),e)},Q=function(a,b){b=b||i;if(a.nodeType&&a.nodeType===E){a=i.body;if(a===null)return[i]}if(a.nodeType&&a.nodeType===m)return[a];if(a.jquery&&typeof a.jquery==="string")return a.toArray();if(b)b=
|
||||
F(b);if(f.isArray(a))return a;else if(typeof a==="string"){for(var d=[],c=0;c<b.length;c++){var e=[b[c]];if(!f.forceSimpleSelectorEngine&&e[0].querySelectorAll){e=e[0].querySelectorAll(a);for(var g=0;g<e.length;g++)d.push(e.item(g))}else d=d.concat(D(a,e))}return d}else return null},G=false;setTimeout(function(){var a=i.body;if(a){var b=i.createElement("script"),d="i"+(new Date).getTime();b.type="text/javascript";try{b.appendChild(i.createTextNode("window."+d+"=1;"))}catch(c){}a.insertBefore(b,a.firstChild);
|
||||
var e=true;if(window[d])delete window[d];else e=false;a.removeChild(b);G=e}else setTimeout(arguments.callee,33)},33);var H=function(a){var b=i.createElement("div");b.innerHTML=a;return{scripts:b.getElementsByTagName("script"),data:a}},I=function(a){a=a.replace(/-/g," ");a=a;var b=true;b=b||false;a=!a?"":a.toString().replace(/^\s*|\s*$/g,"");var d="";if(a.length<=0)a="";else{var c=false;d+=b?a.charAt(0):a.charAt(0).toUpperCase();for(b=1;b<a.length;b++){d+=c?a.charAt(b).toUpperCase():a.charAt(b).toLowerCase();
|
||||
var e=a.charCodeAt(b);c=e==32||e==45||e==46;if(e==99||e==67)if(a.charCodeAt(b-1)==77||a.charCodeAt(b-1)==109)c=true}a=d}return a.replace(/ /g,"")},J={click:"MouseEvents",dblclick:"MouseEvents",mousedown:"MouseEvents",mouseup:"MouseEvents",mouseover:"MouseEvents",mousemove:"MouseEvents",mouseout:"MouseEvents",contextmenu:"MouseEvents",keypress:"KeyEvents",keydown:"KeyEvents",keyup:"KeyEvents",load:"HTMLEvents",unload:"HTMLEvents",abort:"HTMLEvents",error:"HTMLEvents",resize:"HTMLEvents",scroll:"HTMLEvents",
|
||||
select:"HTMLEvents",change:"HTMLEvents",submit:"HTMLEvents",reset:"HTMLEvents",focus:"HTMLEvents",blur:"HTMLEvents",touchstart:"MouseEvents",touchend:"MouseEvents",touchmove:"MouseEvents"},K=function(a,b,d){if(f.isFunction(d)){if(typeof b==="string")b=b.toLowerCase();var c=J[b];if(b.indexOf("on")==0)b=b.substring(2);if(c){c=function(e){var g=arguments.callee,h=e.data||[];h.unshift(e);g=g.fn.apply(a,h);if(typeof g!="undefined"&&g===false){if(e.preventDefault&&e.stopPropagation){e.preventDefault();
|
||||
e.stopPropagation()}else{e.returnValue=false;e.cancelBubble=true}return false}return true};c.fn=d;a.addEventListener?a.addEventListener(b,c,false):a.attachEvent("on"+b,c)}else{if(!a._handlers)a._handlers={};c=a._handlers[b]||[];c.push(d);a._handlers[b]=c}}},f=function(a,b){return(new x).init(a,b)},i=window.document,y=Object.prototype.hasOwnProperty,z=Object.prototype.toString,L=Array.prototype.push,R=Array.prototype.slice,m=1,E=9,A=[],M=false,N=false,q;f.forceSimpleSelectorEngine=false;f.each=function(a,
|
||||
b){var d,c=0,e=a.length;if(e===undefined||f.isFunction(a))for(d in a){if(b.call(a[d],d,a[d])===false)break}else for(d=a[0];c<e&&b.call(d,c,d)!==false;d=a[++c]);return a};f.noop=function(){};f.isFunction=function(a){return z.call(a)==="[object Function]"};f.isArray=function(a){return z.call(a)==="[object Array]"};f.isPlainObject=function(a){if(!a||z.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!y.call(a,"constructor")&&!y.call(a.constructor.prototype,"isPrototypeOf"))return false;
|
||||
var b;for(b in a);return b===undefined||y.call(a,b)};f.merge=function(a,b){var d=a.length,c=0;if(typeof b.length==="number")for(var e=b.length;c<e;c++)a[d++]=b[c];else for(;b[c]!==undefined;)a[d++]=b[c++];a.length=d;return a};f.param=function(a){var b="";a&&f.each(a,function(d,c){b+=(b.length!=0?"&":"")+c+"="+encodeURIComponent(d)});return b};f.evalScripts=function(a){for(var b=i.getElementsByTagName("head")[0]||i.documentElement,d=0;d<a.length;d++){var c=i.createElement("script");c.type="text/javascript";
|
||||
if(G)c.appendChild(i.createTextNode(a[d].text));else c.text=a[d].text;b.insertBefore(c,b.firstChild);b.removeChild(c)}};f.ready=function(){for(M=true;A.length>0;)A.shift()()};var t="jQuery"+B(),S=0,O={};f.noData={embed:true,object:true,applet:true};f.cache={};f.data=function(a,b,d){if(!(a.nodeName&&jQuery.noData[a.nodeName.toLowerCase()])){a=a==window?O:a;var c=a[t];c||(c=a[t]=++S);if(b&&!jQuery.cache[c])jQuery.cache[c]={};if(d!==undefined)jQuery.cache[c][b]=d;return b?jQuery.cache[c][b]:c}};f.removeData=
|
||||
function(a,b){a=a==window?O:a;var d=a[t];if(b){if(jQuery.cache[d]){delete jQuery.cache[d][b];b="";for(b in jQuery.cache[d])break;b||jQuery.removeData(a)}}else{try{delete a[t]}catch(c){a.removeAttribute&&a.removeAttribute(t)}delete jQuery.cache[d]}};f.ajax={status:-1,statusText:"",responseText:null,responseXML:null,send:function(a,b,d){if(f.isFunction(b)){d=b;b={}}if(a){var c=true,e=null,g=null;if(typeof b.async!=="undefined"){c=b.async;delete b.async}if(typeof b.username!=="undefined"){e=b.username;
|
||||
delete b.username}if(typeof b.password!=="undefined"){g=b.password;delete b.password}b=f.param(b);if(b.length!=0)a+=(a.indexOf("?")==-1?"?":"&")+b;b=new XMLHttpRequest;b.open("GET",a,c,e,g);b.send();if(c){a=function(h){var j=arguments.callee;h.status==200?f.ajax.complete(h,j.cb):f.ajax.error(h,j.cb)};a.cb=d;d=function(){var h=arguments.callee;h.req.readyState!=4?setTimeout(h,250):h.xcb(h.req)};d.req=b;d.xcb=a;setTimeout(d,250)}}},complete:function(a,b){f.ajax.status=a.status;f.ajax.responseText=a.responseText;
|
||||
f.ajax.responseXML=a.responseXML;f.isFunction(b)&&b(a.responseText,a.status)},error:function(a,b){f.ajax.status=a.status;f.ajax.statusText=a.statusText;f.isFunction(b)&&b(a.status,a.statusText)}};f.makeArray=function(a,b){var d=b||[];if(a!=null)a.length==null||typeof a==="string"||jQuery.isFunction(a)||typeof a!=="function"&&a.setInterval?L.call(d,a):f.merge(d,a);return d};f.inArray=function(a,b){for(var d=0;d<b.length;d++)if(b[d]===a)return d;return-1};f.trim=function(a){return a!=null?a.toString().replace(/^\s*|\s*$/g,
|
||||
""):""};var x=function(){};x.prototype={selector:"",context:null,length:0,jquery:"jqlite-1.1.1",init:function(a,b){if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1}else if(typeof a==="function")this.ready(a);else{var d=[];if(a.jquery&&typeof a.jquery==="string")d=a.toArray();else if(f.isArray(a))d=a;else if(typeof a==="string"&&f.trim(a).indexOf("<")==0&&f.trim(a).indexOf(">")!=-1){d=f.trim(a).toLowerCase();d=d.indexOf("<option")==0?"SELECT":d.indexOf("<li")==0?"UL":d.indexOf("<tr")==
|
||||
0?"TBODY":d.indexOf("<td")==0?"TR":"DIV";d=i.createElement(d);d.innerHTML=a;d=[d.removeChild(d.firstChild)]}else{if(a.indexOf(",")!=-1){d=a.split(",");for(var c=0;c<d.length;c++)d[c]=f.trim(d[c])}else d=[a];c=[];for(var e=0;e<d.length;e++)c=c.concat(Q(d[e],b));d=c}L.apply(this,d)}return this},each:function(a){return f.each(this,a)},size:function(){return this.length},toArray:function(){return R.call(this,0)},ready:function(a){if(M)a();else{A.push(a);return this}},data:function(a,b){if(typeof a===
|
||||
"undefined"&&this.length)return jQuery.data(this[0]);else if(typeof a==="object")return this.each(function(){jQuery.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===undefined){if(data===undefined&&this.length)data=jQuery.data(this[0],a);return data===undefined&&d[1]?this.data(d[0]):data}else return this.each(function(){jQuery.data(this,a,b)})},removeData:function(a){return this.each(function(){jQuery.removeData(this,a)})},addClass:function(a){return this.each(function(){if(this.className.length!=
|
||||
0){var b=this.className.split(" ");if(f.inArray(a,b)==-1){b.push(a);this.className=b.join(" ")}}else this.className=a})},removeClass:function(a){return this.each(function(){if(this.className.length!=0){var b=this.className.split(" "),d=f.inArray(a,b);if(d!=-1){b.splice(d,1);this.className=b.join(" ")}}})},hasClass:function(a){if(this[0].className.length==0)return false;return f.inArray(a,this[0].className.split(" "))!=-1},isElementName:function(a){return this[0].nodeName.toLowerCase()===a.toLowerCase()},
|
||||
toggleClass:function(a){return this.each(function(){if(this.className.length==0)this.className=a;else{var b=this.className.split(" "),d=f.inArray(a,b);d!=-1?b.splice(d,1):b.push(a);this.className=b.join(" ")}})},hide:function(a){return this.each(function(){if(this.style&&this.style.display!=null)if(this.style.display.toString()!="none"){this._oldDisplay=this.style.display.toString()||(this.nodeName!="span"?"block":"inline");this.style.display="none"}f.isFunction(a)&&a(this)})},show:function(a){return this.each(function(){this.style.display=
|
||||
(this._oldDisplay&&this._oldDisplay!=""?this._oldDisplay:null)||(this.nodeName!="span"?"block":"inline");f.isFunction(a)&&a(this)})},css:function(a,b){if(typeof a==="string"&&b==null)return this[0].style[I(a)];else{a=typeof a==="string"?P(a,b):a;return this.each(function(){var d=this;typeof d.style!="undefined"&&f.each(a,function(c,e){e=typeof e==="number"?e+"px":e;var g=I(c);d.style[g]||(g=c);d.style[g]=e})})}},load:function(a,b,d){if(f.isFunction(b)){d=b;b={}}return this.each(function(){var c=function(e,
|
||||
g){var h=arguments.callee;if(e){var j=H(e);h.elem.innerHTML=j.data;f.evalScripts(j.scripts)}f.isFunction(h.cback)&&h.cback(e,g)};c.cback=d;c.elem=this;f.ajax.send(a,b,c)})},html:function(a){return a?this.each(function(){var b=H(a);this.innerHTML=b.data;f.evalScripts(b.scripts)}):this[0].innerHTML},attr:function(a,b){return typeof a==="string"&&b==null?this[0]?this[0].getAttribute(a):"":this.each(function(){a=typeof a==="string"?P(a,b):a;for(var d in a)this.setAttribute(d,a[d])})},eq:function(a){var b=
|
||||
this.toArray();this.context=this[0]=a<0?b[b.length+a]:b[a];this.length=1;return this},first:function(){this.context=this[0]=this.toArray()[0];this.length=1;return this},last:function(){var a=this.toArray();this.context=this[0]=a[a.length-1];this.length=1;return this},index:function(a){var b=-1;if(this.length!=0){var d=this[0];if(a){var c=f(a)[0];this.each(function(g){if(this===c){b=g;return false}})}else{a=this.parent()[0].firstChild;for(var e=[];a!=null;){a.nodeType===m&&e.push(a);a=a.nextSibling}f.each(a,
|
||||
function(g){if(this===d){b=g;return false}})}}return b},next:function(a){var b=[];if(a){var d=f(a);this.each(function(){for(var c=this.nextSibling;c!=null&&c.nodeType!==m;)c=c.nextSibling;if(c!=null){var e=false;d.each(function(){if(this==c){e=true;return false}});e&&b.push(c)}})}else this.each(function(){for(var c=this.nextSibling;c!=null&&c.nodeType!==m;)c=c.nextSibling;c!=null&&b.push(c)});return f(b)},prev:function(a){var b=[];if(a){var d=f(a);this.each(function(){for(var c=this.previousSibling;c!=
|
||||
null&&c.nodeType!==m;)c=c.previousSibling;if(c!=null){var e=false;d.each(function(){if(this==c){e=true;return false}});e&&b.push(c)}})}else this.each(function(){for(var c=this.previousSibling;c!=null&&c.nodeType!==m;)c=c.previousSibling;c!=null&&b.push(c)});return f(b)},parent:function(a){var b=[];if(a){var d=f(a);this.each(function(){var c=this.parentNode,e=false;d.each(function(){if(this==c){e=true;return false}});e&&b.push(c)})}else this.each(function(){b.push(this.parentNode)});return f(b)},parents:function(a){var b=
|
||||
[];if(a){var d=f(a);this.each(function(){for(var c=this;c!=i.body;){d.each(function(){this==c&&b.push(c)});c=c.parentNode}})}else this.each(function(){for(var c=this;c!=i.body;){c=c.parentNode;b.push(c)}});return f(b)},children:function(a){var b=[];if(a){var d=f(a);this.each(function(){for(var c=this.firstChild;c!=null;){c.nodeType==m&&d.each(function(){this===c&&b.push(c)});c=c.nextSibling}})}else this.each(function(){for(var c=this.firstChild;c!=null;){c.nodeType==m&&b.push(c);c=c.nextSibling}});
|
||||
return f(b)},append:function(a){a=F(a);return this.each(function(){for(var b=0;b<a.length;b++)this.appendChild(a[b])})},remove:function(a){return this.each(function(){a?$(a,this).remove():this.parentNode.removeChild(this)})},empty:function(){return this.each(function(){this.innerHTML=""})},val:function(a){if(a==null){var b=null;if(this&&this.length!=0&&typeof this[0].value!="undefined")b=this[0].value;return b}else return this.each(function(){if(typeof this.value!="undefined")this.value=a})},bind:function(a,
|
||||
b){return this.each(function(){K(this,a,b)})},trigger:function(a,b){return this.each(function(){var d;var c;c=a;if(typeof c==="string")c=c.toLowerCase();var e=null,g=J[c]||"Event";if(i.createEvent){e=i.createEvent(g);e._eventClass=g;c&&e.initEvent(c,true,true)}if(i.createEventObject){e=i.createEventObject();if(c){e.type=c;e._eventClass=g}}c=e;if(c._eventClass!=="Event"){c.data=b;d=this.dispatchEvent(c)}else if(e=(this._handlers||{})[a])for(g=0;g<e.length;g++){var h=f.isArray(b)?b:[];h.unshift(c);
|
||||
h=e[g].apply(this,h);if(!(typeof h=="undefined"?true:h))break}return d})},submit:function(a){return this.each(function(){if(f.isFunction(a))K(this,"onsubmit",a);else this.submit&&this.submit()})}};if(i.addEventListener)q=function(){i.removeEventListener("DOMContentLoaded",q,false);f.ready()};else if(i.attachEvent)q=function(){if(i.readyState==="complete"){i.detachEvent("onreadystatechange",q);f.ready()}};if(!N){N=true;if(i.readyState==="complete")return f.ready();if(i.addEventListener){i.addEventListener("DOMContentLoaded",
|
||||
q,false);window.addEventListener("load",f.ready,false)}else if(i.attachEvent){i.attachEvent("onreadystatechange",q);window.attachEvent("onload",f.ready)}}var P=function(a,b){var d={};d[a]=b;return d},F=function(a){if(a.nodeType&&(a.nodeType===m||a.nodeType===E))a=[a];else if(typeof a==="string")a=f(a).toArray();else if(a.jquery&&typeof a.jquery==="string")a=a.toArray();return a};if(typeof window.jQuery=="undefined"){window.jQuery=f;window.jQuery.fn=x.prototype;window.$=window.jQuery;window.now=B}jQuery.extend=
|
||||
jQuery.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,c=false,e,g,h,j;if(typeof a==="boolean"){c=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!jQuery.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(g in e){h=a[g];j=e[g];if(a!==j)if(c&&j&&(jQuery.isPlainObject(j)||jQuery.isArray(j))){h=h&&(jQuery.isPlainObject(h)||jQuery.isArray(h))?h:jQuery.isArray(j)?[]:{};a[g]=jQuery.extend(c,h,j)}else if(j!==undefined)a[g]=j}return a};jQuery.each("click,dblclick,mouseover,mouseout,mousedown,mouseup,keydown,keypress,keyup,focus,blur,change,select,error,load,unload,scroll,resize,touchstart,touchend,touchmove".split(","),
|
||||
function(a,b){jQuery.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)}})})();
|
||||
(function() {
|
||||
this.Dosbox = (function() {
|
||||
function Dosbox(options) {
|
||||
this.onload = options.onload;
|
||||
this.onrun = options.onrun;
|
||||
this.ui = new Dosbox.UI(options);
|
||||
this.module = new Dosbox.Module({
|
||||
canvas: this.ui.canvas
|
||||
});
|
||||
this.ui.onStart((function(_this) {
|
||||
return function() {
|
||||
_this.ui.showLoader();
|
||||
return _this.downloadScript();
|
||||
};
|
||||
})(this));
|
||||
}
|
||||
|
||||
Dosbox.prototype.run = function(archiveUrl, executable) {
|
||||
return new Dosbox.Mount(this.module, archiveUrl, {
|
||||
success: (function(_this) {
|
||||
return function() {
|
||||
var func, hide;
|
||||
_this.ui.updateMessage("Launching " + executable);
|
||||
hide = function() {
|
||||
return _this.ui.hideLoader();
|
||||
};
|
||||
func = function() {
|
||||
return _this._dosbox_main(_this, executable);
|
||||
};
|
||||
setTimeout(func, 1000);
|
||||
return setTimeout(hide, 3000);
|
||||
};
|
||||
})(this),
|
||||
progress: (function(_this) {
|
||||
return function(total, current) {
|
||||
return _this.ui.updateMessage("Mount " + executable + " (" + (current * 100 / total | 0) + "%)");
|
||||
};
|
||||
})(this)
|
||||
});
|
||||
};
|
||||
|
||||
Dosbox.prototype.requestFullScreen = function() {
|
||||
if (this.module.requestFullScreen) {
|
||||
return this.module.requestFullScreen(true, false);
|
||||
}
|
||||
};
|
||||
|
||||
Dosbox.prototype.downloadScript = function() {
|
||||
this.module.setStatus('Downloading js-dos');
|
||||
this.ui.updateMessage('Downloading js-dos');
|
||||
return new Dosbox.Xhr('https://js-dos.com/cdn/js-dos-v3.js', {
|
||||
success: (function(_this) {
|
||||
return function(script) {
|
||||
var func;
|
||||
_this.ui.updateMessage('Initializing dosbox');
|
||||
func = function() {
|
||||
return _this._jsdos_init(_this.module, script, _this.onload);
|
||||
};
|
||||
return setTimeout(func, 1000);
|
||||
};
|
||||
})(this),
|
||||
progress: (function(_this) {
|
||||
return function(total, current) {
|
||||
return _this.ui.updateMessage("Downloading js-dos (" + (current * 100 / total | 0) + "%)");
|
||||
};
|
||||
})(this)
|
||||
});
|
||||
};
|
||||
|
||||
Dosbox.prototype._jsdos_init = function(module, script, onload) {
|
||||
var Module;
|
||||
Module = module;
|
||||
eval(script);
|
||||
if (onload) {
|
||||
return onload(this);
|
||||
}
|
||||
};
|
||||
|
||||
Dosbox.prototype._dosbox_main = function(dosbox, executable) {
|
||||
var exception, func;
|
||||
try {
|
||||
if (dosbox.onrun) {
|
||||
func = function() {
|
||||
return dosbox.onrun(dosbox, executable);
|
||||
};
|
||||
setTimeout(func, 1000);
|
||||
}
|
||||
return dosbox.module.ccall('dosbox_main', 'int', ['string'], [executable]);
|
||||
} catch (error) {
|
||||
exception = error;
|
||||
if (exception === 'SimulateInfiniteLoop') {
|
||||
|
||||
} else {
|
||||
return typeof console !== "undefined" && console !== null ? typeof console.error === "function" ? console.error(exception) : void 0 : void 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Dosbox;
|
||||
|
||||
})();
|
||||
|
||||
}).call(this);
|
||||
|
||||
(function() {
|
||||
Dosbox.Module = (function() {
|
||||
function Module(options) {
|
||||
this.elCanvas = options.canvas;
|
||||
this.canvas = this.elCanvas[0];
|
||||
}
|
||||
|
||||
Module.prototype.preRun = [];
|
||||
|
||||
Module.prototype.postRun = [];
|
||||
|
||||
Module.prototype.totalDependencies = 0;
|
||||
|
||||
Module.prototype.print = function(text) {
|
||||
text = Array.prototype.slice.call(arguments).join(' ');
|
||||
return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(text) : void 0 : void 0;
|
||||
};
|
||||
|
||||
Module.prototype.printErr = function(text) {
|
||||
text = Array.prototype.slice.call(arguments).join(' ');
|
||||
return typeof console !== "undefined" && console !== null ? typeof console.error === "function" ? console.error(text) : void 0 : void 0;
|
||||
};
|
||||
|
||||
Module.prototype.setStatus = function(text) {
|
||||
return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(text) : void 0 : void 0;
|
||||
};
|
||||
|
||||
Module.prototype.monitorRunDependencies = function(left) {
|
||||
var status;
|
||||
this.totalDependencies = Math.max(this.totalDependencies, left);
|
||||
status = left ? "Preparing... (" + (this.totalDependencies - left) + "/" + this.totalDependencies + ")" : 'All downloads complete.';
|
||||
return this.setStatus(status);
|
||||
};
|
||||
|
||||
return Module;
|
||||
|
||||
})();
|
||||
|
||||
}).call(this);
|
||||
|
||||
(function() {
|
||||
Dosbox.Mount = (function() {
|
||||
function Mount(module, url, options) {
|
||||
this.module = module;
|
||||
new Dosbox.Xhr(url, {
|
||||
success: (function(_this) {
|
||||
return function(data) {
|
||||
var bytes;
|
||||
bytes = _this._toArray(data);
|
||||
if (_this._mountZip(bytes)) {
|
||||
return options.success();
|
||||
} else {
|
||||
return typeof console !== "undefined" && console !== null ? typeof console.error === "function" ? console.error('Unable to mount', url) : void 0 : void 0;
|
||||
}
|
||||
};
|
||||
})(this),
|
||||
progress: options.progress
|
||||
});
|
||||
}
|
||||
|
||||
Mount.prototype._mountZip = function(bytes) {
|
||||
var buffer, extracted;
|
||||
buffer = this.module._malloc(bytes.length);
|
||||
this.module.HEAPU8.set(bytes, buffer);
|
||||
extracted = this.module.ccall('extract_zip', 'int', ['number', 'number'], [buffer, bytes.length]);
|
||||
this.module._free(buffer);
|
||||
return extracted === 0;
|
||||
};
|
||||
|
||||
Mount.prototype._toArray = function(data) {
|
||||
var arr, i, len;
|
||||
if (typeof data === 'string') {
|
||||
arr = new Array(data.length);
|
||||
i = 0;
|
||||
len = data.length;
|
||||
while (i < len) {
|
||||
arr[i] = data.charCodeAt(i);
|
||||
++i;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
return Mount;
|
||||
|
||||
})();
|
||||
|
||||
}).call(this);
|
||||
|
||||
(function() {
|
||||
Dosbox.UI = (function() {
|
||||
function UI(options) {
|
||||
this.appendCss();
|
||||
this.div = $('#' + (options.id || 'dosbox'));
|
||||
this.wrapper = $('<div class="dosbox-container">');
|
||||
this.canvas = $('<canvas class="dosbox-canvas" oncontextmenu="event.preventDefault()">');
|
||||
this.overlay = $('<div class="dosbox-overlay">');
|
||||
this.loaderMessage = $('<div class="dosbox-loader-message">');
|
||||
this.loader = $('<div class="dosbox-loader">').append($('<div class="st-loader">').append($('<span class="equal">'))).append(this.loaderMessage);
|
||||
this.start = $('<div class="dosbox-start">Click to start');
|
||||
this.div.append(this.wrapper);
|
||||
this.wrapper.append(this.canvas);
|
||||
this.wrapper.append(this.loader);
|
||||
this.wrapper.append(this.overlay);
|
||||
this.overlay.append($('<div class="dosbox-powered">Powered by ').append($('<a href="http://js-dos.com">js-dos.com')));
|
||||
this.overlay.append(this.start);
|
||||
}
|
||||
|
||||
UI.prototype.onStart = function(fun) {
|
||||
return this.start.click((function(_this) {
|
||||
return function() {
|
||||
fun();
|
||||
return _this.overlay.hide();
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
UI.prototype.appendCss = function() {
|
||||
var head, style;
|
||||
head = document.head || document.getElementsByTagName('head')[0];
|
||||
style = document.createElement('style');
|
||||
style.type = 'text/css';
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = this.css;
|
||||
} else {
|
||||
style.appendChild(document.createTextNode(this.css));
|
||||
}
|
||||
return head.appendChild(style);
|
||||
};
|
||||
|
||||
UI.prototype.showLoader = function() {
|
||||
this.loader.show();
|
||||
return this.loaderMessage.html('');
|
||||
};
|
||||
|
||||
UI.prototype.updateMessage = function(message) {
|
||||
return this.loaderMessage.html(message);
|
||||
};
|
||||
|
||||
UI.prototype.hideLoader = function() {
|
||||
return this.loader.hide();
|
||||
};
|
||||
|
||||
UI.prototype.css = '.dosbox-container { position: relative; min-width: 320px; min-height: 200px; } .dosbox-canvas { } .dosbox-overlay, .dosbox-loader { position: absolute; left: 0; right: 0; top: 0; bottom: 0; background-color: #333; } .dosbox-start { text-align: center; position: absolute; left: 0; right: 0; bottom: 50%; color: #f80; font-size: 1.5em; text-decoration: underline; cursor: pointer; } .dosbox-overlay a { color: #f80; } .dosbox-loader { display: none; } .dosbox-powered { position: absolute; right: 1em; bottom: 1em; font-size: 0.8em; color: #9C9C9C; } .dosbox-loader-message { text-align: center; position: absolute; left: 0; right: 0; bottom: 50%; margin: 0 0 -3em 0; box-sizing: border-box; color: #f80; font-size: 1.5em; } @-moz-keyframes loading { 0% { left: 0; } 50% { left: 8.33333em; } 100% { left: 0; } } @-webkit-keyframes loading { 0% { left: 0; } 50% { left: 8.33333em; } 100% { left: 0; } } @keyframes loading { 0% { left: 0; } 50% { left: 8.33333em; } 100% { left: 0; } } .st-loader { width: 10em; height: 2.5em; position: absolute; top: 50%; left: 50%; margin: -1.25em 0 0 -5em; box-sizing: border-box; } .st-loader:before, .st-loader:after { content: ""; display: block; position: absolute; top: 0; bottom: 0; width: 1.25em; box-sizing: border-box; border: 0.25em solid #f80; } .st-loader:before { left: -0.76923em; border-right: 0; } .st-loader:after { right: -0.76923em; border-left: 0; } .st-loader .equal { display: block; position: absolute; top: 50%; margin-top: -0.5em; left: 4.16667em; height: 1em; width: 1.66667em; border: 0.25em solid #f80; box-sizing: border-box; border-width: 0.25em 0; -moz-animation: loading 1.5s infinite ease-in-out; -webkit-animation: loading 1.5s infinite ease-in-out; animation: loading 1.5s infinite ease-in-out; }';
|
||||
|
||||
return UI;
|
||||
|
||||
})();
|
||||
|
||||
}).call(this);
|
||||
|
||||
(function() {
|
||||
Dosbox.Xhr = (function() {
|
||||
function Xhr(url, options) {
|
||||
var e;
|
||||
this.success = options.success;
|
||||
this.progress = options.progress;
|
||||
if (window.ActiveXObject) {
|
||||
try {
|
||||
this.xhr = new ActiveXObject('Microsoft.XMLHTTP');
|
||||
} catch (error) {
|
||||
e = error;
|
||||
this.xhr = null;
|
||||
}
|
||||
} else {
|
||||
this.xhr = new XMLHttpRequest();
|
||||
}
|
||||
this.xhr.open('GET', url, true);
|
||||
this.xhr.overrideMimeType('text/plain; charset=x-user-defined');
|
||||
this.xhr.addEventListener('progress', (function(_this) {
|
||||
return function(evt) {
|
||||
if (_this.progress) {
|
||||
return _this.progress(evt.total, evt.loaded);
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
this.xhr.onreadystatechange = (function(_this) {
|
||||
return function() {
|
||||
return _this._onReadyStateChange();
|
||||
};
|
||||
})(this);
|
||||
this.xhr.send();
|
||||
}
|
||||
|
||||
Xhr.prototype._onReadyStateChange = function() {
|
||||
if (this.xhr.readyState === 4 && this.success) {
|
||||
return this.success(this.xhr.responseText);
|
||||
}
|
||||
};
|
||||
|
||||
return Xhr;
|
||||
|
||||
})();
|
||||
|
||||
}).call(this);
|
BIN
src/assets/_root/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/_root/favicon.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
41
src/assets/_root/manifest.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "Its Eric Woodward! (dotcom)",
|
||||
"icons": [
|
||||
{
|
||||
"src": "\/images\/favicons\/android-icon-36x36.png",
|
||||
"sizes": "36x36",
|
||||
"type": "image\/png",
|
||||
"density": "0.75"
|
||||
},
|
||||
{
|
||||
"src": "\/images\/favicons\/android-icon-48x48.png",
|
||||
"sizes": "48x48",
|
||||
"type": "image\/png",
|
||||
"density": "1.0"
|
||||
},
|
||||
{
|
||||
"src": "\/images\/favicons\/android-icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image\/png",
|
||||
"density": "1.5"
|
||||
},
|
||||
{
|
||||
"src": "\/images\/favicons\/android-icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image\/png",
|
||||
"density": "2.0"
|
||||
},
|
||||
{
|
||||
"src": "\/images\/favicons\/android-icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image\/png",
|
||||
"density": "3.0"
|
||||
},
|
||||
{
|
||||
"src": "\/images\/favicons\/android-icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image\/png",
|
||||
"density": "4.0"
|
||||
}
|
||||
]
|
||||
}
|
BIN
src/assets/_root/root/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/_root/root/favicon.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/assets/files/chim/chim.exe
Normal file
422
src/assets/files/chim/chim.faq
Normal file
@ -0,0 +1,422 @@
|
||||
┌─────┐ ┌──────┐ ┌───────┐ ┌─────┐ ┌─┐ ┌─┐ ┌──────┐ ┌─────┐ ┌─────┐ ┌─┐ ┌─┐
|
||||
│ ┌─┐ │ │ ┌──┐ │ │ ┌┐ ┌┐ │ │ ┌─┐ │ │ │ │ │ │ ┌──┐ │ │ ┌─┐ │ │ ┌─┐ │ │ │ │ │
|
||||
│ │ └─┘ │ └──┘ │ │ ││ ││ │ │ └─┘ │ │ └─┘ │ │ └──┘ │ │ └─┘ │ │ └─┘ │ │ └─┘ │
|
||||
│ │ ┌─┐ │ ┌──┐ │ │ │└─┘│ │ │ ┌───┘ │ ┌─┐ │ │ ┌──┐ │ │ ┌───┘ │ ┌───┘ └─┐ ┌─┘
|
||||
│ └─┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
└─────┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘
|
||||
|
||||
┌───┐ ┌─────┐ ┌─┐ ┌──────┐ ┌───┐ ┌─┐ ┌──────┐
|
||||
└┐ ┌┘ │ ┌───┘ │ │ │ ┌──┐ │ │ └┐ │ │ └┐ ┌─┐ │
|
||||
│ │ │ └───┐ │ │ │ └──┘ │ │ ├┐ └┐│ │ │ │ │ │
|
||||
│ │ └───┐ │ │ │ │ ┌──┐ │ │ │└┐ └┤ │ │ │ │ │
|
||||
┌┘ └┐ ┌───┘ │ │ └──┐ │ │ │ │ │ │ └┐ │ ┌┘ └─┘ │
|
||||
└───┘ └─────┘ └────┘ └─┘ └─┘ └─┘ └───┘ └──────┘
|
||||
|
||||
▄██████▄ ▄█████▄ ▄█████▄ ▄█████▄ ▄█████▄ ▄█████▄ ██████▄ ▄██████ ██
|
||||
██ ██ ██ ██▄▄▄██ ██▄▄▄▄ ██▄▄▄▄ ██▄▄▄██ ██ ██ ██ ██▄▄▄▄ ██
|
||||
██ ██ ██ ██▀▀▀██ ▀▀▀▀██ ▀▀▀▀██ ██▀▀▀██ ██ ██████ ██▀▀▀▀ ▀▀
|
||||
██ ██ ██ ██ ██ ▀█████▀ ▀█████▀ ██ ██ ▀█████▀ ██ ▀██ ▀██████ ██
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
|
||||
"We Have Such Sights To Show You... "
|
||||
|
||||
=============================================================================
|
||||
Camp Happy Island Massacre FAQ v1.1
|
||||
by Eric Woodward (hey@itsericwoodward.com)
|
||||
=============================================================================
|
||||
|
||||
Table of Contents
|
||||
|
||||
Section 1 - The Beginning
|
||||
1.0 - SPECIAL NOTE - 20 Years Later
|
||||
1.1 - What is 'Camp Happy Island Massacre'?
|
||||
1.2 - Who Wrote This Wonderful Game?
|
||||
1.3 - What Kind of System Do I Need to Run This Game?
|
||||
1.4 - How Do I Install and Run This Game?
|
||||
1.5 - How Can I Play This Game Online?
|
||||
|
||||
Section 2 - The Game
|
||||
2.1 - The Story So Far...
|
||||
2.2 - So How Do I Play?
|
||||
2.3 - Is There a Map?
|
||||
2.4 - What Horror Movies Inspired This Game (aka The Reference Section)?
|
||||
2.5 - Will There Be a Sequel?
|
||||
|
||||
Section 3 - The Boring Stuff
|
||||
3.0 - SPECIAL NOTE!
|
||||
3.1 - What About Bugs?
|
||||
3.2 - What License is CHIM Released Under?
|
||||
3.3 - Why Isn't CHIM Open Source?
|
||||
3.4 - Where Can I Find This Game?
|
||||
3.5 - Game Version History
|
||||
3.6 - FAQ Version History
|
||||
3.7 - Acknowledgements
|
||||
3.8 - Support (Such As It Is)
|
||||
|
||||
Section 4 - The Secrets
|
||||
4.1 - The Big (well, Fairly Big) Secret
|
||||
4.2 - How Do I Win?
|
||||
4.3 - How (Can / Do) I Cheat?
|
||||
|
||||
-------------------------
|
||||
Section 1 - The Beginning
|
||||
-------------------------
|
||||
|
||||
1.0 - SPECIAL NOTE - 20 Years Later
|
||||
The FAQ that you are about to read was originally written in January 1997,
|
||||
when this game was first released. Since then, alot has happened, and much
|
||||
of this FAQ had grown *severely* out-of-date. As a result, I've gone in and
|
||||
cleaned-up some of the questions, updated several of the references in the
|
||||
answers, and just generally tried to improve the usefulness of these
|
||||
Frequently Asked Questions.
|
||||
|
||||
Thanks for taking a look!
|
||||
|
||||
1.1 - What is 'Camp Happy Island Massacre'?
|
||||
'Camp Happy Island Massacre' (from here-on-out referred to as 'CHIM') is a
|
||||
single-player computer game, with an interface not entirely unlike the old
|
||||
(I prefer the term 'classic') script-adventure style of games, like
|
||||
'The Hitchhiker's Guide to the Galaxy' or the original 'Zork' (among
|
||||
others).
|
||||
|
||||
Why this interface? For several reasons:
|
||||
1) This is my first full game, and I knew that I couldn't start with
|
||||
anything much more involved than a script-adventure,
|
||||
2) This game was programmed in C++, a language that I am still not
|
||||
completely at-ease with (and don't particularly care for, TBH), and
|
||||
3) I wanted to prove that a game could still be fun without it being 100+
|
||||
MBs big and requiring a 3-D graphics card (something that, I feel,
|
||||
far too many game-making companies don't believe).
|
||||
|
||||
If this sounds boring, it should be noted that the bulk of the game's
|
||||
appeal lies in it's comedic elements. As one can probably tell based on the
|
||||
story, the game is supposed to be a tongue-in-cheek parody of horror films,
|
||||
and, I feel that the game works fairly well in a picture-less format (after
|
||||
all, the works of Poe, Lovecraft, and even King are much scarier as books
|
||||
than as movies for just this reason). If you disagree, though, you'll be
|
||||
glad to hear that the sequel (if it ever happens) will be more visual in
|
||||
nature (for more information, see the question concerning the sequel).
|
||||
|
||||
1.2 - Who Wrote This Wonderful Game?
|
||||
I did, over 20 years ago.
|
||||
|
||||
My name is Eric Woodward and, at the time of this game's release, I was in
|
||||
my second year of college, slowly approaching a degree in Computer Science
|
||||
(which I completed in 2000).
|
||||
|
||||
In the 20+ years since, I've gotten married, had a couple of kids, and
|
||||
managed to carve out a fairly comfortable life as a coder / web developer.
|
||||
But, through it all, CHIM remains the only computer game that I ever
|
||||
released (although I did manage to put out a tabletop card game earlier
|
||||
this year called 'Mythic Wars: Clash of the Gods', available at many fine
|
||||
gaming stores from Excalibre Games -- find out more at
|
||||
https://mythicwarsgame.com!).
|
||||
|
||||
Oh yeah... I wrote this FAQ, too.
|
||||
|
||||
1.3 - What Kind of System Do I Need to Run This Game?
|
||||
Initially, I'd said "you need a computer; that's about it. Anything from a
|
||||
286 on up to the latest Pentium II should be sufficient to run the game",
|
||||
and that was true at the time (when MS-DOS still ruled the world). However,
|
||||
to run this game in 2018, you probably need a copy of DOSBox (see the next
|
||||
question for more information).
|
||||
|
||||
Or, if you don't want to wait, you can most likely play it in your browser
|
||||
*right now* on my website: https://www.itsericwoodward.com/chim/.
|
||||
|
||||
1.4 - How Do I Install and Run This Game?
|
||||
To play the game on your own machine, you'll need a couple of different
|
||||
programs. Since the game is distributed in the pkzip archive format, you
|
||||
may need a program to 'unzip' it (unless your OS does it for you, like
|
||||
Windows 10). Additionally, you'll need a copy of
|
||||
DOSBox (https://www.dosbox.com/). If I were you, I'd unzip CHIM to its
|
||||
own folder (ex: `c:\games\chim`), and startup DOSBox. Next, mount the CHIM
|
||||
folder (ex: `mount m c:\games\chim`) and switch to that drive (ex: `m:`).
|
||||
Finally, start the game with the `chim` command.
|
||||
|
||||
1.5 - How Can I Play This Game Online?
|
||||
Thanks to modern software miracles like DOSBox (https://www.dosbox.com/),
|
||||
Emscripten (http://emscripten.org/), and their unholy lovechild,
|
||||
JS-DOS (https://js-dos.com/), you can play this game in a modern browser
|
||||
on my website: https://www.itsericwoodward.com/chim/.
|
||||
|
||||
--------------------
|
||||
Section 2 - The Game
|
||||
--------------------
|
||||
|
||||
2.1 - The Story So Far...
|
||||
Some 15 years ago, someone decided to build a summer camp on
|
||||
'Happy Island', a small island in Lake Hades, near the town of Springfield.
|
||||
Despite the fact that the island had been known as 'Death Island' (that is,
|
||||
until the Springfield Tourism Bureau decided to rename it), or the fact
|
||||
that the town's resident drunk, homeless, crazy, old Native American, John,
|
||||
continually warned of a great, ancient evil living on the island, the camp
|
||||
was built.
|
||||
|
||||
Then, the night before the campers were supposed to arrive, something
|
||||
horrible happened. The next day, all 13 counselors were found dead, their
|
||||
body parts scattered all over the island. Needless to say, the camp was
|
||||
closed.
|
||||
|
||||
Then, 5 years later, someone else decided to re-open the camp, despite the
|
||||
still unsolved murders of 5 years before, and despite the fact that many of
|
||||
Springfield's residents had seen strange lights circling in the sky above
|
||||
the island. Again, 13 counselors were brought from the surrounding areas to
|
||||
help run the camp, and again, the night before the campers were to arrive,
|
||||
something horrbile happened. The next day, the strange lights were gone...
|
||||
and so were the counselors - nothing remained of them but a few scattered
|
||||
arms and legs. And, again, the camp was closed down.
|
||||
|
||||
And it remained closed until 5 years later, when someone else decided to
|
||||
re-open the summer camp. This person, being a little superstitious, decided
|
||||
to hire 14 counselors instead of just 13, hoping to break the apparent
|
||||
curse over the camp. Unbeknownst to anyone, however, the 14th counselor,
|
||||
named Jason Frederick Bates-Myers (his friends called him 'Jack', but his
|
||||
enemies called him 'Mr. Leather-Ghost-Face Man'), was a violent psycopath
|
||||
who, on the night before the campers were to arrive, killed the 13 other
|
||||
counselors using a leaf blower and homemade 'explosive' attachment. In the
|
||||
end, Jack was taken to Belleward Mental Institute, and the camp was closed
|
||||
down.
|
||||
|
||||
And that was 5 years ago. Now, they want to re-open it again, this time
|
||||
with 13 brand-new counselors, and a fresh look on life. Finding counselors
|
||||
was easy - after all, what teenager doesn't want to spend 6 weeks as
|
||||
relatively unsupervised counselors at a co-ed summer camp, away from
|
||||
parents, teachers, and other adults? So what if John, the Native American
|
||||
is talking about the 'ancient evil' again? So what if the strange lights
|
||||
have re-appeared? And so what if that Bates-Myers guy has escaped from
|
||||
Belleward? That doesn't mean anything, right?
|
||||
|
||||
Wrong... Because it's the night before the campers are to arrive... And the
|
||||
camp head, Ranger Bud, has gone into town... And the other 10 counselors
|
||||
have all disappeared into the woods, leaving only the three of you back at
|
||||
camp... And you're scared... You're scared that you might not survive...
|
||||
|
||||
The Camp Happy Island Massacre!
|
||||
|
||||
2.2 - So How Do I Play?
|
||||
When you run the game, it will ask if you need instructions. Moving around
|
||||
the island is accomplished via four keys: 'N' for North, 'S' for South,
|
||||
'E' for East, and 'W' for West. You can't always go all four ways, so it
|
||||
pays to read the descriptions of the areas you find yourself in (if
|
||||
something is blocking a certain direction, you can't go that way).
|
||||
Incidentally, if you can't remember where you can go, you can always use
|
||||
the 'L' (Look) command to read the description again.
|
||||
|
||||
If you encounter an item while you are walking around, you can try to pick
|
||||
it up by using the 'G' (Get) command, but don't be surprised if it tells
|
||||
you that you can't pick an item up (typically, this only happens with items
|
||||
that are particularly nasty or heavy). Once you have an item, you can take
|
||||
a closer look at it by using the 'X' (eXamine) command, and you can try to
|
||||
use it via the 'U' (Use) command. Again, don't be surprised if you beat the
|
||||
game without ever using certain items - many of them are just there for fun
|
||||
and can't be used at all.
|
||||
|
||||
The remaining commands are either used to get on-line help ('?' or 'H' for
|
||||
Quick Help, or 'M' to read the Manual), or to quit the game ('Q').
|
||||
|
||||
Oh yeah - there are some hidden rooms on the island, one of which you will
|
||||
have to find in order to beat the game... Good Luck.
|
||||
|
||||
2.3 - Is There a Map?
|
||||
Included in the distribution should be "MAP.TXT", a map in UTF-8 text-file
|
||||
format (if your distribution didn't include one, get a new distribution).
|
||||
Granted, this map is only slightly helpful, as it doesn't have any actual
|
||||
trails drawn on it, just major landmarks. If you were in fact looking for
|
||||
a map of trails... sorry, but there isn't one (at least, there isn't one
|
||||
that I'm distributing...) I feel that having that kind of a map would
|
||||
subtract SEVERELY from the game's fun-factor. So, in short, make due with
|
||||
what you got, and if you are an enterprising individual and wish to make
|
||||
a map, it shouldn't be too hard.
|
||||
|
||||
2.4 - What Horror Movies Inspired This Game (aka The Reference Section)?
|
||||
This game was, more or less, inspired by a whole slew of movies. Some are
|
||||
more obvious than others (the many and varied 'Friday the 13th' movies
|
||||
stand out in my mind), and sometimes it isn't even movies that inspired
|
||||
certain elements of this game, but rather books (like the works of one H.P.
|
||||
Lovecraft). Regardless, here's a brief list of movies and movie series that
|
||||
inspired me:
|
||||
|
||||
Evil Dead (including Army of Darkness), Scream, Friday the 13th, Psycho,
|
||||
Hellraiser, A Nightmare on Elm Street, Nightbreed, Carrie, Firestarter,
|
||||
Phantasm, Halloween, Texas Chainsaw Massacre, The Re-Animator, Bad Taste,
|
||||
Puppet Master, Dead Alive (Braindead), The Frighteners, Alien, Showgirls,
|
||||
Night of the Living Dead, There's Nothing Out There, and countless others.
|
||||
|
||||
2.5 - Will There Be a Sequel?
|
||||
Initially, I had planned to make one, "Cause nowadays, ya' gotta have a
|
||||
sequel!". I had even begun work on one, a CD-ROM-based game complete with
|
||||
full-motion video, special effects, even a script! But, it never came to
|
||||
fruition, and by now, the code is lost to time (along with the video,
|
||||
the photos, and the age of most of the original cast).
|
||||
|
||||
On the other hand, I have considered making a board or card game based on
|
||||
the characters and concepts from CHIM, so... Who knows what the future has
|
||||
in store for CHIM?
|
||||
|
||||
----------------------------
|
||||
Section 3 - The Boring Stuff
|
||||
----------------------------
|
||||
|
||||
3.0 - SPECIAL NOTE!
|
||||
This version (v1.0) of the game will be the only version ever to be
|
||||
released. This was not my original intention; I had fully intended to fix
|
||||
any and all bugs that turned up, colorize the output (to make it pretty),
|
||||
and then re-release it. Unfortunately, the best laid plans of mice and
|
||||
men (and me) can't stand up to a total hard-drive loss, especially when
|
||||
the last remaining copy of the source code was contained therein. So, you
|
||||
can keep playing CHIM all you want, but there almost certainly won't be a
|
||||
new version of it, ever. We now return you to your regularly scheduled FAQ.
|
||||
|
||||
3.1 - What About Bugs?
|
||||
What Bugs? I write perfect programs.
|
||||
|
||||
Not really. Despite my best efforts, my program does have some unintended
|
||||
(and unwanted) 'features'. The complete (to my knowledge) list is below:
|
||||
- Some problems with garbage characters popping into descriptions of some
|
||||
of the areas (caused by a char-pointer problem).
|
||||
|
||||
If you find a bug that isn't listed here, please feel free to e-mail me
|
||||
at hey@itsericwoodward.com, or snail-mail them to me at the address listed
|
||||
a little later.
|
||||
|
||||
3.2 - What License is CHIM Released Under?
|
||||
CHIM was originally released as "<Whatever>-ware", wherein I asked people
|
||||
who enjoyed the game to "send me something (preferably not something that
|
||||
is alive, ticking, or that has a strange odor)." In the 20 years since the
|
||||
game's original release, free software licensing has grown by leaps and
|
||||
bounds (plus, I'm kind of afraid to ask people to send me things now).
|
||||
|
||||
As a result of these factors, I'm re-releasing CHIM under the so-called
|
||||
MIT License (a copy of which should have been included in your ZIP file
|
||||
distribution as "LICENSE.TXT") and which can be found at:
|
||||
|
||||
https://www.itsericwoodward.com/chim/LICENSE.TXT
|
||||
|
||||
3.3 - Is CHIM Open Source?
|
||||
Well, it would be, but (as noted above), the code was lost during a hard-
|
||||
drive crash in mid-1997 (just a couple of months after the game was
|
||||
released). As a result, there is no source to open (otherwise, I would
|
||||
definitely have re-released it under an open-source license years ago).
|
||||
|
||||
3.4 - Where Can I Find This Game?
|
||||
The latest version of the game, FAQ, and other related files can always be
|
||||
found at:
|
||||
|
||||
https://www.itsericwoodward.com/chim/
|
||||
|
||||
3.5 - Game Version History
|
||||
v0.9b - released on 10/31/97 (Halloween) - Original Beta Release
|
||||
v1.0 - released on 01/05/97 - Original Full Release
|
||||
- Fixed small problem with movement and the map
|
||||
- Made output prettier
|
||||
|
||||
3.6 - FAQ Version History
|
||||
v1.0 - released on 01/05/1997 - Original Full Release
|
||||
v1.1 - released on 10/10/2018 - 21 Years Worth of Updates
|
||||
|
||||
3.7 - Acknowledgements
|
||||
There are many people I want to thank, but I can't, so I'll just hit the
|
||||
major ones...
|
||||
|
||||
- Mark, Donnie, Tommy, Jeff, and Sarah for not laughing at me when I
|
||||
mentioned that I was writing a game;
|
||||
- My playtester, Mike, for actually doing playtesting (maps and all);
|
||||
- All of my professors and friends at school for their help, both direct
|
||||
and implicit;
|
||||
- And my wonderful wife, Stacie, for supporting me.
|
||||
|
||||
3.8 - Support (Such As It Is)
|
||||
I offer no promises for support, but feel free to contact me at any of the
|
||||
following addresses / locations:
|
||||
|
||||
Email: hey@itsericwoodward.com
|
||||
|
||||
WWW: https://www.itsericwoodward.com/chim/
|
||||
|
||||
Fediverse: @eric@social.wonderdome.net
|
||||
|
||||
-----------------------
|
||||
Section 4 - The Secrets
|
||||
-----------------------
|
||||
|
||||
WARNING! - This section contains many gratuitous spoilers. If you read this
|
||||
section, there is a chance that some of the fun of the game will be lost.
|
||||
So, I urge you to skip this section entirely. Just so no secrets get out,
|
||||
let me fill up the rest of this space with, um, filler:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Alright... You asked for it...
|
||||
|
||||
4.1 - The Big (well, Fairly Big) Secret
|
||||
The Fairly Big Secret is that the game doesn't just have 1 ending. Each
|
||||
time the game is loaded, there is a random chance that the killer will be
|
||||
either a group of aliens, an unnamable Thing, or a vengeful psychopath
|
||||
(Jason Frederick Bates-Myers, to be more specific).
|
||||
|
||||
4.2 - How Do I Win?
|
||||
First off, you MUST find the radio tower and signal for help (it may sound
|
||||
like the message didn't get out, but rest assured that it did).
|
||||
|
||||
After that, it depends on who the killer is.
|
||||
- If it's the Aliens - you need to get the credit card type-thing out of
|
||||
the cave, take it to the secret room (in the lodge) and use it, and then
|
||||
go to the baseball field, where the ship will land.
|
||||
- If it's the Thing - you need to get the Unholy Book from the secret room
|
||||
(in the lodge), take it to the baseball field and use it, and then go to
|
||||
the cave, where the thing will be waiting.
|
||||
- If it's the Psycho - you need to get the flashlight from the baseball
|
||||
field, take it to the cave and use it to read the words on the wall, and
|
||||
then you need to go to the secret room (in the lodge) to confront Jason.
|
||||
|
||||
4.3 - How (Can / Do) I Cheat?
|
||||
Well, it seems to me that that's exactly what you're doing right now. But,
|
||||
you can cheat by entering one or two command line arguments when executing
|
||||
the game. They are called by typing 'chim -arg1 -arg2' where the args
|
||||
can be:
|
||||
'-nokiller' - tired of the killer getting you before you can get him? Use
|
||||
this little switch to prevent him from ever finding you until the end of
|
||||
the game.
|
||||
'-aliens' - forces the bad guys to be the aliens.
|
||||
'-thing' - forces the bad guy to be the unnamable thing.
|
||||
'-psycho' - forces the bad guy to be Jason Frederick Bates-Myers.
|
||||
|
||||
So, to make the bad guys be the aliens, type 'chim -aliens' at the
|
||||
command prompt. Or, to make it be the psycho, and to keep him from killing
|
||||
you first, type 'chim -nokiller -psycho'. Of course, entering the
|
||||
command 'chim -aliens -thing' doesn't work; the bad guy winds up being
|
||||
the thing.
|
||||
|
||||
And that, my friend, is how you cheat.
|
BIN
src/assets/files/chim/chim.zip
Normal file
9
src/assets/files/chim/file_id.diz
Normal file
@ -0,0 +1,9 @@
|
||||
Camp Happy Island Massacre v1.0 (1997):
|
||||
Tongue-in-cheek menu-driven text game,
|
||||
inspired by 80s horror movies. Play as
|
||||
the only surviving counselors of an ill-
|
||||
fated summer camp as you try to figure
|
||||
out who (or what) is killing everyone
|
||||
(and how you can survive). 1 player.
|
||||
Freeware (MIT License) by Eric Woodward.
|
||||
https://www.itsericwoodward.com/chim/
|
22
src/assets/files/chim/intro.txt
Normal file
@ -0,0 +1,22 @@
|
||||
┌─────┐ ┌──────┐ ┌───────┐ ┌─────┐ ┌─┐ ┌─┐ ┌──────┐ ┌─────┐ ┌─────┐ ┌─┐ ┌─┐
|
||||
│ ┌─┐ │ │ ┌──┐ │ │ ┌┐ ┌┐ │ │ ┌─┐ │ │ │ │ │ │ ┌──┐ │ │ ┌─┐ │ │ ┌─┐ │ │ │ │ │
|
||||
│ │ └─┘ │ └──┘ │ │ ││ ││ │ │ └─┘ │ │ └─┘ │ │ └──┘ │ │ └─┘ │ │ └─┘ │ │ └─┘ │
|
||||
│ │ ┌─┐ │ ┌──┐ │ │ │└─┘│ │ │ ┌───┘ │ ┌─┐ │ │ ┌──┐ │ │ ┌───┘ │ ┌───┘ └─┐ ┌─┘
|
||||
│ └─┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
|
||||
└─────┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘
|
||||
|
||||
┌───┐ ┌─────┐ ┌─┐ ┌──────┐ ┌───┐ ┌─┐ ┌──────┐
|
||||
└┐ ┌┘ │ ┌───┘ │ │ │ ┌──┐ │ │ └┐ │ │ └┐ ┌─┐ │
|
||||
│ │ │ └───┐ │ │ │ └──┘ │ │ ├┐ └┐│ │ │ │ │ │
|
||||
│ │ └───┐ │ │ │ │ ┌──┐ │ │ │└┐ └┤ │ │ │ │ │
|
||||
┌┘ └┐ ┌───┘ │ │ └──┐ │ │ │ │ │ │ └┐ │ ┌┘ └─┘ │
|
||||
└───┘ └─────┘ └────┘ └─┘ └─┘ └─┘ └───┘ └──────┘
|
||||
|
||||
▄██████▄ ▄█████▄ ▄█████▄ ▄█████▄ ▄█████▄ ▄█████▄ ██████▄ ▄██████ ██
|
||||
██ ██ ██ ██▄▄▄██ ██▄▄▄▄ ██▄▄▄▄ ██▄▄▄██ ██ ██ ██ ██▄▄▄▄ ██
|
||||
██ ██ ██ ██▀▀▀██ ▀▀▀▀██ ▀▀▀▀██ ██▀▀▀██ ██ ██████ ██▀▀▀▀ ▀▀
|
||||
██ ██ ██ ██ ██ ▀█████▀ ▀█████▀ ██ ██ ▀█████▀ ██ ▀██ ▀██████ ██
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
|
||||
"We Have Such Sights To Show You... "
|
22
src/assets/files/chim/license.txt
Normal file
@ -0,0 +1,22 @@
|
||||
Copyright (c) 1997-2018 Eric Woodward (https://itsericwoodward.com).
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
Camp Happy Island Massacre and all characters and locations from the software
|
||||
are trademarks of Eric Woodward.
|
24
src/assets/files/chim/map.txt
Normal file
@ -0,0 +1,24 @@
|
||||
/\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\
|
||||
/^\/^\/^\/^\ T H E M O U N T A I N S /^\/^\/^\/^\
|
||||
/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\(*)/^\/^\/^\/^\/^\/^\/^\
|
||||
|#++++++++++++++++++++++++++++ ^--- Cave(?) ++++++++#|
|
||||
|#+ o ++++++++++++++++++++++++++++++++++++++++++++++#|
|
||||
|#+ | ++++++++++++++++++++++++++++++++++++++++++++++#|
|
||||
|#+ | <-- Radio Tower ++++++++++++++++++++++ [] ++++#|
|
||||
LAKE |# /-\ +++++++++++++++++++++++++++++++++++ / \ ++#| LAKE
|
||||
HADES |#++++++++++++++++++++++++++++++++++++ > [] [] #| HADES
|
||||
|#++++++++ /^\ <-- Hunting ++++++++++ | ++ \ / ++#|
|
||||
|#++++++ /| |\ Lodge(?) +++++++++ | ++++ [] ++++#|
|
||||
|#+++++++ |___| +++++++++++++++++++++ | ++++++++++++#|
|
||||
|#+++++++++++++++++++++++++++++++++++ \- Baseball ++#|
|
||||
|#+++++++++++++++++++++++++++++++++++++++ Field(?) +#|
|
||||
|#++ [====] ++++++++++++++++++++++++++++++++++++++++#|
|
||||
|#++ |Camp| ++++++++++++++++++++++++++++++++++++++++#|
|
||||
\#+ [====] +++++++++++++++++++++++++++++++++++++++#/
|
||||
\##############################################/
|
||||
--------------------------------------------
|
||||
# = Beach LAKE Note: Map is NOT
|
||||
+ = Woods HADES drawn to scale.
|
||||
|
||||
ASCII Map for "Camp Happy Island Massacre" by Eric Woodward,
|
||||
playable online at https://www.itsericwoodward.com/chim/.
|
88
src/assets/files/public.aexpk
Normal file
@ -0,0 +1,88 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
cTTc
|
||||
=tIaI
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
BIN
src/assets/fonts/exo2/Exo2-Bold-webfont.eot
Normal file
1718
src/assets/fonts/exo2/Exo2-Bold-webfont.svg
Normal file
After Width: | Height: | Size: 128 KiB |
BIN
src/assets/fonts/exo2/Exo2-Bold-webfont.ttf
Normal file
BIN
src/assets/fonts/exo2/Exo2-Bold-webfont.woff
Normal file
BIN
src/assets/fonts/exo2/Exo2-BoldItalic-webfont.eot
Normal file
1694
src/assets/fonts/exo2/Exo2-BoldItalic-webfont.svg
Normal file
After Width: | Height: | Size: 131 KiB |
BIN
src/assets/fonts/exo2/Exo2-BoldItalic-webfont.ttf
Normal file
BIN
src/assets/fonts/exo2/Exo2-BoldItalic-webfont.woff
Normal file
BIN
src/assets/fonts/exo2/Exo2-Italic-webfont.eot
Normal file
1641
src/assets/fonts/exo2/Exo2-Italic-webfont.svg
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
src/assets/fonts/exo2/Exo2-Italic-webfont.ttf
Normal file
BIN
src/assets/fonts/exo2/Exo2-Italic-webfont.woff
Normal file
BIN
src/assets/fonts/exo2/Exo2-Regular-webfont.eot
Normal file
1648
src/assets/fonts/exo2/Exo2-Regular-webfont.svg
Normal file
After Width: | Height: | Size: 124 KiB |
BIN
src/assets/fonts/exo2/Exo2-Regular-webfont.ttf
Normal file
BIN
src/assets/fonts/exo2/Exo2-Regular-webfont.woff
Normal file
43
src/assets/fonts/exo2/SIL Open Font License.txt
Normal file
@ -0,0 +1,43 @@
|
||||
Copyright (c) 2013, Natanael Gama (www.ndiscovered.com . info(at)ndiscovered.com), with Reserved Font Name Exo.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
src/assets/images/404-opte-1000x800.jpg
Normal file
After Width: | Height: | Size: 136 KiB |
BIN
src/assets/images/404/hole-bg-1100x1238-50.jpg
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
src/assets/images/404/hole-bg-1100x900-50.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
src/assets/images/404/hole-bg-1600x900-50.jpg
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
src/assets/images/404/hole-bg.jpg
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
src/assets/images/anachronism/ana_logo.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/images/anachronism/immortals/amanda_darieux.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
src/assets/images/anachronism/immortals/antonius_kalas.jpg
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
src/assets/images/anachronism/immortals/black_longcoat.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
src/assets/images/anachronism/immortals/black_trenchcoat.jpg
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
src/assets/images/anachronism/immortals/clamshell_rapier.jpg
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
src/assets/images/anachronism/immortals/clan_macleod.jpg
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
src/assets/images/anachronism/immortals/connor_macleod.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
src/assets/images/anachronism/immortals/darius.jpg
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
src/assets/images/anachronism/immortals/dragons_head_katana.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
src/assets/images/anachronism/immortals/duncan_macleod.jpg
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
src/assets/images/anachronism/immortals/four_horsemen.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
src/assets/images/anachronism/immortals/glenfinnan.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
src/assets/images/anachronism/immortals/grey_trenchcoat.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
src/assets/images/anachronism/immortals/holy_ground.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
src/assets/images/anachronism/immortals/hugh_fitzcairn.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
src/assets/images/anachronism/immortals/immortal_healing.jpg
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
src/assets/images/anachronism/immortals/jacob_kell.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
src/assets/images/anachronism/immortals/james_horton.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
src/assets/images/anachronism/immortals/joe_dawson.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 48 KiB |
BIN
src/assets/images/anachronism/immortals/kells_gang.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
src/assets/images/anachronism/immortals/kronos.jpg
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
src/assets/images/anachronism/immortals/kurgans_broadsword.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
src/assets/images/anachronism/immortals/leather_coat.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
src/assets/images/anachronism/immortals/leather_jacket.jpg
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
src/assets/images/anachronism/immortals/masamune_katana.jpg
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
src/assets/images/anachronism/immortals/methos.jpg
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
src/assets/images/anachronism/immortals/methos_broadsword.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
src/assets/images/anachronism/immortals/motorcycle_jacket.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
src/assets/images/anachronism/immortals/quickening.jpg
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
src/assets/images/anachronism/immortals/richie_ryan.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
src/assets/images/anachronism/immortals/rocker_jacket.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
src/assets/images/anachronism/immortals/tessa_noel.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
src/assets/images/anachronism/immortals/the_game.jpg
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
src/assets/images/anachronism/immortals/the_hunters.jpg
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
src/assets/images/anachronism/immortals/the_kurgan.jpg
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
src/assets/images/anachronism/immortals/the_prize.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
src/assets/images/anachronism/immortals/the_sword_of_kronos.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
src/assets/images/anachronism/immortals/the_watchers.jpg
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
src/assets/images/anachronism/immortals/xavier_saint_cloud.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
src/assets/images/anachronism/links/abraham_lincoln.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/images/anachronism/links/billy_the_kid.jpg
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
src/assets/images/anachronism/links/george_washington.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
src/assets/images/anachronism/links/robert_e_lee.jpg
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
src/assets/images/anachronism/links/stetson_hat.jpg
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
src/assets/images/anachronism/links/the_dictator.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
src/assets/images/anachronism/links/the_us_constitution.jpg
Normal file
After Width: | Height: | Size: 29 KiB |