about summary refs log tree commit diff stats
path: root/src/xhr
diff options
context:
space:
mode:
Diffstat (limited to 'src/xhr')
-rw-r--r--src/xhr/formdata.nim159
1 files changed, 159 insertions, 0 deletions
diff --git a/src/xhr/formdata.nim b/src/xhr/formdata.nim
new file mode 100644
index 00000000..9b45e7ba
--- /dev/null
+++ b/src/xhr/formdata.nim
@@ -0,0 +1,159 @@
+import html/dom
+import html/tags
+import js/javascript
+import types/blob
+import types/formdata
+import utils/twtstr
+
+proc constructEntryList*(form: HTMLFormElement, submitter: Element = nil,
+    encoding: string = ""): Option[seq[FormDataEntry]]
+
+proc newFormData*(form: HTMLFormElement = nil,
+    submitter: HTMLElement = nil): FormData {.jserr, jsctor.} =
+  let this = FormData()
+  if form != nil:
+    if submitter != nil:
+      if not submitter.isSubmitButton():
+        JS_ERR JS_TypeError, "Submitter must be a submit button"
+      if FormAssociatedElement(submitter).form != form:
+        #TODO should be DOMException
+        JS_ERR JS_TypeError, "InvalidStateError"
+    this.entries = constructEntryList(form, submitter).get(@[])
+  return this
+
+#TODO as jsfunc
+proc append*(this: FormData, name: string, svalue: string, filename = "") =
+  this.entries.add(FormDataEntry(
+    name: name,
+    isstr: true,
+    svalue: svalue,
+    filename: filename
+  ))
+
+proc append*(this: FormData, name: string, value: Blob,
+    filename = "blob") =
+  this.entries.add(FormDataEntry(
+    name: name,
+    isstr: false,
+    value: value,
+    filename: filename
+  ))
+
+#TODO hack
+proc append(this: FormData, name: string, value: JSObject,
+    filename = none(string)) {.jsfunc.} =
+  let blob = fromJS[Blob](value.ctx, value.val)
+  if blob.isSome:
+    this.append(name, blob.get, filename.get("blob"))
+  else:
+    let s = fromJS[string](value.ctx, value.val)
+    # toString should never fail (?)
+    this.append(name, s.get, filename.get(""))
+
+proc delete(this: FormData, name: string) {.jsfunc.} =
+  for i in countdown(this.entries.high, 0):
+    if this.entries[i].name == name:
+      this.entries.delete(i)
+
+proc get(ctx: JSContext, this: FormData, name: string): JSValue {.jsfunc.} =
+  for entry in this.entries:
+    if entry.name == name:
+      if entry.isstr:
+        return toJS(ctx, entry.svalue)
+      else:
+        return toJS(ctx, entry.value)
+  return JS_NULL
+
+proc getAll(this: FormData, name: string): seq[Blob] {.jsfunc.} =
+  for entry in this.entries:
+    if entry.name == name:
+      result.add(entry.value) # may be null
+
+proc add(list: var seq[FormDataEntry], entry: tuple[name, value: string]) =
+  list.add(FormDataEntry(
+    name: entry.name,
+    isstr: true,
+    svalue: entry.value
+  ))
+
+func toNameValuePairs*(list: seq[FormDataEntry]):
+    seq[tuple[name, value: string]] =
+  for entry in list:
+    if entry.isstr:
+      result.add((entry.name, entry.svalue))
+    else:
+      result.add((entry.name, entry.name))
+
+# https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-the-form-data-set
+proc constructEntryList*(form: HTMLFormElement, submitter: Element = nil,
+    encoding: string = ""): Option[seq[FormDataEntry]] =
+  if form.constructingentrylist:
+    return
+  form.constructingentrylist = true
+
+  var entrylist: seq[FormDataEntry]
+  for field in form.controls:
+    if field.findAncestor({TAG_DATALIST}) != nil or
+        field.attrb("disabled") or
+        field.isButton() and Element(field) != submitter:
+      continue
+
+    if field.tagType == TAG_INPUT:
+      let field = HTMLInputElement(field)
+      if field.inputType == INPUT_IMAGE:
+        let name = if field.attr("name") != "":
+          field.attr("name") & '.'
+        else:
+          ""
+        entrylist.add((name & 'x', $field.xcoord))
+        entrylist.add((name & 'y', $field.ycoord))
+        continue
+
+    #TODO custom elements
+
+    let name = field.attr("name")
+
+    if name == "":
+      continue
+
+    if field.tagType == TAG_SELECT:
+      let field = HTMLSelectElement(field)
+      for option in field.options:
+        if option.selected or option.disabled:
+          entrylist.add((name, option.value))
+    elif field.tagType == TAG_INPUT and HTMLInputElement(field).inputType in {INPUT_CHECKBOX, INPUT_RADIO}:
+      let value = if field.attr("value") != "":
+        field.attr("value")
+      else:
+        "on"
+      entrylist.add((name, value))
+    elif field.tagType == TAG_INPUT and HTMLInputElement(field).inputType == INPUT_FILE:
+      #TODO file
+      discard
+    elif field.tagType == TAG_INPUT and HTMLInputElement(field).inputType == INPUT_HIDDEN and name.equalsIgnoreCase("_charset_"):
+      let charset = if encoding != "":
+        encoding
+      else:
+        "UTF-8"
+      entrylist.add((name, charset))
+    else:
+      case field.tagType
+      of TAG_INPUT:
+        entrylist.add((name, HTMLInputElement(field).value))
+      of TAG_BUTTON:
+        entrylist.add((name, HTMLButtonElement(field).value))
+      of TAG_TEXTAREA:
+        entrylist.add((name, HTMLTextAreaElement(field).value))
+      else: assert false, "Tag type " & $field.tagType & " not accounted for in constructEntryList"
+    if field.tagType == TAG_TEXTAREA or
+        field.tagType == TAG_INPUT and HTMLInputElement(field).inputType in {INPUT_TEXT, INPUT_SEARCH}:
+      if field.attr("dirname") != "":
+        let dirname = field.attr("dirname")
+        let dir = "ltr" #TODO bidi
+        entrylist.add((dirname, dir))
+
+  form.constructingentrylist = false
+  return some(entrylist)
+
+proc addFormDataModule*(ctx: JSContext) =
+  ctx.registerType(FormData)
b99a77f070536946bd6a37163d99752f9a'>^
f07bb12f ^
c9383c72 ^
c776804d ^
34a60763 ^
a614f048 ^
f07bb12f ^
c776804d ^
34a60763 ^
f07bb12f ^
c9383c72 ^
34a60763 ^
a614f048 ^


c9383c72 ^


f07bb12f ^
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