infod 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/infod +37 -0
- data/config.ru +3 -0
- data/infod/Es/fs.rb +154 -0
- data/infod/Es/groonga.rb +101 -0
- data/infod/Es/redis.rb +3 -0
- data/infod/Es/sqlite.rb +3 -0
- data/infod/Es.rb +67 -0
- data/infod/H.rb +29 -0
- data/infod/K.rb +197 -0
- data/infod/N.rb +248 -0
- data/infod/Rb.rb +71 -0
- data/infod/Th/404.rb +55 -0
- data/infod/Th/500.rb +10 -0
- data/infod/Th/GET.rb +132 -0
- data/infod/Th/HEAD.rb +5 -0
- data/infod/Th/PATCH.rb +5 -0
- data/infod/Th/POST.rb +19 -0
- data/infod/Th/local.rb +22 -0
- data/infod/Th/uid.rb +24 -0
- data/infod/Th.rb +110 -0
- data/infod/W/audio.rb +56 -0
- data/infod/W/blog.rb +3 -0
- data/infod/W/cal.rb +110 -0
- data/infod/W/chat.rb +81 -0
- data/infod/W/color.rb +28 -0
- data/infod/W/core.rb +77 -0
- data/infod/W/css.rb +24 -0
- data/infod/W/csv.rb +13 -0
- data/infod/W/du.rb +35 -0
- data/infod/W/edit.rb +8 -0
- data/infod/W/examine/examine.rb +59 -0
- data/infod/W/examine/exhibit.rb +34 -0
- data/infod/W/examine/hist.rb +55 -0
- data/infod/W/examine/history.rb +19 -0
- data/infod/W/examine/normal.rb +31 -0
- data/infod/W/examine/protovis.rb +30 -0
- data/infod/W/examine/sw.rb +114 -0
- data/infod/W/examine/time/graph.rb +86 -0
- data/infod/W/examine/time/line.rb +24 -0
- data/infod/W/feed.rb +116 -0
- data/infod/W/find.rb +24 -0
- data/infod/W/forum.rb +3 -0
- data/infod/W/grep.rb +27 -0
- data/infod/W/html.rb +143 -0
- data/infod/W/image.rb +61 -0
- data/infod/W/json.rb +44 -0
- data/infod/W/kv.rb +66 -0
- data/infod/W/ls.rb +50 -0
- data/infod/W/mail.rb +248 -0
- data/infod/W/page.rb +30 -0
- data/infod/W/pdf.rb +16 -0
- data/infod/W/post.rb +9 -0
- data/infod/W/rdf.rb +32 -0
- data/infod/W/schema.rb +172 -0
- data/infod/W/search.rb +33 -0
- data/infod/W/shell.rb +30 -0
- data/infod/W/source.rb +35 -0
- data/infod/W/table.rb +87 -0
- data/infod/W/text.rb +94 -0
- data/infod/W/tree.rb +26 -0
- data/infod/W/vfs.rb +175 -0
- data/infod/W/wiki.rb +18 -0
- data/infod/W.rb +34 -0
- data/infod/Y.rb +17 -0
- data/infod/infod.rb +13 -0
- data/infod.rb +13 -0
- 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
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: ' '},
|
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,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
|