wiki-plugin-mech 0.1.3 → 0.1.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.
- package/client/mech.js +115 -3
- package/package.json +1 -1
package/client/mech.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
(function() {
|
|
3
3
|
|
|
4
|
+
const uniq = (value, index, self) => self.indexOf(value) === index
|
|
5
|
+
|
|
4
6
|
function expand(text) {
|
|
5
7
|
return text
|
|
6
8
|
.replace(/&/g, '&')
|
|
@@ -48,6 +50,7 @@
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
function trouble(elem,message) {
|
|
53
|
+
if(elem.innerText.match(/✖︎/)) return
|
|
51
54
|
elem.innerHTML += `<button style="border-width:0;color:red;">✖︎</button>`
|
|
52
55
|
elem.querySelector('button').addEventListener('click',event => {
|
|
53
56
|
elem.outerHTML += `<span style="width:80%;color:gray;">${message}</span>` })
|
|
@@ -57,7 +60,10 @@
|
|
|
57
60
|
if(elem.innerHTML.match(/button/)) return
|
|
58
61
|
if (!body?.length) return trouble(elem,'CLICK expects indented blocks to follow.')
|
|
59
62
|
elem.innerHTML += '<button style="border-width:0;">◉</button>'
|
|
60
|
-
elem.querySelector('button').addEventListener('click',event =>
|
|
63
|
+
elem.querySelector('button').addEventListener('click',event => {
|
|
64
|
+
state.debug = event.shiftKey
|
|
65
|
+
run(body,state)
|
|
66
|
+
})
|
|
61
67
|
}
|
|
62
68
|
|
|
63
69
|
function hello_emit ({elem,args}) {
|
|
@@ -97,6 +103,7 @@
|
|
|
97
103
|
fetch(url)
|
|
98
104
|
.then (res => res.json())
|
|
99
105
|
.then (data => {
|
|
106
|
+
if(state.debug) console.log({sensor,data})
|
|
100
107
|
elem.innerHTML = line + ' ⌛'
|
|
101
108
|
const value = f(avg(Object.values(data)))
|
|
102
109
|
state.temperature = `${value.toFixed(2)}°F`
|
|
@@ -110,12 +117,73 @@
|
|
|
110
117
|
elem.innerHTML = command + `<br><font face=Arial size=32>${value}</font>`
|
|
111
118
|
}
|
|
112
119
|
|
|
120
|
+
function source_emit ({elem,command,args,body,state}) {
|
|
121
|
+
if (!(args && args.length)) return trouble(elem,`Expected Source topic, like "markers" for Map markers.`)
|
|
122
|
+
const topic = args[0]
|
|
123
|
+
const sources = requestSourceData(state.$item, topic)
|
|
124
|
+
if(!sources.length) return trouble(elem,`Expected source for "${topic}" in the lineup.`)
|
|
125
|
+
const count = type => {
|
|
126
|
+
const count = sources
|
|
127
|
+
.filter(source => [...source.div.classList].includes(type))
|
|
128
|
+
.length
|
|
129
|
+
return count ? `${count} ${type}` : null}
|
|
130
|
+
const counts = [count('map'),count('image'),count('frame')]
|
|
131
|
+
.filter(count => count)
|
|
132
|
+
.join(", ")
|
|
133
|
+
if (state.debug) console.log({topic,sources})
|
|
134
|
+
elem.innerHTML = command + ' ⇒ ' + counts
|
|
135
|
+
state[topic] = sources
|
|
136
|
+
if (body) run(body,state)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function preview_emit ({elem,command,args,state}) {
|
|
140
|
+
const round = digits => (+digits).toFixed(7)
|
|
141
|
+
const story = []
|
|
142
|
+
for (const arg of args) {
|
|
143
|
+
switch (arg) {
|
|
144
|
+
case 'map':
|
|
145
|
+
if(!('marker' in state)) return trouble(elem,`"map" preview expects "marker" state, like from "SOURCE marker".`)
|
|
146
|
+
const text = state.marker
|
|
147
|
+
.map(marker => [marker.result])
|
|
148
|
+
.flat(2)
|
|
149
|
+
.map(latlon => `${round(latlon.lat)}, ${round(latlon.lon)} ${latlon.label||''}`)
|
|
150
|
+
.filter(uniq)
|
|
151
|
+
.join("\n")
|
|
152
|
+
story.push({type:'map',text})
|
|
153
|
+
break
|
|
154
|
+
case 'graph':
|
|
155
|
+
if(!('aspect' in state)) return trouble(elem,`"graph" preview expects "aspect" state, like from "SOURCE aspect".`)
|
|
156
|
+
for (const {div,result} of state.aspect) {
|
|
157
|
+
for (const {name,graph} of result) {
|
|
158
|
+
if(state.debug) console.log({div,result,name,graph})
|
|
159
|
+
story.push({type:'paragraph',text:name})
|
|
160
|
+
story.push({type:'graphviz',text:dotify(graph)})
|
|
161
|
+
}
|
|
162
|
+
story.push({type:'pagefold',text:'.'})
|
|
163
|
+
}
|
|
164
|
+
break
|
|
165
|
+
case 'synopsis':
|
|
166
|
+
{const text = `This page has been generated by the Mech plugin. We want to tell you where. That's coming soon.`
|
|
167
|
+
story.push({type:'paragraph',text})}
|
|
168
|
+
break
|
|
169
|
+
default:
|
|
170
|
+
return trouble(elem,`"${arg}" doesn't name an item we can preview`)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const title = "Mech Preview"
|
|
174
|
+
const page = {title,story}
|
|
175
|
+
const options = {}
|
|
176
|
+
wiki.showResult(wiki.newPage(page), options)
|
|
177
|
+
}
|
|
178
|
+
|
|
113
179
|
const blocks = {
|
|
114
180
|
CLICK: {emit:click_emit, bind:null},
|
|
115
181
|
HELLO: {emit:hello_emit, bind:null},
|
|
116
182
|
FROM: {emit:from_emit, bind:null},
|
|
117
183
|
SENSOR: {emit:sensor_emit, bind:null},
|
|
118
|
-
REPORT: {emit:report_emit, bind:null}
|
|
184
|
+
REPORT: {emit:report_emit, bind:null},
|
|
185
|
+
SOURCE: {emit:source_emit, bind:null},
|
|
186
|
+
PREVIEW: {emit:preview_emit, bind:null}
|
|
119
187
|
}
|
|
120
188
|
|
|
121
189
|
function run (nest,state={}) {
|
|
@@ -129,6 +197,7 @@
|
|
|
129
197
|
const next = scope[0]
|
|
130
198
|
const body = next && ('command' in next) ? null : scope.shift()
|
|
131
199
|
const stuff = {command,op,args,body,elem,state}
|
|
200
|
+
if(state.debug) console.log(stuff)
|
|
132
201
|
if (blocks[op])
|
|
133
202
|
blocks[op].emit.apply(null,[stuff])
|
|
134
203
|
else
|
|
@@ -146,8 +215,9 @@
|
|
|
146
215
|
const lines = item.text.split(/\n/)
|
|
147
216
|
const nest = tree(lines,[],0)
|
|
148
217
|
const html = format(nest)
|
|
218
|
+
const state = {$item}
|
|
149
219
|
$item.append(`<div style="background-color:#eee;padding:15px;border-top:8px;">${html}</div>`)
|
|
150
|
-
run(nest)
|
|
220
|
+
run(nest,state)
|
|
151
221
|
}
|
|
152
222
|
|
|
153
223
|
function bind($item, item) {
|
|
@@ -164,4 +234,46 @@
|
|
|
164
234
|
module.exports = {expand}
|
|
165
235
|
}
|
|
166
236
|
|
|
237
|
+
|
|
238
|
+
// library functions
|
|
239
|
+
|
|
240
|
+
// adapted from wiki-plugin-frame/client/frame.js
|
|
241
|
+
function requestSourceData($item, topic) {
|
|
242
|
+
let sources = []
|
|
243
|
+
for (let div of document.querySelectorAll(`.item`)) {
|
|
244
|
+
if (div.classList.contains(`${topic}-source`)) {
|
|
245
|
+
sources.unshift(div)
|
|
246
|
+
}
|
|
247
|
+
if (div === $item.get(0)) {
|
|
248
|
+
break
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return sources.map(div => {
|
|
253
|
+
let getData = div[`${topic}Data`]
|
|
254
|
+
let result = getData ? getData() : null
|
|
255
|
+
return {div,result}
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// adapted from super-collaborator/dotify.js
|
|
260
|
+
function dotify(graph) {
|
|
261
|
+
const tip = props => Object.entries(props).filter(e => e[1]).map(e => `${e[0]}: ${e[1]}`).join("\\n")
|
|
262
|
+
const nodes = graph.nodes.map((node,id) => {
|
|
263
|
+
const label = `${node.type}\\n${node.props.name}`
|
|
264
|
+
return `${id} [label="${label}" ${(node.props.url||node.props.tick)?`URL="${node.props.url||'#'}" target="_blank"`:''} tooltip="${tip(node.props)}"]`
|
|
265
|
+
})
|
|
266
|
+
const edges = graph.rels.map(rel => {
|
|
267
|
+
return `${rel.from}->${rel.to} [label="${rel.type}" labeltooltip="${tip(rel.props)}"]`
|
|
268
|
+
})
|
|
269
|
+
return [
|
|
270
|
+
'digraph {',
|
|
271
|
+
'rankdir=LR',
|
|
272
|
+
'node [shape=box style=filled fillcolor=palegreen]',
|
|
273
|
+
...nodes,
|
|
274
|
+
...edges,
|
|
275
|
+
'}'].join("\n")
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
|
|
167
279
|
}).call(this)
|