/ Check-in [66c6bda839]
DEMO | DOWNLOAD | DEPLOY | SEARCH
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Rename to jsi.jsi
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:66c6bda839e93c25cb5e957d60f82eec14b79862
User & Date: pmacdona 2019-06-22 16:27:32
Context
2019-06-22
16:31
Fix link check-in: 751f1ba760 user: pmacdona tags: trunk
16:27
Rename to jsi.jsi check-in: 66c6bda839 user: pmacdona tags: trunk
2019-06-14
19:07
Release "2.8.37" Rename most "Jsi_XXX.jsi" files to "XXX.jsi". check-in: f1b7aa0069 user: pmacdona tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Name change from js-demos/jsig/bar.jsi to js-demos/bark.jsi.

1
2
3
4
5
6
7
8
function bar(s:string, n:number):any {
   puts("BAR: "+s);
}
function foo(a:number, ...) {
}

bar(1,2);

|
|

<
|
<
<
<
1
2
3

4



function bark(s:string, n:number) {
   puts("BARK: "+s);
}





Deleted js-demos/jsig/demo.html.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script src="../../lib/web/jsig.js"></script>
<script>
   
    function RunTest() {
        foo();
        bar('abc',0);
        bar(9,0);
        bar('9','0');
        bar(9);
        bar(9,0,0);
    }
    
    //$jsi.conf({mode:'log'});
    $jsi.include(['bar.jsi']);
    $jsi.onload(RunTest);
    
</script>
Type checking with <b>jsig</b>
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































Deleted js-demos/jsig/demo1.html.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script src="/jsi/web/jsig.js"></script>
<script>
   
    function RunTest() {
        foo();
        bar('abc',0);
        bar(9,0);
        bar('9','0');
        bar(9);
        bar(9,0,0);
    }
    
    //$jsi.conf({mode:'log'});
    $jsi.include(['bar.jsi']);
    $jsi.onload(RunTest);
    
</script>
Type checking with <b>jsig</b>
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































Changes to lib/DebugUI/html/main.htmli.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html style="height: 100%">
  <head>
    <meta charset=utf-8 />
    <title>DebugUI</title>
    <link rel="icon" href="data:;base64,iVBORw0KGgo=">

    <script src="/jsi/web/jsig.js"></script>
    <script src="/jsi/web/bind.js"></script>
<? 
    // Load .jsi and .cssi for preprocessing inline.
    include(['debugui.jsi', 'debugui.cssi']);
?>
  </head>
  <body id="body" onload="onload()" style="display: flex; height: 97%; flex-direction: column; margin:3px">







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html style="height: 100%">
  <head>
    <meta charset=utf-8 />
    <title>DebugUI</title>
    <link rel="icon" href="data:;base64,iVBORw0KGgo=">

    <script src="/jsi/web/jsi.js"></script>
    <script src="/jsi/web/bind.js"></script>
<? 
    // Load .jsi and .cssi for preprocessing inline.
    include(['debugui.jsi', 'debugui.cssi']);
?>
  </head>
  <body id="body" onload="onload()" style="display: flex; height: 97%; flex-direction: column; margin:3px">

Changes to lib/GenDeep.jsi.

87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

    function main() {
        if (self.create) {
            File.copy('/zvfs/lib/web/markdeep/include.shtml', 'include.shtml');
            File.copy('/zvfs/lib/web/markdeep/nginx_deepdoc.conf', 'nginx_deepdoc.conf');
            File.copy('/zvfs/lib/web/markdeep/jsistyle.css', 'jsistyle.css');
            File.copy('/zvfs/lib/web/dumpdeep.js', 'dumpdeep.js');
            File.copy('/zvfs/lib/web/jsig.js', 'jsig.js');
            File.copy('/zvfs/lib/web/markdeep.min.js', 'markdeep.min.js');
            File.mkdir('md');
            File.copy('/zvfs/lib/web/markdeep/DeepDoc.md', 'md/DeepDoc.md');
            puts("DeepDoc created. View with 'jsish -W -url Deepdoc'");
            return;
        }
        var fl = [];







|







87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

    function main() {
        if (self.create) {
            File.copy('/zvfs/lib/web/markdeep/include.shtml', 'include.shtml');
            File.copy('/zvfs/lib/web/markdeep/nginx_deepdoc.conf', 'nginx_deepdoc.conf');
            File.copy('/zvfs/lib/web/markdeep/jsistyle.css', 'jsistyle.css');
            File.copy('/zvfs/lib/web/dumpdeep.js', 'dumpdeep.js');
            File.copy('/zvfs/lib/web/jsi.js', 'jsi.js');
            File.copy('/zvfs/lib/web/markdeep.min.js', 'markdeep.min.js');
            File.mkdir('md');
            File.copy('/zvfs/lib/web/markdeep/DeepDoc.md', 'md/DeepDoc.md');
            puts("DeepDoc created. View with 'jsish -W -url Deepdoc'");
            return;
        }
        var fl = [];

Changes to lib/Htmlpp.jsi.

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
                        continue;
                    }
                    pre += "<script>"+begin;
                    if (!ispp) 
                        pre += File.read(fn);
                    else {
                        if (self.jsifiles.length==1 && self.noCheck) {
                            pre += File.read(self.dir+'/web/jsig.js');
                            if (self.noCheck)
                                pre += '$jsi.conf({enable:false});\n';
                        }
                        pre += Jspp([fn]);
                    }
                    pre += end + "</script>\n";
                    echo(pre);
                    continue;
                    







|

|







122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
                        continue;
                    }
                    pre += "<script>"+begin;
                    if (!ispp) 
                        pre += File.read(fn);
                    else {
                        if (self.jsifiles.length==1 && self.noCheck) {
                            pre += File.read(self.dir+'/web/jsi.js');
                            if (self.noCheck)
                                pre += '$jsi.conf({disabled:true});\n';
                        }
                        pre += Jspp([fn]);
                    }
                    pre += end + "</script>\n";
                    echo(pre);
                    continue;
                    

Changes to lib/Jspp.jsi.

6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 * 
 *    function foo(s:string='', n:number=1):string {
 * 
 * becomes
 * 
 *    function foo(s, n) { s=Jsi.ArgCheck(...
 * 
 * Code to perform type checking/default values is provided by "web/jsig.js".
 * 
 * Limitation: Comments within function signatures are not supported.
 */

function Jspp(files:array|string=null, conf:object=undefined):string|object
{
    var options = { // A JS preprocessor to convert "typed" functions out to web-browser compatible form







|







6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 * 
 *    function foo(s:string='', n:number=1):string {
 * 
 * becomes
 * 
 *    function foo(s, n) { s=Jsi.ArgCheck(...
 * 
 * Code to perform type checking/default values is provided by "web/jsi.js".
 * 
 * Limitation: Comments within function signatures are not supported.
 */

function Jspp(files:array|string=null, conf:object=undefined):string|object
{
    var options = { // A JS preprocessor to convert "typed" functions out to web-browser compatible form

Changes to lib/SqliteUI/html/main.htmli.

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<head>
<title>SqliteUI</title>
<!--
<script src='sqlite.js'></script> 
<link rel="stylesheet" type="text/css" href="sqlite.css">
-->
<script src='/SqliteUI/jsi/web/jsig.js'></script> 
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<? 
// Load .jsi and .cssi for preprocessing.
include(['sqlite.jsi', 'sqlite.cssi']);
?>
</head>








|







3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<head>
<title>SqliteUI</title>
<!--
<script src='sqlite.js'></script> 
<link rel="stylesheet" type="text/css" href="sqlite.css">
-->
<script src='/SqliteUI/jsi/web/jsi.js'></script> 
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<? 
// Load .jsi and .cssi for preprocessing.
include(['sqlite.jsi', 'sqlite.cssi']);
?>
</head>

Changes to lib/Websrv.jsi.

263
264
265
266
267
268
269

270
271
272
273
274
275
276
...
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
...
389
390
391
392
393
394
395



















396
397
398
399
400
401
402
    }
    
    function WebGetString(ws:userobj, id:number, url:string, query:array) {
        return self.pageStr;
    }
    
    function WebAdmin(ws:userobj, id:number, url:string, query:array) {

        if (self.pageStr !== '')
            return self.pageStr;
        var cv = ws.conf();
        var rv = '<HEAD>'+self.css+'</HEAD>' + tablifyObj(cv, true);
        var rids = ws.ids();
        LogDebug('RIDS: '+id+' '+rids.toString());
        for (var nid of rids) {
................................................................................
            cv = ws.idconf(nid);
            var cur = (id === nid?' (CURRENT)':'');
            rv += '<H3> Conn #'+nid+cur+'</H3>'+tablifyObj(cv, true);
        }
        return "<H2>Websrv Admin</H2>"+rv;
    }
    
    function WebMenu(ws:userobj, id:number, url:string, query:array) {
        return File.read('/zvfs/lib/web/wsMenu.htmli');
    }
    
    // url = getUrlInDir(self.mnt)
    function getUrlInDir(indir:string, depth=1) {
        var flst;
        for (var fext of ['.htmli','.html']) {
            flst = File.glob('*'+fext, {dir:indir});
            if (flst.length === 1)
................................................................................
                self.rootdir = File.dirname(self.url);
            var rlen = self.rootdir.length;
            if (self.url.substr(0, rlen) === self.rootdir)
                self.url = self.url.substr(rlen);
        }
        if (!self.rootdir)
            self.rootdir = '.';
        switch (self.mode) {
            case 'admin':
                self.url = '/admin';
                wo.getRegexp=/^\/admin/;
                wo.onGet=WebAdmin;
                break;
            case 'menu':
                self.url = '/jsi/web/Websrv.htmli';
                break;
            case '': break;
            default: throw('unknown mode: '+self.mode);
        }
        if (urlOrig!=='' && self.timeout === 60000)
            self.timeout = 0;
        if (!self.server && !self.pageStr && self.mode === '' && urlOrig=='' && (!self.url || !File.isfile(self.rootdir+'/'+self.url)))
            throw("url file empty or not found: "+self.url);
        if (self.urlPrefix.length && self.urlPrefix[0] != '/')
            throw('urlPrefix must start with a slash');
        
        // Provide default values for websocket.
        var wo = self.wsopts = {
            local:self.local,
            debug:self.wsdebug,
            extHandler:true,
            onOpen:WsOpen,
            onClose:WsClose,
................................................................................
            onRecv:WsRecv,
            onUpload:WsUpload,
            onFilter:WsFilter,
            port:self.port,
            rootdir:self.rootdir,
            urlPrefix:self.urlPrefix
        };



















        if (self.local)
            wo.noCompress = true;
        if (self.server) {
            self.noGui = true;
            if (self.timeout !== options.timeout)
                self.timeout = 0;
        }







>







 







|

|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
...
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
...
356
357
358
359
360
361
362



















363
364
365
366
367
368
369
...
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
    }
    
    function WebGetString(ws:userobj, id:number, url:string, query:array) {
        return self.pageStr;
    }
    
    function WebAdmin(ws:userobj, id:number, url:string, query:array) {
        LogDebug('url', url);
        if (self.pageStr !== '')
            return self.pageStr;
        var cv = ws.conf();
        var rv = '<HEAD>'+self.css+'</HEAD>' + tablifyObj(cv, true);
        var rids = ws.ids();
        LogDebug('RIDS: '+id+' '+rids.toString());
        for (var nid of rids) {
................................................................................
            cv = ws.idconf(nid);
            var cur = (id === nid?' (CURRENT)':'');
            rv += '<H3> Conn #'+nid+cur+'</H3>'+tablifyObj(cv, true);
        }
        return "<H2>Websrv Admin</H2>"+rv;
    }
    
    /*function WebMenu(ws:userobj, id:number, url:string, query:array) {
        return File.read('/zvfs/lib/web/wsMenu.htmli');
    }*/
    
    // url = getUrlInDir(self.mnt)
    function getUrlInDir(indir:string, depth=1) {
        var flst;
        for (var fext of ['.htmli','.html']) {
            flst = File.glob('*'+fext, {dir:indir});
            if (flst.length === 1)
................................................................................
                self.rootdir = File.dirname(self.url);
            var rlen = self.rootdir.length;
            if (self.url.substr(0, rlen) === self.rootdir)
                self.url = self.url.substr(rlen);
        }
        if (!self.rootdir)
            self.rootdir = '.';



















        // Provide default values for websocket.
        var wo = self.wsopts = {
            local:self.local,
            debug:self.wsdebug,
            extHandler:true,
            onOpen:WsOpen,
            onClose:WsClose,
................................................................................
            onRecv:WsRecv,
            onUpload:WsUpload,
            onFilter:WsFilter,
            port:self.port,
            rootdir:self.rootdir,
            urlPrefix:self.urlPrefix
        };
        switch (self.mode) {
            case 'admin':
                self.url = '/admin';
                wo.getRegexp=/^\/admin/;
                wo.onGet=WebAdmin;
                break;
            case 'menu':
                self.url = '/jsi/web/Websrv.htmli';
                break;
            case '': break;
            default: throw('mode "'+self.mode+'" not one of: admin, menu');
        }
        if (urlOrig!=='' && self.timeout === 60000)
            self.timeout = 0;
        if (!self.server && !self.pageStr && self.mode === '' && urlOrig=='' && (!self.url || !File.isfile(self.rootdir+'/'+self.url)))
            throw("url file empty or not found: "+self.url);
        if (self.urlPrefix.length && self.urlPrefix[0] != '/')
            throw('urlPrefix must start with a slash');
        
        if (self.local)
            wo.noCompress = true;
        if (self.server) {
            self.noGui = true;
            if (self.timeout !== options.timeout)
                self.timeout = 0;
        }

Changes to lib/templates/WebApp.jsi.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function WebApp(args:array|string=void, conf:object=void) {
    
    var self = { done:false };
    var options = { // A  WebSocket server module
        file        :'html/main.htmli', // Source file.
        local       :true,          // Connection listens only on localhost.
        port        :0,             // Port number for server.
        quiet       :false,         // No info messages.
        rootdir     :'',            // Root directory
        server      :false,         // Just the server: do not open a browser.
        useridPass  :'',            // USER:PASS for web GUI.
        wsdebug     :0,             // WebSockets debug level.
        wsOpts      :{}             // WebSockets options.
    };
    







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function WebApp(args:array|string=void, conf:object=void) {
    
    var self = { done:false };
    var options = { // A  WebSocket server module
        file        :'html/main.htmli', // Source file.
        local       :true,          // Connection listens only on localhost.
        port        :0,             // Port number for server.
        quiet       :false,         // Suppress startup messages.
        rootdir     :'',            // Root directory
        server      :false,         // Just the server: do not open a browser.
        useridPass  :'',            // USER:PASS for web GUI.
        wsdebug     :0,             // WebSockets debug level.
        wsOpts      :{}             // WebSockets options.
    };
    

Added lib/web/Websrv.htmli.





































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
<!DOCTYPE html>
<html style="height: 100%">
  <head>
    <meta charset=utf-8 />
    <title>Websrv Menu</title>
    <link rel="icon" href="data:;base64,iVBORw0KGgo=">
    <link rel="stylesheet" href="/jsi/web/jsiweb.css" type="text/css" media="screen" />
    <script src="/jsi/web/jsi.js"></script>
    <script src="/jsi/web/bind.js"></script>
  </head>

<script>
"use strict";
var ws, mainid;


function RunApp(name) {
    console.log("RUNAPP: "+name);
    $('body')[0].classList.add('shade');
    ws.send('{"cmd":"run", "data":"'+name+'"}');
}

function DoneRun(ret) {
    $('body')[0].classList.remove('shade');    
}

function tablifyObj(cv, all) {
    var rv = '<TR><TH>Name</TH><TH>Value</TH></TR>';
    for (var i in cv) {
        rv += '<TR><TD>'+i+'</TD><TD>'+JSON.stringify(cv[i])+'</TD></TR>';
    }
    if (all)
        rv = '<TABLE border=1 class=cmdstbl>'+rv+'</TABLE>';
    return rv;        
}

function MakeButs(lst) {
    var rv = '<ul>';
    for (var i in lst) {
        rv += '<li><button onclick="RunApp(\''+lst[i]+'\')">'+lst[i]+'</button></li>';
    }
    rv += '</ul>';
    return rv;
}

function WebMsg(msg) {
    puts("MMM: "+msg.data);
    var s = JSON.parse(msg.data);
    switch (s.cmd) {
        case 'init':
        puts('SS: '+s.data);
            var rv = MakeButs(s.data);
            mainid.innerHTML = rv;
            break;
        case 'run':
            DoneRun(s.data);
        case 'exit':
            break;
    }
}

function DoInit() {
    puts("INIT");
    mainid = $('#main')[0];
    ws = $jsi.websock();
    ws.onmessage = WebMsg;
    ws.onopen = function() {
        // Kick things off by sending an init msg.
        ws.send('{"cmd":"init"}');
    };
}

</script>

<body onload='DoInit()' >
<!-- Admin Menu interface accessed from Websrv -menu option -->
<h2>Websrv Menu</h2>

<div id=main></div>

</body>
</html>

Name change from lib/web/jsig.js to lib/web/jsi.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
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
...
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426

427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448





449
450
451
452
453
454
455
456
457
458
459




460
461
462
463
464

465
466
467
468


469
470
471
472


473
474
475
476
477

478
479
480
481
482
483
484
485
486
487
488
489

490
491
492

493
// jsig.js: uses signatures for type-checking arguments of function calls.
// Emulates Jsi's builtin typechecking, sans return types.  See https://jsish.org

(function () {
"use strict";

var typeNames={"number":1,"string":1,"boolean":1,"array":1,"function":1,"object":1,"regexp":1,"any":1,"userobj":1,"void":1,"null":1,"undefined":1},
    typeNameStr= Object.keys(typeNames).join(', '),
    sigs = {},
    srcList = [],
    srcCnt = 0,
    onloadFunc = null,
    funcLst = [],
    inline = false,
    modes = ['assert', 'error', 'warn', 'debug', 'log', 'throw', 'alert'],
    errCnt = 0,
    errCur = null,
    config = {
        mode:'error', 
        enable:true,
        debug:true
    },
    LogDebug = function() {},
    LogTrace = function() {},
    LogTest = function() {},
    LogWarn = console.warn.bind(console),
    LogError = console.error.bind(console),
    isChrome = (window.chrome && window.navigator.vendor === "Google Inc.")

;








































































































































function FileExt(fn) {
    var i = fn.lastIndexOf('.');
    if (i<0) return '';
    return fn.substr(i+1);
}

function setOpts(obj, opts) {











































    for (var i in obj)
        if (opts[i] === undefined)
            throw("unknown option: "+i+' is not one of: '+Object.keys(opts).join(', '));
    for (var i in opts)
        if (obj[i] === undefined)
            obj[i] = opts[i];
    return obj;
}

var uuid = Date.now();

// Code to handle function parameter types and default values.
function Jsi() {
}

Jsi.prototype = {
        
    conf: function(vals) { // Configure options.


        for (var i in vals) {

            if (typeof config[i] == 'undefined')
                jsi.errorCmd("Option "+i+" not one of: "+Object.keys(config).join(', '));
            else {
                switch (i) {
                    case 'mode':

                        if (modes.indexOf(vals[i])<0)
                            jsi.errorCmd('invalid mode '+vals[i]+': not one of: '+modes.join(','));
                        break;



                }


                config[i] = vals[i];
            }
        }
    },
    
    errorFunc: function() {
        switch (config.mode) {
            case 'assert':
                if (console.assert) return function(msg) { console.assert(false, msg); };
                debugger;
            case 'throw': return function(msg) { throw(msg); };
            case 'debug':debugger; if (console.error) return console.error;
            case 'error': if (console.error) return console.error;
            case 'warn': if (console.warn) return console.warn;
            case 'log': return console.log;
            case 'alert': return alert;
            default: console.log('unknown mode: '+config.mode);
        }
        return console.log;
    },

    errorCmd: function(msg) {
        errCnt++;
        switch (config.mode) {
            case 'assert': if (console.assert) return console.assert(false, msg);
            case 'debug': if (console.error) { console.error(msg); debugger; return; } debugger;
            case 'error': if (console.error) return console.error(msg);
            case 'warn': if (console.warn) return console.warn(msg);
            case 'log': return console.log(msg);
            case 'throw': throw(msg);
            case 'alert': return alert(msg);
            default: console.log('unknown mode: '+config.mode);
        }
        return console.log(msg);
    },    
    parseError: function(msg) {
        jsi.errorCmd('PARSERR: '+msg+' ==> '+jsi.curSig);
        return [];
    },
    
    ArgCheckType: function(o, aind, val) {
        var af = o.args[aind];
        var tlst = af.typ;
        if (!tlst) return;
        var nam = af.name;
        var vtyp = typeof(val);
        for (var i = 0; i<tlst.length; i++) {
            switch (tlst[i]) {
                case "number":  if (vtyp === 'number') return; break;
                case "string":  if (vtyp === 'string') return; break;
                case "boolean": if (vtyp === 'boolean') return; break;
                case "function":if (vtyp === 'function') return; break;
                case "array":   if (vtyp === 'object' && val && val.constructor === Array) return; break;
                case "regexp":  if (vtyp === 'object' && val && val.constructor === RegExp) return; break;
                case "object":  if (vtyp === 'object' && val && val.constructor !== Array) return; break;
                case "any":     return; break;
                case "userobj": if (vtyp === 'object') return; break;
                case "undefined": case "void": if (val === undefined) return; break;
                case "null": if (val === null) return; break;
                default:
                    jsi.errorCmd("type '"+tlst[i]+'" is unknown: not one of: '+typeNameStr);
                    return;
            }
        }
        return 'type mismatch for arg '+(aind+1)+' "'+nam+'" expected "'+tlst.join('|')+'" got "'+vtyp+'" '+val;
    },

    $jsig: function(sig, args) { // Check function arguments
        if (!config.enable)
            return;


        var o = sig;
        if (typeof sig === 'string') {
            o = sigs[sig];
            if (!o)
                o = sigs[sig] = jsi.SigParse(sig);        
        }
        if (typeof o !== 'object' || typeof args !== 'object')
            throw('bad sig or arguments:'+sig+args);
        
        var len = args.length, msg;
        var pre = 'In "'+o.name+'()" ';
        if (o.max>=0 && len>o.max)
            msg = "extra arguments: expected "+o.max+" got "+len;
        else if (len<o.min)
            msg = "missing arguments: expected "+o.min+" got "+len;
        for (var aind = 0; aind<args.length && !msg; aind++)
            msg = jsi.ArgCheckType(o, aind, args[aind]);
        if (msg) {
            msg+=': calling '+o.name+'('+o.astr+')'+o.retval;
            //msg+=': calling '+o.name+'('+Array.prototype.slice.call(args).join(',')+'), sig ==> FN('+o.astr+')'+o.retval;
            if (!inline)
                jsi.errorCmd(msg);
            errCur = msg;
        }
        return msg;
    },
    errGet: function() {
        return errCur;
    },

    typeValidate: function (typ) {
        if (typ === '') return null;
        var tlst = typ.split('|');
        var i = -1;
        for (i = 0; i<tlst.length; i++)
            if (!typeNames[tlst[i]]) {
                jsi.parseError("type unknown '"+tlst[i]+'" not one of: '+typeNameStr);
                return null;
            }
        if (tlst.length===1 && tlst[0] === 'any')
            return null;
        return tlst;
    },

    SigParse: function(sig) { // Parse string signature and return info.
        jsi.curSig = sig;
        var reg = /^([a-zA-Z0-9_]*)\s*\(([^)]*)\)(:[\|a-z]+|)(\s*)$/;
        var vals = reg.exec(sig);
        var sargs = '';
        if (!vals)
            throw "invalid method: "+sig;
        LogTrace(vals);
        var fnam = vals[1];
        var someDef = sig.indexOf('=')>=0;
        if (sig.indexOf(':')<0 && !someDef) // Skip functions with no types or defaults
            return [];
        var res = {name:fnam}
    
        var astr = vals[2].trim();
        var acall = [];
        if (astr !== '') {
            var alst = astr.split(',');
            var minargs = alst.length, maxargs = minargs;
            var last = alst.length-1;
            for (var i = 0; i<=last; i++) {
                var aval = alst[i].trim();
                if (aval === '...') {
                    if (i != last)
                        return jsi.parseError("expected ... to be at end");
                    maxargs = -1;
                    minargs--;
                    continue;
                }
                var rega = /^([a-zA-Z0-9_]+)(:[|a-z]+|)(=.+|)$/;
                var avals = rega.exec(aval);
                if (!avals)
                    return jsi.parseError("invalid argument: "+aval);
                LogTrace(avals);
                if (i)
                    sargs += ', ';
                var afnam = avals[1];
                if (afnam === '' || avals.length<3)
                    afnam = aval;
                sargs += afnam;
                var defval=undefined, atyp = '';
                if (avals.length>2)
                    atyp = avals[2].substr(1);
                var tlst = jsi.typeValidate(atyp);
                var hasDef = (avals[3] && avals[3] !== '');
                if (hasDef) { // Default value
                    if (avals[3] !== '=void')
                        defval = avals[3].substr(1);
                    if (minargs===alst.length)
                        minargs = i;
                }
                else if (minargs!==alst.length)
                    return jsi.parseError("non-default value follows default: "+aval+' in: '+str);
                acall.push({name:afnam, typ:tlst, def:defval});
            }
        }
        res.astr = astr;
        res.min = minargs;
        res.max = maxargs;
        res.args = acall;
        res.sargs = sargs;
        var rettyp = vals[3].substr(1);
        if (rettyp === '' || rettyp === 'any' || !jsi.typeValidate(rettyp))
            rettyp = '';
        res.rettyp = rettyp;
        res.retval = vals[3];
        res.ssig = sig;
        return res;
    },
    
    SigConvert: function(code) { // Convert typed functions to work in browser, adding $jsig and default-value set.
        
        function reMethod(str) {
            var reg = /^function\s*([a-zA-Z0-9_]*)\s*\(([^)]*)\)(:[\|a-z]+|)(\s*)\{$/;
            var vals = reg.exec(str);
            if (!vals) {
                LogWarn("invalid method: "+str);
                return str;
            }
            LogTrace(vals);
            var fnam = vals[1];
            if (fnam !== '' && fnam[0] !== '_')
                funcLst.push(fnam);
            var someDflt = str.indexOf('=')>=0;
            if (str.indexOf(':')<0 && !someDflt) // Skip functions with no types or defaults
                return str;
            var res = 'function '+fnam+'(';
        
            var astr = vals[2].trim();
            if (astr === '')
                res += str + ') {';
            else {
                var sobj = jsi.SigParse(fnam+'('+vals[2]+')'+vals[3]);
                LogDebug('SOBJ',JSON.stringify(sobj));
                res += sobj.sargs+')' + '{ $jsig("'+sobj.ssig+'", arguments); ';
            }

            return res;
        }
        var reg = /function\s*[a-zA-Z0-9_]*\s*\([^)]*\)(:[\|a-z]+|)\s*\{/g;
        return code.replace(reg, reMethod) + '\n<!--JSIG GEN-->';
    },
    
    SaveFile: function(filename, data, mime) {
        var blob = new Blob([data], {type: (mime?mime:'text/html')});
        if(window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var elem = window.document.createElement('a');
            elem.href = window.URL.createObjectURL(blob);
            elem.download = filename;
            document.body.appendChild(elem);
            elem.click();
            document.body.removeChild(elem);
        }
    },
    
    AddClose: function(id, escFunc) {
        if (id.firstElementChild.className !== 'close-icon') {
            var ct = document.createElement('button');
            ct.onclick = escFunc;
            ct.className='close-icon';
            id.insertBefore(ct, id.firstElementChild);
        }
    },
    
    guid: function(){
        return '_uuid'+(uuid++).toString(16);
    },
    ajax:function(url, opts) {
        function none(){};
        if (typeof url !== 'string') {
            opts = url;
            url = opts.url;
        }
        if (!url) return false;
    
        opts = setOpts(opts, {
            success:    none,
            error:      none,
            complete:   none,
            type:       'GET',
            dataType:   'text', // One of: json, jsonp, script, text.
            data:       {},   // Query data.
            headers:    {},
................................................................................
    
            window[opts.data.callback = jsi.guid()] = function(data) {
                opts.success.call(req, data, null, req);
            };
    
            req.onload = req.onerror = function(e) {
                if (e && e.type === "error")
                    opts.error.call(this, req);
                opts.complete.call(this, req);
                req.remove();
            };
    
            qd = '?';
            for (var key in opts.data)
                qd += encodeURIComponent(key) + '=' + encodeURIComponent(opts.data[key]) +'&';
    
................................................................................
        }
        default:
            throw('dataType not one of: json, jsonp, script, text');
        }
        return req;
    },
    
    srcAdd: function(fn, asData) {
        var f=document.createElement('script');
        f.setAttribute("type","text/javascript");
        if (asData)
            f.innerHTML = fn;
        else {
            f.setAttribute("src", fn);
            srcCnt++;
            f.setAttribute("onload", "$jsi.srcDone()");
        }
        var h = document.querySelector("head");
        h.appendChild(f);
    },
    include: function(fns) {
        if (typeof fns === 'string')
            fns = [fns];
        for (var i in fns) {
            var fn = fns[i];
            if (window.jsiWebSocket)
                return jsi.srcAdd(fn);
            srcList.push(fn);
            srcCnt++;
            jsi.ajax(fn, {success:jsi.srcSuccess, error:jsi.srcError});
        }
    },
    srcDone: function() {
        srcCnt--;
    },
    srcSuccess: function(str, txt, req) {
        srcCnt--;
        LogDebug('Src Success: '+str);
        var hdrs = req.getAllResponseHeaders();
        if (!hdrs || hdrs.indexOf('jsiWebSocket')<0) {
            str = jsi.SigConvert(str);
            LogDebug('Src xlate: '+str);
        }
        jsi.srcAdd(str, true);
        //eval.call({}, str);
    },
    srcError: function(str) {
        srcCnt--;
        LogWarn('Src Error: '+str);

    },
    query: function(sel, top) {
        var rc;
        if (typeof sel === 'function') {
            document.addEventListener("DOMContentLoaded", function () {
             sel(); });
            return null;
        }
        if (!top)
            top = document; 
        if (sel.match(/^#[\w]+$/)) {
            rc = top.getElementById(sel.substr(1));
            rc = (rc?[rc]:[]);
        } else if (top.querySelectorAll)
            rc = top.querySelectorAll(sel);
        else if (top.getElementsByClassName) {
            if (sel.substr(0,1) === '.')
                rc = top.getElementsByClassName(sel.substr(1));
            else
                rc = top.getElementsByTagName(sel);
        }
        return rc;





    },
    websock:function(WsRecv, prot) {
        if (!prot) prot = 'ws';
        var url = document.URL.replace(/^http/,'ws');
        var ws = new WebSocket(url, prot);
        ws.onmessage = WsRecv;
        return ws;
    },
    
    onload: function(f) {
        onloadFunc = f;




    },
    DoOnload: function(f) {
        if (srcCnt>0) {
            LogDebug("waiting for src");
            setTimeout(jsi.DoOnload, 1000);

            return;
        }
        var debug = (window.location.href.search(/\?.*jsig.debug.*/i) !== -1);
        if (debug)


            jsi.conf({mode:'debug'});
    
        if (onloadFunc)
            onloadFunc();


    }

};

var jsi = new Jsi();

window['$jsi'] = jsi;
window['$jsig']= jsi.$jsig;

if (typeof window['puts'] === 'undefined')
    window['puts'] = console.log.bind(console);    
if (typeof window['$'] === 'undefined')
    window['$'] = document.querySelectorAll.bind(document);
if (typeof window['$$'] === 'undefined')
    window['$$'] = document.querySelector.bind(document);

if (document.readyState !== 'loading')
    jsi.DoOnload();

else
    document.addEventListener("DOMContentLoaded", jsi.DoOnload, false); 


}());
|
|




|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|

|
<
>
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|





<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
|

<
<
<
<
<
<
<
<
|
>
>

>
|
|



>

|

>
>
>

>
>
|




<
<
<
<
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
|
<
<
<
>
>


|

|











|


|
<
|
|



|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







|







 







|
|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
|





|
|
|
|
<
<
<
<
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
|
<
<
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
|
<
>
>
>
>
>

|
<

|
<
<


|
<
>
>
>
>

|
|

<
>


<
<
>
>
|

<
<
>
>




<
>
|
|
|
|
|
|
|
|
|
|
|
<
>
|
|
|
>

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
206
207
208








209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233






234
235
























236

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262

263



264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285

286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409





























410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
...
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
...
478
479
480
481
482
483
484













485
486
487
488
489
490
491
492
493
494





495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510






511












512

513
514
515
516
517
518
519

520
521


522
523
524

525
526
527
528
529
530
531
532

533
534
535


536
537
538
539


540
541
542
543
544
545

546
547
548
549
550
551
552
553
554
555
556
557

558
559
560
561
562
563
// jsi.js:  Typed function arguments. https://jsish.org/jsig
// Mostly emulates the builtin typechecking (sans return types) in Jsi.

(function () {
"use strict";

if (window) {















    var LogDebug = function(){},
    LogTrace = function(){},
    LogTest = function(){},
    LogWarn = console.warn.bind(console),
    LogError = console.error.bind(console);

}

function parseError(msg) {
    errorCmd('PARSERR: '+msg+' ==> '+jsi._state.curSig);
    return [];
}

function errorCmd(msg) { // Handle error condition.
    jsi._state.errCnt++;
    switch (jsi._config.mode) {
        case 'error': if (console.error) console.error(msg); return;
        case 'throw': console.log(msg); throw(msg);
        case 'log': break;
        case 'alert': alert(msg); break;
        default: console.log('unknown mode: '+jsi._config.mode);
    }
    return console.log(msg);
}

function typeValidate(typ) {
    if (typ === '') return null;
    var tlst = typ.split('|');
    var i = -1;
    if (!jsi._state.typeNames)
        jsi._state.typeNames = jsi._state.typeNameStr.split(',');
    for (i = 0; i<tlst.length; i++)
        if (jsi._state.typeNames.indexOf(tlst[i])<0) {
            parseError("type unknown '"+tlst[i]+'" not one of: '+jsi._state.typeNameStr);
            return null;
        }
    if (tlst.length===1 && tlst[0] === 'any')
        return null;
    return tlst;
}

function SigParse(sig) { // Parse string signature and return info.
    jsi._state.curSig = sig;
    var reg = /^([a-zA-Z0-9_]*)\s*\(([^)]*)\)(:[\|a-z]+|)(\s*)$/;
    var vals = reg.exec(sig);
    var sargs = '';
    if (!vals)
        throw "invalid method: "+sig;
    LogTrace(vals);
    var fnam = vals[1];
    var someDef = sig.indexOf('=')>=0;
    if (sig.indexOf(':')<0 && !someDef) // Skip functions with no types or defaults
        return [];
    var res = {name:fnam};

    var astr = vals[2].trim();
    var acall = [];
    if (astr !== '') {
        var alst = astr.split(',');
        var minargs = alst.length, maxargs = minargs;
        var last = alst.length-1;
        for (var i = 0; i<=last; i++) {
            var aval = alst[i].trim();
            if (aval === '...') {
                if (i != last)
                    return parseError("expected ... to be at end");
                maxargs = -1;
                minargs--;
                continue;
            }
            var rega = /^([a-zA-Z0-9_]+)(:[|a-z]+|)(=.+|)$/;
            var avals = rega.exec(aval);
            if (!avals)
                return parseError("invalid argument: "+aval);
            LogTrace(avals);
            if (i)
                sargs += ', ';
            var afnam = avals[1];
            if (afnam === '' || avals.length<3)
                afnam = aval;
            sargs += afnam;
            var defval=undefined, atyp = '';
            if (avals.length>2)
                atyp = avals[2].substr(1);
            var tlst = typeValidate(atyp);
            var hasDef = (avals[3] && avals[3] !== '');
            if (hasDef) { // Default value
                if (avals[3] !== '=void')
                    defval = avals[3].substr(1);
                if (minargs===alst.length)
                    minargs = i;
            }
            else if (minargs!==alst.length)
                return parseError("non-default value follows default: "+aval+' in: '+str);
            acall.push({name:afnam, typ:tlst, def:defval});
        }
    }
    res.astr = astr;
    res.min = minargs;
    res.max = maxargs;
    res.args = acall;
    res.sargs = sargs;
    var rettyp = vals[3].substr(1);
    if (rettyp === '' || rettyp === 'any' || !typeValidate(rettyp))
        rettyp = '';
    res.rettyp = rettyp;
    res.retval = vals[3];
    res.ssig = sig;
    return res;
}

function SigConvert(code) { // Convert typed functions to work in browser, adding $jsig and default-value set.
    
    function reMethod(str) {
        var reg = /^function\s*([a-zA-Z0-9_]*)\s*\(([^)]*)\)(:[\|a-z]+|)(\s*)\{$/;
        var vals = reg.exec(str);
        if (!vals) {
            LogWarn("invalid method: "+str);
            return str;
        }
        LogTrace(vals);
        var fnam = vals[1];
        if (fnam !== '' && fnam[0] !== '_')
            jsi._state.funcLst.push(fnam);
        var someDflt = str.indexOf('=')>=0;
        if (str.indexOf(':')<0 && !someDflt) // Skip functions with no types or defaults
            return str;
        var res = 'function '+fnam+'(';
    
        var astr = vals[2].trim();
        if (astr === '')
            res += str + ') {';
        else {
            var sobj = SigParse(fnam+'('+vals[2]+')'+vals[3]);
            LogDebug('SOBJ',JSON.stringify(sobj));
            res += sobj.sargs+')' + '{ $jsig("'+sobj.ssig+'", arguments); ';
        }

        return res;
    }
    var reg = /function\s*[a-zA-Z0-9_]*\s*\([^)]*\)(:[\|a-z]+|)\s*\{/g;
    return code.replace(reg, reMethod) + '\n<!--JSIG GEN-->';
}

function fileext(fn) {
    var i = fn.lastIndexOf('.');
    if (i<0) return '';
    return fn.substr(i+1);
}


function addScript(fn, asData) { // Add script into page.
    var f=document.createElement('script');
    f.setAttribute("type","text/javascript");
    if (asData)
        f.innerHTML = fn;
    else {
        f.setAttribute("src", fn);
        jsi._state.srcCnt++;
        f.setAttribute("onload", "$jsi._state.srcCnt--;");
    }
    var h = document.querySelector("head");
    h.appendChild(f);
}

function errHandler(msg, url, lineNumber) {  
    //save error and send to server for example.
    puts('MMM',msg);
    $jsi.htmladd('<p style="color:red">'+msg+'</p>\n');
    //$jsi.htmladd(msg);
    return true;
}


var jsi = {
    _config: {
        mode:'error', 
        disabled:false,
        insert:false,
        inline:false,
        onload:null
    },
    _state:{ // Internal state information.
        typeNameStr:'number,string,boolean,array,function,object,regexp,any,userobj,void,null,undefined',
        typeNames:null,
        sigs:{},
        uuid:0,
        srcList:[],
        srcCnt:0,
        errCnt:0,
        errCur:null,
        funcLst:[]
    },
    setOpts: function(obj, opts) { // Set options in object.
        for (var i in obj)
            if (opts[i] === undefined)
                throw("unknown option: "+i+' is not one of: '+Object.keys(opts).join(', '));
        for (var i in opts)
            if (obj[i] === undefined)
                obj[i] = opts[i];
        return obj;
    },









    conf: function(vals) { // Configure jsi options.
        if (!vals)
            return jsi._config;
        for (var i in vals) {
            var tt = typeof jsi._config[i];
            if (tt == 'undefined')
                errorCmd("Option "+i+" not one of: "+Object.keys(jsi._config).join(', '));
            else {
                switch (i) {
                    case 'mode':
                        var modes = ['error', 'log', 'throw', 'alert'];
                        if (modes.indexOf(vals[i])<0)
                            errorCmd('invalid mode '+vals[i]+': not one of: '+modes.join(','));
                        break;
                    case 'insert':
                        window.onerror = (vals[i]?errHandler:undefined);
                        break;
                }
                if (jsi._config[i] !== null && tt !== typeof vals[i])
                    errorCmd('type mismatch in conf of '+i+': '+tt+'!='+typeof vals[i]);
                jsi._config[i] = vals[i];
            }
        }
    },
    






    $jsig: function(sig, args) { // Check function arguments
        if (jsi._config.disabled)
























            return;

            
        function ArgCheckType(o, aind, val) {
            var af = o.args[aind];
            var tlst = af.typ;
            if (!tlst) return;
            var nam = af.name;
            var vtyp = typeof(val);
            for (var i = 0; i<tlst.length; i++) {
                switch (tlst[i]) {
                    case "number":  if (vtyp === 'number') return; break;
                    case "string":  if (vtyp === 'string') return; break;
                    case "boolean": if (vtyp === 'boolean') return; break;
                    case "function":if (vtyp === 'function') return; break;
                    case "array":   if (vtyp === 'object' && val && val.constructor === Array) return; break;
                    case "regexp":  if (vtyp === 'object' && val && val.constructor === RegExp) return; break;
                    case "object":  if (vtyp === 'object' && val && val.constructor !== Array) return; break;
                    case "any":     return; break;
                    case "userobj": if (vtyp === 'object') return; break;
                    case "undefined": case "void": if (val === undefined) return; break;
                    case "null": if (val === null) return; break;
                    default:
                        errorCmd("type '"+tlst[i]+'" is unknown: not one of: '+jsi._state.typeNameStr);
                        return;
                }
            }
            return 'type mismatch for arg '+(aind+1)+' "'+nam+'" expected "'+tlst.join('|')+'" got "'+vtyp+'" '+val;

        }





        var o = sig;
        if (typeof sig === 'string') {
            o = jsi._state.sigs[sig];
            if (!o)
                o = jsi._state.sigs[sig] = SigParse(sig);        
        }
        if (typeof o !== 'object' || typeof args !== 'object')
            throw('bad sig or arguments:'+sig+args);
        
        var len = args.length, msg;
        var pre = 'In "'+o.name+'()" ';
        if (o.max>=0 && len>o.max)
            msg = "extra arguments: expected "+o.max+" got "+len;
        else if (len<o.min)
            msg = "missing arguments: expected "+o.min+" got "+len;
        for (var aind = 0; aind<args.length && !msg; aind++)
            msg = ArgCheckType(o, aind, args[aind]);
        if (msg) {
            msg+=': calling '+o.name+'('+o.astr+')'+o.retval;
            if (!jsi._config.inline)

                errorCmd(msg);
            jsi._state.errCur = msg;
        }
        return msg;
    },

    filesave: function(filename, data, mime) { // Save data as filename.
        var blob = new Blob([data], {type: (mime?mime:'text/html')});
        if(window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var elem = window.document.createElement('a');
            elem.href = window.URL.createObjectURL(blob);
            elem.download = filename;
            document.body.appendChild(elem);
            elem.click();
            document.body.removeChild(elem);
        }
    },
    
    schema: function(obj, schm) { // Check schema, or generate when schm null/ommited.
        
        function gettype(m) {
            var typ = typeof m;
            if (m==='object' && Array === m.constructor)
                return 'array';
        }
                
        function gen(obj) {
            function sub(m,name) {
                var p, i, typ = gettype(m), rc = {type:typ};
                switch (typ) {
                    case 'integer':
                    case 'number':
                    case 'boolean':
                    case 'string':
                    case 'null':
                        break;
                    case 'array':
                        if (m[0]===undefined) throw('array must be non-empty '+name);
                        rc.items = sub(m[0], name);
                        break;
                    case 'object':
                        rc.properties = {};
                        var req = [];
                        for (i in m) {
                            rc.properties[i] = sub(m[i], i);
                            req.push(i);
                        }
                        if (req.length)
                            rc.required = req;
                        break;
                        
                    default:
                        console.log('ignoring unsupported type:', typ);
                }
                return rc;
            }
            return sub(obj,'');
        }
    
        function ref(ref, sch, s) {
            if (ref.substr(0,2) === '#/') {
                s = sch;
                ref = ref.substr(2);
            }
            var rlst = ref.split('/');
            for (var i in rlst)
                s = s[rlst[i]];
            return s;
        }

        function check(m, s, name) {
            if (m === undefined) throw('missing value: '+name);
            if (s === undefined) throw('missing schema for: '+name);
            var p, i, typ = gettype(m);
            switch (s.type) {
                case 'integer':
                case 'number':
                case 'string':
                case 'boolean':
                case 'null':
                    if (typ !== s.type) throw('type mismatch: '+typ+'!='+s.type+' at '+name);
                    break;
                case 'array':
                    if (typ !== s.type) throw('type mismatch: '+typ+'!='+s.type+' at '+name);
                    var nn = name+'[]';
                    for (i=0; i<m.length; i++)
                        check(m[i], s.items, name+'['+i+']');
                    break;
                case 'object':
                    var req = s.required;
                    if (req && req.length) {
                        for (p in req) {
                            i = req[p];
                            if (m[i] === undefined) throw('missing required value : "'+i+'" at '+name);
                        }
                    }
                    var keys = Object.keys(m);
                    if (keys.length===1 && keys[0] === '$ref')
                        m = ref(m[keys[0]], sch, s);
                    for (i in m) {
                        if (!m.hasOwnProperty(i)) continue;
                        var spi = s.properties[i];
                        if (!spi) throw('object property not in schema: "'+i+'" at '+name);
                        sub(m[i], spi, name+'.'+i);
                    }
                    break;
                    
                default:
                    throw('unsupported schema type '+s.type);
            }
        }
        if (schm === null || arguments.length==1)
            return gen(obj);
        check(obj, schm, '#');
    },

    guid: function() { // Return unique UUID
        if (!jsi._state.uuid)
            jsi._state.uuid = Date.now();
        return '_uuid'+(jsi._state.uuid++).toString(16);
    },
    ajax:function(url, opts) { // Ajax





























        function none(){};
        if (typeof url !== 'string') {
            opts = url;
            url = opts.url;
        }
        if (!url) return false;
    
        opts = jsi.setOpts(opts, {
            success:    none,
            error:      none,
            complete:   none,
            type:       'GET',
            dataType:   'text', // One of: json, jsonp, script, text.
            data:       {},   // Query data.
            headers:    {},
................................................................................
    
            window[opts.data.callback = jsi.guid()] = function(data) {
                opts.success.call(req, data, null, req);
            };
    
            req.onload = req.onerror = function(e) {
                if (e && e.type === "error")
                    opts.error.call(jsi, req);
                opts.complete.call(jsi, req);
                req.remove();
            };
    
            qd = '?';
            for (var key in opts.data)
                qd += encodeURIComponent(key) + '=' + encodeURIComponent(opts.data[key]) +'&';
    
................................................................................
        }
        default:
            throw('dataType not one of: json, jsonp, script, text');
        }
        return req;
    },
    













    include: function(fns) { // Dyanmic file include, uses ajax when jsish is not the webserver.
        if (typeof fns === 'string')
            fns = [fns];
        for (var i in fns) {
            var fn = fns[i];
            if (window.jsiWebSocket)
                return addScript(fn);
            jsi._state.srcList.push(fn);
            jsi._state.srcCnt++;
            jsi.ajax(fn, {





                success: function(str, txt, req) {
                    jsi._state.srcCnt--;
                    LogDebug('Src Success: '+str);
                    var hdrs = req.getAllResponseHeaders();
                    if (!hdrs || hdrs.indexOf('jsiWebSocket')<0) {
                        str = SigConvert(str);
                        LogDebug('Src xlate: '+str);
                    }
                    addScript(str, true);
                    //eval.call({}, str);
                },
                error:function(str) {
                    jsi._state.srcCnt--;
                    LogWarn('Src Error: '+str);
                }
            });






        }












    },

    htmladd: function(str) { // Insert html into page.
        var f=document.createElement('div');
        f.innerHTML = str;
        var h = document.querySelector("body");
        h.appendChild(f);
    },
    websock:function(prot) { // Create websocket connection.

        var url = document.URL.replace(/^http/,'ws');
        return new WebSocket(url, (prot?prot:'ws'));


    },
    
    onload: function(f) { // Set user function to invoke when page/files loaded.

        if (typeof f !== 'function') {
            throw('onload expects a function');
        }
        jsi._config.onload = f;
    },
    _main: function(f) { // Startup.
        if (jsi._state.srcCnt>0) {
            LogDebug("waiting for src");

            setTimeout("$jsi._main()", 1000);
            return;
        }


        var mode = location.search.match(/^\?jsi.mode=(.+)$/);
        if (mode && mode[1])
            jsi.conf({mode:mode[1]});
    


        if (jsi._config.onload)
            jsi._config.onload();
    }

};


if (window) {
    window['$jsi'] = jsi;
    window['$jsig']= jsi.$jsig;
    
    if (typeof window['puts'] === 'undefined')
        window['puts'] = console.log.bind(console);    
    if (typeof window['$'] === 'undefined')
        window['$'] = document.querySelectorAll.bind(document);
    if (typeof window['$$'] === 'undefined')
        window['$$'] = document.querySelector.bind(document);
    
    if (document.readyState !== 'loading')

        jsi._main();
    else
        document.addEventListener("DOMContentLoaded", function () {jsi._main();}, false); 
}

}());

Name change from md/jsig.md to md/jsi.js.md.

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

















jsig
====

[jsig.js](file/lib/web/jsig.js) is a javascript package provides in-browser runtime type checking of called functions,
without **jsish**.

Here is an example:

    <script src="/jsi/web/jsig.js"></script>
    <script>
       
        function RunTest() {
            foo();
            bar('abc',0);
            bar(9,0);
            bar('9','0');
            bar(9);
            bar(9,0,0);
        }
        
        $jsi.include(['bar.jsi']);
        $jsi.onload(RunTest);
        
    </script>
    Type checking with <b>jsig</b>

And here is **bar.jsi**:

    function bar(s:string, n:number):any {
       puts("BAR: "+s);
    }
    function foo(a:number, ...) {
    }
    
    bar(1,2);

==>

    jsig.js:94 type mismatch for arg 1 "s" expected "string" got "number" 1: calling bar(s:string, n:number):any
    bar.jsi:2 BAR: 1
    jsig.js:94 missing arguments: expected 1 got 0: calling foo(a:number, ...)
    bar.jsi:2 BAR: abc
    jsig.js:94 type mismatch for arg 1 "s" expected "string" got "number" 9: calling bar(s:string, n:number):any
    bar.jsi:2 BAR: 9
    jsig.js:94 type mismatch for arg 2 "n" expected "number" got "string" 0: calling bar(s:string, n:number):any
    bar.jsi:2 BAR: 9
    jsig.js:94 missing arguments: expected 2 got 1: calling bar(s:string, n:number):any
    bar.jsi:2 BAR: 9
    jsig.js:94 extra arguments: expected 2 got 3: calling bar(s:string, n:number):any
    bar.jsi:2 BAR: 9
    

















|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
jsijs
====
<div id="sectmenu"></div>
[jsi.js](/file/lib/web/jsi.js)
contains browser side support for the following features:

Typed Functions
----
**jsi.js** supports [typed function](Types.md) like **bark.jsi**:

    function bark(s:string, n:number) {
       puts("BARK: "+s);
    }

used like so:

    <script src="/jsi/web/jsi.js"></script>
    <script>
        $jsi.include('bark.jsi');
        $jsi.onload(function() {
            bark();
            bark('abc',0);
            bark(9,0);
            bark('9','0');
            bark(9);
            bark(9,0,0);
        });
        
    </script>

outputting to the console:

    BARK: abc
    jsi.js:23 type mismatch for arg 1 "s" expected "string" got "number" 9: calling bark(s:string, n:number)
    BARK: 9
    jsi.js:23 type mismatch for arg 2 "n" expected "number" got "string" 0: calling bark(s:string, n:number)
    BARK: 9
    jsi.js:23 missing arguments: expected 2 got 1: calling bark(s:string, n:number)
    BARK: 9
    jsi.js:23 extra arguments: expected 2 got 3: calling bark(s:string, n:number)
    BARK: 9

Manual
----

    <script src="/jsi/web/jsi.js"></script>
    <script>
    function bark(s, n) { $jsig('bark(s:string, n:number)');
       puts("BARK: "+s);
    }
    </script>

Jsish Server
----

When using **jsish** web server, **.jsi** is handled automatically.
Here is an example:

    <script src="/web/jsi.js"></script>
    <script src="big.js"></script>
    
with **big.jsi**:

    function big(s:string, n:number) {
       console.log("BIG: "+s+n);
    }    
    big(1,2);

Changes to src/jsiCmds.c.

652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669

670
671



672
673
674
675
676
677
678
....
4510
4511
4512
4513
4514
4515
4516

4517
4518
4519
4520

4521
4522
4523
4524
4525
4526
4527
        Jsi_DecrRefCount(interp, fpath);
    if (rc == JSI_ERROR && needErr)
        Jsi_LogError("within require('%s') in file: %s", name, Jsi_DSValue(&dStr));
    Jsi_DSFree(&dStr);
    return rc;
}

// Load package from pkgDirs or executable path.  Note. ver is unused.
static Jsi_RC jsi_PkgLoad(Jsi_Interp *interp, const char *name, Jsi_Number ver) {
    Jsi_RC rc;
    const char *cp = NULL, *path;
    int len;
    uint i;
    Jsi_Value *fval = NULL;
    Jsi_Value *pval = interp->pkgDirs;
    if (pval) {
        Jsi_Obj* obj = pval->d.obj;
        

        for (i=0; i<obj->arrCnt; i++) {
            rc = jsi_PkgLoadOne(interp, name, Jsi_ValueString(interp, obj->arr[i], NULL), -1, &fval, ver);



            if (rc != JSI_CONTINUE)
                goto done;
        }
    }
    // Check executable dir.
    path = jsiIntData.execName;
    if (path)
................................................................................
        }
    }
    return rc;
}

static Jsi_CmdSpec consoleCmds[] = {
    { "assert", jsi_AssertCmd,      1,  3, "expr:boolean|number|function, msg:string=void, options:object=void",  .help="Same as System.assert()", .retType=(uint)JSI_TT_VOID, .flags=0, .info=0, .opts=AssertOptions},

    { "input",  consoleInputCmd,    0,  0, "", .help="Read input from the console", .retType=(uint)JSI_TT_STRING|JSI_TT_VOID },
    { "log",    consoleLogCmd,      1, -1, "val, ...", .help="Same as System.puts, but goes to stderr and includes file:line", .retType=(uint)JSI_TT_VOID, .flags=0 },
    { "printf", consolePrintfCmd,   1, -1, "format:string, ...", .help="Same as System.printf but goes to stderr", .retType=(uint)JSI_TT_VOID, .flags=0 },
    { "puts",   consolePutsCmd,     1, -1, "val, ...", .help="Same as System.puts, but goes to stderr", .retType=(uint)JSI_TT_VOID, .flags=0 },

    { NULL, 0,0,0,0,  .help="Console input and output to stderr" }
};

#ifndef JSI_OMIT_EVENT
static Jsi_CmdSpec eventCmds[] = {
    { "clearInterval",clearIntervalCmd, 1,  1, "id:number", .help="Delete an event (created with setInterval/setTimeout)", .retType=(uint)JSI_TT_VOID },
    { "info",       eventInfoCmd,       1,  1, "id:number", .help="Return info for the given event id", .retType=(uint)JSI_TT_OBJECT },







|




|




<
>
|
|
>
>
>







 







>




>







652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668

669
670
671
672
673
674
675
676
677
678
679
680
681
....
4513
4514
4515
4516
4517
4518
4519
4520
4521
4522
4523
4524
4525
4526
4527
4528
4529
4530
4531
4532
        Jsi_DecrRefCount(interp, fpath);
    if (rc == JSI_ERROR && needErr)
        Jsi_LogError("within require('%s') in file: %s", name, Jsi_DSValue(&dStr));
    Jsi_DSFree(&dStr);
    return rc;
}

// Load package from pkgDirs or executable path.  Note. ver currently unused.
static Jsi_RC jsi_PkgLoad(Jsi_Interp *interp, const char *name, Jsi_Number ver) {
    Jsi_RC rc;
    const char *cp = NULL, *path;
    int len;
    uint i = 0;
    Jsi_Value *fval = NULL;
    Jsi_Value *pval = interp->pkgDirs;
    if (pval) {
        Jsi_Obj* obj = pval->d.obj;

        bool isJsish = !Jsi_Strcmp(name, "Jsish");
        for (; i<obj->arrCnt; i++) {
            const char *pnam = Jsi_ValueString(interp, obj->arr[i], NULL);
            if (isJsish && interp->selfZvfs && Jsi_Strncmp(pnam, JSI_ZVFS_DIR, sizeof(JSI_ZVFS_DIR)-1))
                continue;
            rc = jsi_PkgLoadOne(interp, name, pnam, -1, &fval, ver);
            if (rc != JSI_CONTINUE)
                goto done;
        }
    }
    // Check executable dir.
    path = jsiIntData.execName;
    if (path)
................................................................................
        }
    }
    return rc;
}

static Jsi_CmdSpec consoleCmds[] = {
    { "assert", jsi_AssertCmd,      1,  3, "expr:boolean|number|function, msg:string=void, options:object=void",  .help="Same as System.assert()", .retType=(uint)JSI_TT_VOID, .flags=0, .info=0, .opts=AssertOptions},
    { "error",  consoleLogCmd,      1, -1, "val, ...", .help="Same as log", .retType=(uint)JSI_TT_VOID, .flags=0 },
    { "input",  consoleInputCmd,    0,  0, "", .help="Read input from the console", .retType=(uint)JSI_TT_STRING|JSI_TT_VOID },
    { "log",    consoleLogCmd,      1, -1, "val, ...", .help="Same as System.puts, but goes to stderr and includes file:line", .retType=(uint)JSI_TT_VOID, .flags=0 },
    { "printf", consolePrintfCmd,   1, -1, "format:string, ...", .help="Same as System.printf but goes to stderr", .retType=(uint)JSI_TT_VOID, .flags=0 },
    { "puts",   consolePutsCmd,     1, -1, "val, ...", .help="Same as System.puts, but goes to stderr", .retType=(uint)JSI_TT_VOID, .flags=0 },
    { "warn",   consoleLogCmd,      1, -1, "val, ...", .help="Same as log", .retType=(uint)JSI_TT_VOID, .flags=0 },
    { NULL, 0,0,0,0,  .help="Console input and output to stderr" }
};

#ifndef JSI_OMIT_EVENT
static Jsi_CmdSpec eventCmds[] = {
    { "clearInterval",clearIntervalCmd, 1,  1, "id:number", .help="Delete an event (created with setInterval/setTimeout)", .retType=(uint)JSI_TT_VOID },
    { "info",       eventInfoCmd,       1,  1, "id:number", .help="Return info for the given event id", .retType=(uint)JSI_TT_OBJECT },

Changes to src/jsiEval.c.

460
461
462
463
464
465
466


467
468
469
470
471
472
473
    Jsi_TreeEntry *hPtr;
    Jsi_TreeSearch srch;
    Jsi_Value *v;
    int m = 0;
    Jsi_DSInit(dStr);
    if (obj->isarrlist)
        obj = interp->Array_prototype->d.obj;


    for (hPtr=Jsi_TreeSearchFirst(obj->tree, &srch,  JSI_TREE_ORDER_IN, NULL); hPtr; hPtr=Jsi_TreeSearchNext(&srch)) {
        v = (Jsi_Value*)Jsi_TreeValueGet(hPtr);
        if (!v) continue;
        if ((flags&JSI_NAME_FUNCTIONS) && !Jsi_ValueIsFunction(interp,v)) {
            continue;
        }
        if ((flags&JSI_NAME_DATA) && Jsi_ValueIsFunction(interp,v)) {







>
>







460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
    Jsi_TreeEntry *hPtr;
    Jsi_TreeSearch srch;
    Jsi_Value *v;
    int m = 0;
    Jsi_DSInit(dStr);
    if (obj->isarrlist)
        obj = interp->Array_prototype->d.obj;
    else if (!obj->tree->numEntries && obj->__proto__)
        obj = obj->__proto__->d.obj;
    for (hPtr=Jsi_TreeSearchFirst(obj->tree, &srch,  JSI_TREE_ORDER_IN, NULL); hPtr; hPtr=Jsi_TreeSearchNext(&srch)) {
        v = (Jsi_Value*)Jsi_TreeValueGet(hPtr);
        if (!v) continue;
        if ((flags&JSI_NAME_FUNCTIONS) && !Jsi_ValueIsFunction(interp,v)) {
            continue;
        }
        if ((flags&JSI_NAME_DATA) && Jsi_ValueIsFunction(interp,v)) {

Changes to src/jsiWebSocket.c.

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
...
316
317
318
319
320
321
322

323
324
325
326
327
328
329
...
919
920
921
922
923
924
925





































926
927
928
929
930
931
932
....
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071

1072
1073
1074
1075
1076
1077
1078
....
1254
1255
1256
1257
1258
1259
1260

1261
1262
1263
1264
1265
1266
1267

typedef struct { /* Per server (or client) data. */
    uint sig;
    Jsi_Interp *interp;
    ws_ObjCmd *_;
    Jsi_Hash *pssTable;
    Jsi_Value *onAuth, *onCloseLast, *onClose, *onFilter, *onOpen, *onRecv,
        *onUpload, *onGet, *onUnknown,
        *rootdir, *interface, *address, *mimeTypes, *extArgs, *headers;
    bool client, noUpdate, noWebsock, noWarn, use_ssl, local, extHandlers, handlersPkg, inUpdate, noCompress, noConfig, echo;
    Jsi_Value* version;
    int idx;
    int port;
    int maxUpload;
    int maxDownload;
................................................................................
    jsi_wsStatData stats;
    char *iface;
    const char* urlPrefix, *urlRedirect;
    const char *localhostName;
    const char *clientName;
    const char *clientIP;
    const char *useridPass, *server;
    const char *realm, *includeFile, *jsiFnPattern;
    struct lws_context *instCtx;
    Jsi_Value *getRegexp, *post;
    unsigned int oldus;
    int opts;
    int hasOpts;
    int debug;
    int maxConnects;
................................................................................
    JSI_OPT(FUNC,   jsi_wsCmdObj, onCloseLast,.help="Function to call when last websock connection closes. On object delete arg is null", .flags=0, .custom=0, .data=(void*)"ws:userobj|null"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onFilter,   .help="Function to call on a new connection: return false to kill connection", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number, url:string, ishttp:boolean"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onGet,      .help="Function to call to server handle http-get", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number, url:string, query:array"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onOpen,     .help="Function to call when the websocket connection occurs", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onUnknown,  .help="Function to call to server out content when no file exists", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number, url:string, query:array"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onUpload,   .help="Function to call handle http-post", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number, filename:string, data:string, startpos:number, complete:boolean"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onRecv,     .help="Function to call when websock data recieved", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number, data:string"),

    JSI_OPT(INT,    jsi_wsCmdObj, port,       .help="Port for server to listen on (8080)", jsi_IIOF),
    JSI_OPT(STRING, jsi_wsCmdObj, post,       .help="Post string to serve", jsi_IIOF),
    JSI_OPT(STRKEY, jsi_wsCmdObj, protocol,   .help="Name of protocol (ws/wss)"),
    JSI_OPT(STRKEY, jsi_wsCmdObj, realm,      .help="Realm for basic auth (jsish)", ),
    JSI_OPT(INT,    jsi_wsCmdObj, recvBufMax, .help="Size limit of a websocket message", jsi_IIOF),
    JSI_OPT(INT,    jsi_wsCmdObj, recvBufTimeout,.help="Timeout for recv of a websock msg", jsi_IIOF),
    JSI_OPT(BOOL,   jsi_wsCmdObj, redirMax,   .help="Temporarily disable redirects when see more than this in 10 minutes"),
................................................................................
        if (*cs == '\n')
            cs++;
    }
    Jsi_DSFree(&tStr);
    return rc;

}






































// Handle http GET/POST
static int jsi_wsHttp(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, struct lws *wsi, void *user,
    struct lws_context *context, const char* inPtr, Jsi_DString *tStr, jsi_wsPss *pss)
{
    const char *ext = NULL;
    unsigned char buffer[JSI_BUFSIZ];
................................................................................
            }
        }
    }
    ext = Jsi_Strrchr(inPtr, '.');

    Jsi_Value *rdir = (pss->rootdir?pss->rootdir:cmdPtr->rootdir);
    cmdPtr->curRoot = (rdir?Jsi_ValueString(cmdPtr->interp, rdir, NULL):"./");
    char statPath[PATH_MAX];
    if (!Jsi_Strncmp(inPtr, "/jsi/", 5)) {
        // Get the path for system files, eg /zvfs or /usr/local/lib/jsi (TODO: cache)
        const char *loadFile = NULL;
        Jsi_PkgRequire(interp, "Jsish", 0);
        if (Jsi_PkgVersion(interp, "Jsish", &loadFile)>=0) {
            if (loadFile) {
                snprintf(statPath, sizeof(statPath), "%s", loadFile);
                char *lcp = Jsi_Strrchr(statPath, '/');
                if (lcp) {
                    *lcp = 0;
                    inPtr += 5;
                    cmdPtr->curRoot = statPath;
                }
            }
        }
    }

    snprintf(buf, sizeof(buf), "%s/%s", cmdPtr->curRoot, inPtr);

    if (cmdPtr->debug>1)
        fprintf(stderr, "FILE: %s in %s | %s\n", buf, cmdPtr->curRoot, Jsi_ValueString(interp, cmdPtr->rootdir, NULL));
    char extBuf[100], *cpde = Jsi_Strrchr(buf, '/');
    isJsiWeb = (cpde && cmdPtr->jsiFnPattern && Jsi_GlobMatch(cmdPtr->jsiFnPattern, cpde+1, 0));
    bool isgzip = 0;
    if (!ext || !ext[1])
        mime = "text/html";
................................................................................
        }

        if (Jsi_Strstr(buf, "favicon.ico"))
            rc = jsi_wsServeString(pss, wsi, "data:;base64,iVBORw0KGgo=", 200, NULL, "image/icon");
        else {
            const char *cp = Jsi_Strrchr(buf,'/');
            if (cp && cp[1]) {

                snprintf(statPath, sizeof(statPath), "/zvfs/lib/web%s", cp);
                Jsi_DecrRefCount(interp, fname);
                fname = Jsi_ValueNewStringDup(interp, statPath);
                Jsi_IncrRefCount(interp, fname);
                if (!Jsi_Stat(interp, fname, &jsb) && jsb.st_size>0) {
                    native = 0;
                    goto serve;







|







 







|







 







>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
|
|
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<

>







 







>







117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
...
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
...
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
....
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093










1094




1095
1096
1097
1098
1099
1100
1101
1102
1103
....
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293

typedef struct { /* Per server (or client) data. */
    uint sig;
    Jsi_Interp *interp;
    ws_ObjCmd *_;
    Jsi_Hash *pssTable;
    Jsi_Value *onAuth, *onCloseLast, *onClose, *onFilter, *onOpen, *onRecv,
        *onUpload, *onGet, *onUnknown, *pathAliases,
        *rootdir, *interface, *address, *mimeTypes, *extArgs, *headers;
    bool client, noUpdate, noWebsock, noWarn, use_ssl, local, extHandlers, handlersPkg, inUpdate, noCompress, noConfig, echo;
    Jsi_Value* version;
    int idx;
    int port;
    int maxUpload;
    int maxDownload;
................................................................................
    jsi_wsStatData stats;
    char *iface;
    const char* urlPrefix, *urlRedirect;
    const char *localhostName;
    const char *clientName;
    const char *clientIP;
    const char *useridPass, *server;
    const char *realm, *includeFile, *jsiFnPattern, *jsishPathCache;
    struct lws_context *instCtx;
    Jsi_Value *getRegexp, *post;
    unsigned int oldus;
    int opts;
    int hasOpts;
    int debug;
    int maxConnects;
................................................................................
    JSI_OPT(FUNC,   jsi_wsCmdObj, onCloseLast,.help="Function to call when last websock connection closes. On object delete arg is null", .flags=0, .custom=0, .data=(void*)"ws:userobj|null"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onFilter,   .help="Function to call on a new connection: return false to kill connection", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number, url:string, ishttp:boolean"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onGet,      .help="Function to call to server handle http-get", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number, url:string, query:array"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onOpen,     .help="Function to call when the websocket connection occurs", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onUnknown,  .help="Function to call to server out content when no file exists", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number, url:string, query:array"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onUpload,   .help="Function to call handle http-post", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number, filename:string, data:string, startpos:number, complete:boolean"),
    JSI_OPT(FUNC,   jsi_wsCmdObj, onRecv,     .help="Function to call when websock data recieved", .flags=0, .custom=0, .data=(void*)"ws:userobj, id:number, data:string"),
    JSI_OPT(OBJ,    jsi_wsCmdObj, pathAliases,.help="Path alias lookups", jsi_IIOF),
    JSI_OPT(INT,    jsi_wsCmdObj, port,       .help="Port for server to listen on (8080)", jsi_IIOF),
    JSI_OPT(STRING, jsi_wsCmdObj, post,       .help="Post string to serve", jsi_IIOF),
    JSI_OPT(STRKEY, jsi_wsCmdObj, protocol,   .help="Name of protocol (ws/wss)"),
    JSI_OPT(STRKEY, jsi_wsCmdObj, realm,      .help="Realm for basic auth (jsish)", ),
    JSI_OPT(INT,    jsi_wsCmdObj, recvBufMax, .help="Size limit of a websocket message", jsi_IIOF),
    JSI_OPT(INT,    jsi_wsCmdObj, recvBufTimeout,.help="Timeout for recv of a websock msg", jsi_IIOF),
    JSI_OPT(BOOL,   jsi_wsCmdObj, redirMax,   .help="Temporarily disable redirects when see more than this in 10 minutes"),
................................................................................
        if (*cs == '\n')
            cs++;
    }
    Jsi_DSFree(&tStr);
    return rc;

}

static void jsi_wsPathAlias(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, const char **inPtr, Jsi_DString *dStr) {
    const char *ce, *cp = NULL;
    char *lcp;
    Jsi_Value *val;
    if (!Jsi_Strncmp(*inPtr, "/jsi/", 5)) {
        // Get/cache path for system load file, eg /zvfs/lib/Jsish.jsi.
        if (!(cp = cmdPtr->jsishPathCache)) {
            Jsi_PkgRequire(interp, "Jsish", 0);
            if (Jsi_PkgVersion(interp, "Jsish", &cp)>=0)
                cmdPtr->jsishPathCache = cp;
        }
        if (cp) {
            Jsi_DSAppend(dStr, cp, NULL);
            cp = Jsi_DSValue(dStr);
            if ((lcp = Jsi_Strrchr(cp, '/'))) {
                *lcp = 0;
                *inPtr += 5;
                cmdPtr->curRoot = cp;
            }
        }
    }
    else if (cmdPtr->pathAliases) {
        cp = *inPtr;
        if (*cp == '/') cp++;
        if ((ce = Jsi_Strchr(cp, '/'))) {
            int len = ce-cp;
            Jsi_DSAppendLen(dStr, cp, len);
            cp = Jsi_DSValue(dStr);
            if ((val = Jsi_ValueObjLookup(interp, cmdPtr->pathAliases, cp, 0)) &&
                (cp = Jsi_ValueString(interp, val, NULL))) {
                *inPtr += len;
                cmdPtr->curRoot = cp;
            }
        }
    }
}

// Handle http GET/POST
static int jsi_wsHttp(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, struct lws *wsi, void *user,
    struct lws_context *context, const char* inPtr, Jsi_DString *tStr, jsi_wsPss *pss)
{
    const char *ext = NULL;
    unsigned char buffer[JSI_BUFSIZ];
................................................................................
            }
        }
    }
    ext = Jsi_Strrchr(inPtr, '.');

    Jsi_Value *rdir = (pss->rootdir?pss->rootdir:cmdPtr->rootdir);
    cmdPtr->curRoot = (rdir?Jsi_ValueString(cmdPtr->interp, rdir, NULL):"./");
    Jsi_DString sStr;
    Jsi_DSInit(&sStr);
    jsi_wsPathAlias(interp, cmdPtr, &inPtr, &sStr);















    snprintf(buf, sizeof(buf), "%s/%s", cmdPtr->curRoot, inPtr);
    Jsi_DSFree(&sStr);
    if (cmdPtr->debug>1)
        fprintf(stderr, "FILE: %s in %s | %s\n", buf, cmdPtr->curRoot, Jsi_ValueString(interp, cmdPtr->rootdir, NULL));
    char extBuf[100], *cpde = Jsi_Strrchr(buf, '/');
    isJsiWeb = (cpde && cmdPtr->jsiFnPattern && Jsi_GlobMatch(cmdPtr->jsiFnPattern, cpde+1, 0));
    bool isgzip = 0;
    if (!ext || !ext[1])
        mime = "text/html";
................................................................................
        }

        if (Jsi_Strstr(buf, "favicon.ico"))
            rc = jsi_wsServeString(pss, wsi, "data:;base64,iVBORw0KGgo=", 200, NULL, "image/icon");
        else {
            const char *cp = Jsi_Strrchr(buf,'/');
            if (cp && cp[1]) {
                char statPath[PATH_MAX];
                snprintf(statPath, sizeof(statPath), "/zvfs/lib/web%s", cp);
                Jsi_DecrRefCount(interp, fname);
                fname = Jsi_ValueNewStringDup(interp, statPath);
                Jsi_IncrRefCount(interp, fname);
                if (!Jsi_Stat(interp, fname, &jsb) && jsb.st_size>0) {
                    native = 0;
                    goto serve;

Changes to www/web.wiki.

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147


This provides runtime type-checking of function calls.

To debug, we set a breakpoint on warnings which are output
to the console.

The Jsi support functions are included from: <b>/jsi/lib/jsig.js</b>

<hr>
<h2 id=utilities>Utilities</h2>

The following resources are available to include from <b>/jsi/lib/</b>:

  *  [../lib/web/JSig.js|jsig.js]: Support code for type-checking.
  *  [https://github.com/remy/bind.js|bind.js]: Two way binding between data and elements.
  *  [http://zeptojs.com/|zepto.js]: Lite subset of JQuery.







|






|


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147


This provides runtime type-checking of function calls.

To debug, we set a breakpoint on warnings which are output
to the console.

The Jsi support functions are included from: <b>/jsi/lib/jsi.js</b>

<hr>
<h2 id=utilities>Utilities</h2>

The following resources are available to include from <b>/jsi/lib/</b>:

  *  [../lib/web/jsi.js|jsi.js]: Support code for type-checking.
  *  [https://github.com/remy/bind.js|bind.js]: Two way binding between data and elements.
  *  [http://zeptojs.com/|zepto.js]: Lite subset of JQuery.