-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathform-json.js
118 lines (102 loc) · 3.57 KB
/
form-json.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
(function() {
let api
const _ConfigIgnoreDeepKey_ = 'ignore-deep-key'
const _FlagObject_ = 'obj'
const _FlagArray_ = 'arr'
const _FlagValue_ = 'val'
htmx.defineExtension('form-json', {
init: function(apiRef) {
api = apiRef
},
onEvent: function(name, evt) {
if (name === 'htmx:configRequest') {
evt.detail.headers['Content-Type'] = 'application/json'
}
},
encodeParameters: function(xhr, parameters, elt) {
let object = {}
xhr.overrideMimeType('text/json')
for (const [key, value] of parameters.entries()) {
const input = elt.querySelector(`[name="${key}"]`)
const transformedValue = input ? convertValue(input, value, input.type) : value
if (Object.hasOwn(object, key)) {
if (!Array.isArray(object[key])) {
object[key] = [object[key]]
}
object[key].push(transformedValue)
} else {
object[key] = transformedValue
}
}
// FormData encodes values as strings, restore hx-vals/hx-vars with their initial types
const vals = api.getExpressionVars(elt)
Object.keys(object).forEach(function(key) {
object[key] = Object.hasOwn(vals, key) ? vals[key] : object[key]
})
if(!api.hasAttribute(elt, _ConfigIgnoreDeepKey_)){
const flagMap = getFlagMap(object)
object = buildNestedObject(flagMap, object)
}
return (JSON.stringify(object))
}
})
function convertValue(input, value, inputType) {
if (inputType == 'number' || inputType == 'range') {
return Array.isArray(value) ? value.map(Number) : Number(value)
} else if (inputType === 'checkbox') {
return input.defaultValue || true
}
return value
}
function splitKey(key) {
// Convert 'a.b[]' to a.b[-1]
// and 'a.b[c]' to ['a', 'b', 'c']
return key.replace(/\[\s*\]/g, '[-1]').replace(/\]/g, '').split(/\[|\./)
}
function getFlagMap(map) {
const flagMap = {}
for (const key in map) {
const parts = splitKey(key)
parts.forEach((part, i) => {
const path = parts.slice(0, i+1).join('.')
const isLastPart = i === parts.length - 1
const nextIsNumeric = !isLastPart && !isNaN(Number(parts[i + 1]))
if (isLastPart) {
flagMap[path]= _FlagValue_
} else {
if (!flagMap.hasOwnProperty(path)) {
flagMap[path] = nextIsNumeric ? _FlagArray_ : _FlagObject_
}else if(flagMap[path]===_FlagValue_ || !nextIsNumeric){
flagMap[path] = _FlagObject_
}
}
})
}
return flagMap
}
function buildNestedObject(flagMap, map) {
const out = {}
for (const key in map) {
const parts = splitKey(key)
let current = out
parts.forEach((part, i) => {
const path = parts.slice(0, i + 1).join('.')
const isLastPart = i === parts.length - 1
if (isLastPart){
if (flagMap[path] === _FlagObject_){
current[part] = { '': map[key] }
} else if (part === '-1'){
const val = map[key]
Array.isArray(val) ? current.push(...val) : current.push(val)
} else {
current[part] = map[key]
}
} else if(!current.hasOwnProperty(part)) {
current[part] = flagMap[path] === _FlagArray_ ? [] : {}
}
current = current[part]
})
}
return out
}
})()