@naanlang/naan 1.0.2 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +10 -8
  3. package/bin/index.js +2 -2
  4. package/dist/env_web.js +172 -22
  5. package/dist/naan.min.js +6 -6
  6. package/frameworks/browser/https_request.nlg +120 -0
  7. package/frameworks/browser/sworker.js +299 -91
  8. package/frameworks/browser/terminals.nlg +28 -13
  9. package/frameworks/browser/workers.nlg +27 -12
  10. package/frameworks/client/apiclient.nlg +17 -4
  11. package/frameworks/client/psm_client.nlg +184 -81
  12. package/frameworks/common/common.nlg +105 -25
  13. package/frameworks/common/preferences.nlg +1 -0
  14. package/frameworks/common/watching.nlg +1 -0
  15. package/frameworks/node/apiserver.nlg +31 -15
  16. package/frameworks/node/filesystem.nlg +230 -46
  17. package/frameworks/node/gitter.nlg +27 -6
  18. package/frameworks/node/https_request.nlg +119 -0
  19. package/frameworks/node/node.nlg +1 -0
  20. package/frameworks/node/psm_server.nlg +113 -37
  21. package/frameworks/project/build.nlg +40 -23
  22. package/frameworks/project/proj_cliser.nlg +11 -3
  23. package/frameworks/project/proj_console.nlg +13 -4
  24. package/frameworks/project/proj_folder.nlg +10 -1
  25. package/frameworks/project/proj_lambda.nlg +13 -4
  26. package/frameworks/project/proj_static.nlg +11 -3
  27. package/frameworks/project/projects.nlg +128 -40
  28. package/frameworks/running/debugnub.nlg +2 -5
  29. package/frameworks/running/executors.nlg +1 -0
  30. package/frameworks/storage/csv.nlg +120 -0
  31. package/frameworks/storage/dbt_pouch.nlg +41 -25
  32. package/frameworks/storage/psm.nlg +108 -28
  33. package/frameworks/storage/psm_dbtables.nlg +7 -3
  34. package/frameworks/storage/resources.nlg +30 -2
  35. package/lib/browser/env_web.js +170 -20
  36. package/lib/browser/env_webworker.js +1 -1
  37. package/lib/browser/require.js +1 -1
  38. package/lib/core/naanlib.js +6 -6
  39. package/package.json +2 -1
  40. package/plugins/serviceAws/aws/aws-sdk-node.min.js +2 -0
  41. package/plugins/serviceAws/aws/aws-sdk.min.js +2 -88
  42. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +8 -7
  43. package/plugins/serviceAws/aws_dynamo.nlg +715 -148
  44. package/plugins/serviceAws/aws_dynextra.nlg +294 -0
  45. package/plugins/serviceAws/aws_lambda.nlg +11 -4
  46. package/plugins/serviceAws/aws_s3.nlg +44 -14
  47. package/plugins/serviceAws/dbt_aws.nlg +58 -45
  48. package/plugins/serviceAws/psm_aws.nlg +61 -40
  49. package/plugins/serviceAws/serviceAws.nlg +7 -4
  50. package/plugins/serviceGitHub/psm_github.nlg +41 -25
  51. package/plugins/serviceGitLab/psm_gitlab.nlg +41 -25
  52. package/test/harness.nlg +3 -1
  53. package/test/test_01_core.nlg +5 -5
  54. package/test/test_02_context.nlg +4 -4
  55. package/test/test_03_jsinterop.nlg +9 -3
  56. package/plugins/serviceAws/aws_cognito.nlg +0 -76
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2019-2021 by Richard C. Zulch
9
+ * Copyright (c) 2019-2022 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -75,7 +75,7 @@ closure MakePSMutil(fs, local util) {
75
75
  } (tree, inpath.split(tree.data.pathsep).reverse())
76
76
  if !treedir
77
77
  return (callback(Error("treeDirUpdate: can't find directory within tree", inpath)))
78
- `(error, files) = fs.dirList(dirpath, { head: true })
78
+ `(error, files) = fs.dirList(dirpath, { stat: true })
79
79
  if error
80
80
  return (callback(error))
81
81
  //
@@ -238,46 +238,126 @@ closure MakePSMutil(fs, local util) {
238
238
  //
239
239
  // Perform a deep copy of an array of sources from the current filesystem and specified inpath to
240
240
  // the outfs filesystem and specified outpath. The input path is used to find the source files,
241
- // however it is not added to the output. The result is a standard error tuple, where the errors
242
- // are an array if any occur.
241
+ // however it is not added to the output. If the source array is false then the file or directory
242
+ // at the input path is copied to the destination. The result is a standard error tuple, where
243
+ // the errors are an array if any occur.
244
+ //
245
+ // Supported options are:
246
+ // {
247
+ // overwrite: <boolean> -- overwrite existing files
248
+ // erase: <boolean> -- remove extraneous files in destination folders
249
+ // force: <boolean> -- copy files even if older (default is only copy if new/newer)
250
+ // follow: <boolean> -- follow links instead of treating them as files
251
+ // preserve: <boolean> -- preserve file info where possible
252
+ // (e.g. mtime/atime/mode/uid/gid on NodeJS)
253
+ // }
254
+ //
255
+ // Limitations:
256
+ // - this does not copy info about source folders to destination folders
257
+ // (it would need to make destination folder writable, then update after files copied)
243
258
 
244
- util.deepcopy = closure deepcopy(inpath, sources, outfs, outpath, options, callback, local errors, copies) {
259
+ util.deepcopy = closure deepcopy(inpath, sources, outfs, outpath, options, callback,
260
+ local errors, copies, fsopt, folders) {
245
261
  if !callback
246
262
  return (syncAdapter(deepcopy, inpath, sources, outfs, outpath, options))
247
- if !array(sources) || !string(inpath) || !string(outpath)
263
+ if sources && !array(sources) || !string(inpath) || !string(outpath)
248
264
  return (callback(Error("deepcopy: invalid arguments")))
265
+ if options.follow
266
+ fsopt = { follow: true }
249
267
  errors = []
250
268
  copies = []
251
- sources = new(sources) // don't modify the original!
252
- asyncArray(sources, 10, closure mklist(entry, index, elements, local srcpath, destpath, error, folder, node) {
269
+ folders = { }
270
+ if sources
271
+ sources = new(sources) // don't modify the original!
272
+ else {
273
+ sources = [fs.path.basename(inpath)] // copy inpath itself
274
+ inpath = fs.path.dirname(inpath)
275
+ }
276
+ //
277
+ // Pass 1 - Get info on all source files, and make destination directories
278
+ //
279
+ asyncArray(sources, 10, closure mklist(entry, index, elements,
280
+ local srcpath, destpath, epath, error, stat, folder, node) {
253
281
  srcpath = fs.path.join(inpath, entry)
254
- `(error, folder) = fs.dirList(srcpath)
255
- if error && error.code != "ENOTDIR"
256
- return (errors.push(error))
257
- destpath = outfs.path.join(outpath, entry) // "." if both empty strings
258
- if !error {
259
- if destpath != "."
260
- `(error, node) = outfs.mkdir(destpath) // may already exist but we don't care
261
- if error
262
- return (errors.push(error)) // don't do children if we can't form destination
263
- for node in folder.children
264
- sources.push(fs.path.join(entry, node.name)) // omit inpath from sources
265
- }
266
- else
267
- copies.push({ // copy the file
282
+ destpath = outfs.path.join(outpath, entry)
283
+ if destpath == "."
284
+ destpath = "" // joined empty strings
285
+ `(error, stat) = fs.info(srcpath, fsopt)
286
+ if stat.type != "directory" && (!options.follow || stat.type != "symlink") {
287
+ copies.push({ // copy file or symlink
268
288
  source: srcpath
269
289
  dest: destpath
270
290
  })
291
+ return (list(false, { ok: true })) // generic success not used
292
+ }
293
+ `(error, folder) = fs.dirList(srcpath)
294
+ if error
295
+ return (errors.push(error))
296
+ if destpath == ""
297
+ epath = ""
298
+ else
299
+ epath = outfs.path.join(destpath, outfs.path.sep) // canonical name
300
+ folders[epath] = folder // sources for this folder
301
+ `(error, node) = outfs.mkdir(destpath) // may already exist but we don't care
302
+ if error
303
+ return (errors.push(error)) // don't do children if we can't form destination
304
+ for node in folder.children
305
+ sources.push(fs.path.join(entry, node.name)) // add more sources, without inpath
271
306
  }).wait()
307
+ //
308
+ // Pass 2 - erase extraneous files and folders from destination
309
+ //
310
+ if options.erase {
311
+ asyncArray(keys(folders).toarray, 10, closure eraser(destpath, index, elements
312
+ local error, outfolder, folder, namesin, removes, delopt) {
313
+ `(error, outfolder) = outfs.dirList(destpath) // existing files in our destination
314
+ if error
315
+ return
316
+ folder = folders[destpath] // our source folders, if any
317
+ namesin = { }
318
+ for node in folder.children // make list of top-level dest names
319
+ namesin[node.name] = true
320
+ removes = []
321
+ for node in outfolder.children
322
+ if !namesin[node.name] {
323
+ if node.info.type == "directory"
324
+ delopt = { recursive: true } // deleting a directory
325
+ else
326
+ delopt = false // deleting a file
327
+ removes.push(list(outfs.path.join(destpath, node.name), delopt))
328
+ }
329
+ asyncArray(removes, 10, closure cleardest(delitem, local error, data) {
330
+ `(error, data) = outfs.delete(delitem.0, delitem.1)
331
+ if error
332
+ errors.push(Error("deepcopy: can't remove", error))
333
+ }).wait()
334
+ }).wait()
335
+ }
336
+ //
337
+ // Pass 3 - copy files and links
338
+ //
272
339
  asyncArray(copies, 10, closure cpfiles(entry, index, elements, local error, data, instat, outstat) {
273
- `(error, instat) = fs.info(entry.source)
340
+ `(error, instat) = fs.info(entry.source, fsopt)
274
341
  if !error {
275
342
  `(error, outstat) = outfs.info(entry.dest)
276
- if !error && (instat.md5 && instat.md5 == outstat.md5 || instat.mtimeMs <= outstat.mtimeMs)
277
- return // file unchanged
278
- `(error, data) = fs.readFile(entry.source, { encoding: "binary"})
279
- if !error
280
- `(error, data) = outfs.writeFile(entry.dest, data, { encoding: "binary"})
343
+ if !error &&
344
+ (instat.md5 && instat.md5 == outstat.md5
345
+ || !options.force && instat.mtimeMs <= outstat.mtimeMs)
346
+ return // file "unchanged"
347
+ if !error && !options.overwrite
348
+ error = Error("deepcopy: can't overwrite", entry.dest)
349
+ else if instat.type == "symlink" && !options.follow {
350
+ `(error, data) = fs.readlink(entry.source)
351
+ if !error
352
+ `(error, data) = outfs.symlink(data, entry.dest)
353
+ }
354
+ else {
355
+ `(error, data) = fs.readFile(entry.source, { encoding: "binary"})
356
+ if !error
357
+ `(error, data) = outfs.writeFile(entry.dest, data, { encoding: "binary"})
358
+ if !error && options.preserve
359
+ `(error) = outfs.setInfo(entry.dest, instat)
360
+ }
281
361
  }
282
362
  if error
283
363
  errors.push(error)
@@ -287,7 +287,7 @@ closure FSview(table, rootpath, local view, error) {
287
287
  })))
288
288
  if buildpath.slice(-1) != "/"
289
289
  buildpath = buildpath.concat("/")
290
- `(error, data) = view.table.get(buildpath) // get desired directory
290
+ `(error, data) = view.table.head(buildpath) // get desired directory
291
291
  if error.status == 404
292
292
  `(error, data) = dbWriteRecord(buildpath) // create desired directory
293
293
  if error
@@ -684,8 +684,12 @@ closure FSview(table, rootpath, local view, error) {
684
684
  if !finfo
685
685
  finfo = { }
686
686
  name = item.key.slice(offset)
687
- if name.slice(-1) == "/"
687
+ if name.slice(-1) == "/" {
688
688
  name = name.slice(0,-1) // remove trailing "/" from directory
689
+ if !finfo.type
690
+ finfo.type = "directory"
691
+ } else if !finfo.type
692
+ finfo.type = "file" // everything is a file in UNIX
689
693
  if item.doc._md5
690
694
  finfo.md5 = item.doc._md5
691
695
  if item.doc._length
@@ -703,7 +707,7 @@ closure FSview(table, rootpath, local view, error) {
703
707
  // Read a directory at the specified path
704
708
 
705
709
  view.readir = closure readir(path, callback, local name, dirdict) {
706
- dirList(path, { head: true }, callback)
710
+ dirList(path, { stat: true }, callback)
707
711
  }
708
712
 
709
713
  // tree
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * column positioning: // // !
13
13
  *
14
- * Copyright (c) 2019-2021 by Richard C. Zulch
14
+ * Copyright (c) 2019-2022 by Richard C. Zulch
15
15
  *
16
16
  */
17
17
 
@@ -38,6 +38,21 @@ closure rsrcVault(db, tablename, local vault) {
38
38
  vault.table.add(resID, data)
39
39
  }
40
40
 
41
+ // updateResource
42
+ //
43
+ // Update credentials for accessing a resource, returning the added credential record.
44
+
45
+ vault.updateResource = function updateResource(resID, data, local error, existing) {
46
+ `(error, existing) = vault.table.get(resID)
47
+ if error
48
+ data = false
49
+ else {
50
+ existing.content = data
51
+ `(error, existing) = vault.table.update(existing)
52
+ }
53
+ list(error, data)
54
+ }
55
+
41
56
  // deleteResource
42
57
  //
43
58
  // Delete resource credentials.
@@ -111,8 +126,9 @@ closure rsrcVault(db, tablename, local vault) {
111
126
  * conn.uifields() - entry fields for add this kind of resource in UI
112
127
  * conn.findResource(resource) - find a resource by content, returning resID or false
113
128
  * conn.addResource(resource) - add the resource dict and return `(error, resID)
114
- * conn.info(resID) - return `(error, infoDictionary) tuple
129
+ * conn.updateResource(resID, resource) - update a resource
115
130
  * conn.deleteResource(resID) - delete the specified resource, returning a tuple
131
+ * conn.info(resID) - return `(error, infoDictionary) tuple
116
132
  * conn.connect(resID, serviceID, args) - connect to resource and return object
117
133
  *
118
134
  */
@@ -309,6 +325,18 @@ closure ResourceTracker(reman, local retor, watch) {
309
325
  reman.conns[classID].addResource(resource)
310
326
  }
311
327
 
328
+ // updateResource
329
+ //
330
+ // Update credentials for accessing a resource, returning the added credential record.
331
+
332
+ retor.updateResource = function updateResource(resID, resource, local error, classI) {
333
+ `(error, classID) = resourceClassID(resID)
334
+ if error
335
+ list(error)
336
+ else
337
+ reman.conns[classID].updateResource(resID, resource)
338
+ }
339
+
312
340
  // getResource
313
341
  //
314
342
  // Get a resource for the resID as a standard result tuple.
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * column positioning: // // !
8
8
  *
9
- * Copyright (c) 2017-2021 by Richard C. Zulch
9
+ * Copyright (c) 2017-2022 by Richard C. Zulch
10
10
  *
11
11
  */
12
12
 
@@ -74,15 +74,19 @@ exports.NaanControllerWeb = function() {
74
74
  //
75
75
 
76
76
  var naanlib = new exports.Naanlib();
77
+ var dontSaveUntilWorking; // true when state loaded, must be reset to save state again
77
78
  var contSelf = this; // this is us, for access within nested functions
78
79
  exports.naancon = this; // access to this instance
79
80
  contSelf.anaan = naanlib.js.n;
80
81
  naanlib.js.t = contSelf;
82
+ naanlib.js.q = querystrings;
81
83
  var replstate; // latest known state of the REPL
82
84
  var naanstate; // desired state for the interpreter
83
85
  var replqueue; // text destined for terminal when opened
84
86
  var termlist = []; // list of terminals currently attached
87
+ contSelf.termlist = termlist;
85
88
  var prefs = { };
89
+ var vsiteName;
86
90
 
87
91
  //
88
92
  // termTextOut
@@ -149,8 +153,15 @@ exports.NaanControllerWeb = function() {
149
153
  //
150
154
 
151
155
  this.StateGet = function StateGet() {
152
- if (termlist.length > 0)
153
- replstate = termlist[0].StateSave(); // restore current buffer state to new terminal
156
+ termlist.every(function(term) {
157
+ var state;
158
+ if (term.StateSave && (state = term.StateSave()))
159
+ { // update the state if we can
160
+ replstate = state;
161
+ return (false);
162
+ }
163
+ return (true); // keep searching
164
+ });
154
165
  return (replstate);
155
166
  };
156
167
 
@@ -204,14 +215,14 @@ exports.NaanControllerWeb = function() {
204
215
  if (term !== terminal)
205
216
  term.OnMessage({ // send text to other terminals
206
217
  id: "textout",
207
- text: text + "\r\n"
218
+ text: "\x1b[90m\x1b[3m" + text + "\x1b[0m" + "\r\n"
208
219
  });
209
220
  });
210
221
  window.setTimeout(function () {
211
- naanlib.textLine(text)
222
+ naanlib.textLine(text);
212
223
  }, 1);
213
224
  };
214
-
225
+
215
226
  this.ReplyMessage = function ReplyMessage(data) { // "remote" -> "local"
216
227
  termlist.forEach(function(term) {
217
228
  if (term.OnMessageOut)
@@ -229,9 +240,9 @@ exports.NaanControllerWeb = function() {
229
240
  this.DispatchMessage = function DispatchMessage(data) { // "local" -> "remote"
230
241
  if (nideListener)
231
242
  setTimeout(function() {
232
- nideListener(data)
243
+ nideListener(data);
233
244
  }, 1);
234
- }
245
+ };
235
246
 
236
247
  this.ReplyDebugger = function ReplyDebugger(data) { // "remote" -> "local"
237
248
  termlist.forEach(function(term) {
@@ -250,9 +261,9 @@ exports.NaanControllerWeb = function() {
250
261
  this.DispatchDebugger = function DispatchDebugger(data) { // "local" -> "remote"
251
262
  if (debugListener)
252
263
  setTimeout(function() {
253
- debugListener(data)
264
+ debugListener(data);
254
265
  }, 1);
255
- }
266
+ };
256
267
 
257
268
  //
258
269
  // Status
@@ -290,7 +301,95 @@ exports.NaanControllerWeb = function() {
290
301
  this.Dimensions = function Dimensions(rows, cols) {
291
302
  console.log("Naan terminal dimensions now (" + rows + ", " + cols + ")");
292
303
  };
304
+
305
+ //
306
+ // VsiteRefresh
307
+ //
308
+ // Check if our underlying data has changed, and reload if needed.
309
+ //
310
+
311
+ this.VsiteRefresh = function VsiteRefresh(url_origin) {
312
+ if (window.location.href.startsWith(url_origin))
313
+ window.location.reload(); // we have changed underneath
314
+ };
315
+
316
+
317
+ //==========================================================================
318
+ // Clone Debug Interface
319
+ //--------------------------------------------------------------------------
320
+
321
+ function CloneDebugTerminal()
322
+ {
323
+ var termSelf = this;
324
+ //
325
+ // listen for messages
326
+ //
327
+ window.addEventListener("message", function (event) {
328
+ if (event.origin !== window.origin)
329
+ return; // only talk with our peers
330
+ var msg = event.data;
331
+ if (msg.id == "interrupt")
332
+ contSelf.Interrupt();
333
+ else if (msg.id == "keyline")
334
+ contSelf.Return(msg.text, termSelf); // keyboard text from terminal
335
+ else if (msg.id == "start")
336
+ contSelf.Attach(term); // opener is ready
337
+ else if (msg.id == "import")
338
+ { // for workers
339
+ }
340
+ else if (msg.id == "targetin")
341
+ {
342
+ if (nideListener)
343
+ nideListener(msg.data); // target received a message
344
+ }
345
+ else if (msg.id == "debugin")
346
+ {
347
+ if (debugListener)
348
+ debugListener(msg.data); // target received a message
349
+ }
350
+ });
351
+
352
+ //
353
+ // report messages back to opener
354
+ //
355
+ termSelf.OnMessage = function OnMessage(msg) {
356
+ window.opener.postMessage(msg, window.origin);
357
+ return (msg);
358
+ };
359
+
360
+ //
361
+ // report messages back to opener
362
+ //
363
+ termSelf.OnMessageOut = function OnMessageOut(data) {
364
+ var msg = { // target is sending debug data
365
+ id: "targetout",
366
+ data: data
367
+ };
368
+ return (termSelf.OnMessage(msg));
369
+ };
293
370
 
371
+ //
372
+ // report debug messages back to opener
373
+ //
374
+ termSelf.OnDebugOut = function OnDebugOut(data) {
375
+ var msg = { // target is sending debug data
376
+ id: "debugout",
377
+ data: data
378
+ };
379
+ return (termSelf.OnMessage(msg));
380
+ };
381
+
382
+ //
383
+ // report status back to opener
384
+ //
385
+ termSelf.OnStatus = function OnStatus(status) {
386
+ var msg = { // target is sending debug data
387
+ id: "status",
388
+ status: status
389
+ };
390
+ return (termSelf.OnMessage(msg));
391
+ };
392
+ }
294
393
 
295
394
  //==========================================================================
296
395
  // Host Environment
@@ -299,7 +398,7 @@ exports.NaanControllerWeb = function() {
299
398
  //
300
399
  // SavePref
301
400
  //
302
- // Save a persistent preference objectc that can be retrieved in the future.
401
+ // Save a persistent preference object that can be retrieved in the future.
303
402
  //
304
403
 
305
404
  this.SavePref = function SavePref(key, value) {
@@ -315,6 +414,16 @@ exports.NaanControllerWeb = function() {
315
414
  this.LoadPref = function LoadPref(key) {
316
415
  return (prefs[key]);
317
416
  };
417
+
418
+ //
419
+ // Working
420
+ //
421
+ // The application is working, so it is safe to save state.
422
+ //
423
+
424
+ this.Working = function Working() {
425
+ dontSaveUntilWorking = false;
426
+ };
318
427
 
319
428
  var saveEvent = new Event("NaanSave");
320
429
 
@@ -333,6 +442,7 @@ exports.NaanControllerWeb = function() {
333
442
  window.addEventListener("unload", function (e) {
334
443
  window.dispatchEvent(saveEvent);
335
444
  saveLocal();
445
+ virtualClose();
336
446
  });
337
447
 
338
448
  window.addEventListener("beforeunload", function (e) {
@@ -345,14 +455,14 @@ exports.NaanControllerWeb = function() {
345
455
  statedoc.curversion = kStateCurrentVersion;
346
456
  statedoc.firstver = kStateFirstVersion;
347
457
  statedoc.licensee = "MIT-License";
348
- statedoc.verstring = "1.0.2-1";
458
+ statedoc.verstring = "1.0.5-1";
349
459
  statedoc.date = new Date().toISOString();
350
460
  statedoc.prefs = prefs;
351
- statedoc.naan = naanlib.saveState(true); // true to optimize, which is a bit slower
461
+ statedoc.naan = naanlib.saveState(false); // true to optimize, which is a bit slower
352
462
  termlist.forEach(function(term) {
353
463
  if (!replstate || term.termwin)
354
464
  replstate = term.StateSave(); // state with separate window, otherwise any state
355
- })
465
+ });
356
466
  if (replstate)
357
467
  statedoc.replstate = replstate;
358
468
  return (statedoc);
@@ -363,8 +473,11 @@ exports.NaanControllerWeb = function() {
363
473
  || statedoc.firstver > kStateCurrentVersion
364
474
  || statedoc.curversion < kStateFirstVersion
365
475
  || statedoc.licensee != "MIT-License"
366
- || statedoc.verstring != "1.0.2-1")
476
+ || statedoc.verstring != "1.0.5-1")
477
+ {
478
+ localStorage.removeItem("NaanState_Hosted");
367
479
  return (false);
480
+ }
368
481
  naanstate = statedoc.naan;
369
482
  replstate = statedoc.replstate; // get terminal state, if any
370
483
  if (typeof(statedoc.prefs) == "object")
@@ -374,6 +487,10 @@ exports.NaanControllerWeb = function() {
374
487
 
375
488
  function saveLocal() {
376
489
  /*jshint sub:true */
490
+ if (dontSaveUntilWorking) { // saved state is problematic
491
+ localStorage.removeItem("NaanState_Hosted");
492
+ return (false);
493
+ }
377
494
  if (querystrings["nosave"])
378
495
  return (false);
379
496
  var statedoc = makeState();
@@ -381,8 +498,8 @@ exports.NaanControllerWeb = function() {
381
498
  try {
382
499
  if ("localStorage" in window && window["localStorage"] !== null)
383
500
  {
384
- localStorage.removeItem("NaanState_Nide");
385
- if (localStorage.setItem("NaanState_Nide", statestr) !== undefined)
501
+ localStorage.removeItem("NaanState_Hosted");
502
+ if (localStorage.setItem("NaanState_Hosted", statestr) !== undefined)
386
503
  return (true);
387
504
  }
388
505
  } catch(e) {
@@ -396,7 +513,8 @@ exports.NaanControllerWeb = function() {
396
513
  /*jshint sub:true */
397
514
  if ("localStorage" in window && window["localStorage"] !== null)
398
515
  {
399
- var statestr = localStorage.getItem("NaanState_Nide");
516
+ var statestr = localStorage.getItem("NaanState_Hosted");
517
+ dontSaveUntilWorking = true;
400
518
  if (statestr)
401
519
  return (loadState(JSON.parse(statestr)));
402
520
  }
@@ -405,7 +523,30 @@ exports.NaanControllerWeb = function() {
405
523
  }
406
524
  return (false);
407
525
  }
526
+
527
+ function virtualOpen() {
528
+ vsiteName = window.document.title; // consistent name at open and close
529
+ if (window.opener && window.opener.Naanlang)
530
+ window.opener.Naanlang.naancon.DispatchMessage({
531
+ op: "VsiteOpen",
532
+ name: vsiteName,
533
+ naancont: contSelf,
534
+ title: vsiteName + " 1.0.5-1"
535
+ });
536
+ }
408
537
 
538
+ function virtualClose() {
539
+ if (window.opener && window.opener.Naanlang) {
540
+ window.opener.Naanlang.naancon.DispatchMessage({
541
+ op: "VsiteClose",
542
+ name: vsiteName,
543
+ naancont: contSelf,
544
+ title: vsiteName + " 1.0.5-1"
545
+ });
546
+ termTextOut("\x1b[90m\x1b[3m".concat("\nwindow closed", "\x1b[0m\n"));
547
+ }
548
+ }
549
+
409
550
  /*jshint sub:true */
410
551
  if (querystrings["restart"] || !loadLocal())
411
552
  { // don't load state or can't load state
@@ -419,8 +560,17 @@ exports.NaanControllerWeb = function() {
419
560
  naanlib.banner();
420
561
  naanlib.textLine("print();;\n");
421
562
  }
422
- naanlib.start({
423
- state: naanstate
563
+ if (!naanlib.start({ state: naanstate })) { // state loading failed
564
+ localStorage.removeItem("NaanState_Hosted");
565
+ window.location.reload();
566
+ }
567
+ }
568
+
569
+ if (window.opener) {
570
+ virtualOpen();
571
+ var term = new CloneDebugTerminal();
572
+ term.OnMessage({
573
+ id: "loaded"
424
574
  });
425
575
  }
426
576
  };
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  importScripts(
20
- // remove "_omit" for debugging
20
+
21
21
  "lib/core/naanlib.js"
22
22
  );
23
23
 
@@ -1183,7 +1183,7 @@ exports.require = function require(modpath) {
1183
1183
  resolve: function resolve() {
1184
1184
  var resolvedPath = '';
1185
1185
  var resolvedAbsolute = false;
1186
- var cwd;
1186
+ var cwd = ""; // more or less the current dir
1187
1187
 
1188
1188
  for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
1189
1189
  var path;