wiki-plugin-mech 0.1.12 → 0.1.14

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 (2) hide show
  1. package/client/mech.js +112 -32
  2. package/package.json +1 -1
package/client/mech.js CHANGED
@@ -83,7 +83,7 @@
83
83
  }
84
84
  }
85
85
 
86
- function run (nest,state={},mock) {
86
+ async function run (nest,state={},mock) {
87
87
  const scope = nest.slice()
88
88
  while (scope.length) {
89
89
  const code = scope.shift()
@@ -96,14 +96,15 @@
96
96
  const stuff = {command,op,args,body,elem,state}
97
97
  if(state.debug) console.log(stuff)
98
98
  if (blocks[op])
99
- blocks[op].emit.apply(null,[stuff])
99
+ await blocks[op].emit.apply(null,[stuff])
100
100
  else
101
101
  if (op.match(/^[A-Z]+$/))
102
102
  trouble(elem,`${op} doesn't name a block we know.`)
103
103
  else if (code.command.match(/\S/))
104
104
  trouble(elem, `Expected line to begin with all-caps keyword.`)
105
105
  } else if(typeof code == 'array') {
106
- run(code,state)
106
+ console.warn(`this can't happen.`)
107
+ run(code,state) // when does this even happen?
107
108
  }
108
109
  }
109
110
  }
@@ -113,7 +114,7 @@
113
114
 
114
115
  function click_emit ({elem,body,state}) {
115
116
  if(elem.innerHTML.match(/button/)) return
116
- if (!body?.length) return trouble(elem,'CLICK expects indented blocks to follow.')
117
+ if (!body?.length) return trouble(elem,`CLICK expects indented blocks to follow.`)
117
118
  elem.innerHTML += '<button style="border-width:0;">▶</button>'
118
119
  elem.querySelector('button').addEventListener('click',event => {
119
120
  state.debug = event.shiftKey
@@ -121,15 +122,18 @@
121
122
  })
122
123
  }
123
124
 
124
- function hello_emit ({elem,args}) {
125
- const want = args[0] == 'world' ? ' 🌎' : ' 😀'
126
- elem.innerHTML += want
125
+ function hello_emit ({elem,args,state}) {
126
+ const world = args[0] == 'world' ? ' 🌎' : ' 😀'
127
+ for (const key of Object.keys(state))
128
+ inspect(elem,key,state)
129
+ elem.innerHTML += world
127
130
  }
128
131
 
129
132
  function from_emit ({elem,args,body,state}) {
130
133
  const line = elem.innerHTML
134
+ const url = args[0]
131
135
  elem.innerHTML = line + ' ⏳'
132
- fetch(`//${args[0]}.json`)
136
+ fetch(`//${url}.json`)
133
137
  .then(res => res.json())
134
138
  .then(page => {
135
139
  state.page = page
@@ -144,13 +148,13 @@
144
148
  inspect(elem,'page',state)
145
149
  const datalog = state.page.story.find(item => item.type == 'datalog')
146
150
  if(!datalog) return trouble(elem, `Expect Datalog plugin in the page.`)
147
- const want = args[0]
148
- if(!want) return trouble(elem, 'SENSOR needs a sensor name.')
151
+ const device = args[0]
152
+ if(!device) return trouble(elem, `SENSOR needs a sensor name.`)
149
153
  const sensor = datalog.text.split(/\n/)
150
154
  .map(line => line.split(/ +/))
151
155
  .filter(fields => fields[0] == 'SENSOR')
152
- .find(fields => fields[1] == want)
153
- if(!sensor) return trouble(elem, `Expect to find "${want}" in Datalog.`)
156
+ .find(fields => fields[1] == device)
157
+ if(!sensor) return trouble(elem, `Expect to find "${device}" in Datalog.`)
154
158
  const url = sensor[2]
155
159
 
156
160
  const f = c => 9/5*(c/16)+32
@@ -190,6 +194,10 @@
190
194
  .join(", ")
191
195
  if (state.debug) console.log({topic,sources})
192
196
  elem.innerHTML = command + ' ⇒ ' + counts
197
+ // state.assets = ?
198
+ // state.aspect = ?
199
+ // state.region = ?
200
+ // state.marker = ?
193
201
  state[topic] = sources.map(({div,result}) => ({id:div.dataset.id, result}))
194
202
  if (body) run(body,state)
195
203
  }
@@ -197,8 +205,9 @@
197
205
  function preview_emit ({elem,command,args,state}) {
198
206
  const round = digits => (+digits).toFixed(7)
199
207
  const story = []
200
- for (const arg of args) {
201
- switch (arg) {
208
+ const types = args
209
+ for (const type of types) {
210
+ switch (type) {
202
211
  case 'map':
203
212
  if(!('marker' in state)) return trouble(elem,`"map" preview expects "marker" state, like from "SOURCE marker".`)
204
213
  inspect(elem,'marker',state)
@@ -232,7 +241,7 @@
232
241
  story.push({type:'paragraph',text})}
233
242
  break
234
243
  default:
235
- return trouble(elem,`"${arg}" doesn't name an item we can preview`)
244
+ return trouble(elem,`"${type}" doesn't name an item we can preview`)
236
245
  }
237
246
  }
238
247
  const title = "Mech Preview" + (state.tick ? ` ${state.tick}` : '')
@@ -262,7 +271,7 @@
262
271
  elem.innerHTML = command
263
272
  const nodes = aspects.map(({graph}) => graph.nodes).flat()
264
273
  elem.innerHTML += ` ⇒ ${aspects.length} aspects, ${nodes.length} nodes`
265
- if(steps.find(({graph}) => !graph)) trouble(elem,"WALK skipped sites with no links in sitemaps")
274
+ if(steps.find(({graph}) => !graph)) trouble(elem,`WALK skipped sites with no links in sitemaps`)
266
275
  const item = elem.closest('.item')
267
276
  item.classList.add('aspect-source')
268
277
  item.aspectData = () => aspects
@@ -271,9 +280,9 @@
271
280
 
272
281
  function tick_emit ({elem,args,body,state}) {
273
282
  if(elem.innerHTML.match(/button/)) return
274
- if (!body?.length) return trouble(elem,'TICK expects indented blocks to follow.')
283
+ if (!body?.length) return trouble(elem,`TICK expects indented blocks to follow.`)
275
284
  const count = args[0] || '1'
276
- if (!count.match(/^[1-9][0-9]?$/)) return trouble(elem,"TICK expects a count from 1 to 99")
285
+ if (!count.match(/^[1-9][0-9]?$/)) return trouble(elem,`TICK expects a count from 1 to 99`)
277
286
  let clock = null
278
287
  elem.innerHTML += '<button style="border-width:0;">▶</button>'
279
288
  elem.querySelector('button').addEventListener('click',event => {
@@ -317,37 +326,77 @@
317
326
  function forward_emit ({elem,command,args,state}) {
318
327
  if(args.length < 1) return trouble(elem,`FORWARD expects an argument, the number of steps to move a "turtle".`)
319
328
  if(!('turtle' in state)) state.turtle = new Turtle(elem)
320
- const position = state.turtle.forward(+args[0])
329
+ const steps = args[0]
330
+ const position = state.turtle.forward(+steps)
321
331
  elem.innerHTML = command + ` ⇒ ${position.map(n => (n-200).toFixed(1)).join(', ')}`
322
332
  }
323
333
 
324
334
  function turn_emit ({elem,command,args,state}) {
325
335
  if(args.length < 1) return trouble(elem,`TURN expects an argument, the number of degrees to turn a "turtle".`)
326
336
  if(!('turtle' in state)) state.turtle = new Turtle(elem)
327
- const direction = state.turtle.turn(+args[0])
337
+ const degrees = args[0]
338
+ const direction = state.turtle.turn(+degrees)
328
339
  elem.innerHTML = command + ` ⇒ ${direction}°`
329
340
  }
330
341
 
331
342
  function file_emit ({elem,command,args,body,state}) {
332
343
  if(!('assets' in state)) return trouble(elem,`FILE expects state.assets, like from SOURCE assets.`)
333
344
  inspect(elem,'assets',state)
345
+
346
+ // [ { "id": "b2d5831168b4706b", "result":
347
+ // { "pages/testing-file-mech":
348
+ // { "//ward.dojo.fed.wiki/assets":
349
+ // [ "KWIC-list+axe-files.txt", "KWIC-list-axe-files.tsv" ] } } } ]
350
+
351
+ const origin = '//'+window.location.host
352
+ const assets = state.assets.map(({id,result}) =>
353
+ Object.entries(result).map(([dir,paths]) =>
354
+ Object.entries(paths).map(([path,files]) =>
355
+ files.map(file => {
356
+ const assets = path.startsWith("//") ? path : `${origin}${path}`
357
+ const host = assets.replace(/\/assets$/,'')
358
+ const url = `${assets}/${dir}/${file}`
359
+ return {id,dir,path,host,file,url}
360
+ })))).flat(3)
361
+ if(state.debug) console.log({assets})
362
+
334
363
  if(args.length < 1) return trouble(elem,`FILE expects an argument, the dot suffix for desired files.`)
335
364
  if (!body?.length) return trouble(elem,'FILE expects indented blocks to follow.')
336
365
  const suffix = args[0]
337
- const url = 'http://ward.dojo.fed.wiki/assets/pages/testing-file-mech/KWIC-list-axe-files.tsv'
338
- fetch(url)
339
- .then(res => res.text())
340
- .then(text => {
341
- elem.innerHTML = command + ` ${text.length} bytes`
342
- state.tsv = text
343
- run(body,state)
344
- })
366
+ const choices = assets.filter(asset => asset.file.endsWith(suffix))
367
+ const flag = choice => `<img width=12 src=${choices[choice].host+'/favicon.png'}>`
368
+ if(!choices) return trouble(elem,`FILE expects to find an asset with "${suffix}" suffix.`)
369
+ elem.innerHTML = command +
370
+ `<br><div class=choices style="border:1px solid black; background-color:#f8f8f8; padding:8px;" >${choices
371
+ .map((choice,i) =>
372
+ `<span data-choice=${i} style="cursor:pointer;">
373
+ ${flag(i)}
374
+ ${choice.file} ▶
375
+ </span>`)
376
+ .join("<br>\n")
377
+ }</div>`
378
+ elem.querySelector('.choices').addEventListener('click',event => {
379
+ if (!('choice' in event.target.dataset)) return
380
+ const url = choices[event.target.dataset.choice].url
381
+ // console.log(event.target)
382
+ // console.log(event.target.dataset.file)
383
+ // const url = 'http://ward.dojo.fed.wiki/assets/pages/testing-file-mech/KWIC-list-axe-files.tsv'
384
+ fetch(url)
385
+ .then(res => res.text())
386
+ .then(text => {
387
+ elem.innerHTML = command + ` ⇒ ${text.length} bytes`
388
+ state.tsv = text
389
+ console.log({text})
390
+ run(body,state)
391
+ })
392
+ })
345
393
  }
346
394
 
347
395
  function kwic_emit ({elem,command,args,body,state}) {
348
396
  const template = body && body[0]?.command
349
397
  if(template && !template.match(/\$[KW]/)) return trouble(elem,`KWIK expects $K or $W in link prototype.`)
350
398
  if(!('tsv' in state)) return trouble(elem,`KWIC expects a .tsv file, like from ASSETS .tsv.`)
399
+ inspect(elem,'tsv',state)
351
400
  const prefix = args[0] || 1
352
401
  const lines = state.tsv.trim().split(/\n/)
353
402
 
@@ -397,9 +446,10 @@
397
446
  return trouble(elem,`SHOW expects a slug or site/slug to open in the lineup.`)
398
447
  }
399
448
  } else {
400
- [site,slug] = args[0].includes('/')
401
- ? args[0].split(/\//)
402
- : [null,args[0]]
449
+ const info = args[0]
450
+ [site,slug] = info.includes('/')
451
+ ? info.split(/\//)
452
+ : [null,info]
403
453
  }
404
454
  const lineup = [...document.querySelectorAll('.page')].map(e => e.id)
405
455
  if(lineup.includes(slug)) return trouble(elem,`SHOW expects a page not already in the lineup.`)
@@ -417,6 +467,34 @@
417
467
  state.info = infos[one]
418
468
  }
419
469
 
470
+ function sleep_emit({elem,command,args,body,state}) {
471
+ let count = args[0] || '1'
472
+ if (!count.match(/^[1-9][0-9]?$/)) return trouble(elem,`SLEEP expects seconds from 1 to 99`)
473
+ return new Promise(resolve => {
474
+ if(body)
475
+ run(body,state)
476
+ .then(result => if(state.debug) console.log(command,'children', result))
477
+ elem.innerHTML = command + ` ⇒ ${count} remain`
478
+ let clock = setInterval(()=> {
479
+ if(--count > 0)
480
+ elem.innerHTML = command + ` ⇒ ${count} remain`
481
+ else {
482
+ clearInterval(clock)
483
+ elem.innerHTML = command + ` ⇒ done`
484
+ if(state.debug) console.log(command, 'done')
485
+ resolve()
486
+ }
487
+ }, 1000)
488
+ })
489
+ }
490
+
491
+ function together_emit({elem,command,args,body,state}) {
492
+ if (!body) return trouble(elem,`TOGETHER expects indented commands to run together.`)
493
+ const children = body
494
+ .map(child => run([child],state))
495
+ return Promise.all(children)
496
+ }
497
+
420
498
 
421
499
  // C A T A L O G
422
500
 
@@ -437,7 +515,9 @@
437
515
  FILE: {emit:file_emit},
438
516
  KWIC: {emit:kwic_emit},
439
517
  SHOW: {emit:show_emit},
440
- RANDOM: {emit:random_emit}
518
+ RANDOM: {emit:random_emit},
519
+ SLEEP: {emit:sleep_emit},
520
+ TOGETHER:{emit:together_emit}
441
521
  }
442
522
 
443
523
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiki-plugin-mech",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Federated Wiki - Mechanism Scripting Plugin",
5
5
  "keywords": [
6
6
  "mech",