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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
(** Contains functions available in the global scope ([window] in a browser context) *)
type intervalId
(** Identify an interval started by {! setInterval} *)
type timeoutId
(** Identify timeout started by {! setTimeout} *)
let clearInterval _intervalId = Js_internal.notImplemented "Js.Global" "clearInterval"
let clearTimeout _timeoutId = Js_internal.notImplemented "Js.Global" "clearTimeout"
let setInterval ~f:_ _ = Js_internal.notImplemented "Js.Global" "setInterval"
let setIntervalFloat ~f:_ _ = Js_internal.notImplemented "Js.Global" "setInterval"
let setTimeout ~f:_ _ = Js_internal.notImplemented "Js.Global" "setTimeout"
let setTimeoutFloat ~f:_ _ = Js_internal.notImplemented "Js.Global" "setTimeout"
module URI = struct
let int_of_hex_opt str = try Some (Scanf.sscanf str "%x%!" (fun x -> x)) with _ -> None
let hex_decode str pos =
if pos + 2 >= String.length str then Error "Expecting Hex digit"
else
let first = int_of_hex_opt (Stdlib.String.sub str (pos + 1) 1) in
let second = int_of_hex_opt (Stdlib.String.sub str (pos + 2) 1) in
match (first, second) with
| Some first, Some second -> Ok ((first lsl 4) lor second)
| _ -> Error "Invalid hex digit"
let is_uri_reserved c = Stdlib.String.contains ";/?:@&=+$,#" c
let decode_uri ~component s =
let buf = Buffer.create (String.length s) in
let decode_utf8 pos char n c_min =
let rec loop pos char n =
if n <= 0 then Some (pos, char)
else
match hex_decode s pos with
| Ok c1 when c1 land 0xc0 = 0x80 -> loop (pos + 3) ((char lsl 6) lor (c1 land 0x3f)) (n - 1)
| _ -> raise (Invalid_argument "Invalid hex encoding")
in
match loop pos char n with
| Some (new_pos, char) when char >= c_min && char <= 0x10FFFF && (char < 0xd800 || char >= 0xe000) ->
(new_pos, char)
| _ -> raise (Invalid_argument "Malformed UTF-8")
in
let rec loop pos =
if pos >= String.length s then Buffer.contents buf
else
match Stdlib.String.get s pos with
| '%' -> (
match hex_decode s pos with
| Ok hex when hex >= 0 ->
if hex < 0x80 then
let c = Char.chr hex in
if (not component) && is_uri_reserved c then (
Buffer.add_char buf '%';
Buffer.add_string buf (Stdlib.String.sub s (pos + 1) 2);
loop (pos + 3))
else (
Buffer.add_char buf c;
loop (pos + 3))
else
let new_pos, decoded_char =
if hex >= 0xc0 && hex <= 0xdf then decode_utf8 (pos + 3) (hex land 0x1f) 1 0x80
else if hex >= 0xe0 && hex <= 0xef then decode_utf8 (pos + 3) (hex land 0x0f) 2 0x800
else if hex >= 0xf0 && hex <= 0xf7 then decode_utf8 (pos + 3) (hex land 0x07) 3 0x10000
else raise (Invalid_argument "Invalid UTF-8 start byte")
in
Buffer.add_utf_8_uchar buf (Uchar.of_int decoded_char);
loop new_pos
| _ -> raise (Invalid_argument "Invalid hex encoding"))
| c ->
Buffer.add_char buf c;
loop (pos + 1)
in
try loop 0 with error -> raise error
let is_uri_unescaped c is_component =
c < 0x100
&& ((c >= 0x61 && c <= 0x7a)
|| (c >= 0x41 && c <= 0x5a)
|| (c >= 0x30 && c <= 0x39)
|| Stdlib.String.contains "-_.!~*'()" (Char.chr c)
|| ((not is_component) && is_uri_reserved (Char.chr c)))
let hex_of_int_opt c =
let char_code = if c < 10 then Char.code '0' + c else Char.code 'A' + (c - 10) in
try Some (Char.chr char_code) with _ -> None
let encode_hex value =
let first_digit = hex_of_int_opt (value lsr 4) in
let second_digit = hex_of_int_opt (value land 0x0F) in
match (first_digit, second_digit) with
| Some first_digit, Some second_digit -> Ok (Printf.sprintf "%%%c%c" first_digit second_digit)
| _ -> Error (Printf.sprintf "Invalid hex encoding: %d" value)
let uri_char_escaped c =
match c with
| '\'' -> "'"
| c ->
let escaped = Char.escaped c in
if c = '\\' then Stdlib.String.sub escaped 1 (String.length escaped - 1) else escaped
let encode_uri ~component s =
let buf = Buffer.create (String.length s * 3) in
let rec loop pos =
if pos >= String.length s then Buffer.contents buf
else
let new_pos, encoded_char =
let c = Char.code (Stdlib.String.get s pos) in
let new_pos = pos + 1 in
if is_uri_unescaped c component then
let encoded_char =
try Ok (Char.chr c |> uri_char_escaped) with _ -> raise (Invalid_argument "invalid character")
in
(new_pos, encoded_char)
else if c >= 0xdc00 && c <= 0xdfff then raise (Invalid_argument "invalid character")
else if c >= 0xd800 && c <= 0xdbff then (
if new_pos >= String.length s then raise (Invalid_argument "expecting surrogate pair");
let c1 = Char.code (Stdlib.String.get s new_pos) in
if c1 < 0xdc00 || c1 > 0xdfff then raise (Invalid_argument "expecting surrogate pair");
let c = (((c land 0x3ff) lsl 10) lor (c1 land 0x3ff)) + 0x10000 in
(new_pos + 1, encode_hex c))
else (new_pos, encode_hex c)
in
match encoded_char with
| Ok encoded_char ->
Buffer.add_string buf encoded_char;
loop new_pos
| Error msg -> raise (Invalid_argument msg)
in
loop 0
end
let encodeURI = URI.encode_uri ~component:false
let decodeURI = URI.decode_uri ~component:false
let encodeURIComponent = URI.encode_uri ~component:true
let decodeURIComponent = URI.decode_uri ~component:true
let is_js_whitespace c =
match c with
| '\x09'
| '\x0A'
| '\x0B'
| '\x0C'
| '\x0D'
| '\x20'
| '\xA0' ->
true
| _ -> false
let strip_leading_js_whitespace str =
let len = String.length str in
let rec find_start i =
if i >= len then len
else
let c = String.get str i in
if is_js_whitespace c then find_start (i + 1)
else if c = '\xC2' && i + 1 < len && String.get str (i + 1) = '\xA0' then
find_start (i + 2)
else if c = '\xEF' && i + 2 < len && String.get str (i + 1) = '\xBB' && String.get str (i + 2) = '\xBF' then
find_start (i + 3)
else i
in
let start = find_start 0 in
if start >= len then "" else String.sub str start (len - start)
let parseFloat str =
let trimmed = strip_leading_js_whitespace str in
match Quickjs.Global.parse_float trimmed with Some f -> f | None -> nan
let parseInt ?radix str =
let trimmed = strip_leading_js_whitespace str in
let radix =
match radix with
| Some r -> Some r
| None ->
let len = String.length trimmed in
if len >= 2 then
let first = String.get trimmed 0 in
let second = String.get trimmed 1 in
if first = '0' && (second = 'x' || second = 'X') then Some 16
else if (first = '-' || first = '+') && len >= 3 then
let third = String.get trimmed 2 in
if String.get trimmed 1 = '0' && (third = 'x' || third = 'X') then Some 16 else None
else None
else None
in
match Quickjs.Global.parse_int ?radix trimmed with Some i -> Float.of_int i | None -> nan