infod 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/infod.rb +2 -3
  2. data/infod/Es.rb +31 -67
  3. data/infod/{W/source.rb → Es/code.rb} +6 -10
  4. data/infod/Es/css.rb +21 -0
  5. data/infod/{W → Es}/csv.rb +0 -0
  6. data/infod/Es/du.rb +16 -0
  7. data/infod/{W → Es}/feed.rb +13 -11
  8. data/infod/Es/filter.rb +75 -0
  9. data/infod/Es/find.rb +20 -0
  10. data/infod/Es/fs.rb +145 -136
  11. data/infod/Es/glob.rb +22 -0
  12. data/infod/Es/grep.rb +61 -0
  13. data/infod/Es/groonga.rb +47 -56
  14. data/infod/Es/html.rb +271 -0
  15. data/infod/Es/image.rb +114 -0
  16. data/infod/Es/in.rb +68 -0
  17. data/infod/Es/index.rb +183 -0
  18. data/infod/{W → Es}/json.rb +28 -4
  19. data/infod/Es/kv.rb +60 -0
  20. data/infod/Es/ls.rb +58 -0
  21. data/infod/Es/mail.rb +87 -0
  22. data/infod/Es/man.rb +112 -0
  23. data/infod/Es/mime.rb +59 -0
  24. data/infod/Es/out.rb +52 -0
  25. data/infod/{W/page.rb → Es/pager.rb} +7 -3
  26. data/infod/Es/pdf.rb +19 -0
  27. data/infod/Es/rdf.rb +35 -0
  28. data/infod/Es/schema.rb +99 -0
  29. data/infod/Es/search.rb +24 -0
  30. data/infod/Es/sh.rb +21 -0
  31. data/infod/{W → Es}/text.rb +26 -14
  32. data/infod/H.rb +15 -29
  33. data/infod/H/audio.rb +19 -0
  34. data/infod/H/blog.rb +15 -0
  35. data/infod/{W → H}/cal.rb +2 -31
  36. data/infod/H/edit.rb +88 -0
  37. data/infod/{W/examine/examine.rb → H/facets.rb} +17 -17
  38. data/infod/{W → H}/forum.rb +1 -0
  39. data/infod/{W/examine/sw.rb → H/hf.rb} +12 -12
  40. data/infod/H/histogram.rb +78 -0
  41. data/infod/H/mail.rb +92 -0
  42. data/infod/{W/chat.rb → H/microblog.rb} +21 -16
  43. data/infod/H/threads.rb +77 -0
  44. data/infod/H/time.rb +131 -0
  45. data/infod/H/who.rb +30 -0
  46. data/infod/{W → H}/wiki.rb +0 -0
  47. data/infod/K.rb +28 -60
  48. data/infod/N.rb +151 -74
  49. data/infod/Rb.rb +3 -3
  50. data/infod/Th.rb +27 -101
  51. data/infod/Th/404.rb +29 -36
  52. data/infod/Th/500.rb +36 -5
  53. data/infod/Th/GET.rb +48 -118
  54. data/infod/Th/POST.rb +31 -11
  55. data/infod/Th/perf.rb +37 -0
  56. data/infod/Th/util.rb +89 -0
  57. data/infod/Y.rb +24 -7
  58. data/infod/infod.rb +2 -3
  59. metadata +92 -64
  60. data/infod/Es/redis.rb +0 -3
  61. data/infod/Es/sqlite.rb +0 -3
  62. data/infod/Th/local.rb +0 -22
  63. data/infod/W.rb +0 -34
  64. data/infod/W/audio.rb +0 -56
  65. data/infod/W/blog.rb +0 -3
  66. data/infod/W/color.rb +0 -28
  67. data/infod/W/core.rb +0 -77
  68. data/infod/W/css.rb +0 -24
  69. data/infod/W/du.rb +0 -35
  70. data/infod/W/edit.rb +0 -8
  71. data/infod/W/examine/exhibit.rb +0 -34
  72. data/infod/W/examine/hist.rb +0 -55
  73. data/infod/W/examine/history.rb +0 -19
  74. data/infod/W/examine/normal.rb +0 -31
  75. data/infod/W/examine/protovis.rb +0 -30
  76. data/infod/W/examine/time/graph.rb +0 -86
  77. data/infod/W/examine/time/line.rb +0 -24
  78. data/infod/W/find.rb +0 -24
  79. data/infod/W/grep.rb +0 -27
  80. data/infod/W/html.rb +0 -143
  81. data/infod/W/image.rb +0 -61
  82. data/infod/W/kv.rb +0 -66
  83. data/infod/W/ls.rb +0 -50
  84. data/infod/W/mail.rb +0 -248
  85. data/infod/W/pdf.rb +0 -16
  86. data/infod/W/post.rb +0 -9
  87. data/infod/W/rdf.rb +0 -32
  88. data/infod/W/schema.rb +0 -172
  89. data/infod/W/search.rb +0 -33
  90. data/infod/W/shell.rb +0 -30
  91. data/infod/W/table.rb +0 -87
  92. data/infod/W/tree.rb +0 -26
  93. data/infod/W/vfs.rb +0 -175
@@ -1,9 +1,9 @@
1
1
  #watch __FILE__
2
- %w{exhibit hist history normal protovis sw time/graph time/line}.each{|e|require_relative e}
3
2
  class E
4
3
 
5
- fn 'view/examine',->a,m,e{
6
- a=Hash[(a.split ',').map{|a|[a,{}]}] # facets
4
+ fn 'view/facets/main',->a,m,e{
5
+ # facets
6
+ a = Hash[(a.split ',').map{|a|[a,{}]}]
7
7
 
8
8
  # facet stats
9
9
  m.map{|s,r| a.map{|p,_|
@@ -17,27 +17,26 @@ class E
17
17
  n=->o{
18
18
  i[o]||='f'+(c+=1).to_s}
19
19
 
20
- view=F['view/'+ (e.q['ev'] || 'divine') + '/item']
21
-
20
+ view=F['view/'+ (e.q['fv'] || 'divine') + '/resource']
22
21
  resources=->{
23
22
  m.map{|u,r| # each resource
24
23
  a.map{|p,_| # each facet
25
- [n.(p),r[p].do{|o| # value
24
+ [n[p], r[p].do{|o| # value
26
25
  (o.class==Array ? o : [o]).map{|o|
27
- n.(o.to_s) # identifier
26
+ n[o.to_s] # identifier
28
27
  }}].join ' '
29
28
  }.do{|f|
30
29
  [f.map{|o|'<div class="'+o+'">'}, # facet wrapper
31
- view.(r,e), # resource
30
+ view[r,e], # resource
32
31
  (0..f.size-1).map{|c|'</div>'}]}}}
33
32
 
34
- [(H.css'/css/examine'),(H.js'/js/examine'),(H.js'/js/mu'),
33
+ [(H.css'/css/facets'),(H.js'/js/facets'),(H.js'/js/mu'),
35
34
 
36
- a.map{|b,_|{_: :style, class: n.(b)}},
35
+ a.map{|b,_|{_: :style, class: n[b]}},
37
36
 
38
37
  # facet selection
39
38
  {class: :sidebar, c: a.map{|f,v|
40
- {class: :facet, title: f, facet: n.(f), # predicate
39
+ {class: :facet, title: f, facet: n[f], # predicate
41
40
  c: [f.label,
42
41
  v.sort_by{|k,v|v}.reverse.map{|k,v| # sort by popularity
43
42
  k.respond_to?(:label) &&
@@ -45,15 +44,16 @@ class E
45
44
  c: [{_: :span, class: :count, c: v},
46
45
  {_: :span, class: :name, c: k.label}]}}]}}},
47
46
 
48
- (F['view/'+e.q['ev']+'/base']||
47
+ (F['view/'+e.q['fv']+'/base']||
49
48
  ->m,e,r{r.()}).(m,e,resources)]}
50
49
 
51
- fn 'view/examine/selectFacets',->m,e{
52
- [(H.js '/js/examine.selectFacet'),(H.js '/js/mu'),(H.css '/css/examine'),
50
+ fn 'view/facets/select',->m,e{
51
+ [(H.js '/js/facets.select'),(H.js '/js/mu'),(H.css '/css/facets'),
53
52
  E.graphProperties(m).map{|e|[{c: e},' ']},
54
53
  {_: 'button', c: 'Go'}]}
55
54
 
56
- fn 'view/e',->m,e{
57
- e.q['a'].do{|a|Fn 'view/examine',a,m,e} ||
58
- (Fn 'view/examine/selectFacets',m,e)}
55
+ fn 'view/facets',->m,e{
56
+ e.q['a'].do{|a|Fn 'view/facets/main',a,m,e} ||
57
+ (Fn 'view/facets/select',m,e)}
58
+
59
59
  end
@@ -1,3 +1,4 @@
1
1
  class E
2
2
 
3
+
3
4
  end
@@ -1,9 +1,9 @@
1
1
  #watch __FILE__
2
2
 
3
- #curl http://eibispace.de/dx/sked-b11.csv > s.ssv
4
- #wget http://www1.m2.mediacat.ne.jp/binews/bib11.zip http://hfcc.org/data/b11/b11allx2.zip
5
- #unzip bia11.zip ;unzip b11allx2.zip
6
- #xlsx2csv
3
+ # databases of HF frequencies
4
+ # curl http://eibispace.de/dx/sked-b11.csv > s.ssv
5
+ # wget http://www1.m2.mediacat.ne.jp/binews/bib11.zip http://hfcc.org/data/b11/b11allx2.zip
6
+ # unzip *zip
7
7
 
8
8
  class E
9
9
 
@@ -42,8 +42,7 @@ class E
42
42
  e[:clr]={}
43
43
  e[:fmax]=d.map{|_,r|r['FREQ'][0].to_f}.flatten.max||30000.0
44
44
  e[:scale]=100/(e[:fmax] - (d.map{|_,r|r['FREQ'][0].to_f}.flatten.min||0))
45
- [(H.css '/css/sw'),(H.js '/js/sw'),
46
- # [(H.css '/css/sw',true),(H.js '/js/sw',true),
45
+ [(H.css '/css/sw'),(H.js '/js/mu'),(H.js '/js/sw'),
47
46
  {id: :bands,
48
47
  c: bands.map{|meters,bounds|
49
48
  band += 1
@@ -66,9 +65,10 @@ class E
66
65
  fn 'view/sw/item',->r,x{
67
66
  min=->t{t='%04d' % (t.class==String && t.empty? ? 0 : t)
68
67
  t[0..1].to_i*60+t[2..3].to_i}
69
- u=r['UTC'][0].match(/(\d+)-(\d+)/)
70
- b=u[1].to_i; e=u[2].to_i
71
- f=r['FREQ'][0].to_f
68
+ u = r['UTC'][0].to_s.match(/(\d+)-?(\d+)?/)
69
+ b = u[1].to_i.max 2359
70
+ e = (u[2] ? u[2].to_i : b + 30).max 2359
71
+ f = r['FREQ'][0].to_f
72
72
  fi = f.to_i
73
73
  n = fi / 100
74
74
  x[:clr][n] ||= '#%06x' % rand(16777216)
@@ -101,10 +101,10 @@ width:#{(e-b) * 4.0}px;
101
101
  'big'=>[l.scan(/\b[A-Z][A-Z][A-Z]+\b/)],
102
102
  Content=>[l]}
103
103
  l.scan(/\d{4,}/){|d| d=d.to_i
104
- if (d > 2400) && (d < 30000)
104
+ if d < 2400
105
+ m[u]['UTC']=[d]
106
+ elsif d < 30000
105
107
  m[u]['FREQ']=[d]
106
- elsif
107
- m[u]['UTC']=["#{d}-#{d+30}"]
108
108
  end}
109
109
  m.delete u unless m[u].has_keys ['UTC','FREQ']
110
110
  )}
@@ -0,0 +1,78 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ fn 'view/histogram',->d,e{
5
+
6
+ # a :: attribute to chart
7
+ a = e.q['a'].do{|e|e.expand} || Date
8
+
9
+ # bins :: number of buckets
10
+ n = e.q['bins'].do{|b| b.to_f.max(999.0).min(1)} || 64.0
11
+
12
+ # hv :: bin template
13
+ v = F['view/'+e.q['hv']]
14
+
15
+ # construct histogram bins
16
+ (Fn 'view/histogram/bins',d,a,n).do{|h,m|
17
+
18
+ [H.css('/css/hist'),%w{mu hist}.map{|s|H.js('/js/'+s)},
19
+ (Fn 'view/histogram/render',h),{style: "width: 100%; height: 5em"},
20
+ h.map{|b,r|
21
+ # skip empty bins
22
+ r.empty? ? ' ' :
23
+ (x = m[:min] + m[:bw] * b
24
+ from = a == Date ? Time.at(x).to_s : x.to_s
25
+ to = a == Date ? Time.at(x + m[:bw]).to_s : (x + m[:bw]).to_s
26
+ # wrap bin
27
+ { class: 'histBin b'+b.to_s,
28
+ c: [# label bin
29
+ {_: :h3, c: from + ' &rarr; ' + to },
30
+ # bin children view
31
+ (v.(r,e) if v)]})}]}}
32
+
33
+ F['view/h']=F['view/histogram']
34
+
35
+ # Graph, property, numBins -> {bin -> Graph}
36
+ fn 'view/histogram/bins',->m,p,nb{
37
+ h = {}; bw = 0; min = 0; max = 0
38
+ m.map{|u,r|
39
+ # attribute accessor
40
+ r[p]
41
+ }.flatten.do{|v|
42
+ # values
43
+ v = v.compact.map{|v|
44
+ ( p == Date ? v.to_time : v ).to_f}
45
+ max = v.max || 0
46
+ min = v.min || 0
47
+ width = (max-min).do{|w| w.zero? ? 1 : w}
48
+ bw = width / nb }
49
+
50
+ # construct bins
51
+ (0..nb).map{|b|h[b] = {}}
52
+
53
+ # each resource
54
+ m.map{|u,r|
55
+
56
+ # binnable properties
57
+ r[p].do{|v|
58
+ v.each{|v|
59
+ # bin selector
60
+ b = (((p == Date ? v.to_time : v).to_f - min) / bw).floor
61
+
62
+ # append to bin
63
+ h[b][u] = r }}}
64
+
65
+ # histogram model
66
+ [h, {min: min, max: max, bw: bw}] }
67
+
68
+ fn 'view/histogram/render',->h{
69
+ scale = 255 / h.map{|b,r|r.keys.size}.max.do{|m|m.zero? ? 1 : m}.to_f
70
+ bins = h.keys.sort
71
+ ['<table class=histogram><tr>',
72
+ bins.map{|b|
73
+ mag = h[b].keys.size
74
+ {_: :td, class: 'b' + b.to_s,
75
+ style: 'background-color:#'+('%02x' % (255-mag*scale)).do{|x|'ff'+x+x}}},
76
+ '</tr></table>']}
77
+
78
+ end
data/infod/H/mail.rb ADDED
@@ -0,0 +1,92 @@
1
+ class E
2
+
3
+ fn 'view/mail',->d,e{
4
+ title = nil
5
+
6
+ # JS/CSS
7
+ [(H.once e,'mail.js',
8
+ (H.css '/css/mail'), {_: :style, c: "a {background-color: #{E.cs}}"},
9
+ (H.js '/js/mail'),
10
+ (H.once e,:mu,(H.js '/js/mu')),
11
+
12
+ # up to set-overview
13
+ ({_: :a, id: :up, href: e['REQUEST_PATH'] + e.q.merge({'view' => 'page', 'v' => 'threads'}).qs, c: '&uarr;'} if d.keys.size > 2),
14
+
15
+ # collapse/expand quoted content
16
+ {id: :showQuote, c: :quotes, show: :true},{_: :style, id: :quote}),'<br>',
17
+
18
+ # each message
19
+ d.values.map{|m|
20
+
21
+ # content available?
22
+ [m.class == Hash && (m.has_key? E::SIOC+'content') &&
23
+
24
+ {:class => :mail,
25
+
26
+ c: [# message link
27
+ {_: :a, name: m.uri, href: m.url+'?view=base', rel: :raw, title: :raw, c: '&nbsp;'},
28
+
29
+ # To:, From: index search links
30
+ [['sioc:has_creator',Creator],['sioc:addressed_to',To]].map{|a|
31
+ m[a[1]].do{|m|
32
+ m.map{|f| f.respond_to?(:uri) &&
33
+ {_: :a, property: a[0], href: f.url+'?set=indexPO&p='+a[0]+'&view=threads&c=12', c: f.uri}}}},
34
+
35
+ # mailto URI with embedded reply metadata
36
+ (m['/mail/reply_to']||m[Creator]).do{|r| r[0] && r[0].respond_to?(:uri) &&
37
+ {_: :a, title: :reply, c: 'r',
38
+ href: "mailto:#{r[0].uri}?References=<#{m.uri}>&In-Reply-To=<#{m.uri}>&Subject=#{m[Title].join}"}},
39
+
40
+ {class: :timestamp, c: m[Date].do{|d|d.map{|d|d.to_s[0..18]}}}, '<br clear=all>',
41
+
42
+ # content
43
+ {_: :pre,
44
+ c: m[Content].map{|b|
45
+
46
+ # line count
47
+ i = 0
48
+
49
+ # HTML message content
50
+ b.class==String && b.
51
+
52
+ # erase empty quoted lines
53
+ gsub(/^\s*(&gt;)(&gt;|\s)*\n/,"").
54
+
55
+ # each line
56
+ lines.to_a.map{|l|
57
+
58
+ # line identifier
59
+ f = m.uri + ':' + (i+=1).to_s
60
+
61
+ # wrapper
62
+ {_: :span,
63
+
64
+ # is line quoted?
65
+ class: ((l.match /(^\s*(&gt;|On[^\n]+(said|wrote))[^\n]*)\n/) ? 'q' : 'u'), c:
66
+
67
+ # id
68
+ [{_: :a, id: f},
69
+
70
+ # line
71
+ l.chomp,
72
+
73
+ # link
74
+ (l.size > 64 &&
75
+ {_: :a, class: :line, href: '#'+f,c: '&#160;'}),
76
+
77
+ "\n" ]}}}}, # collate lines
78
+
79
+ # title
80
+ m[Title].do{|t|
81
+ # only show if changed from previous
82
+ title != t[0] && (
83
+ title = t[0] # update title
84
+ [{:class => :title, c: t.html, _: :a, href: m.url+'??=thread#'+m.uri},
85
+ '<br clear=all>'])}]}]}]}
86
+
87
+ # set a default view for RFC822 and SIOC types
88
+ [MIMEtype+'message/rfc822',
89
+ SIOCt+'MailMessage'].
90
+ map{|m| F['view/'+m] = F['view/mail'] }
91
+
92
+ end
@@ -1,23 +1,27 @@
1
1
  #watch __FILE__
2
2
  class E
3
3
 
4
- def triplrLog &f
4
+ # sprintf() formats in <https://github.com/infodaemon/www/blob/60a9b5f51cf15d5723afd9172767843d97190d8f/css/i/lotek.theme>
5
+ def triplrIRC &f
5
6
  i=-1
6
- day = dirname.uri.split('/')[-3..-1].join('-')
7
+ day = dirname.uri.split('/')[-3..-1].do{|dp|
8
+ dp.join('-') if dp[0].match(/^\d{4}$/)
9
+ }||''
7
10
  doc = uri.sub '#','%23'
8
- chan = bare
9
- yield doc,'chan',chan
10
- r.scan(/(\d\d):(\d\d) \[[\s@]*([^\(\]]+)[^\]]*\] (.*)/){|m|
11
- s = doc + '#' + doc + ':' + (i+=1).to_s
12
- yield s,Date,day+'T'+m[0]+':'+m[1]+':00'
13
- yield s,'chan',chan
14
- yield s,Creator,m[2]
15
- yield s,Content,m[3].hrefs(true)
16
- yield s,Type,E[SIOCt+'InstantMessage']
17
- yield s,'hasLink',(m[3].match(/http:\//) ? 'true' : 'false')
18
- yield s,'hasNum','true' if m[3].match(/\d/)
19
- } rescue nil
11
+ channel = bare
20
12
  yield doc,Date,day
13
+ r.lines.map{|l|
14
+ l.scan(/(\d\d):(\d\d) \[[\s@]*([^\(\]]+)[^\]]*\] (.*)/){|m|
15
+ s = doc + '#' + doc + ':' + (i+=1).to_s
16
+ yield s, Date, day+'T'+m[0]+':'+m[1]+':00'
17
+ yield s, SIOCt+'ChatChannel', channel
18
+ yield s, Creator, m[2]
19
+ yield s, Content, m[3].hrefs(true)
20
+ yield s, Type, E[SIOCt+'InstantMessage']
21
+ yield s, Type, E[SIOC+'Post']
22
+ yield s, 'hasLink', (m[3].match(/http:\//) ? 'true' : 'false')
23
+ yield s, 'hasNum', 'true' if m[3].match(/\d/)} rescue (puts "skipped #{l}")
24
+ }
21
25
  end
22
26
 
23
27
  def tw g='m'
@@ -30,6 +34,7 @@ class E
30
34
  nokogiri.css('div.tweet').map{|t|
31
35
  s = base + t.css('a.details').attr('href') # subject URI
32
36
  yield s, Type, E[SIOCt+'MicroblogPost']
37
+ yield s, Type, E[SIOC+'Post']
33
38
  yield s, Creator, E(base+'/'+t.css('.username b')[0].inner_text)
34
39
  yield s, SIOC+'name',t.css('.fullname')[0].inner_text
35
40
  yield s, Atom+"/link/image", E(t.css('.avatar')[0].attr('src'))
@@ -58,11 +63,11 @@ class E
58
63
  [{_: :a, id: line},
59
64
  {_: :a, :class => :date, href: r.url, c: r[Date][0].match(/T([0-9:]{5})/)[1]},
60
65
  {_: :span, :class => :nick, c: {_: :a, href: r[Atom+'/link/alternate'].do{|a|a[0].uri}||r.url,
61
- c: [{_: :img, class: :a, src: r[Atom+"/link/image"][0].uri},
66
+ c: [r[Atom+"/link/image"].do{|p| {_: :img, class: :a, src: p[0].uri}},
62
67
  {_: :span, c: r[SIOC+'name']||r[Creator]||'#'}]}},' ',
63
68
  {_: :span, :class => :tw,
64
69
  c: [r[Atom+'/link/media'].do{|a|
65
- a.map{|a|{_: :a, href: r.url, c: {_: :img, src: a.uri}}}},
70
+ a.compact.map{|a|{_: :a, href: r.url, c: {_: :img, src: a.uri}}}},
66
71
  ((r[Title].to_s==r[Content].to_s || r.uri.match(/twitter/)) && '' ||
67
72
  {_: :a, href: r.url, c: r[Title],:class => r[:mail] ? :titleMail : :title}),
68
73
  r[:mail] ? (r[Content].map{|c|c.lines.to_a.grep(/^[^&@_]+$/)[0..21]}) : r[Content],
@@ -0,0 +1,77 @@
1
+ watch __FILE__
2
+ class E
3
+
4
+ fn 'protograph/thread',->d,_,g{
5
+ d.walk SIOC+'reply_of',g
6
+ F['docsID'][g]}
7
+
8
+ fn 'view/threads',->d,env{
9
+
10
+ # CSS
11
+ [(H.css '/css/threads'),{_: :style, c: "body {background-color: ##{rand(2).even? ? 'fff' : '000'}}"},
12
+
13
+ # predicate tafting & search
14
+ (p = env.q['p']
15
+ o = env['uri'].E
16
+ [(H.js '/js/search'),
17
+ {_: :a, class: :rangeP, href: '/@'+p+'?set=indexP&view=page&v=linkPO&c=12', c: {'sioc:addressed_to' => 'to', 'sioc:has_creator' => 'From'}[p] || p}, '&nbsp;',
18
+ {_: :a, class: :rangePO, href: o.url+'?set=indexPO&view=page&v=threads&c=32&p='+p, c: env['uri']},
19
+ {_: :form, action: (URI.escape (p.expand.E.poIndex o).uri),
20
+ c: [{_: :input, name: :set, value: :grep, type: :hidden},
21
+ {_: :input, name: :view,value: :grep, type: :hidden},
22
+ {_: :input, name: :q}
23
+ ]},
24
+ ] if env.q['set']=='indexPO'),
25
+
26
+ '<table>',
27
+
28
+ # group posts by thread name
29
+ d.values.select{|r| r[Type].do{|t| t.map(&:uri).member? SIOC+'Post'}
30
+ }.group_by{|r|
31
+ [*r[Title]][0].do{|t|t.sub(/^[rR][eE][^A-Za-z]./,'')}}.
32
+
33
+ # group by recipient
34
+ group_by{|r,k| k[0].do{|k|
35
+ k[To].do{|o|o.head.uri}}}.map{|e|
36
+
37
+ # group
38
+ c = E.c
39
+ ['<tr><td class=subject>', e[1].map{|t|
40
+
41
+ # thread
42
+ [{_: :a, property: Title, :class => 'thread', style: "border-color:#{c}", href: t[1][0].url+'??=thread',
43
+ c: t[0].to_s.gsub(/[<>]/,'_').gsub(/\[([a-z\-A-Z0-9]+)\]/,'<span class=g>\1</span>')},
44
+
45
+
46
+ # post
47
+ (t[1].size > 1 &&
48
+ ['<br>', t[1].map{|s|
49
+
50
+ # author name and RDFa
51
+ {_: :a, property: Creator, href: s.url+'??=thread#'+s.uri, :class => 'sender', style: 'background-color:'+c,
52
+ c: (s[SIOC+'name']||s[Creator]).do{|n|n[0]}
53
+ }}]),'<br>']},'</td>',
54
+
55
+ {_: :td, class: :group, property: To,
56
+ c: {_: :a, :class => :to, style: 'background-color:'+c, c: e[0] && e[0].split(/@/)[0],
57
+ href: e[0] && e[0].E.url+'?set=indexPO&p=sioc:addressed_to&view=page&v=threads'}},
58
+
59
+ '</tr>']},'</table>',
60
+
61
+ # link to unabbreviated content of post-set
62
+ {_: :a, id: :down, c: '&darr;',
63
+ href: env['REQUEST_PATH'] + env.q.merge({'view'=>'page','views'=>'timegraph,mail','arc'=>'/parent','v'=>'multi','sort'=>'dc:date','reverse'=>true}).qs}]}
64
+
65
+ F["?"] ||= {}
66
+ F["?"].update({'thread' =>{
67
+ 'graph'=>'thread',
68
+ 'sort' => 'dc:date',
69
+ 'reverse' => nil,
70
+ 'view' => 'mail'},
71
+ 'ann' =>{
72
+ 'view'=>'threads',
73
+ 'set'=>'glob',
74
+ 'matchP' => 'dc:title',
75
+ 'match' => /[^a-zA-Z][Aa][Nn][nN]([oO][uU]|[^a-zA-Z])/}})
76
+
77
+ end
data/infod/H/time.rb ADDED
@@ -0,0 +1,131 @@
1
+ #watch __FILE__
2
+
3
+ class Time
4
+ def html; H({_: :time, datetime: iso8601, c: to_s}) end
5
+ end
6
+
7
+ class Object
8
+ def time?
9
+ (self.class == Time) || (self.class == DateTime)
10
+ end
11
+ def to_time
12
+ time? ? self : Time.parse(self)
13
+ rescue
14
+ nil
15
+ end
16
+ end
17
+
18
+ class E
19
+
20
+ # linked-timeline
21
+ fn 'view/timegraph',->g,e{
22
+ i = F['view/timegraph/item']
23
+ Fn 'view/timegraph/base',g,e,->{
24
+ g.map{|u,r|i.(r,e)}}}
25
+
26
+ # timegraph container-element
27
+ fn 'view/timegraph/base',->d,e,c{
28
+ Fn 'filter/timegraph', e.q,d,nil
29
+
30
+ e[:graph] = d
31
+ e[:group] = {}
32
+ e[:color] = E.cs
33
+ h = e.q['height'].do{|h|h.match(/[0-9]+/) && h.to_i.min(1).max(1024) } || '64'
34
+
35
+
36
+ [H.css('/css/timegraph'),{class: :timegraph, style: "height: #{h}em", c: c.()}]}
37
+
38
+ # timegraph entry
39
+ fn 'view/timegraph/item',->r,x{
40
+
41
+ # on resources w x-axis field
42
+ if r[x.q['x'] || Date]
43
+
44
+ labelP = x.q['label'].do{|l|l.expand} || Creator
45
+ label = ([*r[labelP]][0]).do{|l|
46
+ l.respond_to?(:uri) ? l.uri : l.to_s}
47
+ lc = x[:group][label] ||= E.c
48
+ arc = x.q['arc'].do{|a| a.expand }
49
+
50
+ [{style: "top: #{r['x']}%; left: #{r['y']}%",
51
+ c: [{_: :a,
52
+ title: r[Date][0],
53
+ href: r.url,
54
+ class: :label,
55
+ style: "background-color: #{lc}",
56
+ c: label,
57
+ }]},
58
+
59
+ # arc(s)
60
+ {_: :svg, c:
61
+ r[arc].do{|a|a.map{|e|
62
+ # target resource
63
+ x[:graph][e.uri].do{|e|
64
+ # arc path
65
+ {_: :line, class: :arc, stroke: x[:color], 'stroke-dasharray'=>"2,2",
66
+ y1: e['x'].to_s+'%', x1: e['y'].to_s+'%',
67
+ y2: r['x'].to_s+'%', x2: r['y'].to_s+'%'}}}}}]
68
+ end }
69
+
70
+ fn 'filter/timeofday',->e,m,_{
71
+ m.map{|_,r|r[Date].do{|ds| ds.map{|d|
72
+ d = d.to_time
73
+ r['timeofday']=[60 * d.hour + d.min]}}}}
74
+
75
+ fn 'filter/timegraph',->e,m,_{
76
+
77
+ x = e['x'] || Date # x property
78
+ y = e['y'] # y property
79
+
80
+ # 2D values
81
+ vX = m.map{|_,r|r[x]}.flatten.compact.map(&:to_time).map &:to_f
82
+ vY = m.map{|_,r|r[y]}.flatten.compact.map &:to_f
83
+ maxX = vX.max || 0
84
+ minX = vX.min || 0
85
+ maxY = vY.max || 0
86
+ minY = vY.min || 0
87
+
88
+ # scaling-ratio to normalize values to %
89
+ scaleX = 100/((maxX-minX).do{|v|v==0 ? 100 : v}||100)
90
+ scaleY = 100/((maxY-minY).do{|v|v==0 ? 100 : v}||100)
91
+
92
+ # annotate resources
93
+ m.map{|u,r|
94
+ r['x'] = [*r[x]][0].do{|v|(maxX - v.to_time.to_f)*scaleX} || 0
95
+ r['y'] = y.do{|y|[*r[y]][0].do{|v|(maxY - v.to_f)*scaleY} || 0} || rand(100)}}
96
+
97
+ ## SIMILE Timeline
98
+ # http://www.simile-widgets.org/
99
+
100
+ fn Render+'application/timeline',->d,e{
101
+ {dateTimeFormat: 'iso8601',
102
+ events: d.values.map{|r|
103
+ r[Date].do{|d|
104
+ {description: r.uri,
105
+ title: r[Title],
106
+ start: [*d][0],
107
+ link: r.url,
108
+ }}}.compact}.to_json}
109
+
110
+ fn 'head/timeline',->d,e{
111
+ ['<script>var t="'+e['REQUEST_PATH']+e.q.except('view','?').merge({format: 'application/timeline'}).qs+'"</script>',
112
+ (H.js '/js/timeline'),
113
+ (H.js 'http://api.simile-widgets.org/timeline/2.3.1/timeline-api')]}
114
+
115
+ fn 'view/timeline',->d,e{'<div id="tl" class="timeline-default" style="height: 300px;"></div>'}
116
+
117
+ # tripleStream -> tripleStream
118
+ def dateNorm *f
119
+ send(*f){|s,p,o|
120
+ yield *({'CreationDate' => true,
121
+ 'Date' => true,
122
+ RSS+'pubDate' => true,
123
+ Date => true,
124
+ Purl+'dc/elements/1.1/date' => true,
125
+ Atom+'published' => true,
126
+ Atom+'updated' => true
127
+ }[p] ?
128
+ [s,Date,Time.parse(o).utc.iso8601] : [s,p,o])}
129
+ end
130
+
131
+ end