Initial commit
This commit is contained in:
commit
19843f416f
7 changed files with 418 additions and 0 deletions
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module github.com/ejv2/pajen-site
|
||||||
|
|
||||||
|
go 1.21.5
|
46
index.css
Normal file
46
index.css
Normal 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
25
index.gohtml
Normal 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
123
index.js
Normal 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
79
main.go
Normal 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
72
pajen.go
Normal 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
70
pajen.pl
Executable 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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue