Browse Source

Commit

master
Gisle Aune 8 years ago
commit
35f065e19e
  1. 2
      .gitignore
  2. 3
      config.json
  3. 1
      data/data.json
  4. 51
      index.js
  5. 17
      package.json
  6. 2
      run.sh
  7. 48
      scripts/bg.js
  8. 27
      scripts/data.js
  9. 81
      scripts/generate.js
  10. 52
      scripts/info.js
  11. 17
      scripts/list.js
  12. 239
      www-root/css/main.css
  13. BIN
      www-root/img/bg_da.png
  14. BIN
      www-root/img/bg_da_dalish_elf.png
  15. BIN
      www-root/img/bg_es.png
  16. BIN
      www-root/img/bg_me.png
  17. BIN
      www-root/img/bg_me_asari.png
  18. 32
      www-root/index.html
  19. 4
      www-root/js/jquery.js
  20. 3360
      www-root/js/ngn4-0.4.1.js
  21. 221
      www-root/js/page.js

2
.gitignore

@ -0,0 +1,2 @@
node_modules/
access.log

3
config.json

@ -0,0 +1,3 @@
{
"port": 8000
}

1
data/data.json
File diff suppressed because it is too large
View File

51
index.js

@ -0,0 +1,51 @@
var fs = require('fs');
var path = require('path');
var express = require('express');
var morgan = require('morgan')
var ngn4 = require('ngn4');
var config = require('./config.json');
var app = express();
var accessLogStream = fs.createWriteStream(__dirname + '/access.log', {flags: 'a'});
app.use(require('compression')());
app.use(morgan('combined', {stream: accessLogStream}));
// Load data
var set = new ngn4.NameGeneratorSet();
var dir = path.join(__dirname, 'data');
fs.readdirSync(dir).forEach(function(file) {
console.log('Loading ' + file + '...');
set.import(require('./data/' + file), true);
});
// Prepare merged data file for client to use.
var data = set.export();
// Load scripts
dir = path.join(__dirname, 'scripts');
fs.readdirSync(dir).forEach(function(file) {
console.log('Running ' + file + '...');
require('./scripts/' + file)(set, app, data);
});
// Prepare static directories
app.use('/js', express.static('www-root/js'));
app.use('/css', express.static('www-root/css'));
app.use('/img/', express.static('www-root/img'));
// Set up index
function sendIndexHtml(req, res) {
res.header("Content-Type", "text/html; charset=utf-8");
res.sendFile(path.join(__dirname + '/www-root/index.html'));
}
app.get('/index.html', sendIndexHtml);
app.get('/', sendIndexHtml);
var server = app.listen(config.port, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Server listening at http://%s:%s', host, port);
});

17
package.json

@ -0,0 +1,17 @@
{
"name": "ngn4-server",
"version": "0.1.0",
"description": "HTTP server frontent for ngn4",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Gisle",
"license": "MIT",
"dependencies": {
"express": "^4.13.3",
"jsonpack": "^1.1.4",
"morgan": "^1.7.0",
"ngn4": "0.x.x"
}
}

2
run.sh

@ -0,0 +1,2 @@
pkill node
node .

48
scripts/bg.js

@ -0,0 +1,48 @@
var fs = require('fs');
var path = require('path');
var IMGDIR = dir = path.join(__dirname, '/../www-root/img/');
function existsSync(filename) {
try {
fs.accessSync(filename);
return true;
} catch(ex) {
return false;
}
}
module.exports = function(set, app, data) {
var keys = Object.keys(set.generators);
var cache = {};
for(var i = 0; i < keys.length; ++i) {
var fileName = "bg_" + keys[i].split('/').join('_') + ".png";
if(existsSync(IMGDIR + fileName)) {
fileName = "/img/" + fileName;
} else {
fileName = "bg_" + keys[i].split('/')[0] + ".png";
if(existsSync(IMGDIR + fileName)) {
fileName = "/img/" + fileName;
} else {
fileName = null;
}
}
var binding = {'json': JSON.stringify(fileName), 'fileName': fileName};
app.get('/b/' + keys[i], (function (req, res) {
var agent = req.headers['user-agent'];
res.header("Content-Type", "application/json; charset=utf-8");
if(agent.indexOf('curl/') != -1) {
res.end(this.fileName);
} else {
res.end(this.json);
}
}).bind(binding));
console.log('[INFO] bg for "'+keys[i]+'" = "'+fileName+'"');
}
}

27
scripts/data.js

@ -0,0 +1,27 @@
module.exports = function(set, app, data) {
var keys = Object.keys(set.generators);
var cache = {};
for(var i = 0; i < keys.length; ++i) {
var bindObj = {key: keys[i]}
var func = (function (req, res) {
var key = this.key;
var gen = set.generators[key];
if(!cache.hasOwnProperty(key)) {
data.generators[key].catId = key.split('/')[0];
cache[key] = JSON.stringify(data.generators[key]);
}
res.header("Content-Type", "application/json; charset=utf-8");
res.end(cache[key]);
}).bind(bindObj);
app.get('/d/' + keys[i], func);
if(i === 0) {
app.get('/d/default', func);
}
}
}

81
scripts/generate.js

@ -0,0 +1,81 @@
var MAX_COUNT = 64;
function generateName(req, res, set, cat, id, format, gender, count)
{
var key = this.key;
var agent = req.headers['user-agent'];
if(typeof(cat) != 'undefined') {
id = cat + '/' + id;
}
var gen = set.getGenerator(req.params.id);
if(typeof(gender) === 'undefined' && gen != null)
{
gender = gen.genders[0];
}
if(typeof(count) === 'undefined')
{
count = 1;
}
if(count > MAX_COUNT) {
count = MAX_COUNT;
}
if(agent.indexOf('curl/') != -1 || req.query.hasOwnProperty('t')) {
if(gen != null)
{
var results = "";
for(var i = 0; i < count; ++i) {
results += gen.generate(format, gender) + '\n';
}
res.header("Content-Type", "text/plain; charset=utf-8");
res.end(results);
} else {
res.header("Content-Type", "text/plain; charset=utf-8");
res.end("ERROR: GENERATOR NOT FOUND");
}
} else {
if(gen != null) {
var results = [];
for(var i = 0; i < count; ++i) {
results.push(gen.generate(format, gender));
}
res.header("Content-Type", "application/json; charset=utf-8");
res.end(JSON.stringify(results));
} else {
res.header("Content-Type", "application/json; charset=utf-8");
res.end(JSON.stringify(null));
}
}
}
module.exports = function(set, app) {
var min = 1;
var max = 32;
app.get('/g/:id', function (req, res) {
generateName(req, res, set, undefined, req.params.id);
});
app.get('/g/:cat/:id', function (req, res) {
generateName(req, res, set, req.params.cat, req.params.id);
});
app.get('/g/:cat/:id/:format', function (req, res) {
generateName(req, res, set, req.params.cat, req.params.id, req.params.format);
});
app.get('/g/:cat/:id/:format/:gender', function (req, res) {
generateName(req, res, set, req.params.cat, req.params.id, req.params.format, req.params.gender);
});
app.get('/g/:cat/:id/:format/:gender/:count', function (req, res) {
generateName(req, res, set, req.params.cat, req.params.id, req.params.format, req.params.gender, req.params.count);
});
}

52
scripts/info.js

@ -0,0 +1,52 @@
module.exports = function(set, app) {
var keys = Object.keys(set.generators);
for(var i = 0; i < keys.length; ++i) {
var bindObj = {key: keys[i]}
app.get('/i/' + keys[i], (function (req, res) {
var key = this.key;
var gen = set.generators[key];
var agent = req.headers['user-agent'];
var results = {
genders: gen.genders,
formats: {}
};
var formatKeys = Object.keys(gen.formats);
for(var j = 0; j < formatKeys.length; ++j) {
var format = gen.formats[formatKeys[j]];
results.formats[formatKeys[j]] = format.name;
}
if(agent.indexOf('curl/') != -1 || req.query.hasOwnProperty('t')) {
var str = '';
for(var i = 0; i < results.genders.length; ++i) {
if(i > 0) {
str += '\n';
}
str += 'Gender: ' + results.genders[i];
}
var lastFormat = "";
for(var j = 0; j < formatKeys.length; ++j) {
var formatKey = formatKeys[j].split('.')[0];
if(formatKey != lastFormat) {
str += '\nFormat: ' + formatKey + ' ' + results.formats[formatKeys[j]];
}
lastFormat = formatKey;
}
res.header("Content-Type", "text/plain; charset=utf-8");
res.end(str);
} else {
res.header("Content-Type", "application/json; charset=utf-8");
res.end(JSON.stringify(results));
}
}).bind(bindObj));
}
}

17
scripts/list.js

@ -0,0 +1,17 @@
module.exports = function(set, app) {
var setHeader = {
categories: set.categoryNames,
generators: {}
};
var genKeys = Object.keys(set.generators);
var catKeys = Object.keys(set.categoryNames);
for(var i = 0; i < genKeys.length; ++i) {
setHeader.generators[genKeys[i]] = set.generators[genKeys[i]].name;
}
app.get('/l/generators', function (req, res) {
res.header("Content-Type", "application/json; charset=utf-8");
res.end(JSON.stringify(setHeader));
});
}

239
www-root/css/main.css

@ -0,0 +1,239 @@
body, html, div {
padding: 0;
margin: 0;
color: #777;
}
body, html {
background-color: #555;
background-size: cover;
font-family: sans-serif;
height: 100%;
width: 100%;
overflow: hidden;
}
a {
display: inline-block;
padding: 0.125em 0.25em;
background-color: rgba(64, 64, 64, 0.5);
color: #ccc;
text-decoration: none;
cursor: pointer;
}
hr {
border: 1px solid rgba(96, 96, 96, 0.5);
}
div.ngn4-panel {
background-color: rgba(0, 0, 0, 0.75);
}
div.ngn4-menu {
position: absolute;
left: 0;
right: 0;
overflow-y: visible;
}
div.ngn4-menu h1 {
margin: 0;
padding: 0.25em 0.5em;
display: inline-block;
font-size: 1em;
}
div.ngn4-geninfo {
padding: 0.5em;
position: absolute;
left: 15%;
right: 15%;
top: 4em;
min-height: calc(100% - 7em);
text-align: center;
overflow-x: hidden;
overflow-y: auto;
}
div.ngn4-geninfo#loading-screen {
overflow-y: hidden;
}
div.ngn4-geninfo a {
margin: 0.25em;
border: 1px solid #000;
}
div.ngn4-geninfo a.selected {
background-color: rgba(192, 128, 32, 0.125);
color: #fff;
border: 1px solid #c82;
}
div.ngn4-footer {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(192, 128, 32, 0.8);
color: #fff;
font-size: 1.5em;
padding: 0.5em;
}
div.ngn4-footer a {
border: 1px solid #000;
}
.vertical-center center {
vertical-align: middle;
position: absolute;
top: calc(50% - 0.5em);
height: 100%;
width: 100%;
font-size: 3em;
}
#gi-name {
padding-top: 2em;
color: #aaa;
font-size: 2em;
}
#gi-race {
color: #555;
font-size: 0.9em;
}
#gi-button {
background-color: rgba(32, 32, 32, 0.9);
color: #ccc;
border: 1px solid #000;
font-size: 1.5em;
margin: 0.5em;
}
#gi-button:focus {
border: 1px solid #c82;
outline: 0;
}
/* DROPDOWN FUNCTIONALITY */
#menu-items div.item {
position: relative;
display: inline-block;
margin: 0;
padding: 0.25em 0.5em;
background: none;
color: #ccc;
text-decoration: none;
cursor: pointer;
}
#menu-items div.item div {
display: none;
position: absolute;
top: 1.65em;
margin-left: -0.5em;
min-width: 12ch;
z-index: 10;
background: none;
}
#menu-items div.item:hover {
background-color: rgba(192, 128, 32, 0.5);
color: #fff;
}
#menu-items div.item:hover div {
display: block;
}
#menu-items div.item div a {
padding: 0.25em;
margin: 0;
background-color: rgba(0, 0, 0, 0.75);
display: block;
}
#menu-items div.item div a:hover {
background-color: rgba(192, 128, 32, 0.75);
color: #fff;
}
@media screen and (max-width: 980px) {
body {
font-size: 1em;
}
div.ngn4-geninfo {
position: absolute;
left: 0.5em;
right: 0.5em;
top: 3em;
height: calc(100% - 4.5em);
overflow-y: auto;
}
div.ngn4-menu {
overflow-y: visible;
white-space: nowrap;
}
div.ngn4-menu .item, div.ngn4-menu h1 {
font-size: 1.5em;
}
div.ngn4-footer {
font-size: 0.75em;
left: 0;
right: 0;
top: 0;
bottom: 0;
overflow-y: auto;
}
div.ngn4-footer a {
padding: 1em;
}
#gi-name {
padding-top: 0.25em;
}
#gi-button {
padding: 0.5em;
}
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none; /* non-prefixed version, currently
not supported by any browser */
}

BIN
www-root/img/bg_da.png

After

Width: 628  |  Height: 326  |  Size: 204 KiB

BIN
www-root/img/bg_da_dalish_elf.png

After

Width: 220  |  Height: 256  |  Size: 128 KiB

BIN
www-root/img/bg_es.png

After

Width: 455  |  Height: 256  |  Size: 197 KiB

BIN
www-root/img/bg_me.png

After

Width: 301  |  Height: 256  |  Size: 93 KiB

BIN
www-root/img/bg_me_asari.png

After

Width: 432  |  Height: 256  |  Size: 149 KiB

32
www-root/index.html

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>ngn4.io</title>
<link rel="stylesheet" href="css/main.css" />
<meta charset="utf-8" />
<meta name="HandheldFriendly" content="true"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="/js/jquery.js"></script>
<script src="/js/ngn4-0.4.1.js"></script>
<script src="/js/page.js"></script>
<style id="generator-style"></style>
<style id="name-style"></style>
</head>
<body>
<div class="ngn4-panel ngn4-menu noselect">
<h1>NGN4</h1><span id="menu-items"></span>
</div>
<div class="ngn4-panel ngn4-geninfo" id="generator-screen" style="display: none;">
<div id="gi-name">#name</div>
<div id="gi-header">#header</div>
<hr>
<div id="gi-genders" class="noselect"></div>
<div id="gi-formats" class="noselect"></div>
<button id="gi-button" onclick="generate(); return false;">Generate</button>
</div>
<div class="ngn4-panel ngn4-geninfo vertical-center" id="loading-screen" style="display: block;">
<center>Loading...</center>
</div>
<div class="ngn4-panel ngn4-footer"><p>Copyrighted content and trademarks are the property of the publishers and developers of the game series, and the developer of this website is in no way affilated with them.</p><p>By continuing to use this site, you agree to the use of cookies. These are only used to recall where you were between sessions, and are not shared with any third-parties.</p><p>This is still a work in progress, with bugs and poor optimization. I recommend you do not browse this website on a very limited metered connection.</p><center><a href="javascript:hideFooter();">CLOSE</a></center></div>
</body>
</html>

4
www-root/js/jquery.js
File diff suppressed because it is too large
View File

3360
www-root/js/ngn4-0.4.1.js
File diff suppressed because it is too large
View File

221
www-root/js/page.js

@ -0,0 +1,221 @@
var set = new NameGeneratorSet();
var categories = {};
var backgrounds = {};
var previous = null;
var generator = null;
var big = true;
var gender = null;
var format = null;
function selectGenerator(id) {
var gen = set.getGenerator(id);
var element = $('div.item:hover');
var index = element.index();
var parent = element.parent();
element.detach();
element = element.clone();
if(index > 0) {
element.insertAfter(parent.children().eq(index - 1));
} else {
parent.prepend(element);
}
if(gen != null) {
setGenerator(id, gen);
} else {
setMode(0);
$.get('/d/' + id, {}, function(data) {
var fullId = data.catId + '/' + data.id;
setGenerator(fullId, set.addGenerator(fullId, data, true));
});
}
}
function setMode(mode) {
$('#loading-screen').hide();
$('#generator-screen').hide();
switch(mode) {
case 0: $('#loading-screen').show(); break;
case 1: $('#generator-screen').show(); break;
}
}
function setGenerator(id, gen) {
setCookie('last_generator', id, 14);
$('#gi-header').html(gen.name);
$('#gi-genders').html('');
$('#gi-formats').html('');
if(gen.genders.length > 1) {
for(var i = 0; i < gen.genders.length; ++i) {
var className = '';
if(i == 0) {
className = "selected";
}
$('#gi-genders').append($('<a class="'+className+'" onclick="selectGender(this);">'+gen.genders[i]+'</a>'));
}
}
var prev = null;
var keys = Object.keys(gen.formats);
if(keys.length > 1) {
for(var i = 0; i < keys.length; ++i) {
var className = '';
if(i == 0) {
className = "selected";
}
$('#gi-formats').append($('<a class="'+className+'" onclick="selectFormat(this);" data-id="'+keys[i]+'">'+gen.formats[keys[i]].name+'</a>'));
}
}
setMode(1);
selectBackground(id);
gender = gen.genders[0];
format = keys[i];
generator = gen;
$('#gi-button').focus();
if(previous != id) {
generate();
}
previous = id;
}
function selectBackground(id) {
if(backgrounds.hasOwnProperty(id)) {
setBackground(backgrounds[id]);
} else {
$.get('/b/' + id, {}, function(data) {
backgrounds[id] = data;
setBackground(backgrounds[id]);
});
}
}
function setBackground(fileName) {
$('#generator-style').html('body { background-image: url(' + fileName + '); }');
}
function hideFooter() {
$('.ngn4-footer').hide();
setCookie('hide_footer', 'yes', 60);
}
function generate() {
if(generator != null) {
$('#gi-name').html(generator.generate(format, gender));
}
}
function updateLists() {
$('#menu-items').html("Loading...");
$.get('/l/generators', function(data, err) {
// 1. Categories
var keys = Object.keys(data.categories);
var items = {};
$('#menu-items').html('');
for(var i = 0; i < keys.length; ++i) {
var key = keys[i];
var item = $('<div class="item" data-id="'+key+'"><span>'+data.categories[key]+'</span><div class="ngn4-panel"></div></div>');
items[key] = item;
$('#menu-items').append(item);
categories[key] = data.categories[key];
}
// 2. Sub menus
keys = Object.keys(data.generators);
for(var i = 0; i < keys.length; ++i) {
var key = keys[i];
var item = $('<a href="javascript:selectGenerator(\''+key+'\')">'+data.generators[key]+'</a>');
$('div', items[key.split('/')[0]]).append(item);
}
applyMediaQueries(true);
});
}
function applyMediaQueries(force) {
var mq = window.matchMedia("(max-width: 980px)");
if(typeof(force) !== 'boolean') {
force = false;
}
if (mq.matches && (!big || force)) {
$('#menu-items .item').each(function(index) {
$('span', this).html($(this).data('id').toUpperCase());
});
big = true;
}
if (!mq.matches && (big || force)) {
$('#menu-items .item').each(function(index) {
$('span', this).html(categories[$(this).data('id')]);
});
big = false;
}
}
function selectGender(elem) {
gender = $(elem).text();
$('#gi-genders a.selected').attr('class', '');
$(elem).attr('class', 'selected');
}
function selectFormat(elem) {
format = $(elem).data('id');
$('#gi-formats a.selected').attr('class', '');
$(elem).attr('class', 'selected');
}
$(document).ready(function() {
if(getCookie('hide_footer') === 'yes') {
$('.ngn4-footer').hide();
}
var lastGenerator = getCookie('last_generator');
if(lastGenerator.length > 0) {
selectGenerator(lastGenerator);
} else {
selectGenerator('default');
}
updateLists();
});
$(window).resize(applyMediaQueries);
// http://www.w3schools.com/js/js_cookies.asp
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1);
if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
}
return "";
}
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+d.toUTCString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
Loading…
Cancel
Save