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.
Files changed (2) hide show
  1. package/client/mech.js +203 -44
  2. 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 = (command.match(/\b(steps|days|weeks|months|hubs)\b/) || ['steps'])[0]
278
- const steps = walks(way,state.neighborhood)
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
- item.classList.add('aspect-source')
287
- item.aspectData = () => aspects
288
- if (aspects.length)
289
- state.aspect = [...(state.aspect||[]), {id:item.dataset.id,result:aspects}]
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 = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
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(stop).toLocaleDateString()
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
- for (const info of here) {
855
- const nid = node(info)
856
- for (const link in (info.links||{})) {
857
- const linked = find(link)
858
- if(linked)
859
- graph.addRel('',nid,node(linked))
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
- for(const link in info.links)
874
- hits[link] = (hits[link]||0) + 1
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,10)
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 = `${hub[1]}-${hub[0]}`
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 linked = find(link)
895
- if(linked)
896
- graph.addRel('',nid,node(linked))
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiki-plugin-mech",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "Federated Wiki - Mechanism Scripting Plugin",
5
5
  "keywords": [
6
6
  "mech",