wiki-plugin-mech 0.1.25 → 0.1.27
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 +203 -44
- package/package.json +1 -1
package/client/mech.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
(function() {
|
|
3
3
|
"use strict"
|
|
4
4
|
const uniq = (value, index, self) => self.indexOf(value) === index
|
|
5
|
+
const delay = time => new Promise(res => setTimeout(res,time))
|
|
5
6
|
|
|
6
7
|
function expand(text) {
|
|
7
8
|
return text
|
|
@@ -274,8 +275,9 @@
|
|
|
274
275
|
function walk_emit ({elem,command,args,state}) {
|
|
275
276
|
if(!('neighborhood' in state)) return trouble(elem,`WALK expects state.neighborhood, like from NEIGHBORS.`)
|
|
276
277
|
inspect(elem,'neighborhood',state)
|
|
277
|
-
const way =
|
|
278
|
-
|
|
278
|
+
const [,count,way] = command.match(/\b(\d+)? *(steps|days|weeks|months|hubs)\b/) || []
|
|
279
|
+
if(!way && command != 'WALK') return trouble(elem, `WALK can't understand rest of this block.`)
|
|
280
|
+
const steps = walks(count,way,state.neighborhood)
|
|
279
281
|
const aspects = steps.filter(({graph})=>graph)
|
|
280
282
|
if(state.debug) console.log({steps})
|
|
281
283
|
elem.innerHTML = command
|
|
@@ -283,10 +285,16 @@
|
|
|
283
285
|
elem.innerHTML += ` ⇒ ${aspects.length} aspects, ${nodes.length} nodes`
|
|
284
286
|
if(steps.find(({graph}) => !graph)) trouble(elem,`WALK skipped sites with no links in sitemaps`)
|
|
285
287
|
const item = elem.closest('.item')
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
288
|
+
if (aspects.length) {
|
|
289
|
+
state.aspect = state.aspect || []
|
|
290
|
+
const obj = state.aspect.find(obj => obj.id == elem.id)
|
|
291
|
+
if(obj) obj.result = aspects
|
|
292
|
+
else state.aspect.push({id:elem.id, result:aspects, source:command})
|
|
293
|
+
item.classList.add('aspect-source')
|
|
294
|
+
item.aspectData = () => state.aspect.map(obj => obj.result).flat()
|
|
295
|
+
if(state.debug) console.log({command,state:state.aspect,item:item.aspectData()})
|
|
296
|
+
|
|
297
|
+
}
|
|
290
298
|
}
|
|
291
299
|
|
|
292
300
|
function tick_emit ({elem,command,args,body,state}) {
|
|
@@ -668,6 +676,43 @@
|
|
|
668
676
|
elem.innerHTML = command + ` ⇒ sent`
|
|
669
677
|
}
|
|
670
678
|
|
|
679
|
+
async function solo_emit({elem,command,state}) {
|
|
680
|
+
if(!('aspect' in state)) return trouble(elem,`"SOLO" expects "aspect" state, like from "WALK".`)
|
|
681
|
+
inspect(elem,'aspect',state)
|
|
682
|
+
elem.innerHTML = command
|
|
683
|
+
const todo = state.aspect.map(each => ({
|
|
684
|
+
source:each.source || each.id,
|
|
685
|
+
aspects:each.result
|
|
686
|
+
}))
|
|
687
|
+
const aspects = todo.reduce((sum,each) => sum+each.aspects.length, 0)
|
|
688
|
+
elem.innerHTML += ` ⇒ ${todo.length} sources, ${aspects} aspects`
|
|
689
|
+
|
|
690
|
+
// from Solo plugin, client/solo.js
|
|
691
|
+
const pageKey = elem.closest('.page').dataset.key
|
|
692
|
+
const doing = {type:'batch', sources:todo, pageKey}
|
|
693
|
+
console.log({pageKey,doing})
|
|
694
|
+
|
|
695
|
+
if (typeof window.soloListener == "undefined" || window.soloListener == null) {
|
|
696
|
+
console.log('**** Adding solo listener')
|
|
697
|
+
window.soloListener = soloListener
|
|
698
|
+
window.addEventListener("message", soloListener)
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
await delay(750)
|
|
702
|
+
const popup = window.open('/plugins/solo/dialog/#','solo','popup,height=720,width=1280')
|
|
703
|
+
if (popup.location.pathname != '/plugins/solo/dialog/'){
|
|
704
|
+
console.log('launching new dialog')
|
|
705
|
+
popup.addEventListener('load', event => {
|
|
706
|
+
console.log('launched and loaded')
|
|
707
|
+
popup.postMessage(doing, window.origin)
|
|
708
|
+
})
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
console.log('reusing existing dialog')
|
|
712
|
+
popup.postMessage(doing, window.origin)
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
671
716
|
|
|
672
717
|
// C A T A L O G
|
|
673
718
|
|
|
@@ -696,7 +741,8 @@
|
|
|
696
741
|
ROSTER: {emit:roster_emit},
|
|
697
742
|
LINEUP: {emit:lineup_emit},
|
|
698
743
|
LISTEN: {emit:listen_emit},
|
|
699
|
-
MESSAGE: {emit:message_emit}
|
|
744
|
+
MESSAGE: {emit:message_emit},
|
|
745
|
+
SOLO: {emit:solo_emit}
|
|
700
746
|
}
|
|
701
747
|
|
|
702
748
|
|
|
@@ -779,21 +825,25 @@
|
|
|
779
825
|
}
|
|
780
826
|
|
|
781
827
|
// inspired by aspects-of-recent-changes/roster-graphs.html
|
|
782
|
-
function walks(way,neighborhood) {
|
|
828
|
+
function walks(count,way='steps',neighborhood) {
|
|
829
|
+
const find = slug => neighborhood.find(info => info.slug == slug)
|
|
783
830
|
const prob = n => Math.floor(n * Math.abs(Math.random()-Math.random()))
|
|
784
831
|
const rand = a => a[prob(a.length)]
|
|
832
|
+
const good = info => info.links && Object.keys(info.links).length < 10
|
|
833
|
+
const back = slug => neighborhood.filter(info => good(info) && slug in info.links)
|
|
834
|
+
const newr = infos => infos.toSorted((a,b)=>b.date-a.date).slice(0,3)
|
|
785
835
|
const domains = neighborhood
|
|
786
836
|
.map(info => info.domain)
|
|
787
837
|
.filter(uniq)
|
|
788
838
|
switch(way) {
|
|
789
|
-
case 'steps': return steps()
|
|
790
|
-
case 'days': return periods(1)
|
|
791
|
-
case 'weeks': return periods(7)
|
|
792
|
-
case 'months': return periods(30)
|
|
793
|
-
case 'hubs': return hubs()
|
|
839
|
+
case 'steps': return steps(count)
|
|
840
|
+
case 'days': return periods(way,1,count)
|
|
841
|
+
case 'weeks': return periods(way,7,count)
|
|
842
|
+
case 'months': return periods(way,30,count)
|
|
843
|
+
case 'hubs': return hubs(count)
|
|
794
844
|
}
|
|
795
845
|
|
|
796
|
-
function steps() {
|
|
846
|
+
function steps(count=5) {
|
|
797
847
|
return domains.map(domain => {
|
|
798
848
|
const name = domain.split('.').slice(0,3).join('.')
|
|
799
849
|
const done = new Set()
|
|
@@ -802,7 +852,6 @@
|
|
|
802
852
|
const here = neighborhood
|
|
803
853
|
.filter(info => info.domain==domain && ('links' in info))
|
|
804
854
|
if(!here.length) return {name,graph:null}
|
|
805
|
-
const find = slug => neighborhood.find(info => info.slug == slug)
|
|
806
855
|
const node = info => {
|
|
807
856
|
nid = graph.addNode('',{
|
|
808
857
|
name:info.title.replaceAll(/ /g,"\n"),
|
|
@@ -829,58 +878,74 @@
|
|
|
829
878
|
})
|
|
830
879
|
}
|
|
831
880
|
|
|
832
|
-
function periods(days) {
|
|
881
|
+
function periods(way,days,count=12) {
|
|
833
882
|
const interval = days*24*60*60*1000
|
|
834
|
-
const iota = [
|
|
883
|
+
const iota = [...Array(Number(count)).keys()]
|
|
835
884
|
const dates = iota.map(n => Date.now()-n*interval)
|
|
836
885
|
const aspects = []
|
|
837
886
|
for(const stop of dates) {
|
|
838
887
|
const start = stop-interval
|
|
839
|
-
const name = new Date(
|
|
840
|
-
// const done = new Set()
|
|
841
|
-
const graph = new Graph()
|
|
842
|
-
const find = slug => neighborhood.find(info => info.slug == slug)
|
|
843
|
-
const node = info => {
|
|
844
|
-
return graph.addUniqNode('',{
|
|
845
|
-
name:info.title.replaceAll(/ /g,"\n"),
|
|
846
|
-
title:info.title,
|
|
847
|
-
site:info.domain
|
|
848
|
-
})
|
|
849
|
-
}
|
|
888
|
+
const name = `${way.replace(/s$/,'')} ${new Date(start).toLocaleDateString()}`
|
|
850
889
|
const here = neighborhood
|
|
851
890
|
.filter(info => info.date < stop && info.date >= start)
|
|
852
891
|
.filter(info => !(info.links && Object.keys(info.links).length > 5))
|
|
853
892
|
if(here.length) {
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
893
|
+
const domains = here.reduce((set,info) => {set.add(info.domain); return set}, new Set())
|
|
894
|
+
for (const domain of domains) {
|
|
895
|
+
const graph = new Graph()
|
|
896
|
+
const node = info => {
|
|
897
|
+
return graph.addUniqNode('',{
|
|
898
|
+
name:info.title.replaceAll(/ /g,"\n"),
|
|
899
|
+
title:info.title,
|
|
900
|
+
site:info.domain,
|
|
901
|
+
date:info.date
|
|
902
|
+
})
|
|
903
|
+
}
|
|
904
|
+
const author = domain.split(/\.|\:/)[0]
|
|
905
|
+
for (const info of here.filter(info => info.domain == domain)) {
|
|
906
|
+
const nid = node(info)
|
|
907
|
+
for (const link in (info.links||{})) {
|
|
908
|
+
const linked = find(link)
|
|
909
|
+
if(linked)
|
|
910
|
+
graph.addRel('',nid,node(linked))
|
|
911
|
+
}
|
|
860
912
|
}
|
|
913
|
+
aspects.push({name:`${name} ${author}`,graph})
|
|
861
914
|
}
|
|
862
|
-
aspects.push({name,graph})
|
|
863
915
|
}
|
|
864
916
|
}
|
|
865
917
|
return aspects
|
|
866
918
|
}
|
|
867
919
|
|
|
868
|
-
function hubs() {
|
|
920
|
+
function hubs(count=12) {
|
|
869
921
|
const aspects = []
|
|
922
|
+
const ignored = new Set()
|
|
870
923
|
const hits = {}
|
|
871
924
|
for (const info of neighborhood)
|
|
872
925
|
if(info.links)
|
|
873
|
-
|
|
874
|
-
|
|
926
|
+
if(Object.keys(info.links).length <= 15) {
|
|
927
|
+
for(const link in info.links)
|
|
928
|
+
if(find(link))
|
|
929
|
+
hits[link] = (hits[link]||0) + 1
|
|
930
|
+
} else {
|
|
931
|
+
ignored.add(info.slug)
|
|
932
|
+
}
|
|
933
|
+
if(ignored.size > 0)
|
|
934
|
+
console.log('hub links ignored for large pages:',[...ignored])
|
|
875
935
|
const hubs = Object.entries(hits)
|
|
876
936
|
.sort((a,b) => b[1]-a[1])
|
|
877
|
-
.slice(0,
|
|
937
|
+
.slice(0,count)
|
|
878
938
|
console.log({hits,hubs})
|
|
879
939
|
|
|
940
|
+
// hub[0] => slug
|
|
941
|
+
// find(slug) => info
|
|
942
|
+
// node(info) => nid
|
|
943
|
+
// back(slug) => infos
|
|
944
|
+
// newr(infos) => infos
|
|
945
|
+
|
|
880
946
|
for(const hub of hubs) {
|
|
881
|
-
const name =
|
|
947
|
+
const name = `hub ${hub[1]} ${hub[0]}`
|
|
882
948
|
const graph = new Graph()
|
|
883
|
-
const find = slug => neighborhood.find(info => info.slug == slug)
|
|
884
949
|
const node = info => {
|
|
885
950
|
return graph.addUniqNode('',{
|
|
886
951
|
name:info.title.replaceAll(/ /g,"\n"),
|
|
@@ -888,18 +953,68 @@
|
|
|
888
953
|
site:info.domain
|
|
889
954
|
})
|
|
890
955
|
}
|
|
956
|
+
// hub
|
|
891
957
|
const info = find(hub[0])
|
|
892
958
|
const nid = node(info)
|
|
959
|
+
|
|
960
|
+
// parents of hub
|
|
961
|
+
for(const parent of newr(back(info.slug))) {
|
|
962
|
+
graph.addRel('',node(parent),nid)
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// hub children
|
|
893
966
|
for(const link in (info.links||{})) {
|
|
894
|
-
const
|
|
895
|
-
if(
|
|
896
|
-
|
|
967
|
+
const child = find(link)
|
|
968
|
+
if(child) {
|
|
969
|
+
const cid = node(child)
|
|
970
|
+
graph.addRel('',nid,cid)
|
|
971
|
+
|
|
972
|
+
// parents of children
|
|
973
|
+
for(const parent of newr(back(child.slug))) {
|
|
974
|
+
graph.addRel('',node(parent),cid)
|
|
975
|
+
}
|
|
976
|
+
}
|
|
897
977
|
}
|
|
978
|
+
|
|
979
|
+
|
|
898
980
|
aspects.push({name,graph})
|
|
899
981
|
}
|
|
900
982
|
return aspects
|
|
901
983
|
}
|
|
902
984
|
|
|
985
|
+
|
|
986
|
+
/*
|
|
987
|
+
|
|
988
|
+
for (const name of newest(Object.keys(info.links||{}))){
|
|
989
|
+
const kid = node(name)
|
|
990
|
+
graph.addRel('',nid,kid)
|
|
991
|
+
|
|
992
|
+
const parents = neighborhood.find(info => info.slug==name)?.cites || []
|
|
993
|
+
for (const parent of newest(parents))
|
|
994
|
+
graph.addRel('',node(parent),kid)
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
for (const name of newest(info.cites))
|
|
998
|
+
graph.addRel('',node(name),nid)
|
|
999
|
+
return graph
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function newest(slugs) {
|
|
1003
|
+
const recent = slug => neighborhood
|
|
1004
|
+
.filter(info => info.slug == slug)
|
|
1005
|
+
return slugs
|
|
1006
|
+
.map(slug => [slug,recent(slug)])
|
|
1007
|
+
.filter(pair => pair[1].length)
|
|
1008
|
+
.map(pair => [pair[0],pair[1].sort((a,b) => b.date - a.date)[0]])
|
|
1009
|
+
.sort((a,b) => b[1].date - a[1].date)
|
|
1010
|
+
.map(pair => pair[0])
|
|
1011
|
+
.slice(0,3)
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
*/
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
|
|
903
1018
|
}
|
|
904
1019
|
|
|
905
1020
|
|
|
@@ -1077,6 +1192,50 @@
|
|
|
1077
1192
|
page.journal.push(action);
|
|
1078
1193
|
}
|
|
1079
1194
|
|
|
1195
|
+
|
|
1196
|
+
// adapted from Solo client
|
|
1197
|
+
|
|
1198
|
+
function soloListener(event) {
|
|
1199
|
+
|
|
1200
|
+
if (!event.data) return
|
|
1201
|
+
const { data } = event
|
|
1202
|
+
if (data?.action == "publishSourceData" && data?.name == "aspect") {
|
|
1203
|
+
if (wiki.debug) console.log('soloListener - source update', {event,data})
|
|
1204
|
+
return
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// only continue if event is from a solo popup.
|
|
1208
|
+
// events from a popup window will have an opener
|
|
1209
|
+
// ensure that the popup window is one of ours
|
|
1210
|
+
|
|
1211
|
+
if (!event.source.opener || event.source.location.pathname !== '/plugins/solo/dialog/') {
|
|
1212
|
+
if (wiki.debug) {console.log('soloListener - not for us', {event})}
|
|
1213
|
+
return
|
|
1214
|
+
}
|
|
1215
|
+
if (wiki.debug) {console.log('soloListener - ours', {event})}
|
|
1216
|
+
|
|
1217
|
+
const { action, keepLineup=false, pageKey=null, title=null, context=null, page=null} = data;
|
|
1218
|
+
|
|
1219
|
+
let $page = null
|
|
1220
|
+
if (pageKey != null) {
|
|
1221
|
+
$page = keepLineup ? null : $('.page').filter((i, el) => $(el).data('key') == pageKey)
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
switch (action) {
|
|
1225
|
+
case 'doInternalLink':
|
|
1226
|
+
wiki.pageHandler.context = context
|
|
1227
|
+
wiki.doInternalLink(title, $page)
|
|
1228
|
+
break
|
|
1229
|
+
case 'showResult':
|
|
1230
|
+
const options = keepLineup ? {} : {$page}
|
|
1231
|
+
wiki.showResult(wiki.newPage(page), options)
|
|
1232
|
+
break
|
|
1233
|
+
default:
|
|
1234
|
+
console.error({ where:'soloListener', message: "unknown action", data })
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
|
|
1080
1239
|
function create(revIndex, data) {
|
|
1081
1240
|
revIndex = +revIndex;
|
|
1082
1241
|
const revJournal = data.journal.slice(0, revIndex + 1);
|