infod 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/bin/infod +37 -0
  2. data/config.ru +3 -0
  3. data/infod/Es/fs.rb +154 -0
  4. data/infod/Es/groonga.rb +101 -0
  5. data/infod/Es/redis.rb +3 -0
  6. data/infod/Es/sqlite.rb +3 -0
  7. data/infod/Es.rb +67 -0
  8. data/infod/H.rb +29 -0
  9. data/infod/K.rb +197 -0
  10. data/infod/N.rb +248 -0
  11. data/infod/Rb.rb +71 -0
  12. data/infod/Th/404.rb +55 -0
  13. data/infod/Th/500.rb +10 -0
  14. data/infod/Th/GET.rb +132 -0
  15. data/infod/Th/HEAD.rb +5 -0
  16. data/infod/Th/PATCH.rb +5 -0
  17. data/infod/Th/POST.rb +19 -0
  18. data/infod/Th/local.rb +22 -0
  19. data/infod/Th/uid.rb +24 -0
  20. data/infod/Th.rb +110 -0
  21. data/infod/W/audio.rb +56 -0
  22. data/infod/W/blog.rb +3 -0
  23. data/infod/W/cal.rb +110 -0
  24. data/infod/W/chat.rb +81 -0
  25. data/infod/W/color.rb +28 -0
  26. data/infod/W/core.rb +77 -0
  27. data/infod/W/css.rb +24 -0
  28. data/infod/W/csv.rb +13 -0
  29. data/infod/W/du.rb +35 -0
  30. data/infod/W/edit.rb +8 -0
  31. data/infod/W/examine/examine.rb +59 -0
  32. data/infod/W/examine/exhibit.rb +34 -0
  33. data/infod/W/examine/hist.rb +55 -0
  34. data/infod/W/examine/history.rb +19 -0
  35. data/infod/W/examine/normal.rb +31 -0
  36. data/infod/W/examine/protovis.rb +30 -0
  37. data/infod/W/examine/sw.rb +114 -0
  38. data/infod/W/examine/time/graph.rb +86 -0
  39. data/infod/W/examine/time/line.rb +24 -0
  40. data/infod/W/feed.rb +116 -0
  41. data/infod/W/find.rb +24 -0
  42. data/infod/W/forum.rb +3 -0
  43. data/infod/W/grep.rb +27 -0
  44. data/infod/W/html.rb +143 -0
  45. data/infod/W/image.rb +61 -0
  46. data/infod/W/json.rb +44 -0
  47. data/infod/W/kv.rb +66 -0
  48. data/infod/W/ls.rb +50 -0
  49. data/infod/W/mail.rb +248 -0
  50. data/infod/W/page.rb +30 -0
  51. data/infod/W/pdf.rb +16 -0
  52. data/infod/W/post.rb +9 -0
  53. data/infod/W/rdf.rb +32 -0
  54. data/infod/W/schema.rb +172 -0
  55. data/infod/W/search.rb +33 -0
  56. data/infod/W/shell.rb +30 -0
  57. data/infod/W/source.rb +35 -0
  58. data/infod/W/table.rb +87 -0
  59. data/infod/W/text.rb +94 -0
  60. data/infod/W/tree.rb +26 -0
  61. data/infod/W/vfs.rb +175 -0
  62. data/infod/W/wiki.rb +18 -0
  63. data/infod/W.rb +34 -0
  64. data/infod/Y.rb +17 -0
  65. data/infod/infod.rb +13 -0
  66. data/infod.rb +13 -0
  67. metadata +129 -0
data/infod/W/audio.rb ADDED
@@ -0,0 +1,56 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ F["?"]||={}; F["?"].update({'af'=>{'graph' => 'audioFind','view' => 'audioplayer'},
5
+ 'a'=>{'set' => 'audio','view' => 'audioplayer'},
6
+ 'v'=>{'set' => 'video','view' => 'audioplayer','video'=>true}})
7
+
8
+ def audioNodes;take.select &:audioNode end;def audioNode;true if ext.match /(aif|wav|flac|mp3|m4a|aac|ogg)/i end
9
+ def videoNodes;take.select &:videoNode end;def videoNode;true if ext.match /(avi|flv|mkv|mpg|mp4|wmv)/i end
10
+
11
+ fn 'set/audio',->d,e,m{d.audioNodes}
12
+ fn 'set/video',->d,e,m{d.videoNodes}
13
+
14
+ fn 'graph/audioFind',->e,q,m{
15
+ t=q['day'] && q['day'].match(/^\d+$/) && '-ctime -'+q['day']
16
+ s=q['size'] && q['size'].match(/^\d+$/) && '-size +'+q['size']+'M'
17
+ r=(q['find'] ? '.*'+q['find'].gsub(/[^a-zA-Z0-9\.\ ]+/,'.*') : '') + '.*.\(aif\|flac\|m4a\|mp3\|aac\|ogg\|wav\)'
18
+ `find #{e.sh} #{t} #{s} -iregex "#{r}"`.lines.map{|p|p.pathToURI.do{|a|m[a.uri]=a}}}
19
+
20
+ fn 'view/audioplayer/item',->m,e{
21
+ {_: :a,class: :entry, href: '#'+m.uri.gsub('%','%25').gsub('#','%23'),
22
+ c: m.E.bare+" \n"}}
23
+
24
+ fn 'view/audioplayer/base',->d,e,c=nil{
25
+ [{_: :span, id: :jump,c: 'Ⴢ   '},{_: :span, id: :rand,r: :true,c: :r},
26
+ (H.once e,:mu,(H.js '/js/mu')),(H.js '/js/audio'),H.css('/css/audio'),H.css('/css/table'),
27
+ {_: e.q.has_key?('video') ? :video : :audio, id: :player, controls: true},
28
+ {id: :data},
29
+ {id: :playlist,
30
+ c: {:class => :playlistItems,c: c.()}}]}
31
+
32
+ fn 'view/audioplayer',->d,e{i=F['view/audioplayer/item']
33
+ Fn 'view/audioplayer/base',d,e,->{
34
+ d.group_by{|u,r|
35
+ u.split('/')[0..-2].join '/'}.map{|b,g|
36
+ [{_: :a, class: :directory, href: b, c: ['<br>',b,'<br>']},"\n",
37
+ g.map{|r|i.(r[1],e)},"\n"]}}}
38
+
39
+ def audioScan
40
+ a('.png').e ||
41
+ (e = ext; d = sh
42
+ un = e.match /^(aif|wav)$/i # compressed?
43
+ a = un ? d : d+'.wav' # analyze
44
+ (case e
45
+ when 'flac'
46
+ `flac -d #{d} -o #{a}`
47
+ when 'mp3'
48
+ `lame --decode #{d} #{a}`
49
+ when 'ogg'
50
+ `oggdec #{d} -o #{a}`
51
+ end) unless un
52
+ `waveformgen #{a} #{d}.png -l`
53
+ `sndfile-spectrogram --no-border #{a} 1280 800 #{d}.spec.png`
54
+ `rm #{a}` unless un) end
55
+
56
+ end
data/infod/W/blog.rb ADDED
@@ -0,0 +1,3 @@
1
+ class E
2
+
3
+ end
data/infod/W/cal.rb ADDED
@@ -0,0 +1,110 @@
1
+ #watch __FILE__
2
+ class E
3
+ # tripleStream -> tripleStream
4
+ def dateNorm *f
5
+ send(*f){|s,p,o|
6
+ yield *({'CreationDate' => true,
7
+ 'Date' => true,
8
+ RSS+'pubDate' => true,
9
+ Date => true,
10
+ Purl+'dc/elements/1.1/date' => true,
11
+ Atom+'published' => true,
12
+ Atom+'updated' => true
13
+ }[p] ?
14
+ [s,
15
+ Date,
16
+ Time.parse(o).utc.iso8601] :[s,p,o])} end
17
+
18
+ fn 'cal/day',->{Time.now.strftime '%Y/%m/%d/'}
19
+ fn 'cal/month',->{Time.now.strftime '%Y/%m/'}
20
+
21
+ # y=day forwards to current day's directory
22
+ %w{day month}.map{|i|
23
+ fn 'req/'+i,->e,r{
24
+ [303,{'Location'=>e.send(i).uri+r.q.except('y').qs},[]]}}
25
+
26
+ def day; as Fn 'cal/day' end
27
+ def month; as Fn 'cal/month' end
28
+
29
+ fn 'graph/cal',->d,e,m{
30
+ DateTime.parse(e['s']||'2011-03-03').
31
+ upto(e['f'].do{|f|DateTime.parse f} || DateTime.now).
32
+ map{|d|m[d.iso8601]={Date=>[d]}}
33
+ m }
34
+
35
+ fn 'table/year',->d{ m={}
36
+ d.map{|u,r|
37
+ r[Date][0].do{|t|
38
+ t = Time.parse t unless t.time?
39
+ o=12*t.year + t.month - 1
40
+ x=o/3
41
+ y=o%3
42
+ m[x] ||= {}
43
+ m[x][y] ||= {}
44
+ m[x][y][u] = r
45
+ m[x][y][:t] = t
46
+ }}
47
+ m }
48
+
49
+ fn 'table/day',->d{ m={}
50
+ d.map{|u,r|
51
+ r[Date][0].do{|t|
52
+ t = Time.parse t unless t.time?
53
+ h=t.hour
54
+ s=h/12
55
+ h12=h%12
56
+ m[h12] ||= {}
57
+ m[h12][s] ||= {}
58
+ m[h12][s][u] = r
59
+ m[h12][s][:t] = t
60
+ }}
61
+ m }
62
+
63
+ fn 'table/month',->d{ m={}
64
+ d.map{|u,r|
65
+ r[Date][0].do{|t|
66
+ t = DateTime.parse t unless t.time?
67
+ w=t.strftime('%Y%W').to_i
68
+ d=t.cwday
69
+ m[w] ||= {}
70
+ m[w][d] ||= {}
71
+ m[w][d][u] = r
72
+ m[w][d][:t] = t
73
+ }}
74
+ m }
75
+
76
+ fn 'view/year',->d,e{[(H.css '/css/cal'),(Fn 'view/t',d,e,'year','month.label')]}
77
+ fn 'view/month',->d,e{Fn 'view/t',d,e,'month','day.label'}
78
+ fn 'view/day',->d,e{Fn 'view/t',d,e,'day','hour'}
79
+
80
+ fn 'view/hour',->d,e{
81
+ t = d.delete :t
82
+ {style: 'background-color:#'+(t.hour % 2 == 1 ? 'ccc' : 'fff'),c:[{_: :b,style:'float:left;font-size:1.3em', c: [t.hour==0 && {_: :span, style: 'font-size:.8em;color:white;background-color:#ff%02xff' % rand(256),c: t.strftime('%e %B')},t.hour]},
83
+ (Fn 'view/'+(e.q['hourv']||'title'),d,e)
84
+ ]}}
85
+
86
+ fn 'view/month.label',->d,e{
87
+ t = d.delete :t
88
+ {class: :month, style: 'background-color:#bb%02xff'%(rand(64)+192), c:
89
+ ['<b>',(t.month==1 && ['<span class=year>',t.year,'</span> ']),t.strftime('%B'),'</b>',(Fn 'view/'+(e.q['monthv']||'month'),d,e)]}}
90
+
91
+ fn 'view/day.label',->d,e{
92
+ t = d.delete :t
93
+ e[:m]||={}
94
+ c=(e[:m][t.month]||='00%02xff' % (64+rand(192)))
95
+ {style: 'padding:.4em;background-color:#'+c,c:[{_: :b,property: Date, c: [t.day, t.day==1 && {_: :span, style: 'font-size:.5em',c: t.strftime('%b')}]},
96
+ (Fn 'view/'+(e.q['dayv']||'title'),d,e)
97
+ ]}}
98
+
99
+ end
100
+
101
+ class Object
102
+ def time?
103
+ (self.class == Time) || (self.class == DateTime)
104
+ end
105
+ def to_time
106
+ time? ? self : Time.parse(self)
107
+ rescue
108
+ nil
109
+ end
110
+ end
data/infod/W/chat.rb ADDED
@@ -0,0 +1,81 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ def triplrLog &f
5
+ i=-1
6
+ day = dirname.uri.split('/')[-3..-1].join('-')
7
+ 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
20
+ yield doc,Date,day
21
+ end
22
+
23
+ def tw g='m'
24
+ no.readlines.shuffle.each_slice(22){|s|
25
+ E['https://twitter.com/search/realtime?q='+s.map{|u|'from:'+u.chomp}.intersperse('+OR+').join].addJSON :triplrTweets, g}
26
+ end
27
+
28
+ def triplrTweets
29
+ base = 'http://twitter.com'
30
+ nokogiri.css('div.tweet').map{|t|
31
+ s = base + t.css('a.details').attr('href') # subject URI
32
+ yield s, Type, E[SIOCt+'MicroblogPost']
33
+ yield s, Creator, E(base+'/'+t.css('.username b')[0].inner_text)
34
+ yield s, SIOC+'name',t.css('.fullname')[0].inner_text
35
+ yield s, Atom+"/link/image", E(t.css('.avatar')[0].attr('src'))
36
+ yield s, Date, Time.at(t.css('[data-time]')[0].attr('data-time').to_i).iso8601
37
+ content = t.css('.tweet-text')[0]
38
+ content.css('a').map{|a|
39
+ u = a.attr 'href'
40
+ a.set_attribute('href',base + u) if u.match /^\//}
41
+ yield s, Content, content.inner_html
42
+ }
43
+ end
44
+
45
+ fn 'head/chat',->d,e{
46
+ t = d.map{|_,r|r[Date]}.flatten.compact.map &:to_time
47
+ c = d.map{|_,r|r[Content]}.compact.size
48
+ [{_: :title, c: "#{c} post#{c!=1 && 's'} #{t.min} -> #{t.max}"},
49
+ (Fn 'head.formats',e)]}
50
+
51
+ fn 'view/chat',->d,e{
52
+ Fn'view/chat/base',d,e,->{d.map{|u,r|Fn 'view/chat/item',r,e}}}
53
+
54
+ fn 'view/chat/item',->r,e{
55
+ line = r.E.frag
56
+ r[Type] && r[Type].map(&:uri).include?(SIOCt+'MailMessage') && r[:mail]=true
57
+ r[Content] &&
58
+ [{_: :a, id: line},
59
+ {_: :a, :class => :date, href: r.url, c: r[Date][0].match(/T([0-9:]{5})/)[1]},
60
+ {_: :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},
62
+ {_: :span, c: r[SIOC+'name']||r[Creator]||'#'}]}},' ',
63
+ {_: :span, :class => :tw,
64
+ c: [r[Atom+'/link/media'].do{|a|
65
+ a.map{|a|{_: :a, href: r.url, c: {_: :img, src: a.uri}}}},
66
+ ((r[Title].to_s==r[Content].to_s || r.uri.match(/twitter/)) && '' ||
67
+ {_: :a, href: r.url, c: r[Title],:class => r[:mail] ? :titleMail : :title}),
68
+ r[:mail] ? (r[Content].map{|c|c.lines.to_a.grep(/^[^&@_]+$/)[0..21]}) : r[Content],
69
+ ]},' ',
70
+ {_: :a, class: :line, href: '#'+line, c: '&nbsp;'},
71
+ "<br>\n"] if r.uri}
72
+
73
+ fn 'view/chat/base',->d,e,c{
74
+ [(H.once e,'chat.head',(H.css '/css/tw'),{_: :style, c: "body, span.nick span, a {background-color: #{E.c}}\n"}),
75
+ {:class => :ch, c: c.()},
76
+ (H.once e,'chat.tail',{id: :b})]}
77
+
78
+ F['view/'+SIOCt+'InstantMessage']=F['view/chat']
79
+ F['view/'+SIOCt+'MicroblogPost']=F['view/chat']
80
+
81
+ end
data/infod/W/color.rb ADDED
@@ -0,0 +1,28 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ def E.c; '#%06x' % rand(16777216)end
5
+
6
+ fn 'view/color',->d,e{
7
+
8
+ n = (e.q['n']||42).to_i.max 255
9
+
10
+ hsv2rgb=->h,s,v{
11
+ i = h.floor
12
+ f = h - i
13
+ p = v * (1 - s)
14
+ q = v * (1 - (s * f))
15
+ t = v * (1 - (s * (1 - f)))
16
+ r,g,b=[[v,t,p],
17
+ [q,v,p],
18
+ [p,v,t],
19
+ [p,q,v],
20
+ [t,p,v],
21
+ [v,p,q]][i].map{|q|q*255.0}}
22
+
23
+ [H.css('/css/color'),
24
+ {style: 'display:table;width:100%',
25
+ c: [(1..10).map{|s| {class: :row, c: (0..n-1).map{|h| {class: :c,style: 'background-color:#%02x%02x%02x' % hsv2rgb.(h/(n/6.0),s/10.0,1.0)}}}},
26
+ (1..10).to_a.reverse.map{|v| {class: :row, c: (0..n-1).map{|h| {class: :c,style: 'background-color:#%02x%02x%02x' % hsv2rgb.(h/(n/6.0),1.0,v/10.0)}}}}]}]}
27
+
28
+ end
data/infod/W/core.rb ADDED
@@ -0,0 +1,77 @@
1
+
2
+ class Hash
3
+ def graph g
4
+ g.merge!({uri=>self})
5
+ end
6
+ %w{graphFromFile q}.map{|m|alias_method m,:graph}
7
+ def mergeGraph g
8
+ g.values.each do |r|
9
+ r.triples do |s,p,o|
10
+ self[s] = {'uri' => s} unless self[s].class == Hash
11
+ self[s][p] ||= []
12
+ self[s][p].push o unless self[s][p].member? o
13
+ end
14
+ end
15
+ self
16
+ end
17
+ def attr p;map{|_,r|r[p].do{|o|return o}}; nil end
18
+ # Hash -> tripleStream
19
+ def triples
20
+ s = uri
21
+ map{|p,o|
22
+ o.class == Array ? o.each{|o| yield s,p,o} : yield(s,p,o) unless p=='uri'}
23
+ end
24
+ end
25
+
26
+ class E
27
+
28
+ # fromStream :: Graph -> tripleStream -> Graph
29
+ def fromStream m,*i
30
+ send(*i) do |s,p,o|
31
+ m[s] = {'uri' => s} unless m[s].class == Hash
32
+ m[s][p] ||= []
33
+ m[s][p].push o unless m[s][p].member? o
34
+ end; m
35
+ end
36
+
37
+ # tripleStream transformer stack
38
+ fn 'graph/|',->e,_,m{e.fromStream m, *_['|'].split(/,/)}
39
+
40
+ def E.graphFromStream s
41
+ fn 'graph/'+s.to_s,->e,_,m{e.fromStream m, s}
42
+ end
43
+
44
+ # placeholder to circumvent empty-graph 404
45
+ fn 'graph/_',->d,_,m{ m[d.uri] = {} }
46
+
47
+ # to_h :: -> Hash
48
+ def to_h
49
+ {'uri'=>uri}
50
+ end
51
+
52
+ def triplrMIMEdispatch &b;mime.do{|mime|
53
+ yield uri,E::Type,(E MIMEtype+mime)
54
+ (MIMEsource[mime]||
55
+ MIMEsource[mime.split(/\//)[0]]).do{|s|
56
+ send *s,&b }}
57
+ end
58
+
59
+ def graphFromFile g={}
60
+ g.mergeGraph r(true) if ext=='e' # JSON -> graph
61
+ [:triplrInode, # filesystem data
62
+ :triplrMIMEdispatch].# format-specific tripleStream
63
+ each{|i| fromStream g,i } # tripleStream -> Graph
64
+ g
65
+ end
66
+
67
+ def graph g={}
68
+ docs.map{|d|d.graphFromFile g} # tripleStream -> graph
69
+ g
70
+ end
71
+
72
+ # render :: MIME, Graph, env -> String
73
+ def render mime, d, e
74
+ E[Render+mime].y d,e
75
+ end
76
+
77
+ end
data/infod/W/css.rb ADDED
@@ -0,0 +1,24 @@
1
+ class E
2
+
3
+ def nokogiri; require 'nokogiri'; Nokogiri::HTML.parse read end
4
+
5
+ def triplrCSSselect
6
+ glob.select(&:f).do{|f|(f.empty? ? [self] : f).map{|r|r.nokogiri.do{|c|c.css(sel).map{|e|
7
+ yield r.uri+'#css:'+sel,Content,e.to_s
8
+ }}}} end
9
+
10
+ fn 'graph/css',->d,e,m{
11
+ d.fromStream m,:triplrCSSselect,e['selector']}
12
+
13
+ MIMEsource['text/css'] ||= [:triplrSourceCode]
14
+
15
+ end
16
+
17
+ class H
18
+
19
+ def H.css a,inline=false
20
+ p=a+'.css'
21
+ inline ? {_: :style, c: p.E.r} :
22
+ {_: :link, href: p, rel: :stylesheet, type: E::MIME[:css]} end
23
+
24
+ end
data/infod/W/csv.rb ADDED
@@ -0,0 +1,13 @@
1
+ class E
2
+
3
+ # CSV -> tripleStream
4
+ def triplrCSV d
5
+ d = @r.q['delim']||d
6
+ open(node).readlines.map{|l|l.chomp.split(d) rescue []}.do{|t|
7
+ t[0].do{|x|
8
+ t[1..-1].each_with_index{|r,ow|r.each_with_index{|v,i|
9
+ yield uri+'#r'+ow.to_s,x[i],v
10
+ }}}}
11
+ end
12
+
13
+ end
data/infod/W/du.rb ADDED
@@ -0,0 +1,35 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ F["?"]||={}
5
+ F["?"].update({'du'=>{
6
+ 'graph' => 'du',
7
+ 'view' => 'protovis',
8
+ 'protovis.data' => 'protovis/du',
9
+ 'protovis.view' => 'starburst'
10
+ }})
11
+
12
+ fn 'graph/du',->e,_,m{
13
+ `du -a #{e.sh}`.lines.to_a[0..-2].map{|p|
14
+ begin
15
+ s,p = p.split /\t/ # size, path
16
+ p = p.pathToURI # path -> URI
17
+ m[p.uri]={'uri'=>p.uri,'size'=>s.to_i}
18
+ rescue
19
+ nil
20
+ end
21
+ }
22
+ m}
23
+
24
+ fn 'protovis/du',->e,c{
25
+ m={} # model
26
+ e.map{|u,r| # each resource
27
+ s = u.sub(/http:..[^\/]+./,'').split '/' # split path
28
+ p = m # pointer
29
+ s[0..-2].map{|s| # each path segment
30
+ p[s] = {} if !p[s] || p[s].class != Hash # create branch
31
+ p = p[s]} # advance pointer down tree
32
+ p[s[-1]]||=r['size']} # append size to leaf
33
+ m}
34
+
35
+ end
data/infod/W/edit.rb ADDED
@@ -0,0 +1,8 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ fn 'view/edit',->d,env{
5
+ {_: :form, name: :editor, c: 'edit'}
6
+ }
7
+
8
+ end
@@ -0,0 +1,59 @@
1
+ #watch __FILE__
2
+ %w{exhibit hist history normal protovis sw time/graph time/line}.each{|e|require_relative e}
3
+ class E
4
+
5
+ fn 'view/examine',->a,m,e{
6
+ a=Hash[(a.split ',').map{|a|[a,{}]}] # facets
7
+
8
+ # facet stats
9
+ m.map{|s,r| a.map{|p,_|
10
+ r[p].do{|o|
11
+ (o.class==Array ? o : [o]).map{|o|
12
+ a[p][o]=(a[p][o]||0)+1}}}}
13
+
14
+
15
+ # facet identifiers
16
+ i={}; c=-1
17
+ n=->o{
18
+ i[o]||='f'+(c+=1).to_s}
19
+
20
+ view=F['view/'+ (e.q['ev'] || 'divine') + '/item']
21
+
22
+ resources=->{
23
+ m.map{|u,r| # each resource
24
+ a.map{|p,_| # each facet
25
+ [n.(p),r[p].do{|o| # value
26
+ (o.class==Array ? o : [o]).map{|o|
27
+ n.(o.to_s) # identifier
28
+ }}].join ' '
29
+ }.do{|f|
30
+ [f.map{|o|'<div class="'+o+'">'}, # facet wrapper
31
+ view.(r,e), # resource
32
+ (0..f.size-1).map{|c|'</div>'}]}}}
33
+
34
+ [(H.css'/css/examine'),(H.js'/js/examine'),(H.js'/js/mu'),
35
+
36
+ a.map{|b,_|{_: :style, class: n.(b)}},
37
+
38
+ # facet selection
39
+ {class: :sidebar, c: a.map{|f,v|
40
+ {class: :facet, title: f, facet: n.(f), # predicate
41
+ c: [f.label,
42
+ v.sort_by{|k,v|v}.reverse.map{|k,v| # sort by popularity
43
+ k.respond_to?(:label) &&
44
+ {facet: n.(k.to_s), # predicate-object tuple
45
+ c: [{_: :span, class: :count, c: v},
46
+ {_: :span, class: :name, c: k.label}]}}]}}},
47
+
48
+ (F['view/'+e.q['ev']+'/base']||
49
+ ->m,e,r{r.()}).(m,e,resources)]}
50
+
51
+ fn 'view/examine/selectFacets',->m,e{
52
+ [(H.js '/js/examine.selectFacet'),(H.js '/js/mu'),(H.css '/css/examine'),
53
+ E.graphProperties(m).map{|e|[{c: e},' ']},
54
+ {_: 'button', c: 'Go'}]}
55
+
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)}
59
+ end
@@ -0,0 +1,34 @@
1
+ class E
2
+ fn Render+'application/json+exhibit',->d,e{
3
+ fields=e.q['f'].do{|f|f.split /,/}
4
+ {items: d.values.map{|r|
5
+ r.keys.-(['uri']).map{|k|
6
+ f=k.frag.do{|f|(f.gsub /\W/,'').downcase} # alphanumeric id restriction
7
+ if !fields || (fields.member? f)
8
+ r[f]=r[k][0].to_s # rename fieldnames, unwrap value
9
+ r.delete k unless f==k # cleanup unless id same as before
10
+ else
11
+ r.delete k
12
+ end}
13
+ r[:label]=r.delete 'uri' # requires label only
14
+ r
15
+ }}.to_json}
16
+
17
+ fn 'head/exhibit',->d,e{
18
+ '<link href="'+e['REQUEST_PATH']+e.q.merge({'format' => 'application/json+exhibit'}).qs+'" type="application/json" rel="exhibit/data" />
19
+ <script src="http://api.simile-widgets.org/exhibit/2.2.0/exhibit-api.js?autoCreate=false" type="text/javascript"></script>
20
+ <script>SimileAjax.jQuery(document).ready(function(){var fDone=function(){
21
+ window.exhibit = Exhibit.create()
22
+ window.exhibit.configureFromDOM()
23
+ database.getAllProperties().map(function(f){
24
+ if (!f.match(/^(label|content)$/)){
25
+ var a=document.createElement("div")
26
+ document.getElementById("sidebar").appendChild(a)
27
+ var x=Exhibit.ListFacet.create({"expression": "."+f},a,exhibit.getUIContext())
28
+ exhibit.setComponent("facet-"+f, x)}})}
29
+ window.database = Exhibit.Database.create()
30
+ window.database.loadDataLinks(fDone)})</script>'}
31
+
32
+ fn 'view/exhibit',->d,e{'<table width="100%"><tr valign="top"><td width="25%" id=sidebar></td><td><div ex:role="view"></div></td></tr></table>'}
33
+
34
+ end
@@ -0,0 +1,55 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ # histogram
5
+ # ?view=h&a=dc:date
6
+ fn 'view/h',->d,e{
7
+ a=e.q['a'].do{|e|e.expand} # a : attribute to chart
8
+ !a && 'attribute required' || (
9
+ n=e.q['bins']&&e.q['bins'].to_f.max(999.0).min(2)||42.0 # n : number of bins
10
+ v=F['view/'+(e&&e.q['hv']||'tab')] # hv : bin template
11
+ (Fn 'u/hist',d,a,n).do{|h| # construct histogram bins
12
+ [H.css('/css/hist'),H.js('/js/hist'),(Fn 'view/hist',h),
13
+ h.map{|b,r|{style: 'display:none',
14
+ :class => 's'+b.to_s.sub(/\./,'_'),
15
+ c: v.(r,e)}}]})}
16
+
17
+ # hist :: Graph, property, numBins -> {bin -> Graph}
18
+ fn 'u/hist',->m,p,nb=32.0{
19
+ h={}; bw=0; max=0; min=0
20
+ m.map{|u,r|
21
+ r[p]
22
+ }.flatten.do{|v|
23
+ v=v.compact.map{|v|v.to_time.to_f}# values
24
+ bw = (v.max - v.min) / nb.min(1)} # bin width
25
+ m.map{|u,r|
26
+ r[p].do{|v|v.each{|v|
27
+ b=(v.to_time.to_f/bw).floor*bw # bin selector
28
+ h[b] ||= {}
29
+ h[b][u]=r}}} # append
30
+ h} # histogram
31
+
32
+ # histTable :: hist -> htmlTable
33
+ fn 'view/hist',->h{
34
+ scale = 255 / h.map{|b,r|r.keys.size}.max.to_f
35
+ b=h.keys.sort
36
+ span=(b.size / 8).min 1
37
+ i=-1
38
+ '<table cellspacing=0 style="width:100%;max-width:100%"><tr class=histLegend>'+
39
+ H(b.select{|b|
40
+ i = i + 1
41
+ i % span == 0
42
+ }.map{|b|
43
+ {_: :td,
44
+ :class => :histLegendPt,
45
+ colspan: span,
46
+ c: {_: :span, :class => :histLabel, c: b > 1 ? b.to_i : b}}})+
47
+ '</tr><tr class=hist>'+
48
+ H(b.map{|b|{_: :td, title:b.to_s.sub(/\./,'_'),style: 'background-color:#'+
49
+ ('%02x' % (255-(h[b].do{|p|
50
+ p.keys.size * scale
51
+ }||0))).do{|x|
52
+ 'ff'+x+x}}})+
53
+ '</tr></table>'}
54
+
55
+ end
@@ -0,0 +1,19 @@
1
+ class E
2
+
3
+ def triplrMozHist
4
+ c = @r.q['c'].match(/[0-9]+/) ? @r.q['c'] : '18'
5
+ q = @r.q['match'].do{|m|m.match(/[a-zA-Z\-_\.\/]+/) && "and p.url like '#{@r.q.has_key?('full')?'':'%'}#{m}%'"}
6
+ t = @r.q['t'].do{|t| "and h.visit_date < #{Time.parse(t).to_f*1e6}"}
7
+
8
+ `sqlite3 -separator "\t" #{sh} "select p.url, r.url, h.visit_date, hr.visit_date from moz_places as p,moz_places as r, moz_historyvisits as h, moz_historyvisits as hr where p.id = h.place_id and r.id = hr.place_id and hr.id = h.from_visit #{q} #{t} order by h.visit_date desc limit #{c}"`.lines.to_a.map{|i|i.split "\t"}.do{|l|
9
+
10
+ yield 'prev','url',@r['REQUEST_PATH']
11
+ yield 'prev','t',Time.at(l[-1][2].to_f/1e6).iso8601
12
+
13
+ l.map{|i|
14
+ yield i[0], Date, Time.at(i[2].to_f/1e6)
15
+ yield i[1], Date, Time.at(i[3].to_f/1e6)
16
+ yield i[0],'referer',i[1].E }
17
+
18
+ } end
19
+ end
@@ -0,0 +1,31 @@
1
+ #watch __FILE__
2
+ class E
3
+ fn 'filter/map',->o,m,_{
4
+ o.except('filter','graph','view').map{|p,n|
5
+ p=p.expand
6
+ n=n.expand
7
+ p!=n &&
8
+ m.values.map{|r|
9
+ r[p].do{|o|
10
+ r[n]=o
11
+ r.delete p }}}}
12
+
13
+ fn 'view/map',->d,e{
14
+ [H.js('/js/normal'),(H.once e,:mu,(H.js '/js/mu')),
15
+ '<style>.b {display:inline-block;font-weight:bold;padding-right:.8em;text-align:right;min-width:12em}
16
+ .exerpt {display:inline-block;max-height:1em;overflow:hidden;max-width:44em;font-size: .9em} </style>',
17
+ {_: :form, c:
18
+ [d.values.map(&:keys).flatten.uniq.-(['uri']).do{|ps|
19
+ ps.map{|p|
20
+ [{class: :b, c: p},{_: :select, name: p, c:
21
+ (ps + [Date,Creator,Content,Title]).map{|q|
22
+ {_: :option, c: q}.
23
+ update(p==q ? {selected: :selected}:{})}},
24
+ {class: :exerpt, c: d.values.map{|r|r[p]}.flatten.uniq.html},
25
+ '<br>']}},
26
+ {_: :input, type: :hidden, name: :view, value: :tab},
27
+ {_: :input, type: :hidden, name: :filter, value: :map},
28
+ {_: :input, type: :submit}
29
+ ]}]}
30
+
31
+ end
@@ -0,0 +1,30 @@
1
+ #watch __FILE__
2
+ class E
3
+
4
+ F["?"]||={}
5
+ F["?"].update({'arc'=>{
6
+ 'view' => 'protovis',
7
+ 'protovis.data' => 'protovis/net',
8
+ 'protovis.view' => 'arc'
9
+ }})
10
+
11
+ fn 'protovis/net',->d,e{
12
+ i=-1
13
+ x={} # uri -> index
14
+ a=[] # arcs
15
+ d.values.each{|r|
16
+ r.triples{|s,p,o|
17
+ o.respond_to?(:uri) &&
18
+ o.uri.do{|o|
19
+ x[s]||=i+=1
20
+ x[o]||=i+=1
21
+ a.push({source: x[s], target: x[o], value: 3})}}}
22
+ {nodes: x.map{|u,_|{nodeName: d[u][Title]||u.label, group: 0}},
23
+ links: a}}
24
+
25
+ fn 'view/protovis',->d,e{
26
+ [H.js('/js/protovis/protovis-r3.2'),{id: :fig},
27
+ {_: :script,type: 'text/javascript+protovis',
28
+ c: ['var d='+(Fn e.q['protovis.data'],d,e).to_json,
29
+ E['http://'+e['SERVER_NAME']+'/js/protovis/'+e.q['protovis.view']+'.js'].r].cr}]}
30
+ end