Initial commit

This commit is contained in:
Ethan Marshall 2024-01-22 23:55:15 +00:00
commit 19843f416f
Signed by: ejv2
GPG key ID: EC2FAEF4DB8968D8
7 changed files with 418 additions and 0 deletions

3
go.mod Normal file
View file

@ -0,0 +1,3 @@
module github.com/ejv2/pajen-site
go 1.21.5

46
index.css Normal file
View file

@ -0,0 +1,46 @@
/*
* index.css - Pajenerator stylesheet
*/
body {
font: 1.2em/1.62 sans-serif;
margin: 1em auto;
padding:0 .62em 3.24em;
}
header {
padding-bottom: 15px;
}
input[type="text"] {
padding: 4px;
min-width: 75%;
text-align: center;
margin: 10px;
font-weight: bold;
font-size: larger;
}
button {
font-size: large;
font-weight: bold;
color: white;
background-color: rgb(25, 135, 84);
padding: 6px;
margin-top: 4px;
cursor: pointer;
}
button[disabled] {
cursor: not-allowed;
background-color: gray;
}
input.highlight {
border: solid medium rgb(13, 202, 240);
}
.loading {
cursor: wait !important;
}

25
index.gohtml Normal file
View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Pajenerator - The Indian Name Generator!</title>
<link rel="stylesheet" href="/index.css" />
<script src="/index.js"></script>
</head>
<body onload="onready()">
<center>
<header>
<h1>Pajenerator - The Indian Name Generator</h1>
<hr>
</header>
<label for="name-box">Your randomly generated Indian name is:</label>
<br>
<input type="text" placeholder="Pajeet" name="name-box" id="name-box" readonly>
<br>
<button onclick="generate()" id="btn">Do the needful</button>
</center>
</body>
</html>

123
index.js Normal file
View file

@ -0,0 +1,123 @@
/*
* index.js - Pajenerator client code
*/
// State
var generating = false;
var names = [];
var flashcount = 0;
var flashinterval;
// Needed elements
let root;
let btn;
let out;
function onready()
{
root = document.querySelector("html")
btn = document.querySelector("#btn");
out = document.querySelector("#name-box");
}
// Returns the names from the API - as many as are configured.
function get_names(onget)
{
const error_response = ["Errar", "Prablem"];
const uri = "/api";
let req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (this.readyState == 4) {
if (this.status != 200) {
onget(error_response);
}
try {
let arr = JSON.parse(this.responseText)
onget(arr);
} catch {
console.log("bad json: "+this.responseText)
onget(error_response);
}
}
}
req.open("GET", uri, true);
req.send();
}
/*
* 0.2x + 0.2e^(x-27)
* where:
* x - nth iteration
* returns - time in seconds
*/
function timeout(i)
{
return (0.2*i) + (0.2*Math.exp(i - 27));
}
function cleanup_generate()
{
// Reset button text (partially)
btn.textContent = "Do the needful";
btn.disabled = true;
// Reset cursor
root.classList.remove("loading");
btn.classList.remove("loading");
out.classList.remove("loading");
flashinterval = setInterval(() => {
out.classList.toggle("highlight");
if (flashcount++ >= 6) {
flashcount = 0;
generating = false;
names = [];
clearInterval(flashinterval);
out.classList.remove("highlight");
btn.disabled = false;
}
}, 500)
}
function run_generate()
{
for (let i = 0; i < names.length; i++) {
setTimeout(() => {
out.value = names[i];
if (i == names.length - 1) {
cleanup_generate();
}
}, 1000*timeout(i));
}
}
function pull_names(arr)
{
names = names.concat(arr);
if (names.length >= 30) {
run_generate();
} else {
get_names(pull_names)
}
}
function generate()
{
if (generating)
return;
generating = true;
// Button text
btn.textContent = "Ok sar...!";
// Cursors
root.classList.add("loading");
btn.classList.add("loading");
out.classList.add("loading");
get_names(pull_names);
}

79
main.go Normal file
View file

@ -0,0 +1,79 @@
package main
import (
"encoding/json"
"flag"
"html/template"
"log"
"net/http"
)
// Server configs.
var (
listenAddr = flag.String("listen", "0.0.0.0:80", "Listen address for server, in the form addr:port")
scriptPath = flag.String("script", "./pajen.pl", "The relative path to pajen.pl. Must be executable!")
apiQty = flag.Int("quantity", 5, "The number of names returned at a time from the API")
)
var (
pajen *Pajen
indexTmpl = template.Must(template.ParseFiles("index.gohtml"))
)
func handleRoot(w http.ResponseWriter, r *http.Request) {
if r.RequestURI != "/" {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("404 not found"))
return
}
indexTmpl.Execute(w, nil)
}
func handleAPI(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" || r.RequestURI != "/api" {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("404 not found"))
return
}
a := make([]string, 0, *apiQty)
for i := 0; i < *apiQty; i++ {
a = append(a, <-pajen.Chan)
}
b, err := json.Marshal(a)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
b, _ = json.Marshal(map[string]any{
"status": "Failed",
"error": err,
})
}
w.Write(b)
}
func main() {
log.Println("Starting pajen.pl frontend - the famous indian name generator")
flag.Parse()
p, err := NewPajen(*scriptPath)
if err != nil {
log.Fatal(err)
}
pajen = p
http.HandleFunc("/", handleRoot)
http.HandleFunc("/api", handleAPI)
http.HandleFunc("/index.css", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.css")
})
http.HandleFunc("/index.js", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.js")
})
if err := http.ListenAndServe(*listenAddr, nil); err != nil {
log.Fatalln(err)
}
}

72
pajen.go Normal file
View file

@ -0,0 +1,72 @@
package main
import (
"fmt"
"io"
"log"
"os/exec"
"strings"
)
// Pajen represents an instance of the pajen.pl script running in infinite
// generation mode.
type Pajen struct {
Chan chan string
proc *exec.Cmd
out io.ReadCloser
}
// NewPajen starts the pajen script at the path given.
func NewPajen(path string) (*Pajen, error) {
p := new(Pajen)
p.proc = exec.Command("/usr/bin/perl", path, "-i")
p.proc.Stdin = p
pi, err := p.proc.StdoutPipe()
if err != nil {
return nil, fmt.Errorf("pajen: pipe pajen.pl: %w", err)
}
p.out = pi
p.proc.Stderr = p.proc.Stdout
p.Chan = make(chan string)
if err := p.proc.Start(); err != nil {
return nil, fmt.Errorf("pajen: start pajen.pl: %w", err)
}
go p.run()
return p, nil
}
// Read feeds constant newlines to the program.
func (r *Pajen) Read(b []byte) (int, error) {
if len(b) < 1 {
return 0, nil
}
return copy(b, []byte("\n")), nil
}
func (r *Pajen) run() {
buf := make([]byte, 255)
for {
_, err := r.out.Read(buf)
if err != nil {
log.Println("pajen read error:", err)
return
}
wk := string(buf)
lines := strings.Split(wk, "\n")
for _, l := range lines {
if l != "" && l != "\n" {
r.Chan <- l
}
}
}
}

70
pajen.pl Executable file
View file

@ -0,0 +1,70 @@
#!/usr/bin/perl
# Pajeet name generator (pajen.pl)
# Written by Ethan Marshall - 2024
# Material from https://desu-usergeneratedcontent.xyz/g/image/1705/80/1705808467048.png
use strict;
use diagnostics;
use warnings;
use Getopt::Long;
# Generated names consist of a prefix followed by a suffix
our @prefixes = (
"Pa",
"Ra",
"Ab",
"A",
"Na",
"Vi",
"Pra",
"Ku",
"Gi",
"Ha",
"San",
"Bel"
);
our @suffixes = (
"jeet",
"kesh",
"hul",
"jeesh",
"jesh",
"mit",
"mit",
"hesh",
"raj",
"nil",
"jith",
"tik"
);
sub fatal {
printf("%s\n", $_[0]);
exit(1);
}
sub usage {
fatal("Usage: pajen [-n count] [-ihu]")
}
# Command line options
my $count = 1;
my $infinite = '';
GetOptions(
'number=i' => \$count,
"infinite" => \$infinite,
"help|usage" => sub { usage() }
) or usage();
for (my $i = 0; $i < $count || $infinite; $i++) {
my $prefix = int(rand(@prefixes));
my $suffix = int(rand(@suffixes));
printf("%s%s\n", $prefixes[$prefix], $suffixes[$suffix]);
if ($infinite) {
(<stdin> eq "q\n") && exit(0);
}
}