Skip to content

Commit

Permalink
Add example of json enc custom
Browse files Browse the repository at this point in the history
  • Loading branch information
godsarmy committed Jun 22, 2024
1 parent 0377b31 commit 9071778
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 5 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ all: \
infinite-scroll/main \
inline-validation/main \
keyboard-shortcuts/main \
json-enc-custom/main \
lazy-load/main \
loading-states/main \
modal-bootstrap/main \
Expand Down
52 changes: 52 additions & 0 deletions json-enc-custom/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"embed"
"html/template"
"io/fs"
"net/http"

"github.com/gin-gonic/gin"
)

var htmx_version = "latest"
var nunjucks_version = "3.2.4"
var bootstrap_version = "latest"

//go:embed templates/* script/*
var embed_fs embed.FS

func main() {

router := gin.Default()
templ := template.Must(
template.New("").Delims("{[{", "}]}").ParseFS(embed_fs, "templates/*.tmpl"),
)
router.SetHTMLTemplate(templ)
router.GET("/", func(c *gin.Context) {
c.HTML(
http.StatusOK,
"index.html.tmpl",
gin.H{
"htmx_version": htmx_version,
"nunjucks_version": nunjucks_version,
"bootstrap_version": bootstrap_version,
},
)
})

var script_fs, _ = fs.Sub(embed_fs, "script")
router.StaticFS("/script", http.FS(script_fs))

router.POST("/data", func(c *gin.Context) {
json_body, err := c.GetRawData()
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
json_str := string(json_body)
c.JSON(http.StatusOK, gin.H{"data": json_str})
})

router.Run(":8080")
}
120 changes: 120 additions & 0 deletions json-enc-custom/script/json-enc-custom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
htmx.defineExtension('json-enc-custom', {
onEvent: function (name, evt) {
if (name === "htmx:configRequest") {
evt.detail.headers['Content-Type'] = "application/json";
}
},
encodeParameters: function (xhr, parameters, elt) {
xhr.overrideMimeType('text/json');
let encoded_parameters = encodingAlgorithm(parameters);
return encoded_parameters;
}
});

function encodingAlgorithm(parameters) {
let resultingObject = Object.create(null);
const PARAM_NAMES = Object.keys(parameters);
const PARAM_VALUES = Object.values(parameters);
const PARAM_LENGHT = PARAM_NAMES.length;

for (let param_index = 0; param_index < PARAM_LENGHT; param_index++) {
let name = PARAM_NAMES[param_index];
let value = PARAM_VALUES[param_index];
let steps = JSONEncodingPath(name);
let context = resultingObject;

for (let step_index = 0; step_index < steps.length; step_index++) {
let step = steps[step_index];
context = setValueFromPath(context, step, value);
}
}

let result = JSON.stringify(resultingObject);
return result
}

function JSONEncodingPath(name) {
let path = name;
let original = path;
const FAILURE = [{ "type": "object", "key": original, "last": true, "next_type": null }];
let steps = Array();
let first_key = String();
for (let i = 0; i < path.length; i++) {
if (path[i] !== "[") first_key += path[i];
else break;
}
if (first_key === "") return FAILURE;
path = path.slice(first_key.length);
steps.push({ "type": "object", "key": first_key, "last": false, "next_type": null });
while (path.length) {
// [123...]
if (/^\[\d+\]/.test(path)) {
path = path.slice(1);
let collected_digits = path.match(/\d+/)[0]
path = path.slice(collected_digits.length);
let numeric_key = parseInt(collected_digits, 10);
path = path.slice(1);
steps.push({ "type": "array", "key": numeric_key, "last": false, "next_type": null });
continue
}
// [abc...]
if (/^\[[^\]]+\]/.test(path)) {
path = path.slice(1);
let collected_characters = path.match(/[^\]]+/)[0];
path = path.slice(collected_characters.length);
let object_key = collected_characters;
path = path.slice(1);
steps.push({ "type": "object", "key": object_key, "last": false, "next_type": null });
continue;
}
return FAILURE;
}
for (let step_index = 0; step_index < steps.length; step_index++) {
if (step_index === steps.length - 1) {
let tmp_step = steps[step_index];
tmp_step["last"] = true;
steps[step_index] = tmp_step;
}
else {
let tmp_step = steps[step_index];
tmp_step["next_type"] = steps[step_index + 1]["type"];
steps[step_index] = tmp_step;
}
}
return steps;
}

function setValueFromPath(context, step, value) {
if (step.last) {
context[step.key] = value;
}

//TODO: make merge functionality and file suport.

//check if the context value already exists
if (context[step.key] === undefined) {
if (step.type === "object") {
if (step.next_type === "object") {
context[step.key] = {};
return context[step.key];
}
if (step.next_type === "array") {
context[step.key] = [];
return context[step.key];
}
}
if (step.type === "array") {
if (step.next_type === "object") {
context[step.key] = {};
return context[step.key];
}
if (step.next_type === "array") {
context[step.key] = [];
return context[step.key];
}
}
}
else {
return context[step.key];
}
}
119 changes: 119 additions & 0 deletions json-enc-custom/templates/index.html.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<!DOCTYPE html>
<html lang='en' class=''>

<head>

<meta charset='UTF-8'>
<title>json enc custom</title>

<script src="https://unpkg.com/htmx.org@{[{ .htmx_version }]}"></script>
<script src="https://unpkg.com/htmx-ext-client-side-templates@{[{ .htmx_version }]}/client-side-templates.js"></script>
<script src="script/json-enc-custom.js"></script>
<script src="https://unpkg.com/nunjucks@{[{ .nunjucks_version }]}/browser/nunjucks.js"></script>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@{[{ .bootstrap_version }]}/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@{[{ .bootstrap_version }]}/dist/css/bootstrap.min.css" rel="stylesheet">

<style id="INLINE_PEN_STYLESHEET_ID">
.hiddenRow {
padding: 0 !important;
}
</style>

</head>

<body>
<div class="container" hx-ext="client-side-templates">
<a href="https://github.com/Emtyloc/json-enc-custom" class="link-primary">htmx json enc custom example</a>
<p>

<h3>Example 1</h3>
<form hx-post="/data"
hx-target="#display"
hx-ext="json-enc-custom"
nunjucks-template="show-template">
<input name="name" value="Bender">
<select name="hind">
<option selected>Bitable</option>
<option>Kickable</option>
</select>
<input type="checkbox" name="shiny" checked>
<button class="btn btn-primary">submit</button>
</form>

<h3>Example 2</h3>
<form hx-post="/data"
hx-target="#display"
hx-ext="json-enc-custom"
nunjucks-template="show-template">
<input type="number" name="bottle-on-wall" value="1">
<input type="number" name="bottle-on-wall" value="2">
<input type="number" name="bottle-on-wall" value="3">
<button class="btn btn-primary">submit</button>
</form>

<h3>Example 3</h3>
<form hx-post="/data"
hx-target="#display"
hx-ext="json-enc-custom"
nunjucks-template="show-template">
<input name="pet[species]" value="Dahut">
<input name="pet[name]" value="Hypatia">
<input name="kids[1]" value="Thelma">
<input name="kids[0]" value="Ashley">
<button class="btn btn-primary">submit</button>
</form>

<h3>Example 4</h3>
<form hx-post="/data"
hx-target="#display"
hx-ext="json-enc-custom"
nunjucks-template="show-template">
<input name="hearbeat[0]" value="thunk">
<input name="hearbeat[2]" value="thunk">
<button class="btn btn-primary">submit</button>
</form>

<h3>Example 5</h3>
<form hx-post="/data"
hx-target="#display"
hx-ext="json-enc-custom"
nunjucks-template="show-template">
<input name="pet[0][species]" value="Dahut">
<input name="pet[0][name]" value="Hypatia">
<input name="pet[1][species]" value="Felis Stultus">
<input name="pet[1][name]" value="Billie">
<button class="btn btn-primary">submit</button>
</form>

<h3>Example 6</h3>
<form hx-post="/data"
hx-target="#display"
hx-ext="json-enc-custom"
nunjucks-template="show-template">
<input name="wow[such][deep][3][much][power][!]" value="Amaze">
<button class="btn btn-primary">submit</button>
</form>

<h3>Example 10</h3>
<form hx-post="/data"
hx-target="#display"
hx-ext="json-enc-custom"
nunjucks-template="show-template">
<input name="error[good]" value="BOOM!">
<input name="error[bad" value="BOOM BOOM!">
<button class="btn btn-primary">submit</button>
</form>

<div class="mb-3">
<label for="display" class="form-label">You posted</label>
<textarea class="form-control" id="display" rows="3"></textarea>
</div>

<script id="show-template" type="x-tmpl-nunjucks">
{{ data }}
</script>
</div>
</body>

</html>
4 changes: 2 additions & 2 deletions server-sent-events/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ func main() {
c.Stream(func(w io.Writer) bool {
// Stream message to client from message channel
if currentTime, ok := <-message; ok {
data := map[string]string{"time": currentTime}
msg, _ := json.Marshal(data)
data := map[string]string{"time": currentTime}
msg, _ := json.Marshal(data)
c.SSEvent("message", string(msg))
return true
}
Expand Down
2 changes: 1 addition & 1 deletion sortable/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func find_item(id int) *Item {
for _, item := range items {
if item.ID == id {
found = &item
break
break
}
}
return found
Expand Down
2 changes: 1 addition & 1 deletion tab/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func find_tab(tab_name string) *Tab {
for _, tab := range tabs {
if tab.Name == tab_name {
found = &tab
break
break
}
}
return found
Expand Down
2 changes: 1 addition & 1 deletion value-select/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func find_make(name string) *Make {
for _, maker := range makes {
if maker.Name == name {
found = &maker
break
break
}
}
return found
Expand Down

0 comments on commit 9071778

Please sign in to comment.