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
(** Contains functions available in the global scope ([window] in a browser context) *)
type intervalId = unit Lwt.t
(** Identify an interval started by {! setInterval} *)
type timeoutId = unit Lwt.t
(** Identify timeout started by {! setTimeout} *)
let sleep_seconds_of_millis millis = if millis <= 0. then 0. else millis /. 1000.
let start_timeout ~f millis : timeoutId =
let task =
let%lwt () = Lwt_unix.sleep (sleep_seconds_of_millis millis) in
f ();
Lwt.return_unit
in
Lwt.async (fun () -> Lwt.catch (fun () -> task) (function Lwt.Canceled -> Lwt.return_unit | exn -> raise exn));
task
let start_interval ~f millis : intervalId =
let delay = sleep_seconds_of_millis millis in
let rec loop () =
let%lwt () = Lwt_unix.sleep delay in
f ();
loop ()
in
let task = loop () in
Lwt.async (fun () -> Lwt.catch (fun () -> task) (function Lwt.Canceled -> Lwt.return_unit | exn -> raise exn));
task
let clearInterval (intervalId : intervalId) = Lwt.cancel intervalId
let clearTimeout (timeoutId : timeoutId) = Lwt.cancel timeoutId
let setInterval ~f millis : intervalId = start_interval ~f (Stdlib.float_of_int millis)
let setIntervalFloat ~f millis : intervalId = start_interval ~f millis
let setTimeout ~f millis : timeoutId = start_timeout ~f (Stdlib.float_of_int millis)
let setTimeoutFloat ~f millis : timeoutId = start_timeout ~f millis
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 parseFloat str = match Quickjs.Global.parse_float str with Some f -> f | None -> nan
let parseInt ?radix str = match Quickjs.Global.parse_int_float ?radix str with Some f -> f | None -> nan